本篇为学习笔记,内容来自于崔庆才的《Python3 网络爬虫开发实战 第二版》,如果内容涉及侵权,请联系我进行删除,同时,在编写过程中也存在不足之处,还请各位帮忙指正。相关代码模块在码云上
目录
用途
> 该模块主要用来处理一些常见的采集任务,比如有几百个网页需要采集,可以使用本模块来提升采集速度速度对比
使用requests进行采集
# 使用requests进行同步采集
import requests
import time
header = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
}
urls = [f"https://spa5.scrape.center/api/book/?limit=18&offset={i * 18}" for i in range(0, 500)]
responses = []
start_time = time.time()
for url in urls:
print(url)
res = requests.get(url, headers=header)
responses.append(res.json())
for res in responses:
print(res)
print("共计用时:", time.time() - start_time)
共计用时: 117.82907366752625
使用模块进行采集
from asyRequest import AsyRequest
import time
urls = [f"https://spa5.scrape.center/api/book/?limit=18&offset={i * 18}" for i in range(0, 500)]
start_time = time.time()
A = AsyRequest()
A.PutRequests(urls)
A.start()
responses = A.GetResponses()
for res in responses:
print(res.json())
print("共计用时:", time.time() - start_time)
共计用时: 77.28473448753357
模块构造函数
> 在实例化模块的时候,需要提供两个参数,`CONCURRNCY`和`TimeInterval`,其中,`CONCURRNCY`代表的是在一个周期内可以并行的协程数量,这个数值越大,采集速度越快(也不要特别大,可能会对网站造成不好的影响,且特别大的时候,也不一定能更快),`TimeInterval`则代表着暂停时间,这个参数数值越大,采集速度越慢,但是对网站影响越小。传入采集网站列表
首先在构造函数中,我们初始化了两个队列,分别是`request_qneue`和`response_qneue`,其中`request_qneue`用来保存我们的请求内容,为了能够方便我们更好的构造请求,提供了两个对外的api接口,分别是`PutRequests`和`PutMethodRequest`。PutRequests
接收的是一个包含需要采集的所有网站链接的列表内容,作用是将这些网站链接自动封装为简单的GET请求(如果存在POST请求链接,则这个方法就行不通了,且不能个性化设计采集的其他参数),但是这种方法更加方便、快捷。
PutMethodRequest
接收的是一个个性化的链接采集配置,可以自行设置各项参数,然后将这个链接加入到采集队列中,缺点是需要一个链接一个链接的设置。
启动函数
当我们设置好采集队列以后,我们可以通过`实例.start()`启动模块进行采集。获取采集到的响应列表
模块会将采集到的链接保存到`response_qneue`队列中去,我们可以通过`实例.GetResponses获取到它`用到的asyncio的API描述
asyncio.run
运行一个协程函数一般用async装饰的函数我们就称为协程函数,并等待其返回结果。
例如下面这样:
import asyncio
async def runner():
print("hello")
await asyncio.sleep(10) # 阻塞代码,如果存在多个协程的时候,将切换到其他协程上
print("world")
return 10
asyncio.run(runner())
asyncio.Semaphore
用来限制协程的并发数量,需要保证这个对象是全局对象。
asyncio.gather
并发执行多个协程,且会等待多个协程执行完成并提取各个协程函数的返回值
完整模块代码
import asyncio
import aiohttp
class CustomResponse:
def __init__(self, *, url=None, status_code=404, text=None, content=None, jsp=None):
self.url = url
self.text = text
self.status_code = status_code
self.content = content
self.jsp = jsp
def json(self):
return self.jsp
class AsyRequest:
header = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
}
def __init__(self, CONCURRNCY=10, TimeInterval=-1):
"""
:param CONCURRNCY: 周期内的并发数量
:param TimeInterval: 周期 单位为秒(小于0为不设置周期)
"""
self.Interval = TimeInterval # 时间间隔
self.sem = asyncio.Semaphore(CONCURRNCY)
self.request_qneue = [] # 保存封装的请求
self.response_qneue = [] # 保存封装的响应
self.session = None
def PutRequests(self, urls:list):
for url in urls:
self.request_qneue.append(
{
"url":url,
"method":"GET",
"headers":self.header,
'kwargs':{}
}
)
def PutMethodRequest(self, url, *, method="GET", headers=None, **kwargs):
self.request_qneue.append(
{
"url": url,
"method": "GET" if method == "GET" else "POST",
"headers": headers if headers else self.header,
'kwargs': kwargs
}
)
async def Response(self, response: aiohttp.ClientResponse):
status_code = response.status
url = response.url
text = await response.text()
content = await response.read()
json_data = await response.json()
return CustomResponse(url=url, status_code=status_code, text=text, content=content, jsp=json_data)
async def Request(self, item):
async with self.sem:
print(item['url'])
url = item['url']
headers = item['headers']
method = item['method']
kwargs = item.get('kwargs', {}) # 确保 kwargs 是一个字典
if self.Interval > 0:
await asyncio.sleep(self.Interval)
if method == "GET":
async with self.session.get(url, headers=headers, **kwargs) as response:
Res = await self.Response(response)
else:
async with self.session.post(url, headers=headers, **kwargs) as response:
Res = await self.Response(response)
return Res
async def run(self):
async with aiohttp.ClientSession() as session:
self.session = session
tasks = [self.Request(item) for item in self.request_qneue]
self.request_qneue = []
self.response_qneue = await asyncio.gather(*tasks)
def start(self):
asyncio.run(self.run())
def GetResponses(self):
return self.response_qneue