如何在scrapy中基于url过滤重复请求

时间:2012-09-23 14:25:07

标签: python web-crawler scrapy

我正在为使用CrawlSpider scrapy的网站编写一个抓取工具。

Scrapy提供了一个内置的重复请求过滤器,可根据网址过滤重复的请求。此外,我可以使用CrawlSpider的规则成员过滤请求。

我想要做的是过滤以下请求:

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它不应该根据网址发送特定请求的方式是什么?

5 个答案:

答案 0 :(得分:39)

您可以编写自定义中间件以进行重复删除,并将其添加到设置中

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'

它应该在那之后工作

答案 1 :(得分:10)

在ytomar的带领下,我编写了这个过滤器,它基于通过检查内存集已经看到的URL进行过滤。我是一个Python noob,所以如果我搞砸了,请告诉我,但它似乎可以正常工作:

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'

答案 2 :(得分:3)

https://github.com/scrapinghub/scrapylib/blob/master/scrapylib/deltafetch.py

此文件可能对您有所帮助。此文件从url创建唯一delta获取密钥的数据库,用户在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

例如。 id = 345 scrapy.Request(URL,元= {deltafetch_key:345},回调=解析)

答案 3 :(得分:1)

这是我的自定义过滤器基于scrapy 0.24.6。

在此过滤器中,它只关注网址中的id。例如

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 retainAll

被视为相同的网址。但

public List<Bongo> getBongos(List<String> names) { Map<String, Wongo> copy = new HashMap<>(nameToWongoMap); copy.keySet().retainAll(names); return copy.values().stream().map(Bongo::new).collect( Collectors.toList()); }

不会。

SELECT * FROM XSales_Code SC
    WHERE SC.Status = 1
        AND SC.SCode NOT IN
            (
            SELECT DISTINCT SCode FROM XTransactions_01
            WHERE Last_Mdt > '2012-01-01'
                AND SCode IS NOT NULL
            )
        AND SC.Last_Mdt < '2014-01-01'
ORDER BY Last_Mdt desc

答案 4 :(得分:0)

在最新的scrapy中,我们可以使用默认的复制过滤器或扩展并具有自定义过滤器。

在蜘蛛设置中定义以下配置

private var blankWindow: BlankWindow? // MARK: Shared AppDelegate extension AppDelegate { static func blankWindowShouldAppear(blankWindow: inout BlankWindow?) { blankWindow = BlankWindow(frame: UIScreen.main.bounds) blankWindow?.makeKeyAndVisible() } static func blankWindowShouldDisappear(window: UIWindow?, blankWindow: inout BlankWindow?) { window?.makeKeyAndVisible() blankWindow = nil } @available(iOS 13.0, *) static func blankWindowShouldAppear(_ windowScene: UIWindowScene, blankWindow: inout BlankWindow?) { blankWindow = BlankWindow(windowScene: windowScene) blankWindow?.makeKeyAndVisible() } } // MARK: Old life cycle methods extension AppDelegate { /// ⚠️ Methods here will not be called under iOS 13 due to new SceneDelegate life cycle func applicationWillEnterBackground(_ application: UIApplication) { AppDelegate.blankWindowShouldAppear(blankWindow: &blankWindow) } func applicationWillEnterForeground(_ application: UIApplication) { AppDelegate.blankWindowShouldDisappear(window: window, blankWindow: &blankWindow) } }