You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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仅包含某一个站点的商品数据,每次运行可能对应不同站点,但始终只有一个站点的结果。请问实际原因是什么?如何获取所有站点的输出结果?


原因分析

核心问题有两个:

  1. 类共享变量污染:你用self.main_url存储当前站点域名,但self是爬虫类的全局实例属性。当遍历start_urls时,后续站点的域名会覆盖self.main_url的值——比如先处理.com站点设置了self.main_url,接着处理.de站点时会更新这个值,此时之前排队的.com站点的crawl请求会错误使用新的.de域名拼接商品链接,导致这些请求要么失败,要么爬取的是.de站点的内容,最终只有最后一个被设置的站点数据能正确输出。
  2. 默认文件写入模式: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 01:52:44