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

Scrapy+Playwright无限滚动失效:response对象无HTML内容

问题分析与解决方案

核心问题点

  1. 滚动脚本变量错误:脚本定义了const scrolls = 8,但判断条件用了未定义的numScrolls,导致定时器无法停止,页面持续滚动,后续等待逻辑失效。
  2. CSS选择器写法错误:多class选择器需用.连接(如._1E5b._2I59...),原代码用空格是后代选择器语法,无法匹配目标元素,导致wait_for_selector超时,页面未完成加载就返回空响应。
  3. 异步滚动未等待完成:evaluate执行滚动脚本后立即返回,不会等待滚动全部完成,后续等待逻辑无法同步滚动进度。
  4. 请求中止函数缩进错误:should_abort_request中return False缩进不正确,导致函数逻辑混乱,可能错误中止必要请求。

修正步骤

  • 修复滚动脚本变量名错误,将numScrolls改为scrolls,并封装为Promise确保Scrapy等待滚动完成。
  • 修正CSS选择器的多class写法,用.连接每个类名。
  • 调整滚动脚本为Promise形式,让evaluate等待滚动全部完成后再执行后续操作。
  • 修正should_abort_request的缩进问题,确保逻辑正确。
  • 直接从Playwright的page对象获取页面HTML,避免依赖response的潜在同步问题。

修正后的完整代码

import scrapy
import re
from scrapy_playwright.page import PageMethod
from bs4 import BeautifulSoup


def should_abort_request(req):
    if req.resource_type == "image":
        return True
    if req.method.lower() == 'post':
        return True
    return False  # 修复缩进错误


scrolling_script = """
const scrolls = 8;
let scrollCount = 0;

return new Promise((resolve) => {
    const scrollInterval = setInterval(() => {
        window.scrollTo(0, document.body.scrollHeight);
        scrollCount++;

        if (scrollCount === scrolls) {
            clearInterval(scrollInterval);
            // 最后一次滚动后额外等待2秒,确保内容加载完成
            setTimeout(resolve, 2000);
        }
    }, 5000);
});
"""


class AuchanSpider(scrapy.Spider):
    name = 'auchan'
    custom_settings = {
        'PLAYWRIGHT_ABORT_REQUEST': should_abort_request
    }
    start_urls = ['https://zakupy.auchan.pl/shop/list/8029?shType=id']

    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(
                url=url,
                callback=self.parse,
                meta={
                    "playwright": True,
                    "playwright_include_page": True,
                    "playwright_page_methods": [
                        PageMethod("evaluate", scrolling_script),  # 等待滚动完成后再执行后续操作
                        # 修正选择器:用.连接多class
                        PageMethod("wait_for_selector", "._1E5b._2I59._1wkJ._3YFw.igxN._7Zx6.Eb4X._390_"),
                        PageMethod("wait_for_selector", "._1E5b._2I59._1wkJ._3YFw.igxN._7Zx6.Eb4X._390_:nth-child(60)")
                    ],
                },
                errback=self.close_page,
                cb_kwargs=dict(main_url=url, page_number=0),
            )

    async def parse(self, response, main_url, page_number):
        # 直接从page对象获取最新HTML,确保拿到滚动后的完整内容
        page = response.meta["playwright_page"]
        page_html = await page.content()
        soup = BeautifulSoup(page_html, 'html.parser')
        
        product_containers = soup.find_all('div', class_='_1E5b _2I59 _1wkJ _3YFw igxN _7Zx6 Eb4X _390_')
        for product_container in product_containers:
            # 增加异常处理,避免个别元素缺失导致爬虫中断
            try:
                price = product_container.find(class_='_1-UB _1Evs').get_text(strip=True)
                price = re.sub(r"[\n\t\s]*", "", price)
                yield {
                    'productName': product_container.find(class_='_1DGZ').get_text(strip=True),
                    'price': price
                }
            except AttributeError:
                self.logger.warning(f"跳过异常商品容器: {product_container}")
                continue

    async def close_page(self, failure):
        page = failure.request.meta["playwright_page"]
        await page.close()

额外说明

  • 滚动脚本改为Promise后,evaluate会等待8次滚动全部完成,最后额外等待2秒确保新商品内容完全渲染。
  • 增加异常处理,避免个别商品元素缺失导致整个爬虫任务中断。
  • 从page对象直接获取HTML,确保拿到的是滚动后的最新页面内容,避免response同步延迟问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 07:40:24