Python中如何设置Scrapy每个网站最大爬行时间,以及实现条件触发放弃某个目录

RT,现在鄙人有两个需求:

1.设置 scrapy 的每个站的最大爬行时间。

由于最近需要批量采集一些网站,然后部分网站可能是论坛和博客之类的.
我这边如果没法预判网站类型的话,希望在 1-2 小时候把超时的网站直接 ban 掉。
但谷歌了下并没有看见 setting.py 里有解决方案。

2.条件触发放弃爬行某个目录

在爬行的时候,可能会遇到多重目录,比如日期+随机码,案例如下:
http://test.com/mua/213213/123213.shtml

针对这类我已经设置了去重算法,但是无奈一旦爬到那个目录,他会一直爬下去。 就算立即抛弃已经爬到的目录也会耽误很久。 所以想问问各位大佬,比如有没有到了某个频率触发条件,可以直接放弃 mua 目录下的爬行任务的法子? 同样没有谷歌到解决方案。


Python中如何设置Scrapy每个网站最大爬行时间,以及实现条件触发放弃某个目录

2 回复

在Scrapy里控制每个网站的爬行时间,可以用DOWNLOAD_TIMEOUT设置单个请求超时,但这不是全局的。要限制整个网站的爬行时间,得用自定义扩展或中间件。我一般用spider_idle信号配合计时器来实现。

下面这个扩展类能解决你的问题。把它放到项目的extensions.py里,然后在settings.py里启用:

# extensions.py
import time
from scrapy import signals
from scrapy.exceptions import NotConfigured

class SiteTimeLimitExtension:
    def __init__(self, time_limit, crawler):
        self.time_limit = time_limit
        self.crawler = crawler
        self.start_time = None
        self.timeout_triggered = False
        
    @classmethod
    def from_crawler(cls, crawler):
        time_limit = crawler.settings.getint('SITE_TIME_LIMIT', 300)  # 默认5分钟
        if not time_limit:
            raise NotConfigured
        ext = cls(time_limit, crawler)
        crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
        crawler.signals.connect(ext.spider_idle, signal=signals.spider_idle)
        return ext
    
    def spider_opened(self, spider):
        self.start_time = time.time()
        spider.logger.info(f"站点时间限制已启用: {self.time_limit}秒")
    
    def spider_idle(self, spider):
        if self.timeout_triggered:
            return
            
        elapsed = time.time() - self.start_time
        if elapsed >= self.time_limit:
            self.timeout_triggered = True
            spider.logger.info(f"达到站点时间限制 {self.time_limit}秒,停止爬取")
            self.crawler.engine.close_spider(spider, 'time_limit_reached')

关于条件触发放弃某个目录,可以在爬虫里用rules配合process_links回调,或者在parse方法里判断。比如只爬/blog/目录,遇到其他目录就跳过:

from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

class MySpider(CrawlSpider):
    name = 'example'
    allowed_domains = ['example.com']
    start_urls = ['http://example.com/']
    
    rules = (
        Rule(LinkExtractor(allow=r'/blog/.*'), callback='parse_item', follow=True),
    )
    
    def parse_item(self, response):
        # 处理/blog/目录下的页面
        yield {'url': response.url}
    
    def process_links(self, links):
        # 过滤掉非/blog/目录的链接
        filtered = []
        for link in links:
            if '/blog/' in link.url:
                filtered.append(link)
            else:
                self.logger.debug(f"跳过非目标目录: {link.url}")
        return filtered

如果要在运行时动态判断,比如某个目录响应太慢就放弃,可以在下载中间件里实现:

# middlewares.py
from scrapy.http import Request
from scrapy.exceptions import IgnoreRequest

class DirectorySkipMiddleware:
    def process_request(self, request, spider):
        # 如果URL包含要跳过的目录模式
        skip_patterns = getattr(spider, 'skip_directories', [])
        for pattern in skip_patterns:
            if pattern in request.url:
                spider.logger.info(f"跳过目录 {pattern}: {request.url}")
                raise IgnoreRequest()
        return None

settings.py里启用这些组件:

EXTENSIONS = {
    'myproject.extensions.SiteTimeLimitExtension': 500,
}
DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.DirectorySkipMiddleware': 543,
}
SITE_TIME_LIMIT = 600  # 10分钟

这样就能同时控制网站总爬行时间和跳过特定目录了。总结:用扩展控制时间,用中间件或规则过滤目录。


没人么…v 站的大佬们可以给点建议么?

回到顶部