如何在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'

【讨论】:

我应该把文件放在哪里? @WilliamKinaan custom_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过滤重复请求的主要内容,如果未能解决你的问题,请参考以下文章

如何在 java Servlet 过滤器中更改传入 URL 并将请求重定向到新 URL [重复]

Scrapy五大核心组件工作流程

如何让 Scrapy 在日志中根据下载请求显示用户代理?

python Scrapy框架 报301

如何在过滤器映射中排除 url [重复]

如何获取scrapy失败的URL?