Scrapy爬虫仅返回单个站点数据,如何获取全量结果?
问题
我正在爬取一个拥有多个国家站点的网站(示例:amazon.com、.mx、.fr、.de、.es等,实际并非亚马逊)。已构建各基础URL的列表,通过start_requests遍历每个URL并调用parse方法:
def start_requests(self): for url in self.start_urls: yield scrapy.Request(url, callback=self.parse)
同时有一组搜索关键词(示例:toshiba、apple、hp、ibm等)。在parse方法中,遍历每个关键词构造搜索URL(示例:amazon.de/search?={keyword}),并调用crawl回调:
def parse(self, response): for keyword in self.keywords: ...(构造带关键词的URL)... yield scrapy.Request(build_url, meta={'current_page': 1}, callback=self.crawl)
crawl方法会获取页面中每个商品列表的href,并发起请求调用followListing回调:
def crawl: ...(获取所有商品列表的href)... for listing in listings: yield scrapy.Request(self.main_url + href, callback=self.followListing,meta={'data':data})
data是Scrapy Item,在followListing方法中填充所需数据(如名称、描述、价格、image_urls等),最后通过yield data输出,期望保存到输出文件:
def followListing(self, response): ...(填充data Item)... yield data
但运行命令scrapy crawl my_crawler -o output.json时,output.json仅包含某一个站点的商品数据,每次运行可能对应不同站点,但始终只有一个站点的结果。请问实际原因是什么?如何获取所有站点的输出结果?
原因分析
核心问题有两个:
- 类共享变量污染:你用
self.main_url存储当前站点域名,但self是爬虫类的全局实例属性。当遍历start_urls时,后续站点的域名会覆盖self.main_url的值——比如先处理.com站点设置了self.main_url,接着处理.de站点时会更新这个值,此时之前排队的.com站点的crawl请求会错误使用新的.de域名拼接商品链接,导致这些请求要么失败,要么爬取的是.de站点的内容,最终只有最后一个被设置的站点数据能正确输出。 - 默认文件写入模式:Scrapy默认用
w(覆盖写入)模式处理输出文件,若多个站点的爬虫任务几乎同时完成,后写入的内容会覆盖先写入的,但这个概率较低,主要问题还是变量污染。
解决方法
1. 用meta传递站点域名,避免变量污染
不要用类属性存储当前站点域名,而是通过请求的meta字段把域名传递到后续回调:
- 在
parse中传递站点域名:
def parse(self, response): # 从当前响应URL提取主域名,比如从"https://example.de"中拿到"https://example.de" main_url = '/'.join(response.url.split('/')[:3]) for keyword in self.keywords: build_url = f"{main_url}/search?q={keyword}" # 构造搜索URL yield scrapy.Request( build_url, meta={'current_page': 1, 'main_url': main_url}, callback=self.crawl )
- 在
crawl中从meta取域名,而非self.main_url:
def crawl(self, response): main_url = response.meta['main_url'] data = YourItem() # 初始化你的Item类 # 获取商品列表href listings = response.css('your-selector::attr(href)').getall() for listing in listings: yield scrapy.Request( f"{main_url}{listing}", callback=self.followListing, meta={'data': data} )
2. 配置文件追加写入(可选,避免潜在覆盖)
在运行命令时指定追加模式,或在settings.py中全局配置:
- 命令行方式:
scrapy crawl my_crawler -o output.json -s FEED_WRITE_MODE=append -s FEED_EXPORT_ENCODING=utf-8
- 全局配置(settings.py):
FEED_WRITE_MODE = 'append' FEED_EXPORT_ENCODING = 'utf-8'
3. 给Item添加站点标记(验证用)
在followListing中给Item增加site字段,方便验证每个商品所属站点:
def followListing(self, response): data = response.meta['data'] # 提取站点域名作为标记 data['site'] = response.url.split('/')[2] # 填充其他字段 data['name'] = response.css('your-name-selector::text').get().strip() data['price'] = response.css('your-price-selector::text').get().strip() # ...其他字段填充 yield data
内容的提问来源于stack exchange,提问作者DrSocket
相关产品推荐
相关产品推荐

