无法在CrawlSpider内修改allowed_domains的并行爬虫问题求助
解决Scrapy并行爬虫中OffsiteMiddleware不识别动态设置allowed_domains的问题
问题核心在于默认OffsiteMiddleware会在爬虫初始化阶段就缓存allowed_domains,而你在start_requests中设置该属性的时机太晚,中间件已经完成初始化,无法读取到新值。以下是三种可行的解决方案:
方案1:在爬虫初始化时直接设置allowed_domains
重写爬虫的__init__方法,在初始化阶段就从settings中读取并赋值allowed_domains,确保OffsiteMiddleware初始化时能获取到正确的域名列表:
from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor class DomainSpecificSpider(CrawlSpider): name = "domain_spider" rules = ( Rule(LinkExtractor(), callback='parse_item', follow=True), ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 初始化阶段就从settings中读取allowed_domains和start_url self.allowed_domains = self.settings['crawl_start'][1] self.start_urls = [self.settings['crawl_start'][0]] def parse_item(self, response): # 你的解析逻辑 pass
这种方法无需修改中间件,实现简单,适合大多数场景。
方案2:自定义OffsiteMiddleware支持动态读取allowed_domains
默认OffsiteMiddleware会在open_spider时缓存allowed_domains,自定义中间件可以跳过缓存,每次检查请求时直接读取当前爬虫的allowed_domains:
- 首先在项目的
settings.py中禁用默认中间件,启用自定义版本:
SPIDER_MIDDLEWARES = { 'your_project.middlewares.CustomOffsiteMiddleware': 500, 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None, }
- 在
middlewares.py中编写自定义中间件:
from scrapy.spidermiddlewares.offsite import OffsiteMiddleware class CustomOffsiteMiddleware(OffsiteMiddleware): def should_follow(self, request, spider): # 每次检查请求时,实时读取爬虫当前的allowed_domains if not spider.allowed_domains: return True # 提取请求的域名并检查是否在允许列表内 request_domain = request.url.split('/')[2] return any(request_domain.endswith(domain) for domain in spider.allowed_domains)
这种方法适合需要在爬取过程中动态调整allowed_domains的场景。
方案3:启动爬虫时直接传入参数(推荐多进程场景)
在多进程启动爬虫时,直接将allowed_domains和start_url作为参数传入爬虫,避免依赖settings传递,彻底消除竞争条件:
- 多进程启动代码示例:
from multiprocessing import Process from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings from your_project.spiders import DomainSpecificSpider def run_single_spider(start_url, allowed_domains): settings = get_project_settings() process = CrawlerProcess(settings) # 直接将参数传入爬虫 process.crawl(DomainSpecificSpider, allowed_domains=allowed_domains, start_urls=[start_url]) process.start() # 定义需要爬取的域名列表 crawl_tasks = [ ("https://example.com", ["example.com"]), ("https://test.org", ["test.org"]), ] # 启动多进程 processes = [] for url, domains in crawl_tasks: p = Process(target=run_single_spider, args=(url, domains)) p.start() processes.append(p) for p in processes: p.join()
- 爬虫代码调整:
class DomainSpecificSpider(CrawlSpider): name = "domain_spider" rules = ( Rule(LinkExtractor(), callback='parse_item', follow=True), ) def __init__(self, allowed_domains=None, start_urls=None, *args, **kwargs): super().__init__(*args, **kwargs) if allowed_domains: self.allowed_domains = allowed_domains if start_urls: self.start_urls = start_urls def parse_item(self, response): # 你的解析逻辑 pass
这种方法参数传递更清晰,每个进程的爬虫配置独立,完全避免了settings共享导致的竞争问题。
内容的提问来源于stack exchange,提问作者Stevod
相关产品推荐
相关产品推荐

