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

Scrapy Playwright爬取Google Flights点击切日期返回重复值问题

问题根源

你的代码重复返回初始日期,是三个逻辑错误导致的:

  • 循环中始终读取初始请求返回的response.text:Scrapy的response是首次请求完成时的页面固定快照,后续通过Playwright操作页面切换日期后,这个快照不会自动同步更新,所以每次提取到的都是首次加载的7月3日数据。
  • 点击操作后未等待页面渲染:点击日期相关按钮后,页面需要时间加载新日期对应的航班数据、更新日期输入框值,你没有等待DOM更新完成就直接提取内容,读到的必然是旧数据。
  • 点击目标错误:你代码里写的长层级选择器是打开日期选择面板的按钮,不是直接切换到下一天的箭头按钮,点击后并不会自动切换日期。
修复方案

修复逻辑遵循以下步骤:

  1. 初始页面加载完成后,先提取第一天的日期和对应航班数据
  2. 后续每轮循环,点击日期栏带Next date标识的下一天切换按钮(这类带语义化aria-label的选择器比长层级CSS选择器稳定得多,不会因为前端调整DOM层级失效)
  3. 点击后等待页面加载完成:通过等待日期输入框的值更新、航班列表加载完成两个条件,确认新日期的内容已经渲染完毕
  4. 每次确认加载完成后,通过Playwright的page.content()获取当前最新的页面HTML,再做解析提取,不要复用初始的response对象
  5. 所有日期采集完成后关闭Playwright页面,避免资源泄漏

修复后的可运行代码如下:

import scrapy
from scrapy_playwright.page import PageCoroutine
from bs4 import BeautifulSoup

class PwExSpider(scrapy.Spider):
    name = "pw_ex"

    headers = {
        "authority": "www.google.com",
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "accept-language": "en,ru;q=0.9",
        "cache-control": "max-age=0",
        "referer": "https://www.google.com/",
        "sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="100", "Yandex";v="22"',
        "sec-ch-ua-arch": '"x86"',
        "sec-ch-ua-bitness": '"64"',
        "sec-ch-ua-full-version": '"22.5.0.1879"',
        "sec-ch-ua-full-version-list": '" Not A;Brand";v="99.0.0.0", "Chromium";v="100.0.4896.143", "Yandex";v="22.5.0.1879"',
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-model": '""',
        "sec-ch-ua-platform": '"Linux"',
        "sec-ch-ua-platform-version": '"5.4.0"',
        "sec-ch-ua-wow64": "?0",
        "sec-fetch-dest": "document",
        "sec-fetch-mode": "navigate",
        "sec-fetch-site": "same-origin",
        "sec-fetch-user": "?1",
        "upgrade-insecure-requests": "1",
        "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.143 Safari/537.36",
    }

    def start_requests(self):
        yield scrapy.Request(
            "https://www.google.com/travel/flights/search?tfs=CBwQAhooagwIAxIIL20vMDE3N3oSCjIwMjItMDctMDNyDAgDEggvbS8wNmM2MhooagwIAxIIL20vMDZjNjISCjIwMjItMDctMjJyDAgDEggvbS8wMTc3enABggELCP___________wFAAUgBmAEB&tfu=EgYIARABGAA&curr=EUR",
            headers=self.headers,
            meta=dict(
                playwright=True,
                playwright_include_page=True,
                playwright_page_coroutines=[
                    # 等待航班列表标题加载完成,确认初始页面渲染完毕
                    PageCoroutine("wait_for_selector", "h3.zBTtmb.ZSxxwc"),
                ],
            ),
        )

    async def parse(self, response):
        page = response.meta["playwright_page"]
        total_days = 5
        # 先提取第一天的数据
        current_html = await page.content()
        soup = BeautifulSoup(current_html, "html.parser")
        # 用placeholder属性定位出发日期输入框,比索引定位更稳定
        date_input = soup.find("input", attrs={"placeholder": "Departure"})
        current_date = date_input["value"] if date_input else ""
        yield {"search_date": current_date}

        # 循环采集剩余4天数据
        for _ in range(total_days - 1):
            # 定位下一天切换按钮
            next_btn = await page.wait_for_selector('button[aria-label="Next date"]', timeout=5000)
            await next_btn.click()
            
            # 等待日期值更新,确认页面切换完成
            await page.wait_for_function(
                """(oldDate) => {
                    const input = document.querySelector('input[placeholder="Departure"]');
                    return input && input.value !== oldDate;
                }""",
                arg=current_date,
                timeout=10000
            )
            # 额外等待航班列表渲染完成
            await page.wait_for_selector("h3.zBTtmb.ZSxxwc", state="visible")

            # 获取最新页面内容解析
            current_html = await page.content()
            soup = BeautifulSoup(current_html, "html.parser")
            date_input = soup.find("input", attrs={"placeholder": "Departure"})
            current_date = date_input["value"] if date_input else ""
            
            # 可在此处添加对应日期的航班数据提取逻辑
            yield {"search_date": current_date}
        
        # 采集完成关闭页面释放资源
        await page.close()
注意事项
  • 如果初始加载遇到Cookie同意弹窗,需要在等待航班列表加载前增加弹窗按钮的点击逻辑,否则后续按钮点击会被弹窗遮挡失效
  • 若网络环境较差,可以适当调大timeout参数的数值,避免等待超时抛出错误
  • 提取航班数据时,必须基于每次拿到的最新current_html做解析,不要复用旧的soup对象

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 05:06:05