高级网络请求模块
Requests-Cache的安装与测试
Requests-Cache模块是requests模块的一个扩展功能,用于为requests模块提供持久化缓存支持。如果requests模块向一个URL发送重复请求时,Requests-Cache模块将会自动判断当前的网络请求时否产生了缓存。如果已经产生了缓存,就会从缓存中读取数据作为响应内容。如果没有缓存就会向服务器发送网络请求,获取服务器所返回的响应内容。使用Requests-Cache模块可以减少网络资源避免重复请求的次数,这样可以变相地躲避一些反爬机制。
说 明
无论是否使用了Anaconda,都需要单独安装Requests-Cache模块,因为Anaconda中并不包含该模块。
Python 3.9.9 (v3.9.9:ccb0e6a345, Nov 15 2021, 13:29:20)
[Clang 6.0 (clang-600.0.57)] on darwin
>>>import requests_cache
>>>version = requests_cache.__version__
>>>print(version)
0.9.1
缓存的应用
调用install_cache()函数实现Requests-Cache的请求,语法格式如下:
install_cache(cache_name='cache', backend=None, expire_after=None, allowable_codes=(200, ), allowable_methods=('GET', ), session_factory=<class 'requests_cache.core.CachedSession'>,
**backend_options)
Install_cache()函数中包含了多个参数,每个参数的含义如下:
§ cache_name: 表示缓存文件的名称,默认为cache
§ backend: 表示设置缓存的存储机制,默认为None,表示默认使用sqlite进行存储
§ expire_after: 表示设置缓存的有效时间,默认为None,表示永久有效
§ allowable_codes:表示设置状态码,默认为200
§ allowable_methods:表示设置请求方式,默认为GET,表示只有GET请求才可以生产缓存
§ session_factory: 表示设置缓存执行的对象,需要实现CachedSession类
§ **backend_options: 如果缓存的存储方式为sqlite、mongo、redis数据库,该参数表示设置数据库的连接方式
在使用install_cache()函数实现请求缓存时,一般情况下时不需要单独设置任何参数的,只需要使用默认参数即可。判断是否存在缓存的代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :1/30/22 8:32 AM
# 文件 :判断缓存是否存在.py
# IDE :PyCharm
# 导入requests_cache模块
import requests_cache
# 导入网络请求模块
import requests
# 设置缓存
requests_cache.install_cache()
# 清理缓存
requests_cache.clear()
# 定义测试地址
url = 'http://httpbin.org/get'
# 第一次发送网络请求
r = requests.get(url)
# False表示不存在缓存
print('是否存在缓存:', r.from_cache)
# 第二次发送网络请求
r = requests.get(url)
# True表示存在缓存
print('是否存在缓存:', r.from_cache)
程序运行结果如下:
是否存在缓存: False
是否存在缓存: True
针对反爬措施,在多次请求中设置延时是不错的选择。但是如果在第一次请求后生成了缓存,那么第二次请求时就无需设置延时,为此Requests-Cache模块可以使用自定义钩子函数的方式,合理判断是否需要设置延时操作。示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :1/30/22 8:57 AM
# 文件 :利用钩子函数判断是否需要设置延时.py
# IDE :PyCharm
# 导入Requests_Cache模块
import requests_cache
# 导入时间模块
import time
# 设置缓存
requests_cache.install_cache()
# 清理缓存
requests_cache.clear()
# 定义钩子函数
def make_throttle_hook(timeout = 0.1):
def hook(response, *args, **kwargs):
print(response.text)
# 判断没有缓存时就添加延时
if not getattr(response, 'from_cache', False):
print('等待', timeout, '秒!')
# 等待指定时间
time.sleep(timeout)
else:
# 存在缓存输出True
print('是否存在请求缓存!', response.from_cache)
return response
return hook
if __name__ == '__main__':
requests_cache.install_cache()
requests_cache.clear()
s = requests_cache.CachedSession()
s.hooks = {'response': make_throttle_hook(2)} # 配置钩子函数
# 模拟发送第一次网络请求
s.get('http://httpbin.org/get')
# 模拟发送第二次网络请求
s.get('http://httpbin.org/get')
程序运行结果:
第一次运行结果:
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.27.1",
"X-Amzn-Trace-Id": "Root=1-61f5e91a-5c923abe41da544561b5f400"
},
"origin": "139.209.219.102",
"url": "http://httpbin.org/get"
}
等待2秒
第二次请求结果
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.27.1",
"X-Amzn-Trace-Id": "Root=1-61f5e91a-5c923abe41da544561b5f400"
},
"origin": "139.209.219.102",
"url": "http://httpbin.org/get"
}
二次请求存在缓存
是否存在请求缓存! True
从以上的运行结果看,通过配置钩子函数可以实现:在第一次请求时,因为没有请求缓存所以执行了2秒等待延时;第二次请求时则没有执行2秒 延时,并输出是否存在请求缓存行为True。
说 明
Requests-Cache模块支持4种不同的存储机制,分别为money、sqlite、mongoDB以及redid,具体说明如下:
§ memory: 以字典的形式将缓存存储在内存当中,程序运行完后缓存将被销毁
§ sqlite: 将缓存存储在sqlite数据库中
§ mongoDB:将缓存存储在mongoDB数据库中
§ redis: 将缓存存储在redis数据库当中
使用Requests-Cache模块指定缓存不同的存储机制时,只需要为install_cache()函数中backend参数赋值即可,设置方式如下:
# 导入Requests_Cache模块
import requests_cache
# 设置缓存为内存的存储机制
requests_cache.install_cache(backend='memory')
# 设置缓存为sqlite数据库的存储机制
requests_cache.install_cache(backend='sqlite')
# 设置缓存为mongoDB数据库的存储机制
requests_cache.install_cache(backend='mongoDB')
# 设置缓存为redis数据库的存储机制
requests_cache.install_cache(backend='redis')
设置存储机制为mongoDB与redis数据库时,需要提取安装对应的操作模块与数据库。安装模块的命令如下:
pip install pymongo
pip install redis
强大的Requests-HTML模块
Requests-HTML 模块是requets模块的亲兄弟,是同一个开发者所开发的。Requests-HTML模块不仅包含了requests模块中的所有功能,还增加了对JavaScript的支持、数据提取以及模拟真实浏览器等功能。
使用Requests-HTML模块实现网络请求
§ 1、get()请求
通过’pip install requests-html’命令进行模块的安装,然后导入Requests-HTML模块中的HTMLSession类,接着需要创建HTML会话对象,通过会话实例进行网络请求的发送,示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :1/30/22 3:48 PM
# 文件 :使用Requests-HTML模块实现网络请求.py
# IDE :PyCharm
# 导入HTMLSession类
from requests_html import HTMLSession
# 创建HTML会话对象
session = HTMLSession()
# 定义请求地址
url = 'http://news.youth.cn/'
# 发送网络请求
r = session.get(url)
# 打印网络请求的URL地址
print(r.html)
程序运行结果如下:
<HTML url='http://news.youth.cn/'>
§ 2、post()请求
在实现网络请求时,POST请求也是一种比较常见的请求方式,使用Requests-HTML实现POST请求与requests的实现方法类似都需要单独设置表单参数data,不过它也是需要通过会话实例进行网络请求的发送,示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :1/30/22 4:44 PM
# 文件 :post()请求.py
# IDE :PyCharm
# 导入HTMLSession类
from requests_html import HTMLSession
# 创建HTML会话对象
session = HTMLSession()
# 模拟表单登录的数据
data = {'user':'admin', 'password':123456}
# 发送POST请求
r = session.post('http://httpbin.org/post', data = data)
# 判断请求是否成功
if r.status_code == 200:
print(r.text)
程序运行结果如下:
{
"args": {},
"data": "",
"files": {},
# 表单数据
"form": {
"password": "123456",
"user": "admin"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "26",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
# User-Agent对应的值
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8",
"X-Amzn-Trace-Id": "Root=1-61f6509d-56a381b4543229480fe07e56"
},
"json": null,
"origin": "139.209.219.102",
"url": "http://httpbin.org/post"
}
从以上的运行结果中,不仅可以看到form所对应的表单内容,还可以看到User-Agent所对应的值,并不是像requests模块发送网络请求时返回的默认值(python-requests/2.27.1),而是一个真实的浏览器请求头信息,这与requests模块所发送的网络请求有着细小的改进。
§ 3、修改请求头信息
说到请求头信息,Requests-HTML模块是可通过指定headers参数来对默认的浏览器请求头信息进行修改的,修改请求头信息的关键代码如下:
ua = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.76'
}
r = session.post('http://httpbin.org/post', data = data, headers = ua)
§ Requests-HTML模块中添加了UserAgent类,使用该类可以实现随机生成请求头。示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :1/30/22 9:06 PM
# 文件 :生成随机请求头信息.py
# IDE :PyCharm
# 导入HTMLSession类
from requests_html import HTMLSession, UserAgent
# 创建HTML会话对象
session = HTMLSession()
# 创建随机请求头
ua =UserAgent().random
r = session.get('http://httpbin.org/get', headers = {'user-agent': ua})
# 判断请求是否成功
if r.status_code == 200:
# 以文本形式打印返回结果
print(r.text)
程序运行结果如下:
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
# 请求头信息
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0.6",
"X-Amzn-Trace-Id": "Root=1-61f69454-0b37eb40198f0f4b237fc7e8"
},
"origin": "139.209.219.102",
"url": "http://httpbin.org/get"
}
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
# 请求头信息
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17",
"X-Amzn-Trace-Id": "Root=1-61f693aa-0d20428a3a73766723017536"
},
"origin": "139.209.219.102",
"url": "http://httpbin.org/get"
}
备注
以上运行是在Anaconda的Jupiter notebook 下运行。Pycharm 报错,应该是解释器的问题。
数据的提取
以往使用requests模块实现爬虫程序时,还需要为其配置一个解析HTML代码的搭档。Requests-HTML模块对此进行了一个比较大的升级,不仅支持CSS选择器还支持XPath的节点提取方式。
• 1、CSS选择器
CSS选择器中需要使用HTML的find()方法,该方法中包含5个参数,其语法格式与参数含义如下:
find(selector:str='*', containing:_Containing=None, clean:bool=False,first:bool=False,_encoding:str=None)
§ selector: 使用CSS选择器定位网页元素
§ containing:通过指定文本获取网页元素
§ clean:是否清除HTML中的
§ first:是否只返回网页中第一个元素,默认为False表示全部返回
§ _encoding:表示编码格式
• 2、XPath选择器
Xpath选择器同样需要使用HTML进行调用,该方法中有4个参数,其语法格式与参数含义如下:
xpath(selector:str, clean:bool=False,first:bool=False,_encoding:str=None)
§ selector:使用XPath选择器定位网页元素
§ clean:是否清楚HTML中的
§ first:是否只返回网页中第一个元素,默认为False表示全部返回
§ _encoding:表示编码格式
• 3、爬取即时新闻
学习了Requests-HTML模块中两种提取数据的函数后,以爬取‘中国青年网’即时新闻为例,数据提取的具体步骤如下:
(1)在浏览器中打开网页地址(http://news.youth.cn/jsxw/index.htm),然后按快捷键在‘开发者工具’界面中选择Elements功能选项,确认‘即时新闻’列表内新闻信息所在HTML标签的位置。如下图:
(2)在图中可以看出新闻标题在
- 标签中的标签内,而标签中的href属性值为当前新闻详情页的部分URL地址时,会自动显示完整的详情页地址。如下图:
(3)定位以上‘新闻标题’‘新闻详情url地址’以及‘新闻发布时间’信息位置以后,首先创建HTML会话与获取随机请求对象,然后对‘即时新闻’首页发送网络请求,代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :1/30/22 10:34 PM
# 文件 :爬取即时新闻.py
# IDE :PyCharm
# 导入HTMLSession类
from requests_html import HTMLSession,UserAgent
# 创建HTML会话对象
session = HTMLSession()
# 创建随机请求头
ua = UserAgent().random
# 发送网络请求
r = session.get('http://news.youth.cn/jsxw/index.htm', headers = {'user-agent': ua})
# 编码
r.encoding='gb2312'
(4)网络请求发送完成以后,需要通过请求状态码判断请求是否为200,如果是200则表示请求成功,然后根据数据定位的标签分别获取‘新闻标题’,‘新闻详情url地址‘以及新闻的发布时间,代码如下:
# 判断请求是否成功
if r.status_code == 200:
# 获取所有class=tj3_1中的<li>标签
li_all = r.html.xpath('.//ul[@class="tj3_1"]/li')
# 遍历每个<li>标签
for li in li_all:
# 提取新闻标题内容
news_title = li.find('a')[0].text
# 获取新闻详情对应的地址
news_href = 'http://news.youth.cn/jsxw'+\
li.find('a[href]')[0].attrs.get('href').lstrip('.')
# 获取新闻发布时间
news_time = li.find('font')[0].text
# 打印新闻标题
print('新闻标题为:', news_title)
# 打印新闻URL地址
print('新闻url地址为:', news_href)
# 打印新闻发布时间
print('新闻发布时间为:', news_time)
程序运行结果如下:
新闻标题为: 北京市气象台发布道路结冰黄色预警信号
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415644.htm
新闻发布时间为: 2022-01-30 20:44:00
新闻标题为: 天津新增11例确诊病例 均为隔离管控人员
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415638.htm
新闻发布时间为: 2022-01-30 20:38:00
新闻标题为: 速看!2月起这些新规将施行,如何影响你我生活?
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415533.htm
新闻发布时间为: 2022-01-30 19:18:00
新闻标题为: 想带萌宠坐火车回家过年?这些事你需要知道!
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415416.htm
新闻发布时间为: 2022-01-30 18:00:00
新闻标题为: 我国首批54家国家级旅游休闲街区名单正式公布
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415357.htm
新闻发布时间为: 2022-01-30 17:29:00
新闻标题为: 北京新增高风险地区3个,现有3地高风险7地中风险
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415338.htm
新闻发布时间为: 2022-01-30 17:25:00
新闻标题为: 杭州富阳区公布11例确诊病例活动轨迹
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415155.htm
新闻发布时间为: 2022-01-30 16:15:00
新闻标题为: 央行:2021年末金融机构人民币各项贷款余额192.69万亿元,同比增长11.6%
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415179.htm
新闻发布时间为: 2022-01-30 16:24:00
新闻标题为: 俄乌紧张局势未缓 英国考虑对北约进行大规模军力部署
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415173.htm
新闻发布时间为: 2022-01-30 16:21:00
新闻标题为: 科普:虎年说虎——全球老虎生存保护状况
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415168.htm
新闻发布时间为: 2022-01-30 16:21:00
新闻标题为: 央行:2021年末金融机构各项贷款余额超192万亿元
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415166.htm
新闻发布时间为: 2022-01-30 16:21:00
新闻标题为: 日本冬奥会参赛选手史上最多 四对姐妹花引人注目
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415165.htm
新闻发布时间为: 2022-01-30 16:21:00
新闻标题为: 杭州市卫健委:核酸检测能力充裕 完全能满足需求
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415095.htm
新闻发布时间为: 2022-01-30 16:00:00
新闻标题为: 联合国专家遇害 刚果(金)判51人死刑
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13415082.htm
• 4、find()方法中containing 参数
如果需要获取
- 标签中指定的新闻内容时,可以使用find()方法中的containing 参数,以获取关于‘新冠疫情’相关新闻内容为例,示例代码如下:
for li in r.html.find('li',containing='新冠疫情'):
# 提取新闻标题内容
news_titile = li.find('a')[0].text
# 获取新闻详情对应的地址
news_href = 'http://news.youth.cn/jsxw'+\
li.find('a[href]')[0].attrs.get('href').lstrip('.')
# 获取新闻发布时间
news_time = li.find('font')[0].text
# 打印新闻标题
print('新闻标题为:', news_title)
# 打印新闻URL地址
print('新闻url地址为:', news_href)
# 打印新闻发布时间
print('新闻发布时间为:', news_time)
程序运行结果如下:
新闻发布时间为: 2022-01-30 08:54:00
新闻标题为: 黑龙江省新增本土确诊病例4例
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414311.htm
新闻发布时间为: 2022-01-30 09:48:00
新闻标题为: 秘鲁遇严重生态灾难:海滩原油泄漏量高达12000桶
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414296.htm
新闻发布时间为: 2022-01-30 09:42:00
新闻标题为: 渲染乌克兰局势,蔡英文向美表“忠心”
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414291.htm
新闻发布时间为: 2022-01-30 09:39:00
新闻标题为: 国家卫健委:昨日新增本土确诊病例54例
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414270.htm
新闻发布时间为: 2022-01-30 09:24:00
新闻标题为: 天津全面加强春节期间常态化疫情防控
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414267.htm
新闻发布时间为: 2022-01-30 09:27:00
新闻标题为: 北京新增20例本土确诊病例,本轮本土疫情感染者已过百
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414261.htm
新闻发布时间为: 2022-01-30 09:23:00
新闻标题为: 朝鲜发射导弹 韩将举行国家安全保障会议全体会议
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414253.htm
新闻发布时间为: 2022-01-30 09:18:00
新闻标题为: 杭州民政:疫情期间“白事”出殡送葬人数控制在10人以内
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414235.htm
新闻发布时间为: 2022-01-30 09:12:00
• 5、search()方法与search_all()方法
除了使用find()与path()这两种方法提取数据以外,还可以使用serach()方法与searc_all()方法,通过关键字提取相应的数据信息。其中search()方法表示查找符合条件的第一个元素,而search_all()方法则表示符合条件的所有元素。
使用search()方法获取关于’新冠疫情’新闻信息为例,示例代码如下:
for li in r.html.find('li', containing='新冠疫情'):
a = li.search('<a href="{}">{}</a>') # 获取<li>标签中<a>标签内的新闻地址与新闻标题
# 提取新闻标题
news_title = a[1]
# 提取新闻地址
news_href = 'http://news.youth.cn/jsxw'+a[0]
# 获取与新冠疫情相关新闻的发布时间
print('新闻标题为:',news_title)
# 打印新闻标题
print('新闻url地址为:', news_href)
# 打印新闻发布时间
print('新闻发布时间:', news_time)
新闻标题为: 国家卫健委:昨日新增本土确诊病例54例
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414270.htm
新闻发布时间为: 2022-01-30 09:24:00
新闻标题为: 天津全面加强春节期间常态化疫情防控
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414267.htm
新闻发布时间为: 2022-01-30 09:27:00
新闻标题为: 北京新增20例本土确诊病例,本轮本土疫情感染者已过百
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414261.htm
新闻发布时间为: 2022-01-30 09:23:00
新闻标题为: 朝鲜发射导弹 韩将举行国家安全保障会议全体会议
新闻url地址为: http://news.youth.cn/jsxw/202201/t20220130_13414253.htm
新闻发布时间为: 2022-01-30 09:18:00
使用search_all()方法获取关于‘新冠疫情’新闻信息为例,示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/1/22 9:08 PM
# 文件 :search()与search_all()方法.py
# IDE :PyCharm
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/1/22 7:08 PM
# 文件 :find()方法中containing参数.py
# IDE :PyCharm
# 导入HTMLSession类
from requests_html import HTMLSession,UserAgent
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
# 创建HTML会话对象
session = HTMLSession()
# 创建随机请求头
#ua = UserAgent().random
ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36'
# 发送网络请求
r = session.get('http://news.youth.cn/jsxw/index.htm', headers = {'user-agent': ua})
r.encoding='gb2312'
# 判断请求是否成功
if r.status_code == 200:
for li in r.html.find('li', containing='新冠疫情'):
a = li.search('<a href="{}">{}</a>')
# 提取新闻标题内容
news_title = a[1]
# 获取新闻详情对应的地址
news_href = 'http://news.youth.cn/jsxw' + a[0].lstrip('.')
# 获取新闻发布时间
news_time = li.search('<font>{}</font>')[0]
# 打印新闻标题
print('新闻标题为:', news_title)
# 打印新闻URL地址
print('新闻url地址为:', news_href)
# 打印新闻发布时间
print('新闻发布时间为:', news_time)
使用search_all()方法获取关于‘疫情’新闻信息为例,代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/1/22 9:32 PM
# 文件 :search_all()方法.py
# IDE :PyCharm
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :1/30/22 10:34 PM
# 文件 :爬取即时新闻.py
# IDE :PyCharm
# 导入HTMLSession类
from requests_html import HTMLSession,UserAgent
import ssl
# 导入正则表达式模块
import re
ssl._create_default_https_context = ssl._create_unverified_context
# 创建HTML会话对象
session = HTMLSession()
# 创建随机请求头
ua = UserAgent().random
# 发送网络请求
r = session.get('http://news.youth.cn/jsxw/index.htm', headers = {'user-agent': ua})
r.encoding = 'gb2312'
# 判断请求是否成功
if r.status_code == 200:
# 获取所有class=tj3_1中的<li>标签
class_tj3_1 = r.html.xpath('.//ul[@class="tj3_1"]')
#使用search_all()方法获取所有class=tj3_1中的<li>标签
li_all = class_tj3_1[0].search_all('<li>{}</li>')
# 遍历每个<li>标签
for li in li_all:
# 判断<li>标签内容中是否存在关键字'新冠疫情'
if '新冠' in li[0]:
# 通过正则表达式获取<a>标签中的新闻信息
a = re.findall('<font>(.*?)</font><a href="(.*?)">(.*?)</a>', li[0])
# 提取新闻标题内容
news_title = a[0][2]
# 获取新闻详情对应的地址
news_href = 'http://news.youth.cn/jsxw'+ a[0][1]
# 获取新闻发布时间
news_time = a[0][0]
# 打印新闻标题
print('新闻标题为:', news_title)
# 打印新闻URL地址
print('新闻url地址为:', news_href)
# 打印新闻发布时间
print('新闻发布时间为:', news_time)
程序运行结果如下:
新闻标题为: 广东惠州报告1例新冠肺炎确诊病例 系深圳病例的密接者
新闻url地址为: http://news.youth.cn/jsxw./202202/t20220201_13418373.htm
新闻发布时间为: 2022-02-01 18:35:00
新闻标题为: 全国新冠疫苗接种超30亿剂次
新闻url地址为: http://news.youth.cn/jsxw./202201/t20220131_13416613.htm
新闻发布时间为: 2022-01-31 15:19:00
新闻标题为: 全国新冠疫苗接种剂次超30亿
新闻url地址为: http://news.youth.cn/jsxw./202201/t20220131_13416596.htm
新闻发布时间为: 2022-01-31 15:01:00
新闻标题为: 天津市1月30日新增11例本土新冠肺炎确诊病例
新闻url地址为: http://news.youth.cn/jsxw./202201/t20220131_13416089.htm
新闻发布时间为: 2022-01-31 08:23:00
新闻标题为: 绥芬河市公布19例新冠肺炎确诊病例、无症状感染者活动轨迹
新闻url地址为: http://news.youth.cn/jsxw./202201/t20220130_13414507.htm
新闻发布时间为: 2022-01-30 11:09:00
新闻标题为: 调查:超七成美国人对新冠大流行感到疲倦和沮丧
新闻url地址为: http://news.youth.cn/jsxw./202201/t20220130_13414465.htm
新闻发布时间为: 2022-01-30 10:48:00
说 明
在使用search()与search_all()方法获取数据时,方法中的一个"{}"表示获取一个内容
获取动态加载的数据
在爬取网页数据时,经常会遇到直接对网页地址发送请求,可返回的HTML代码中并没有所需要的数据情况。遇到这样的情况,多数都是因为网页数据使用了Ajax请求并由JavaScript渲染到网页当中。例如爬取(https://movie.douban.com/tag/#?sort=U&range=0, 10&tags=%E7%94%B5%E5%BD%B1,2021)豆瓣2021年电影数据,就是需要通过浏览器开发者工具获取Ajax请求后的电影信息。
为了避免如上图所示的麻烦操作,Requests-HTML模块提供了render()方法,第一次调用该方法将会自动下载Chromium浏览器,然后通过该浏览器直接加载JavaScript渲染后的信息, 使用render()方法爬取豆瓣2021年电影数据的具体步骤如下:
(1) 创建HTML会话与随机请求头对象,然后发送网络请求,在请求成功的情况下调用render()方法获取网页中的JavaScript渲染后的信息,代码如下 :
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/1/22 10:27 PM
# 文件 :使用render()方法获取动态加载的数据.py
# IDE :PyCharm
# 导入HTMLSession类
from requests_html import HTMLSession, UserAgent
# 创建HTML会话对象
session = HTMLSession()
# 创建随机请求头
ua = UserAgent().random
# 发送网络请求
r = session.get('https://movie.douban.com/tag/#?sort=U&range=0, 10&tags=%E7%94%B5%E5%BD%B1,2021', headers = {'user-agent':ua})
# 编码
r.encoding='gb2312'
# 判断请求是否成功
if r.status_code == 200:
# 调用render()方法,没有Chromium浏览器就自动下载
r.html.render()
(2) 运行步骤2中代码,由于第一次调用render()方法,所以会自动下载Chromium浏览器,下载完成后控制台将显示如下图所示的信息。
注 意
在第一次调用render()方法,如果报错,在命令提示符中执行pip install-U "urllib3<1.25"命令,降低Anaconda中urllib3模块版本即可解决。
(3)打开浏览器开发者工具,在Elements功能选项的界面中确认电影信息所在HTML标签的位置。
(4)编写获取电影信息的代码,首先获取当前页面中所有电影信息的标签,然后在标签中逐个获取电影名称、电影评分、详情页URL地址以及图片地址。代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/1/22 10:27 PM
# 文件 :使用render()方法获取动态加载的数据.py
# IDE :PyCharm
# 导入HTMLSession类
from requests_html import HTMLSession, UserAgent
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
# 创建HTML会话对象
session = HTMLSession()
# 创建随机请求头
ua = UserAgent().random
# 发送网络请求
r = session.get('https://movie.douban.com/tag/#/?sort=U&range=0,10&tags=%E7%94%B5%E5%BD%B1,2021', headers = {'user-agent':ua})
# 编码
r.encoding='gb2312'
# 判断请求是否成功
if r.status_code == 200:
# 调用render()方法,没有Chromium浏览器就自动下载
r.html.render(scrolldown=4, sleep=2) # render()必填参数scrolldown, 否则返回结果为空,因为Ajax加载的动态网页
# 获取当前页面中所有电影信息的a标签
class_wp = r.html.xpath(".//div[@class='list-wp']/a")
for a in class_wp:
title = a.find('p span')[0].text # 获取电影名称
rate = a.find('p span')[1].text # 获取电影评分
details_url = a.attrs.get('href') # 获取详情页URL地址
img_url = a.find('img')[0].attrs.get('src') # 获取图片URL地址
print('电影名称:', title)
print('电影评分:', rate)
print('详情页地址:', details_url)
print('图片地址:', img_url)
程序运行结果如下:
电影名称: 长津湖
电影评分: 7.4
详情页地址: https://movie.douban.com/subject/25845392/
图片地址: https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2681329386.jpg
电影名称: 我和我的父辈
电影评分: 6.6
详情页地址: https://movie.douban.com/subject/35294995/
图片地址: https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2683055011.jpg
电影名称: 摩加迪沙
电影评分: 8.1
详情页地址: https://movie.douban.com/subject/33457594/
图片地址: https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2695961421.jpg
电影名称: 沙丘
电影评分: 7.7
详情页地址: https://movie.douban.com/subject/3001114/
图片地址: https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2687443734.jpg
电影名称: 失控玩家
电影评分: 7.5
详情页地址: https://movie.douban.com/subject/30337388/
图片地址: https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2677520025.jpg
电影名称: 怒火·重案
电影评分: 7.2
详情页地址: https://movie.douban.com/subject/30174085/
图片地址: https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2673412189.jpg
电影名称: 盛夏未来
电影评分: 7.1
详情页地址: https://movie.douban.com/subject/35158124/
图片地址: https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2673420377.jpg
电影名称: 中国医生
电影评分: 6.9
详情页地址: https://movie.douban.com/subject/35087699/
图片地址: https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2666591984.jpg
电影名称: 你好,李焕英
电影评分: 7.8
详情页地址: https://movie.douban.com/subject/34841067/
图片地址: https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2629056068.jpg
电影名称: 图兰朵:魔咒缘起
电影评分: 3.1
总 结
这个案例最初运行的时候返回结果为None,xpath路径完全没有问题,百思不得其解。因为第一次接触render()函数,示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/1/22 10:27 PM
# 文件 :使用render()方法获取动态加载的数据.py
# IDE :PyCharm
# 导入HTMLSession类
from requests_html import HTMLSession, UserAgent
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
# 创建HTML会话对象
session = HTMLSession()
# 创建随机请求头
ua = UserAgent().random
# 发送网络请求
r = session.get('https://movie.douban.com/tag/#/?sort=U&range=0,10&tags=%E7%94%B5%E5%BD%B1,2021', headers = {'user-agent':ua})
# 编码
r.encoding='gb2312'
# 判断请求是否成功
if r.status_code == 200:
# 调用render()方法,没有Chromium浏览器就自动下载
r.html.render() # 最初是没有任何参数的
# 获取当前页面中所有电影信息的a标签
class_wp = r.html.xpath(".//div[@class='list-wp']/a")
for a in class_wp:
title = a.find('p span')[0].text # 获取电影名称
rate = a.find('p span')[1].text # 获取电影评分
details_url = a.attrs.get('href') # 获取详情页URL地址
img_url = a.find('img')[0].attrs.get('src') # 获取图片URL地址
print('电影名称:', title)
print('电影评分:', rate)
print('详情页地址:', details_url)
print('图片地址:', img_url)
程序运行结果如下:
/Users/liuxiaowei/PycharmProjects/爬虫练习/venv/bin/python /Users/liuxiaowei/PycharmProjects/爬虫练习/高级网络请求模块/获取动态加载的数据/使用render()方法获取动态加载的数据.py
[]
Process finished with exit code 0
通过查询render()渲染网页,相关用法,了解到Ajax动态网页数据加载更多内容,其实就类似网页滚动,要触发加载,就涉及到了scrolldown这个参数,本例设置render(scrolldown = 4, sleep = 2)。至此问题圆满解决。
归纳总结