请求管理要做哪些事情
请求去重管理:防止发出重复请求
请求缓冲管理:临时存储请求
请求调度管理: 派遣并控制调度请求,请求优先级管理
请求去重管理
请求去重的判断依据:
- 请求方法
- 请求地址 url
- url 的查询参数
- 请求体
去重方案:
- 基于信息摘要算法求指纹去重
- 基于布隆过滤器的去重
请求数据处理
- 统一大小写
- url 查询参数排序
- 请求体排序(data)
相关的代码实现
首先封装一个请求对象:
class Request(object):
def __init__(self, url, name=None, method='GET', query=None, body=None):
"""
构建请求对象
:param url:
:param name:
:param method:
:param query:
:param body:
"""
self.url = url
self.name = name
self.method = method
self.query = query
self.body = body
然后来创建对请求进行去重的类 :
# 实现请求去重的逻辑
import sys
import urllib.parse
from filter_tools import get_filter_class
from request_tools.my_requests import Request
class RequestFilter(object):
def __init__(self, filter_obj):
self.filter_obj = filter_obj
def get_request_filter_data(self, request_obj):
"""
从请求对象中组合出判断去重的数据
:param request_obj:
:return:
"""
# 将协议和域名部分进行大小写统一 其余的保留原始的大小写格式
# 处理 url 的几个模块:
# import urllib.parse
# import w3lib.url
# 方法名统一大写
method = request_obj.method.upper()
url = request_obj.url
_ = urllib.parse.urlparse(url)
url_without_query = _.scheme + "://" + _.hostname + _.path
url_query = urllib.parse.parse_qsl(_.query)
if not url_query:
url_query = []
query = request_obj.query
if not query:
query = {}
query = list(query.items())
# 将 url 里面的请求参数和 query 里面的进行合并
all_query = sorted(query + url_query)
url_with_query = url_without_query + "?" + urllib.parse.urlencode(all_query)
body = request_obj.body
if body:
str_body = str(sorted(list(body.items())))
data = url_with_query + method + str_body
else:
data = url_with_query + method
# print(data)
return data
def is_exist(self, request_obj: Request):
"""
判断请求是否已经处理过
:param request_obj:
:return:
"""
data = self.get_request_filter_data(request_obj)
print(data)
return self.filter_obj.is_exist(data)
def mark_request(self, request_obj):
"""
标记已经处理过的请求对象 即将这个数据保存到过滤器中
下次再出现的时候就不会将这个请求再次纳入
:param request_obj:
:return:
"""
data = self.get_request_filter_data(request_obj)
self.filter_obj.save(data)
if __name__ == "__main__":
q = RequestFilter(None)
q.get_request_filter_data(Request("https://www.baidu.com/s?wd=python", query={"name": "ruiyang"}))
q.get_request_filter_data(Request("https://www.baidu.com/s", query={"name": "ruiyang"}))
q.get_request_filter_data(Request("https://www.baidu.com/s"))
# 创建一个基于内存的过滤器
filter_obj = get_filter_class("memory")()
# 基于该过滤器创建一个请求过滤器
request_filter = RequestFilter(filter_obj)
# 创建一些示例请求
r1 = Request(name="r1", url="https://www.baidu.com/s?wd=python")
r2 = Request(name="r2", url="https://www.baidu.com/s?wd=python", query={"name": "ruiyang"})
r3 = Request(name="r3", url="https://www.baidu.com/s", query={"name": "ruiyang", "wd": 'python'})
r4 = Request(name="r4", url="HTTPS://www.baidu.com/s?wd=python")
r5 = Request(name="r5", url="HTTPS://www.baidu.com/s?wd=python", query={"name": "kailun"})
rs = [r1, r2, r3, r4, r5]
for r in rs:
if request_filter.is_exist(r):
print("重复请求{}".format(r))
else:
request_filter.mark_request(r)
print("标记请求{}".format(r))
常见的临时队列
- 内置的队列模块
- asyncio 中的队列模块
- gevent 中的队列模块
- tornado 中的队列模块
持久化队列
现成的有:
- queuelib中的 disk_queue
- 基于 redis 实现的 queue(例如 pyspider 中的 redis_queue)
我们可以使用 python 实现基于 redis 的 FIFO, LIFO, Priority 队列。
- RedisFifoQueue
- RedisLifoQueue
- RedisPriorityQueue
我们可以详细去看 scrapy 中的 queuelib 模块
以及 pyspider 中的message_queue模块中的 redis_queue来完成自己的实现。
https://github.com/binux/pyspider/tree/master/pyspider/message_queue
现在我们就基于 pyspider 中的 redis_queue 来实现自己的队列:
具体内容下一篇文章继续。
更新时间: 2020-02-04