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 站的大佬们可以给点建议么?

