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

Scrapy框架中如何正确拆分爬虫的多套页面解析逻辑

问题根因

你当前的写法无法拿到全量解析结果,核心有三个问题:

  • Scrapy调度器默认开启URL去重,你在parse方法中对同一个详情页URL连续发起2次携带不同回调的请求,第二个请求会直接被过滤丢弃,仅会执行第一个绑定的回调。
  • BaseParser类存在基础语法错误:parse_price方法直接调用未定义的response变量,类本身未实现xpath方法却在parse_ex中直接调用self.xpath,运行时会直接抛出异常,即便请求正常到达也无法返回正确结果。
  • 你错误地把代码层面的解析逻辑拆分,等同于HTTP请求链路拆分:把不同字段的提取逻辑绑定到独立的请求回调上,没有做数据聚合,即便两个请求都执行完成,也只会返回两个独立的零散字典,无法得到关联的完整数据。
正确拆分方式

解析逻辑拆分是代码结构层面的拆分,不需要为了不同解析规则重复请求同一个页面,按以下三层调整即可:

1. 修正重复请求逻辑

同一URL仅发起一次请求,在回调中完成所有解析逻辑调用,避免无意义的带宽浪费。如果确实需要对同一URL发起多次请求(比如需要携带不同cookie/请求头),可以在Request中传入dont_filter=True关闭去重,但非必要不这么做。
原来的错误写法:

# 同URL重复发请求,第二个会被去重过滤
yield scrapy.Request(response.urljoin(url), callback=self.parse_requem)
yield scrapy.Request(response.urljoin(url), callback=self.parse_obj)

替换为:

# 同URL仅发一次请求,统一进入详情页解析回调
yield scrapy.Request(response.urljoin(url), callback=self.parse_detail)

2. 补全BaseParser基础实现

把解析类的基础依赖绑定到实例上,每个字段的解析逻辑拆分为独立方法,最后通过构造方法按需聚合返回:

from scrapy import Selector

class BaseParser:
    def __init__(self, response):
        # 把response和选择器绑定到实例,供各个解析方法调用
        self.response = response
        self.selector = Selector(response=response)

    # 单个字段解析逻辑独立拆分,互不影响,方便后续修改规则
    def parse_price(self):
        # 修正原来直接调用未定义response的问题
        price = self.response.body
        return price

    def parse_ex(self):
        # 修正原来直接调用不存在的self.xpath的问题
        exists = self.selector.xpath('//text()').extract_first()
        return exists

    # 按需返回不同结构的解析结果,不需要额外发请求
    def construct_price_item(self):
        return {"price": self.parse_price()}

    def construct_ex_item(self):
        return {"ex": self.parse_ex()}

    # 如果需要全量字段,直接聚合所有解析结果即可
    def construct_full_item(self):
        item = {}
        item.update(self.construct_price_item())
        item.update(self.construct_ex_item())
        return item

如果后续不同站点/不同页面的解析规则有差异,直接继承BaseParser重写对应字段的解析方法即可,不需要修改爬虫主逻辑。

3. 简化爬虫侧回调逻辑

去掉不必要的中间请求跳转,在同一个详情页回调中调用所有需要的解析方法:

def parse(self, response):
    xpath = '//div[@class="proinfor"]//div[@class="prolist_casinforimg"]/a/@href'
    urls = response.xpath(xpath).extract()
    for url in urls:
        url = url.replace("//", "", 1)
        yield scrapy.Request(
            response.urljoin(url),
            callback=self.parse_detail
        )

def parse_detail(self, response):
    parser = BaseParser(response)
    # 需要什么结构的数据,直接调用对应的构造方法即可
    yield parser.construct_price_item()
    yield parser.construct_ex_item()

# 如果你原来的parse_requem是需要跳转到其他接口拿额外数据,用meta传递已解析的字段做聚合即可
def parse_requem(self, response):
    # 先拿到当前页已经解析的内容
    exist_item = BaseParser(response).construct_ex_item()
    # 发起下一个请求时,把已解析的数据通过meta传递
    yield scrapy.Request(
        url="价格接口地址",
        callback=self.parse_item,
        meta={"item": exist_item}
    )

def parse_item(self, response):
    # 取出之前传递的已解析数据
    item = response.meta["item"]
    # 补充新解析的价格字段
    item["price"] = response.body
    yield item
注意事项
  • 不要为了拆分解析逻辑刻意拆分HTTP请求,只有当数据确实在不同的URL下时,才需要通过多请求+meta传参的方式聚合数据。
  • 解析规则的修改尽量收敛在Parser类中,爬虫层只负责请求调度和页面跳转,不要把xpath选择器、字段处理逻辑全写在回调函数里,方便后续维护。

内容的提问来源于stack exchange,提问作者Rodion Ilnitskiy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 00:18:26