scrapy中自定义过滤规则以及start_urls不进过滤器的问题

为什么要自定义过滤规则呢? 首先,我们需要过滤,但是不是说抓一次就不抓了,因为我们的抓取是一段时间抓取一次

自定义策略如下:

首先我试图直接继承RFPDupeFilter

在settings.py同级的目录下新建dupefilter.py文件,按照网上说的方法,写了内容如下

from scrapy.dupefilter import RFPDupeFilter
import hashlib
from scrapy.utils.request import request_fingerprint
from scrapy.dupefilter import BaseDupeFilter


class URLFilter(RFPDupeFilter):

    def __init(self):
        RFPDupeFilter.__init__(self)

    def request_seen(self, request):

        fp = self.request_fingerprint(request)
        added = self.server.sadd(self.key, fp)

        return added == 0

在settings.py中添加

DUPEFILTER_CLASS = 'CrawlBaiduMobile.dupefilter.URLFilter'

但是启动spider会报如下错误:

ValueError: ("Failed to instantiate dupefilter class '%s': %s", 'CrawlBaiduMobile.dupefilter.URLFilter', TypeError("__init__() got an unexpected keyword argument 'debug'",))

或者报

ValueError: ("Failed to instantiate dupefilter class '%s': %s", 'CrawlBaiduMobile.dupefilter.URLFilter', TypeError("__init__() got an unexpected keyword argument 'key'",))

含义其实也很明显, 调用RFPDupeFilter的构造方法时出错了,可能是我的版本比较新吧。

于是我找到了RFPDupeFilter的源码,写了一个基本和它一致的, 但是过滤策略有些不同

from scrapy_redis.dupefilter import RFPDupeFilter
from scrapy_redis.connection import get_redis_from_settings
from scrapy.dupefilters import BaseDupeFilter
from scrapy.utils.request import request_fingerprint
from scrapy_redis import defaults
import hashlib

class URLFilter(BaseDupeFilter):

    def __init__(self, server, key, debug=False):
        self.server = server
        self.key = key
        self.debug = debug
        self.logdupes = True
        self.timeout = 60*60*24*10    #设置过期时间10天,10天内重复的url不重复抓取

    @classmethod
    def from_settings(cls, settings):
        server = get_redis_from_settings(settings)
        key = defaults.DUPEFILTER_KEY % {'timestamp': int(time.time())}
        debug = settings.getbool('DUPEFILTER_DEBUG')
        return cls(server, key=key, debug=debug)

    @classmethod
    def from_crawler(cls, crawler):
        return cls.from_settings(crawler.settings)

    def request_seen(self, request):
        key = self.request_fingerprint(request)
        if self.server.get(key):
            return True
        else:
            self.server.set(key, 1, self.timeout) 

    def request_fingerprint(self, request):
        return hashlib.md5(request.url).hexdigest()

我依然用了redis存储缓存key,但是用的字符串,没有再用有序集合,因为我们的策略是一段时间内不重复抓取,所以需要给抓取过的key设置一个缓存时间

此外,缓存key的生成方式也做了修订,改用了md5算法生成了一个40位的key

 

在实验过程中发现一个问题,start_urls中的url没有进这个过滤器

首先看下start_urls中的初始url是如何被构造成Request的

def start_requests(self):
    for url in self.start_urls:
        yield self.make_requests_from_url(url)

def make_requests_from_url(self, url):
    return Request(url, dont_filter=True)

在start_requests中会将start_urls里面的url(当然也可以是redis中的),通过make_requests_from_url方法构造成Request对象

但是传参dont_filter=True,也就是不经过过滤器的,所以默认情况下,初始start_urls中的url不会经过上面配置的过滤器

可以在spider中覆写这个方法, 设置dont_filter=False:

class CrawlBaiduMobile(RedisCrawlSpider):

    #some other code

    def make_requests_from_url(self, url):
        return Request(url, dont_filter=False)

此外,图片下载的Request也没有进这个过滤器

配置页面中的图片下载:

class CrawlImagePipeline(ImagesPipeline):

    def get_media_requests(self, item, info):
        if isinstance(item, CrawlImageItem):
            for image_url in item['image_urls']:
                self.default_headers['referer'] = image_url
                yield Request(image_url, headers=self.default_headers, dont_filter=False)

这里也用了yield Request,  但是这个Request不会进入到上面配置的过滤器中, 即使给了dont_filter=False,当然更具体是什么原因,我没有深究了, 因为我们的系统对于抓取图片有不同的过滤规则,的确也不需要经过这个过滤器

图片下载应该是有一套自己的排重策略,就是用过期时间,可以在settings.py中配置,

IMAGES_EXPIRES = 90   #90天内重复的图片不会再下载, 如果服务重启了,之前保存的下载过的图片会重新下载

 

你可能感兴趣的:(Python)