如何在scrapy中根据url过滤重复请求
Posted
技术标签:
【中文标题】如何在scrapy中根据url过滤重复请求【英文标题】:how to filter duplicate requests based on url in scrapy 【发布时间】:2012-09-15 05:01:45 【问题描述】:我正在使用带有 CrawlSpider 的 scrapy 为网站编写爬虫。
Scrapy 提供了一个内置的重复请求过滤器,它根据 url 过滤重复请求。另外,我可以使用 CrawlSpider 的 rules 成员过滤请求。
我想要做的是过滤请求:
http:://www.abc.com/p/xyz.html?id=1234&refer=5678
如果我已经去过
http:://www.abc.com/p/xyz.html?id=1234&refer=4567
注意:refer 是一个不会影响我得到的响应的参数,所以我不在乎该参数的值是否发生变化。
现在,如果我有一个集合所有 ids 我可以在我的回调函数 parse_item (这是我的回调函数)中忽略它以实现此功能。
但这意味着我至少仍在获取该页面,而我不需要。
那么我可以通过什么方式告诉 scrapy 它不应该根据 url 发送特定请求?
【问题讨论】:
【参考方案1】:您可以编写自定义中间件以进行重复删除并将其添加到设置中
import os
from scrapy.dupefilter import RFPDupeFilter
class CustomFilter(RFPDupeFilter):
"""A dupe filter that considers specific ids in the url"""
def __getid(self, url):
mm = url.split("&refer")[0] #or something like that
return mm
def request_seen(self, request):
fp = self.__getid(request.url)
if fp in self.fingerprints:
return True
self.fingerprints.add(fp)
if self.file:
self.file.write(fp + os.linesep)
那么你需要在settings.py中设置正确的DUPFILTER_CLASS
DUPEFILTER_CLASS = 'scraper.duplicate_filter.CustomFilter'
它应该在那之后工作
【讨论】:
我将您的代码放在蜘蛛文件夹中的一个文件中,但我收到此错误`dupefilter = dupefilter_cls.from_settings(settings) exceptions.AttributeError: 'module' object has no attribute 'from_settin gs'` 谢谢,这可行,但我如何从我的 customfilter 类中访问spider
对象?【参考方案2】:
在 ytomar 的带领下,我编写了这个过滤器,它完全基于通过检查内存集已经看到的 URL 进行过滤。我是 Python 新手,所以如果我搞砸了,请告诉我,但它似乎可以正常工作:
from scrapy.dupefilter import RFPDupeFilter
class SeenURLFilter(RFPDupeFilter):
"""A dupe filter that considers the URL"""
def __init__(self, path=None):
self.urls_seen = set()
RFPDupeFilter.__init__(self, path)
def request_seen(self, request):
if request.url in self.urls_seen:
return True
else:
self.urls_seen.add(request.url)
正如 ytomar 所说,一定要将DUPEFILTER_CLASS
常量添加到settings.py
:
DUPEFILTER_CLASS = 'scraper.custom_filters.SeenURLFilter'
【讨论】:
我应该把文件放在哪里? @WilliamKinaancustom_filters.py
是我放置它的位置,与settings.py
在同一目录中。但是我最终只使用了scrapy的默认URL过滤器,因为它对我来说已经足够好了。这更像是学习如何编写自定义过滤器的练习。我没有看过内部实现,但听说它使用了bloom filter,它提供了更高的查找性能(以可能重新访问一些 URL为代价)。
感谢您的评论。另外,请问scrapy's default URL filter
是什么?另外,您可以发布它的官方文档吗?提前致谢
@WilliamKinaan 默认过滤器是RFPDupeFilter
类,来源:github.com/scrapy/scrapy/blob/… 至于文档,我怀疑这个特定类是否有任何文档。或许将您的问题发布到 scrapy 邮件列表:groups.google.com/forum/#!forum/scrapy-users
感谢您的评论。我知道即使我像上面的答案一样创建了一个继承自RFPDupeFilter
的类,或者我只是将设置中的DUPEFILTER_CLASS
变量设置为RFPDupeFilter
对吗?【参考方案3】:
https://github.com/scrapinghub/scrapylib/blob/master/scrapylib/deltafetch.py
此文件可能会对您有所帮助。该文件从 url 创建一个唯一 delta fetch key 的数据库,用户传入一个 scrapy.Reqeust(meta='deltafetch_key':uniqe_url_key)。 这可以让您避免过去已经访问过的重复请求。
使用 deltafetch.py 的示例 mongodb 实现
if isinstance(r, Request):
key = self._get_key(r)
key = key+spider.name
if self.db['your_collection_to_store_deltafetch_key'].find_one("_id":key):
spider.log("Ignoring already visited: %s" % r, level=log.INFO)
continue
elif isinstance(r, BaseItem):
key = self._get_key(response.request)
key = key+spider.name
try:
self.db['your_collection_to_store_deltafetch_key'].insert("_id":key,"time":datetime.now())
except:
spider.log("Ignoring already visited: %s" % key, level=log.ERROR)
yield r
例如。编号 = 345 scrapy.Request(url,meta=deltafetch_key:345,callback=parse)
【讨论】:
【参考方案4】:这是我基于 scrapy 0.24.6 的自定义过滤器。
在这个过滤器中,它只关心 url 中的 id。例如
http://www.example.com/products/cat1/1000.html?p=1
http://www.example.com/products/cat2/1000.html?p=2
被视为相同的网址。但是
http://www.example.com/products/cat2/all.html
不会。
import re
import os
from scrapy.dupefilter import RFPDupeFilter
class MyCustomURLFilter(RFPDupeFilter):
def _get_id(self, url):
m = re.search(r'(\d+)\.html', url)
return None if m is None else m.group(1)
def request_fingerprint(self, request):
style_id = self._get_id(request.url)
return style_id
【讨论】:
很好,但是你有没有建议通过蜘蛛到蜘蛛的方式来做?【参考方案5】:在最新的scrapy中,我们可以使用默认的重复过滤器或扩展并自定义一个。
在蜘蛛设置中定义以下配置
DUPEFILTER_CLASS = 'scrapy.dupefilters.BaseDupeFilter'
【讨论】:
以上是关于如何在scrapy中根据url过滤重复请求的主要内容,如果未能解决你的问题,请参考以下文章