Python复杂操作—实践:爬虫与数据分析统计、文本词频统计

类Class

定义类

定义一个类Animals:

  • _ init( ) _定义构造函数,与其他面向对象语言不同的是,Python语言中,会明确地把代表自身实例的self作为第一个参数传入
  • 创建一个实例化对象 cat,init( )方法接收参数
  • 使用点号 . 来访问对象的属性

继承/调用类

通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。

JSON序列化与反序列化

JSON序列化

  • json.dumps 用于将 Python 对象编码成 JSON 字符串

JSON反序列化

  • json.loads 用于解码 JSON 数据,该函数返回 Python 字段的数据类型

Pandas库

介绍

  • Pandas是Python第三方库,提供高性能易用数据类型和分析工具

  • Pandas基于Numpy实现,常与Numpy和matplotlib一同使用

  • Pandas中有两大核心数据结构: Series(一维数据)DataFrame (多特征数据,既有行索引,又有列索

    引)

使用

Series

  • Series是一种类似于一维数组的对 象,它由一维数组(各种Numpy数据类型)以及一组与之相关的数据标签(即索引)组成

  • Series的创建:

    • 使用Python数组创建
    • 使用Numpy数组创建
    • 使用Python字典创建

    注意: 与字典不同的是:Series允许索引重复

  • Series的字符串表现形式为:索引在左边,值在右边

  • 如果没有为数据指定索引,则自动创建一个0到N-1(N为数据的长度)的整数型索引

  • 可以通过Series的values和index属性获取其数组表示形式和索引对象

  • 与普通Numpy数组相比,可以通过索引的方式选取Series中的单个或一组值
  • Series中最重要的一个功能是:它会在算术运算中自动对齐不同索引的数据

DataFrame

  • DataFrame是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)
  • DataFrame既有行索引也有列索引,它可以被看做由Series组成的字典(共用同一个索引)
  • 跟其他类似的数据结构相比(如R语言的data.frame),DataFrame中面向行和面向列的操作基本上是平衡的
  • 构成DataFrame的方法很多,最常用的一种是直接传入一个由等长列表或Numpy数组组成的字典
  • DataFrame结果会自动加上索引(跟Series一样),且全部会被有序排列
  • 如果指定了列顺序,则DataFrame的列就会按照指定顺序进行排列
  • 跟原Series一样,如果传入的列在数据中找不到,就会产生NAN值
  • 列可以通过赋值的方式进行修改:例如,给那个空的“delt”列赋上一个标量值或一组值
  • 将列表或数组赋值给某个列时,其长度必须跟DataFrame的长度相匹配
  • 如果赋值的是一个Series,就会精确匹配DataFrame的索引,所有空位都将被填上缺失值
  • 为不存在的列赋值会创建出一个新列
  • 关键字del用于删除列
  • 嵌套字典(也就是字典的字典)传给DataFrame,它就会被解释为:外层字典的键作为列,内层键则作为行索引
  • 也可以对上述结果进行转置
  • 如果设置了DataFrame的index和columns的name属性,则这些信息也会被显示出来
  • 跟Series一样,values属性也会以二维ndarray的形式返回DataFrame中的数据
  • 如果DataFrame各列的数据类型不同,则数组的数据类型就会选用能兼容所有列的数据类型
  • Pandas的索引对象负责管理轴标签和其他元数据(比如轴名称等)
  • 构建DataFrame时,所用到的任何数组或其他序列的标签都会被转换成一个Index
  • Index对象是不可修改的,因此用户不能对其进行修改
  • Pandas的每个索引都有一些方法和属性,它们可用于设置逻辑回答有关该索引包含的数据的常见问题。下表中列出了Index的方法和属性

实战

明星图片爬取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import requests
import os
import urllib

class GetImage():
def __init__(self,keyword='大雁',paginator=1):
# self.url: 链接头
self.url = 'http://image.baidu.com/search/acjson?'
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT\
10.0; WOW64) AppleWebKit/537.36\
(KHTML, like Gecko) Chrome/69.0.\
3497.81 Safari/537.36'}
self.headers_image = {
'User-Agent': 'Mozilla/5.0 (Windows\
NT 10.0; WOW64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/69.0.\
3497.81 Safari/537.36',
'Referer': 'http://image.baidu.com/\
search/index?tn=baiduimage&ipn=r&\
ct=201326592&cl=2&lm=-1&st=-1&\
fm=result&fr=&sf=1&fmq=1557124645631_R&\
pv=&ic=&nc=1&z=&hd=1&latest=0&copyright\
=0&se=1&showtab=0&fb=0&width=&height=\
&face=0&istype=2&ie=utf-8&sid=&word=%\
E8%83%A1%E6%AD%8C'}
self.keyword = keyword # 定义关键词
self.paginator = paginator # 定义要爬取的页数

def get_param(self):
# 将中文关键词转换为符合规则的编码
keyword = urllib.parse.quote(self.keyword)
params = []
# 为爬取的每页链接定制参数
for i in range(1, self.paginator + 1):
params.append(
'tn=resultjson_com&ipn=rj&ct=201326592&is=&\
fp=result&queryWord={}&cl=2&lm=-1&ie=utf-8&o\
e=utf-8&adpicid=&st=-1&z=&ic=&hd=1&latest=0&\
copyright=0&word={}&s=&se=&tab=&width=&height\
=&face=0&istype=2&qc=&nc=1&fr=&expermode=&for\
ce=&cg=star&pn={}&rn=30&gsm=78&1557125391211\
='.format(keyword, keyword, 30 * i))
return params # 返回链接参数

def get_urls(self, params):
urls = []
for param in params:
# 拼接每页的链接
urls.append(self.url + param)
return urls # 返回每页链接

def get_image_url(self, urls):
image_url = []
for url in urls:
json_data = requests.get(url, headers=self.headers).json()
json_data = json_data.get('data')
for i in json_data:
if i:
image_url.append(i.get('thumbURL'))
return image_url

def get_image(self, image_url):
"""
根据图片url,在本地目录下新建一个以搜索关键字命名的文件夹,然后将每一个图片存入。
:param image_url:
:return:
"""
cwd = os.getcwd()
file_name = os.path.join(cwd, self.keyword)
if not os.path.exists(self.keyword):
os.mkdir(file_name)
for index, url in enumerate(image_url, start=1):
with open(file_name+'/{}_0.jpg'.format(index), 'wb') as f:
f.write(requests.get(url, headers=self.headers_image).content)
if index != 0 and index % 30 == 0:
print('第{}页下载完成'.format(index/30))

def __call__(self, *args, **kwargs):
params = self.get_param() # 获取链接参数
urls = self.get_urls(params)
image_url = self.get_image_url(urls)
self.get_image(image_url)

if __name__ == '__main__':
spider = GetImage('明星', 3)
spider()
# spider = GetImage('雕', 3)
# spider()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 首先我们要导入相关的包
# request:提供爬虫相关的接口函数
# json:主要负责处理字典类型数据在字符串与字典之间进行转换
import requests
import json
import os

# 直接使用程序爬取网络数据会被网站识别出来,然后封禁该IP,导致数据爬
# 取中断,所以我们需要首先将程序访问页面伪装成浏览器访问页面
# User-Agent:定义一个真实浏览器的代理名称,表明自己的身份(是哪种浏览器),本demo为谷歌浏览器
# Accept:告诉WEB服务器自己接受什么介质类型,*/* 表示任何类型
# Referer:浏览器向WEB服务器表明自己是从哪个网页URL获得点击当前请求中的网址/URL
# Connection:表示是否需要持久连接
# Accept-Language:浏览器申明自己接收的语言
# Accept-Encoding:浏览器申明自己接收的编码方法,通常指定压缩方法,是
# 否支持压缩,支持什么压缩方法(gzip,deflate)
def getPicinfo(url):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36",
"Accept": "*/*",
"Referer": "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E4%B8%AD%E5%9B%BD%E8%89%BA%E4%BA%BA&fenlei=256&rsv_pq=cf6f24c500067b9f&rsv_t=c2e724FZlGF9fJYeo9ZV1I0edbhV0Z04aYY%2Fn6U7qaUoH%2B0WbUiKdOr8JO4&rqlang=cn&rsv_dl=ib&rsv_enter=1&rsv_sug3=15&rsv_sug1=6&rsv_sug7=101",
"Host": "sp0.baidu.com",
"Connection": "keep-alive",
"Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,zh-TW;q=0.6",
"Accept-Encoding": "gzip, deflate"
}
# 根据url,使用get()方法获取页面内容,返回相应
response = requests.get(url,headers)
# 成功访问了页面
if response.status_code == 200:
return response.text
# 没有成功访问页面,返回None
return None

#图片存放地址
Download_dir='picture'
if os.path.exists(Download_dir)==False:
os.mkdir(Download_dir)

pn_num=1 # 爬取多少页
rn_num=10 # 每页多少个图片

for k in range(pn_num): # for循环,每次爬取一页
url="https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?resource_id=28266&from_mid=1&&format=json&ie=utf-8&oe=utf-8&query=%E4%B8%AD%E5%9B%BD%E8%89%BA%E4%BA%BA&sort_key=&sort_type=1&stat0=&stat1=&stat2=&stat3=&pn="+str(k)+"&rn="+str(rn_num)+"&_=1613785351574"

res = getPicinfo(url) # 调用函数,获取每一页内容
json_str=json.loads(res) # 将获取的文本格式转化为字典格式
figs=json_str['data'][0]['result']

for i in figs: # for循环读取每一张图片的名字
name=i['ename']
img_url=i['pic_4n_78'] # img_url:图片地址
img_res=requests.get(img_url) # 读取图片所在页面内容
if img_res.status_code==200:
ext_str_splits=img_res.headers['Content-Type'].split('/')
ext=ext_str_splits[-1] # 索引-1指向列表倒数第一个元素
fname=name+"."+ext
# 保存图片
open(os.path.join(Download_dir,fname), 'wb' ).write(img_res.content)
print(name,img_url,"saved")

科比职业生涯数据爬取与分析

导包

1
2
3
4
5
6
7
8
9
10
11
import requests
from bs4 import BeautifulSoup
import csv
import matplotlib.pyplot as plt
import pandas as pd

# 设置显示中文
plt.rcParams['font.sans-serif'] = ['simhei'] # 指定默认字体
# plt.rcParams['font.sans-serif']=['Fangsong'] # 用来显示中文标签
plt.rcParams['axes.unicode_minus']=False # 用来显示负号
plt.rcParams['figure.dpi'] = 100 # 每英寸点数

数据爬取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#coding=utf-8
'''
Created on 2022年04月07日
@author: liuwq
'''

def getKobeList(code):
url = "http://www.stat-nba.com/player/stat_box/195_"+code+".html"
response = requests.get(url)
resKobe = response.text
return resKobe

#获取kobe历史数据
def getRow(resKobe,code):
soup = BeautifulSoup(resKobe,"html.parser")
table = soup.find_all(id='stat_box_avg')

#表头
header = []
if code == "season":
header = ["赛季","出场","首发","时间","投篮","命中","出手","三分","命中","出手","罚球","命中","出手","篮板","前场","后场","助攻","抢断","盖帽","失误","犯规","得分","胜","负"]
if code == "playoff":
header = ["赛季","出场","时间","投篮","命中","出手","三分","命中","出手","罚球","命中","出手","篮板","前场","后场","助攻","抢断","盖帽","失误","犯规","得分","胜","负"]
if code == "allstar":
header = ["赛季","首发","时间","投篮","命中","出手","三分","命中","出手","罚球","命中","出手","篮板","前场","后场","助攻","抢断","盖帽","失误","犯规","得分"]

#数据
rows = [];
rows.append(header)

for tr in table[0].find_all("tr",class_="sort"):
row = []
for td in tr.find_all("td"):
rank = td.get("rank")
if rank != "LAL" and rank != None:
row.append(td.get_text())
rows.append(row)
return rows

#写入csv文件,rows为数据,dir为写入文件路径
def writeCsv(rows,dir):
with open(dir, 'w', encoding='utf-8-sig', newline='') as f:
writer = csv.writer(f)
writer.writerows(rows)

#常规赛数据
resKobe = getKobeList("season")
rows = getRow(resKobe,"season")
#print(rows)
writeCsv(rows,"season.csv")
print("season.csv saved")

#季后赛数据
resKobe = getKobeList("playoff")
rows = getRow(resKobe,"playoff")
#print(rows)
writeCsv(rows,"playoff.csv")
print("playoff.csv saved")

#全明星数据
resKobe = getKobeList("allstar")
rows = getRow(resKobe,"allstar")
#print(rows)
writeCsv(rows,"star.csv")
print("star.csv saved")

数据分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# 篮板、助攻、得分
def show_score(game_name='season', item='篮板', plot_name='line'):

# game_name: season, playoff, star
# item: 篮板,助攻,得分
# plot_name: line,bar

file_name = game_name+'.csv'
data = pd.read_csv(file_name)
X= data['赛季'].values.tolist()
X.reverse()
if item=='all':
Y1 = data['篮板'].values.tolist()
Y2 = data['助攻'].values.tolist()
Y3 = data['得分'].values.tolist()
Y1.reverse()
Y2.reverse()
Y3.reverse()
else:
Y = data[item].values.tolist()
Y.reverse()
if plot_name=='line':
if item=='all':
plt.plot(X,Y1,c='r',linestyle="-.")
plt.plot(X,Y2,c='g',linestyle="--")
plt.plot(X,Y3,c='b',linestyle="-")
legend=['篮板','助攻','得分']
else:
plt.plot(X,Y,c='g',linestyle="-")
legend=[item]
elif plot_name=='bar':
#facecolor:表面的颜色;edgecolor:边框的颜色
if item=='all':
fig = plt.figure(figsize=(15,5))
ax1 = plt.subplot(131)
plt.bar(X,Y1,facecolor = '#9999ff',edgecolor = 'white')
plt.legend(['篮板'])
plt.title('Kobe职业生涯数据分析:'+game_name)
plt.xticks(rotation=60)
plt.ylabel('篮板')

ax2 = plt.subplot(132)
plt.bar(X,Y2,facecolor = '#999900',edgecolor = 'white')
plt.legend(['助攻'])
plt.title('Kobe职业生涯数据分析:'+game_name)
plt.xticks(rotation=60)
plt.ylabel('助攻')

ax3 = plt.subplot(133)
plt.bar(X,Y3,facecolor = '#9988ff',edgecolor = 'white')
legend=['得分']
else:
plt.bar(X,Y,facecolor = '#9900ff',edgecolor = 'white')
legend=[item]
else:
return

plt.legend(legend)
plt.title('Kobe职业生涯数据分析:'+game_name)
plt.xticks(rotation=60)
plt.xlabel('赛季')
if item!='all':
plt.ylabel(item)
else:
plt.ylabel('得分')
plt.savefig('work/Kobe职业生涯数据分析_{}_{}.png'.format(game_name,item))
plt.show()

# 篮板、助攻、得分
game_name = 'season'
for game_name in ['season','playoff','star']:
show_score(game_name=game_name, item='篮板', plot_name='bar')
show_score(game_name=game_name, item='助攻', plot_name='bar')
show_score(game_name=game_name, item='得分', plot_name='bar')
show_score(game_name=game_name, item='篮板', plot_name='line')
show_score(game_name=game_name, item='助攻', plot_name='line')
show_score(game_name=game_name, item='得分', plot_name='line')
show_score(game_name=game_name, item='all', plot_name='bar')
show_score(game_name=game_name, item='all', plot_name='line')