Scrapy+Playwright无限滚动失效:response对象无HTML内容
问题分析与解决方案
核心问题点
- 滚动脚本变量错误:脚本定义了
const scrolls = 8,但判断条件用了未定义的numScrolls,导致定时器无法停止,页面持续滚动,后续等待逻辑失效。 - CSS选择器写法错误:多class选择器需用
.连接(如._1E5b._2I59...),原代码用空格是后代选择器语法,无法匹配目标元素,导致wait_for_selector超时,页面未完成加载就返回空响应。 - 异步滚动未等待完成:
evaluate执行滚动脚本后立即返回,不会等待滚动全部完成,后续等待逻辑无法同步滚动进度。 - 请求中止函数缩进错误:
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
相关产品推荐
相关产品推荐

