0%

python爬虫学习

1 爬虫简介

什么是爬虫:通过编写程序,模拟浏览器上网,然后让其去互联网上抓取数据的过程。
爬虫的价值:抓取互联网上的数据,有了大量的数据,就如同有了一个数据银行一样,下一步做的就是如何将这些爬取的数据产品化、商品化。

2 爬虫合法性探究

1)在法律中是不被禁止
2)具有违法风险
3)善意爬虫,恶意爬虫

爬虫带来的风险可以体现在如下2方面:
1)爬虫干扰了被访问网站的正常运营
2)爬虫抓取了受到法律保护的特定类型的数据或信息

如何正确使用爬虫:
1)时常优化自己的程序,避免干扰被访问网站的正常运行
2)在使用、传播爬取到的数据时,审查抓取到的内容,如果发现了涉及到用户隐私、商业机密等敏感内容需要及时停止爬取或传播

3 爬虫初始深入

3.1 爬虫在使用场景中的分类

3.1.1 通用爬虫

抓取系统重要组成部分。抓取的是一整张页面数据。

3.1.2 聚焦爬虫

是建立在通用爬虫的基础之上。抓取的是页面中特定的局部内容。

3.1.3 增量式爬虫

监测网站中数据更新的情况。只会抓取网站中最新更新出来的数据。

3.2 反爬机制

门户网站,可以通过制定相应的策略或者技术手段,防止爬虫程序进行网站数据的爬取。

3.3 反反爬策略

爬虫程序可以通过制定相关的策略或技术手段,破解门户网站中具备的反爬机制,从而可以获取门户网站数据。

3.4 robots.txt协议

君子协议。规定了网站中哪些数据可以被爬虫爬取,哪些数据不可以被爬取。
例如访问淘宝的robots协议

4 http和https协议

4.1 http协议

http就是服务器和客户端进行数据交互的一种形式。

4.1.1 常用请求头信息

1)User-Agent:请求载体的身份标识。(Google浏览器F12进入network模块,随便打开一个数据包)
2)Connection:请求完毕后,是断开连接还是保持连接

4.1.2 常用响应头信息

Content-Type:服务器响应回客户端的数据类型。

4.2 https

安全的超文本传输协议。

4.2.1 加密方式

1)对称密钥加密
2)非对称密钥加密
3)证书密钥加密

5 requests模块

5.1 python网络请求的相关模块

python中涉及网络请求的模块:
1)urllib模块(古老)
2)requests模块(普遍使用)

requests模块:python中原生的一款基于网络请求的模块,功能非常强大,简单便捷,效率极高。
作用:模拟浏览器发请求。

5.2 如何使用requests模块

5.2.1 requests模块的编码流程

也是浏览器发请求的过程
1)指定url
2)发起请求
3)获取响应数据
4)持久化存储(将数据存储到本地或是数据库)

5.2.2 爬取搜狗首页的页面数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 需求:爬取搜狗首页的页面数据
import requests
if __name__ == "__main__":
# 1 指定url
url = 'https://www.sogou.com/'
# 2 发起请求
# get方法会返回一个响应对象
response = requests.get(url=url)
# 3 获取响应数据,text返回的是字符串形式的响应数据
page_text = response.text
print(page_text)
# 4 持久化存储
with open('./爬虫实战/sougou.html','w',encoding='utf-8') as fp:
fp.write(page_text)
print('爬取数据结束')

6 requests巩固深入案例介绍

需求一:爬取搜狗指定词条对应的搜索结果页面,也就是一个简易网页采集器
需求二:破解百度翻译
需求三:爬取豆瓣电影分类排行榜 https://movie.douban.com/ 中的电影详情数据
需求四:爬取肯德基餐厅查询http://www.kfc.com.cn/kfccda/index.aspx 中指定地点的餐厅数据
数据五:爬取国家药品监督管理总局基于中华人民共和国化妆品生产许可证相关数据http://scxk.nmpa.gov.cn:81/xk/

7 requests模块巩固深入案例之简易网页采集器

7.1 UA检测和UA伪装

UA检测
UA:User-Agent(请求载体的身份表示)
UA伪装:门户网站的服务器会检测对应请求的载体身份标识,如果检测到请求的载体身份标识为某一款浏览器,说明该请求是一个正常的请求。但是,如果检测到请求的载体身份标识不是基于某一款浏览器的,则表示该请求为不正常的请求(爬虫),服务器很有可能拒绝该次请求。
UA伪装
让爬虫对应的请求载体身份标识伪装成某一款浏览器。

7.2 网页采集器源码

由于信息头中的信息不完整,导致遇到百度安全验证,没有成功爬取到数据,解决方法

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
import requests
if __name__ == "__main__":
#UA伪装:将对应的User-Agent封装到一个字典中
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
"Connection": "keep-alive",
"Accept-Encoding": "gzip, deflate, br",
"Host": "www.baidu.com",
# 需要更换Cookie
"Cookie": 'BIDUPSID=DBD0225D9BF8DEB79CA1C7887D1732C0; PSTM=1649423443; H_WISE_SIDS=110085_127969_174443_184716_189755_194085_194519_194529_195342_196427_197242_197471_197711_197956_199022_199568_201108_201193_201700_202284_202651_203190_203310_203504_203517_204031_204123_204255_204726_204779_204864_204905_205218_205235_205379_205548_205568_205909_206008_206167_207021_207178_207235_207473_207616_207670_207715_207716_207831_207863_207887_208055_208115_208225_208267_208271_208312_208344_208494_208716_208721_208757_208758_208771_208773_208790_209115_209295_209310_209455_209473_209486_209519_209568_209630_209674_209704_209748_209817; BDUSS=EZXNm9pcHpIaXhoRkx5bnR-LTVmR0RFc0Nvb3JNVEJMR3dBUEZaZDVKSnVFYmxpRVFBQUFBJCQAAAAAAAAAAAEAAAAqpKZT1u648MvvwcEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG6EkWJuhJFiU; BDUSS_BFESS=EZXNm9pcHpIaXhoRkx5bnR-LTVmR0RFc0Nvb3JNVEJMR3dBUEZaZDVKSnVFYmxpRVFBQUFBJCQAAAAAAAAAAAEAAAAqpKZT1u648MvvwcEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG6EkWJuhJFiU; H_WISE_SIDS_BFESS=110085_127969_174443_184716_189755_194085_194519_194529_195342_196427_197242_197471_197711_197956_199022_199568_201108_201193_201700_202284_202651_203190_203310_203504_203517_204031_204123_204255_204726_204779_204864_204905_205218_205235_205379_205548_205568_205909_206008_206167_207021_207178_207235_207473_207616_207670_207715_207716_207831_207863_207887_208055_208115_208225_208267_208271_208312_208344_208494_208716_208721_208757_208758_208771_208773_208790_209115_209295_209310_209455_209473_209486_209519_209568_209630_209674_209704_209748_209817; BAIDUID=03278F35A48ECC64A1E2A882027FEF73:SL=0:NR=50:FG=1; ZFY=rClc:AfuGhuwKe3KzODcg9SGhZEHh:BE4dmMgsfV:A:A3R8:C; BAIDUID_BFESS=03278F35A48ECC64A1E2A882027FEF73:SL=0:NR=50:FG=1; channel=google; H_PS_PSSID=37156_36557_37357_36885_34813_37403_37398_36806_36789_37444_26350_37284_37364_37457; BA_HECTOR=8l0k8h8k040h04012k0k9a151hjkvq21b; Hm_lvt_55b574651fcae74b0a9f1cf9c8d7c93a=1662514438,1662601585,1663160955,1664790536; ab_sr=1.0.1_NTM0ZGRhNjNjMmE2YTcyZTMxNTJkZGY1MTdmNGFkYzhiZWQzYjE3ODRmYmE4ZmY2OTQ4NjA2N2RlMzMzMDg0NDNhZDE1NzJhNTdkMDU3MTEzYmIyM2RlZDY0ZmFjMjA1Njk1NWY4YmExODJjYmFjODY5MDg3NjlkZjIwY2JhMDAwN2RlMWFlYWJiYTAwMjQ2MDhjZDkwY2IyNmQyYTg0MTczMWI0MDM4MDJmZmQzNmE1NDQ0MGZmZTU1ZmI1Mzhm; baikeVisitId=56320696-49f7-4da7-8213-0e20716f3b1a; RT="z=1&dm=baidu.com&si=cr3jt70ia88&ss=l8slat3w&sl=3&tt=4x3&bcn=https://fclog.baidu.com/log/weirwood?type=perf"; Hm_lpvt_55b574651fcae74b0a9f1cf9c8d7c93a=1664792369'}
# 1 指定url
url = 'https://www.baidu.com/s?ie=UTF-8&'
# 处理url携带的参数:封装到字典中
kw = input('enter a word:')
param = {'wd':kw}
# 2 发起请求 对指定的url发起的请求对应的url是携带参数的,并且请求过程中处理了参数
# headers为数据包的头部信息
response = requests.get(url=url,params=param,headers=headers,timeout=10)
# 用于解决乱码
response.encoding = response.apparent_encoding
# 3 获取响应数据,text返回的是字符串形式的响应数据
page_text = response.text
# 4 持久化存储
filename = './爬虫实战/'+kw+'.html'
with open(filename,'w',encoding='utf-8') as fp:
fp.write(page_text)
print('爬取数据结束')

8 requests模块巩固深入案例之破解百度翻译

ajax可以实现在不刷新页面的前提下提交请求。
经过使用google开发者工具分析(想看kw需要点击headers模块旁边的payload模块),得到了百度翻译的相关要点如下:
1)对应请求是一个post请求(携带了参数)
2)响应数据是一组json数据

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
import requests
import json
if __name__ == "__main__":
# 1 进行UA伪装
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
"Accept": "application/json, text/javascript, */*; q=0.01",
"Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
"Connection": "keep-alive",
"Accept-Encoding": "gzip, deflate, br",
# 需要更换Cookie
"Cookie": 'BIDUPSID=DBD0225D9BF8DEB79CA1C7887D1732C0; PSTM=1649423443; H_WISE_SIDS=110085_127969_174443_184716_189755_194085_194519_194529_195342_196427_197242_197471_197711_197956_199022_199568_201108_201193_201700_202284_202651_203190_203310_203504_203517_204031_204123_204255_204726_204779_204864_204905_205218_205235_205379_205548_205568_205909_206008_206167_207021_207178_207235_207473_207616_207670_207715_207716_207831_207863_207887_208055_208115_208225_208267_208271_208312_208344_208494_208716_208721_208757_208758_208771_208773_208790_209115_209295_209310_209455_209473_209486_209519_209568_209630_209674_209704_209748_209817; BDUSS=EZXNm9pcHpIaXhoRkx5bnR-LTVmR0RFc0Nvb3JNVEJMR3dBUEZaZDVKSnVFYmxpRVFBQUFBJCQAAAAAAAAAAAEAAAAqpKZT1u648MvvwcEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG6EkWJuhJFiU; BDUSS_BFESS=EZXNm9pcHpIaXhoRkx5bnR-LTVmR0RFc0Nvb3JNVEJMR3dBUEZaZDVKSnVFYmxpRVFBQUFBJCQAAAAAAAAAAAEAAAAqpKZT1u648MvvwcEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG6EkWJuhJFiU; H_WISE_SIDS_BFESS=110085_127969_174443_184716_189755_194085_194519_194529_195342_196427_197242_197471_197711_197956_199022_199568_201108_201193_201700_202284_202651_203190_203310_203504_203517_204031_204123_204255_204726_204779_204864_204905_205218_205235_205379_205548_205568_205909_206008_206167_207021_207178_207235_207473_207616_207670_207715_207716_207831_207863_207887_208055_208115_208225_208267_208271_208312_208344_208494_208716_208721_208757_208758_208771_208773_208790_209115_209295_209310_209455_209473_209486_209519_209568_209630_209674_209704_209748_209817; BAIDUID=03278F35A48ECC64A1E2A882027FEF73:SL=0:NR=50:FG=1; ZFY=rClc:AfuGhuwKe3KzODcg9SGhZEHh:BE4dmMgsfV:A:A3R8:C; BAIDUID_BFESS=03278F35A48ECC64A1E2A882027FEF73:SL=0:NR=50:FG=1; H_PS_PSSID=37156_36557_37357_36885_34813_37403_37398_36806_36789_37444_26350_37284_37364_37457; BA_HECTOR=8l0k8h8k040h04012k0k9a151hjkvq21b; RT="z=1&dm=baidu.com&si=v42m1kx0xcg&ss=l8srkef6&sl=0&tt=0&bcn=https://fclog.baidu.com/log/weirwood?type=perf&ul=1u2&hd=1xr"; APPGUIDE_10_0_2=1; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1664801076; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1664801137; ab_sr=1.0.1_NDFlOWRiZjY2ODY2MmNhNWU0NDkwMzEwODg2NmZiNDhhYTcwNzgyMzM0MDBlOWViZGU4Y2JkNDY1YThkYjA3ODQ2MGUyYjZhYWEwZmQxYjdiN2UxYzBkZmVmNTdkNzY5NmQ5OTQ3NzYyMDY2Nzc0MzhlMDVhMDZjZmNjZGVhMDNmMjYzZWM4OTRiODE5YTI5MTY2YTA4NWQ2NDBlYzBmMTJjMGUxOWY3Y2QxMjMzNGJmZTlkMjA0NDlhNGIxMDEz'}
# 2 指定url
post_url = 'https://fanyi.baidu.com/sug'
# 3 post请求参数处理(同get请求一致)
word = input('enter a word:')
data = {'kw':word}
# 4 请求发送
response = requests.post(url=post_url,data=data,headers=headers)
# 5 获取响应数据:json方法返回的是obj(如果确认响应数据是json类型的,才可以使用json,从content-Type获得)
dic_obj = response.json()
# 6 持久化存储
filename = './爬虫实战/'+word+'.json'
fp = open(filename,'w',encoding='utf-8')
json.dump(dic_obj,fp=fp,ensure_ascii=False)
print("数据爬取结束")

在实战的过程中注意根据抓包工具分析的结果灵活地修改header信息。

9 requests模块巩固深入案例之豆瓣电影

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import json

if __name__ == '__main__':
url = 'https://movie.douban.com/j/chart/top_list?'
param = {'type':'24',
'interval_id':'100:90',
'action':'',
# 表示开始的电影编号
'start':'0',
# 表示一次请求取出的电影个数
'limit':'20'}
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
'Accept':"*/*",
'Accept-Language':"zh-CN,zh;q=0.9",
'Connection':"keep-alive",
'Accept-Encoding':"gzip, deflate, br",
'Cookie':'bid=1uxw3rydo9g; ll="118375"; _ga=GA1.2.1418048783.1664847198; _gid=GA1.2.1522197539.1664847198; Hm_lvt_19fc7b106453f97b6a84d64302f21a04=1664847600; Hm_lpvt_19fc7b106453f97b6a84d64302f21a04=1664847600; _ck_desktop_mode=1; vmode=pc; _pk_ref.100001.4cf6=["","",1664847659,"https://m.douban.com/"]; _pk_ses.100001.4cf6=*; ap_v=0,6.0; __utma=30149280.1418048783.1664847198.1664847664.1664847664.1; __utmb=30149280.0.10.1664847664; __utmc=30149280; __utmz=30149280.1664847664.1.1.utmcsr=m.douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/; __utma=223695111.1418048783.1664847198.1664847664.1664847664.1; __utmb=223695111.0.10.1664847664; __utmc=223695111; __utmz=223695111.1664847664.1.1.utmcsr=m.douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/; _pk_id.100001.4cf6=704fb404cefb3879.1664847659.1.1664847668.1664847659.'}
response = requests.get(url=url,params=param,headers=headers)
# 从抓包工具得到response最外面是一个[]
list_response = response.json()
fp = open('./爬虫实战/douban.json','w',encoding='utf-8')
json.dump(list_response,fp = fp,ensure_ascii=False)
print("爬取结束")

10 实战练习

注意这个url不能删减。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import json

if __name__ == '__main__':
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
keyword = input('input a city:')
param = {'cname':'',
'pid':'',
'keyword':keyword,
'pageIndex':'1',
'pageSize':'10'}
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
'Accept':'appliacation/json, text/javascript, */*;q=0.01',
'Accept-Language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Accept-Encoding':'gzip, deflate, br',
'Cookie':'ASP.NET_SessionId=qhtoewbc1sro4z45gs1kqldj; route-cell=ksa; Hm_lvt_1039f1218e57655b6677f30913227148=1664851918; Hm_lpvt_1039f1218e57655b6677f30913227148=1664851918; SERVERID=a8ef50e74cdb73da974d5f9b427a5159|1664852839|1664851918'}
data = requests.post(url=url,params=param,headers=headers)
data_json = data.json()
fp = open('./爬虫实战/kfc.json','w',encoding='utf-8')
json.dump(data_json,fp=fp,ensure_ascii=False)
print("爬取结束")

11 数据解析概述

11.1 聚焦爬虫

聚焦爬虫:爬取页面中指定的页面内容。可能会用到数据解析。
编码流程:
1)指定url
2)发起请求
3)获取响应数据
4)数据解析
5)持久化存储

11.2 数据解析分类

1)正则表达式
2)bs4
3)xpath(通用性较强)

11.3 数据解析原理

解析数据可能存在的地方:
1)标签的中间。
2)对应标签的属性中。

解析的步骤:
1)进行指定标签的定位
2)标签或者标签对应的属性中存储的数据进行提取。(解析)

12 图片数据爬取

12.1 正则表达式

一、限定符
‘?’表示’d’需要出现0次或是1次,’d’可有可无,used?可以和use匹配,也可以和used匹配。
‘表示’b’可以出现0次或多次。ab\c可以和ac匹配,也可以和abbbc匹配。
‘+表示’b’可以出现1次或是多次。ab+c可以和abc匹配,也可以和abbbc匹配。
使用{}来限制允许字符出现的次数。如果要限制b出现的次数在2到6之间,使用ab{2,6}c即可。如果是要限制b出现的次数在2次以上,那么可以使用ab{2,}c来实现。
‘()’可以用来限制{}内数字的作用域,如我们需要在字符串中ab多次出现,可以使用(ab)+来匹配具有ababab特征的字符串。
二、或运算
‘a (cat|dog)’可以用来匹配”a cat”或者”a dog”这样的字符串。
三、字符类
‘[]’内的内容代表要求匹配的字符只能取自于它们。例如’[abc]+’会与’aabbcc’匹配,但是不会与’dog’匹配。
同时我们可以在’[]’内指定字符的范围,例如’[a-z]+’代表所有小写英文字符。’[a-zA-Z]+’表示所有的英文字符。’[a-zA-Z0-9]+’代表所有英文字符和数字。
‘^’表示非,例如’0-9+’表示所有的非数字字符(包括换行符)。
四、元字符
正则表达式中预先定义好了一系列常用的字符类型,比如数字、空白符、单词开头、结尾等等。正则表达式中的大多数元字符都以反斜杠开头:
1)’\d’代表数字字符。’\d’+等价于’[0-9]+’。
2)’\w’代表单词字符。也就是所用的英文字符、数字和下划线。
3)’\s’代表空白符(包含Tab和换行符)。
4)’\D’代表非数字字符。
5)’\W’代表非单词字符。
6)’\S’代表非空白字符。
‘.’在正则表达式中是一个特殊字符,它代表任意字符,但不包含换行符。
‘^’匹配行首,’$’匹配行尾。例如’^a’只会匹配位于行首的a,’a$’只会匹配位于行尾的a。
五、贪婪与懒惰匹配
在*,+,{}去匹配字符串的时候,默认会去匹配尽可能多的字符。例如我们使用’<.+>’会匹配到完整的html语句。但是如果我们在+之后加上一个?,则会将正则表达式中默认的贪婪匹配切换为懒惰匹配,’<.+?>’就可以匹配到所有位于’<>’内的标签。
举个栗子1:匹配所有的RGB颜色值需要使用的语句是’#[a-fA-F0-9]{6}\b’,其中’\b’表示单词字符的边界,这样可以避免这里的文本也被识别成RGB颜色值。
举个栗子2:IPV4地址匹配需要使用的语句是’\d+.\d+.\d+.\d+’,’.‘是用反斜杠做转义,其作用是匹配字典。但是上述语句没有限定IP地址的范围是0-255,我们可以这样来限定:’25[0-5]|2[0-4]\d|[01]?\d\d?’。这是分情况讨论了三位中每一位数字的情况,同时IP地址有可能只有两位或是一位,所以我们在表达式中加了问号来表示这种情况。在实际匹配中,我们使用’上述表达式{3}上述表达式’就可以完成没有前导0的IP地址的匹配。
实战源码:

1
2
key = 'saas and sas and saaas'
re.findall('sa{1,2}s',key)

问题在于我们如何爬取网页上的图片数据呢?

1
2
3
4
5
6
7
8
9
10
import requests

if __name__ == '__main__':
# 如何获取图片数据
url = 'https://monica-y.github.io/images/快速排序.png'
# content返回的是二进制形式的图片数据
# text(字符串),content(二进制),json(对象)
img_data = requests.get(url=url).content
with open('./爬虫实战/blog.png','wb') as fp:
fp.write(img_data)

13 正则解析案例1

爬取百度百科词条诸葛亮对应页面下的所有图案。

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
import requests
import re
import os

if __name__ == '__main__':
url = 'https://baike.baidu.com/item/诸葛亮/21048'
# UA伪装
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Accept-Encoding':'gzip, deflate, br',
'Cookie':'zhishiTopicRequestTime=1664883739169; BAIKE_SHITONG={"data":"df15e34a625616491a92c5f4ebee725d2b8040b74ceff0e59f4bd890e7110ab4111cf9176d8512c95b6b008a45b3508b1e58bd011af8dbf7d35f78fc2c089ae299e42fe5c840bc39c68acb020d9a3e973d22e51741597a44ff12bfa6fbcf237c","key_id":"10","sign":"7721bcc8"}; BIDUPSID=DBD0225D9BF8DEB79CA1C7887D1732C0; PSTM=1649423443; H_WISE_SIDS=110085_127969_174443_184716_189755_194085_194519_194529_195342_196427_197242_197471_197711_197956_199022_199568_201108_201193_201700_202284_202651_203190_203310_203504_203517_204031_204123_204255_204726_204779_204864_204905_205218_205235_205379_205548_205568_205909_206008_206167_207021_207178_207235_207473_207616_207670_207715_207716_207831_207863_207887_208055_208115_208225_208267_208271_208312_208344_208494_208716_208721_208757_208758_208771_208773_208790_209115_209295_209310_209455_209473_209486_209519_209568_209630_209674_209704_209748_209817; BDUSS=EZXNm9pcHpIaXhoRkx5bnR-LTVmR0RFc0Nvb3JNVEJMR3dBUEZaZDVKSnVFYmxpRVFBQUFBJCQAAAAAAAAAAAEAAAAqpKZT1u648MvvwcEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG6EkWJuhJFiU; BDUSS_BFESS=EZXNm9pcHpIaXhoRkx5bnR-LTVmR0RFc0Nvb3JNVEJMR3dBUEZaZDVKSnVFYmxpRVFBQUFBJCQAAAAAAAAAAAEAAAAqpKZT1u648MvvwcEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG6EkWJuhJFiU; H_WISE_SIDS_BFESS=110085_127969_174443_184716_189755_194085_194519_194529_195342_196427_197242_197471_197711_197956_199022_199568_201108_201193_201700_202284_202651_203190_203310_203504_203517_204031_204123_204255_204726_204779_204864_204905_205218_205235_205379_205548_205568_205909_206008_206167_207021_207178_207235_207473_207616_207670_207715_207716_207831_207863_207887_208055_208115_208225_208267_208271_208312_208344_208494_208716_208721_208757_208758_208771_208773_208790_209115_209295_209310_209455_209473_209486_209519_209568_209630_209674_209704_209748_209817; BAIDUID=03278F35A48ECC64A1E2A882027FEF73:SL=0:NR=50:FG=1; ZFY=rClc:AfuGhuwKe3KzODcg9SGhZEHh:BE4dmMgsfV:A:A3R8:C; BAIDUID_BFESS=03278F35A48ECC64A1E2A882027FEF73:SL=0:NR=50:FG=1; channel=google; BAIDU_SSP_lcr=https://www.google.com/; BAIDU_WISE_UID=wapp_1664883581852_651; Hm_lvt_55b574651fcae74b0a9f1cf9c8d7c93a=1662601585,1663160955,1664790536,1664883739; Hm_lpvt_55b574651fcae74b0a9f1cf9c8d7c93a=1664883739; baikeVisitId=b4b0fd02-5d25-4b56-be74-13522b7d6d99; ab_sr=1.0.1_ODgzYTBmOGUwMTU0OGMwYmQ3MmE0Njc1NDYyMzEzMTEzOGM1NTE3MjhiNTUxNWEwZDBkOWFhZTQ0OTAzZjIwM2ExYmFlYWEwMzU0ZmJjNTRlYzk4OTA2ZjBjZWMyYTc0ZGE0Mjc5YzZlMjMxYzllOTJiOTk2YTRhY2Q0MzUyYzBiZmIyOTdlZDgxNTc0OWRjOGNiZGYyYjE2MzkyNDE3ZDRkMDljN2UwODQ5YTY0NWE2NDMzNDFkZmRmMDlmMDMy; RT="z=1&dm=baidu.com&si=784f20e0-98cd-4c2d-98c3-01ce1b209343&ss=l8u4p2h9&sl=5&tt=bg2&bcn=https://fclog.baidu.com/log/weirwood?type=perf&ld=3igr&cl=5kvg&ul=417ak"'}
# 使用通用爬虫对url对应的一整张页面进行爬取
page_text = requests.get(url=url,headers=header).text
# fp = open('./爬虫实战/zgl.html','w',encoding='utf-8')
# fp.write(page_text.text)
# 使用聚焦爬虫将页面中所有的图片进行解析/提取
# <div class="lemma-picture J-lemma-picture text-pic layout-right" style="width:220px; float: right;">
# ex = '<div class = "content">.*?<img class src="(.*?)" alt.*?</div>'
ex = 'data-src="(.*?)"'
# re.S单行匹配,re.M多行匹配
img_src = re.findall(ex,page_text,re.S)
# print(img_src)
# 创建一个文件夹,保存所有的图片
if not os.path.exists('./爬虫实战/诸葛亮图片'):
os.mkdir('./爬虫实战/诸葛亮图片')
cnt = 'a'
for src in img_src:
# 请求到了图片的二进制数据
img_data = requests.get(url=src,headers=header).content
# 生成图片名称
filename = cnt+'.webp'
cnt = cnt+'a'
img_path = './爬虫实战/诸葛亮图片/'+filename
with open(img_path,'wb') as fp:
fp.write(img_data)
print(filename,'下载成功')

14 正则解析案例2

在url中设置变量的方法。

1
2
3
4
5
6
url = 'https://www.qiushibaike.com/pic/page/%d/?s=5184961'
# (1,36)是页码范围
for pageNum in(1,36):
# 对应页码的url
new_url = format(url%pageNum)
# '{}'.format()

15 bs4解析概述

15.1 bs4解析原理

python特有的解析方式。
bs4数据解析的原理:
1)实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中 。
2)通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取。

15.2 beautifulSoup实例化

1
from bs4 import BeautifulSoup

1.将本地的html文档中的数据加载到该对象中。

1
2
fp = open('./爬虫实战/zgl.html','r',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')

2.将互联网上获取的页面源码加载到该对象中。

1
2
page_text = response.text
soup = BeautifulSoup(page_text,'lxml')

16 bs4解析具体使用详解

16.1 beautifulSoup提供的用于数据解析的方法和属性

soup.tagName 返回的是html中第一次出现的tagName标签。
soup.find():
1.soup.find(tagName)等同于soup.div
2.属性定位 soup.find(‘a’,class_/id/attr=”declare-details”)
soup.find_all(‘tagName’):返回符合要求的所有标签(列表)
soup.select():
1.select(‘某种选择器(id,class,标签…选择器)’),返回的是一个列表。
2.soup.select(‘.header > .layout > .declare-wrap’),层级选择器,’>’表示的是一个层级,中间不可以跨层级。
3.soup.select(‘.header .declare-wrap’),空格可以表示多个层级。

16.2 获取标签之间的文本数据

soup.a.text/string/get_text(),具体使用时注意加上下标:

1
soup.select('.header .declare')[0].text

text/get_text():可以获得该标签下所有的文本内容,即也会获取到子标签的文本内容。
string:只可以获取该标签下直系的文本内容。

16.3 获取标签中属性值

soup.a[‘href’],使用实例如下:

1
soup.select('.header .declare-details')[0]['href']

17 bs4解析案例实战

从诗词名句网上爬取一整部的三国演义小说。遇到的问题有:
1.乱码问题。

1
2
# 用于解决乱码
page_text.encoding = page_text.apparent_encoding

2.NoneType无法调用get_text()函数。

1
if text_tag is None:continue

完整源码:

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
import requests
from bs4 import BeautifulSoup
import time

if __name__ == '__main__':
# 对首页的页面数据进行爬取
url = 'http://www.purepen.com/sgyy/'
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.9',
'Host':'www.purepen.com',
'Proxy-Connection':'keep-alive'}
page_text = requests.get(url=url,headers=headers)
# 用于解决乱码
page_text.encoding = page_text.apparent_encoding
page_text = page_text.text
# print(page_text.text)
# 在首页中解析出章节的标题和详情页的url
# 1 实例化BeautifulSoup的对象,需要将页面源码数据加载到该对象中
soup = BeautifulSoup(page_text,'lxml')
a_list = soup.select('table a')
fp = open('./爬虫实战/三国.txt','w',encoding='utf-8')
# print(a_list)
################debug#####################
# detail_url = 'http://www.purepen.com/sgyy/'+a_list[113]['href']
# # 对详情页发起请求,解析出章节内容
# detail_page_text = requests.get(url=detail_url,headers=headers)
# detail_page_text.encoding = detail_page_text.apparent_encoding
# detail_page_text = detail_page_text.text
# # print(detail_page_text)
# detail_soup = BeautifulSoup(detail_page_text,'lxml')
# print(detail_soup.find('font',face='宋体'))
# text_tag = detail_soup.find('font',face='宋体')
# detail_text = text_tag.get_text()
# print(detail_text)
################debug#####################
for elem in a_list:
title = elem.string
detail_url = 'http://www.purepen.com/sgyy/'+elem['href']
# 对详情页发起请求,解析出章节内容
detail_page_text = requests.get(url=detail_url,headers=headers)
detail_page_text.encoding = detail_page_text.apparent_encoding
detail_page_text = detail_page_text.text
# 解析出详情页中相关的章节内容
detail_soup = BeautifulSoup(detail_page_text,'lxml')
text_tag = detail_soup.find('font',face='宋体')
if text_tag is None:continue
detail_text = text_tag.get_text()
# 持久化处理
fp.write(title+':'+detail_text+'\n')
print(title,'爬取成功')

18 xpath解析基础1

xpath解析:最常用且便捷高效的一种解析方式,最为通用。

18.1 xpath解析原理

1.实例化一个etree的对象,且需要被解析的页面源码加载到对象中。
2.调用etree对象中的xpath方法结合xpath表达式实现标签的定位和内容的捕获。

18.2 实例化etree对象

1
from lxml import etree

1.将本地的html文档中的源码数据加载到etree对象中。

1
etree.parse(filePath)

2.可以将从互联网上获取的源码数据加载到该对象中。

1
etree.HTML(page_text)

19 xpath解析基础2

19.1 xpath(‘xpath’表达式)

1.’/‘:表示的是从根节点开始的定位。同时在表达式中一个’/‘表示的是下一个层级。
2.’//‘表示多个层级。’//div’的功能是找到源码中所有的div标签。
3.属性定位的方法’//meta[@name=”description”]’。通用格式是’tag[@attrName=”attrValue”]’
4.索引定位方法’//head/meta[4]’,注意list的索引不是从0开始的,而是从1开始的。

19.2 获取文本

1.’//head/title/text()’:只能获取标签的直系文本内容。
2.’//div[@class=”declare-wrap”]//text()’:或许标签的所有文本内容,包括子标签间。

19.3 获取属性

‘//div[@class=”declare-wrap”]//a/@href’:获取a标签的href属性。格式为’/@attrName’。

20 xpath实战-58二手房

爬取58二手房中相关的房源信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
from lxml import etree

if __name__ == '__main__':
# 爬取页面源码数据
url = "https://bj.58.com/ershoufang/?PGTID=0d100000-0000-104c-1acb-491d6609ee01&ClickID=5"
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
'Accept':'*/*',
'Accept-encoding':'gzip, deflate, br',
'Accept-Language':'zh-CN,zh;q=0.9',
'Cookie':'id58=CocIjmM+yhVLn+gHLrwxAg==; city=bj; 58home=bj; 58tj_uuid=7af828cc-f973-4d2a-95cc-032f488f20ed; new_uv=1; utm_source=; spm=; init_refer=; wmda_uuid=9602a30531f6d88e561302899c8331c5; wmda_new_uuid=1; wmda_session_id_11187958619315=1665059392160-5e12be9b-c21d-fc2a; wmda_visited_projects=;11187958619315; als=0; aQQ_ajkguid=3ECFB47A-B884-4B18-A6FC-00AD1E6F0EBF; sessid=86E82895-80AB-499C-8785-9D16B1732620; ajk-appVersion=; ctid=1; fzq_h=73060fa7621b58b5bcf02b70bcd33cc8_1665059430571_b164ddb5b7b44a74a51e25c0a1aed939_1908977250; new_session=0; JSESSIONID=CE4EEE52A5CDDBC2C8B8C8AEF109F233; xxzl_cid=7e2fb22a84144e3096678912e56d6361; xxzl_deviceid=COpSCAAH8vpuxSiIZJzXkVfavvuoH9JGOt4O6xM2vDWU7FsK18PgIMjWZ65e6QyW'}
# page_text = requests.get(url=url,headers=header).text
# fp = open('./爬虫实战/58.html','w',encoding='utf-8')
# fp.write(page_text)
# 数据解析
filepath = './爬虫实战/58.html'
tree = etree.parse(filepath,parser=etree.HTMLParser(encoding='utf-8'))
title_list = tree.xpath('//h3/@title')
fp = open('./爬虫实战/58.txt','w',encoding='utf-8')
for elem in title_list:
fp.write(elem+'\n')
print(title_list)
# for elem in title_list:
# elem.xpath('./@title') .表示从elem所在层级开始,而不是从根开始

21 xpath解析案例-4k图片解析下载

视频中解决中文乱码的问题的方法:

1
img_name.encode('iso-8859-1').decode('gbk')

实验源码

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
import requests
from lxml import etree
import os

if __name__ == '__main__':
url = 'http://pic.netbian.com/4kmeinv/'
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-encoding':'gzip, deflate',
'Accept-language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Cookie':'__yjs_duid=1_60a75cb337fd0b67a092fac41e69956d1665137066790; zkhanecookieclassrecord=,54,; Hm_lvt_c59f2e992a863c2744e1ba985abaea6c=1665137069; Hm_lpvt_c59f2e992a863c2744e1ba985abaea6c=1665137069',
'Host':'pic.netbian.com'}
# 这里需要解决一下乱码问题,也可以直接requests.get().content
response = requests.get(url=url,headers=header)
# 设置编码格式建议用:response.encoding=response.apparent_encoding
response.encoding=response.apparent_encoding
page_text = response.text
# print(page_text)
# 数据解析 src的属性值和alt的属性值
tree = etree.HTML(page_text)
li_list = tree.xpath('//ul[@class="clearfix"]/li')
# print(li_list)
# 创建一个文件夹
if not os.path.exists('./爬虫实战/美女'):
os.mkdir('./爬虫实战/美女')
for li in li_list:
img_src = 'http://pic.netbian.com'+li.xpath('.//img/@src')[0]
img_name = li.xpath('.//img/@alt')[0]+'.jpg'
# print(img_name,img_src)
img_data = requests.get(url=img_src,headers=header).content
img_path = './爬虫实战/美女/'+img_name
with open(img_path,'wb') as fp:
fp.write(img_data)
print(img_name,'爬取成功')

22 xpath解析案例-全国城市名称爬取

1
2
3
4
5
6
7
8
# //div[@class="bottom"]/ul/li/a 热门城市a标签的层级关系
# //div[@class="bottom"]/ul/div[2]/li/a 全部城市a标签的层级关系
# 两个表达式用|连接
tree.xpath('//div[@class="bottom"]/ul/li/a|//div[@class="bottom"]/ul/div[2]/li/a')
all_city_name = []
for a in a_list:
city_name = a.xpath('./text()')
all_city_names.append(city_name)

完整源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
from lxml import etree
import os

if __name__ == '__main__':
url = 'https://www.aqistudy.cn/historydata/'
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-encoding':'gzip, deflate, br',
'Accept-language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Cookie':'Hm_lvt_6088e7f72f5a363447d4bafe03026db8=1665149440; Hm_lpvt_6088e7f72f5a363447d4bafe03026db8=1665149440',
'Host':'www.aqistudy.cn'}
page_text = requests.get(url=url,headers=header)
page_text.encoding = page_text.apparent_encoding
page_text = page_text.text
tree = etree.HTML(page_text,parser=etree.HTMLParser(encoding='utf-8'))
hot_list = tree.xpath('//ul[@class="unstyled"]//a/text()')
all_city_name = []
all_city_name.append(hot_list)
print(all_city_name)

23 验证码识别简介

反爬机制:验证码。识别验证码图片中的数据,用于模拟登陆操作。
识别验证码的操作:
1.人工肉眼识别。(不推荐)
2.第三方自动识别。(推荐,但没钱)