scrapy爬虫url或者body遇到随机数或者随机字符串该去重或过滤
在有些网站的开发中,经常会在链接或者参数中增加可变的量,比如增加随机数、增加随机字符串、增加时间戳或者增加不同的字符串等等来进行请求。有些情况下,我们不在链接或者参数中添加 可变的量也可以请求成功(比如不加时间戳也可以请求成功),随机数不变也可以请求成功,但是有些情况下这两种方式可能都无法请求成功,这就需要我们完全按照他们的要求去请求了。
首先来介绍下主要原理。参考scrapy-redis调度器源码(即scrapy_redis.scheduler.Scheduler):
-
# 此段代码就是从scrapy_redis中截取下来的
-
# 函数名的含义就是请求入队列,即能不能让一个request进入请求队列是这个方法来判断的
-
def enqueue_request(self, request):
-
# 如果request的dont_filter不是True并且self.df.request_seen方法返回的是True。就会return False,表示已去重
-
# 然后这个self.df就是过滤器对象,其实就是settings中设置的DUPEFILTER_CLASS类的对象。
-
if not request.dont_filter and self.df.request_seen(request):
-
self.df.log(request, self.spider)
-
return False
-
if self.stats:
-
self.stats.inc_value('scheduler/enqueued/redis', spider=self.spider)
-
# 所以如果self.df.request_seen方法返回的是Fasle,就会把请求push进队列,就会return True,表示已压进队列
-
self.queue.push(request)
-
return True
所以我们可以修改过滤器类,在过滤器类中对原始请求进行复制,复制的请求用来处理可变参数,此时复制的请求就发生了变化,而对此副本请求进行去重,但原始请求不变,仍用来压进请求队列。以下将通过爬虫提交参数的不同方法来分别进行举例讲解。
1、GET方法的url链接参数可变量
比如下面两个网址对应的都是同一html资源:
-
https://www.百度.com/info?id=1234&r=0.1536745
-
https://www.百度.com/info?id=1234&r=0.84682
如果我们再scrapy中直接去请求这两个链接时,是无法把另一个链接去重的,此时我们就需要自己构造一个过滤器了。方法如下:
首先我们需要在scrapy项目路径下,和pipelines.py文件同一级新建一个“dupefilter.py”文件。然后在文件中输入以下代码:
-
from scrapy_redis.dupefilter import RFPDupeFilter
-
-
## 类名自定义,继承了scrapy_redis的RFPDupeFilter过滤器
-
class GetRandomDupefilter(RFPDupeFilter):
-
def request_seen(self, request):
-
url = request.url
-
# 此处是将url中的随机参数替换成了空字符串,注意哦,此处不止替换了随机数,还有键名等
-
url = re.sub('[\?\&]r=[0-9\.] ', "", url)
-
# 然后用新url来替换之前的url,就出现了复制的请求request_copy。要注意replace方法是返回一个新的请求对象,不对原请求做任何改变
-
request_copy = request.replace(url=url)
-
# 再对复制的请求request_copy进行指纹输出用于去重
-
fp = self.request_fingerprint(request_copy)
-
# 指纹结果用于add进去重集合,如果可以add进,则集合无该请求,added=1, 否则added=0。added的值表示add进集合的个数。
-
added = self.server.sadd(self.key, fp)
-
# 如果added=1,表示无重复,返回False,反之有重复,返回True
-
return added == 0
这样的话我们就可以不改变原始请求request来用副本request_copy(修改url)进行去重,从而做到对原始request地去重。
2、POST方法的JSON参数的可变量
比如下面两个json参数(Content-Type: application/json)对应的都是同一html资源,POST的url是“https://www.百度.com/info”:
{ "id": 1234, "r": 0.045612, "name": "abc", "sex": "male" } |
{ "id": 1234, "r": 0.45816, "name": "abc", "sex": "male" } |
该方法和上面类似,是将json中的可变量删除进行去重:
-
from scrapy_redis.dupefilter import RFPDupeFilter
-
import json
-
-
## 类名自定义,继承了scrapy_redis的RFPDupeFilter过滤器
-
class PostJsonRandomDupefilter(RFPDupeFilter):
-
def request_seen(self, request):
-
# 此时body是b'{"id": 1234, "r": 0.045612, "name": "abc", "sex": "male"}'
-
body = request.body
-
# 因为是采用的json.loads,所以body最好是json.dumps得到的。
-
# 此时body_data是{"id": 1234, "r": 0.045612, "name": "abc", "sex": "male"}
-
body_data = json.loads(body)
-
# body_data删除"r"参数,此时body_data是{"id": 1234, "name": "abc", "sex": "male"}
-
body_data.pop("r", None)
-
# 然后用新body_data字符串来替换之前的body,就出现了复制的请求request_copy。
-
request_copy = request.replace(body=json.dumps(body_data))
-
# 再对复制的请求request_copy进行指纹输出用于去重
-
fp = self.request_fingerprint(request_copy)
-
# 指纹结果用于add进去重集合,如果可以add进,则集合无该请求,added=1, 否则added=0。added的值表示add进集合的个数。
-
added = self.server.sadd(self.key, fp)
-
# 如果added=1,表示无重复,返回False,反之有重复,返回True
-
return added == 0
此处是将body先json化然后再删除可变键值对,当然也可以直接用正则表达式替换成空字符串也可以。(字符串替换要注意body是字节类型)
3、POST方法的form参数的可变量
比如下面两个form参数(Content-Type: application/x-www-form-urlencoded)对应的都是同一html资源,POST的url是“https://www.百度.com/info”:
Form Data id: 1234 |
Form Data id: 1234 |
同样和第一种GET方法类似,是将body中的可变量替换成空,替换时要注意body是字节类型:
-
from scrapy_redis.dupefilter import RFPDupeFilter
-
-
## 类名自定义,继承了scrapy_redis的RFPDupeFilter过滤器
-
class PostFormRandomDupefilter(RFPDupeFilter):
-
def request_seen(self, request):
-
# 此时body的值是b'id=1234&r=0.16512&name=abc&sex=male'
-
body = request.body
-
# 此处是将body中的随机参数替换成了空字符串,注意哦,此处不止替换了随机数,还有键名等。
-
# 此时body的值是b'id=1234&name=abc&sex=male'
-
body = re.sub(b'\&*r=[0-9\.] ', b"", body)
-
# 然后用新body来替换之前的body,就出现了复制的请求request_copy。
-
request_copy = request.replace(body=body)
-
# 再对复制的请求request_copy进行指纹输出用于去重
-
fp = self.request_fingerprint(request_copy)
-
# 指纹结果用于add进去重集合,如果可以add进,则集合无该请求,added=1, 否则added=0。added的值表示add进集合的个数。
-
added = self.server.sadd(self.key, fp)
-
# 如果added=1,表示无重复,返回False,反之有重复,返回True
-
return added == 0
最后我们只需要在settings.py文件中对过滤器进行指定,即根据情况选择输入以下代码:
-
DUPEFILTER_CLASS = "myproject.dupefilter.GetRandomDupefilter" # GET方法的url链接参数可变量的过滤器
-
DUPEFILTER_CLASS = "myproject.dupefilter.PostJsonRandomDupefilter" # POST方法的JSON参数的可变量的过滤器
-
DUPEFILTER_CLASS = "myproject.dupefilter.PostFormRandomDupefilter" # POST方法的form参数的可变量的过滤器
-
# 根据具体情况决定使用哪种过滤器
以上方法是将可变量替换成空字符串(或者删除可变量键值对)的操作,如果可变量太多的话,我们可以用re.findall()提取出不变的量用作新的url或者body来去重。
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgfibac
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01