使用Puppeteer爬取温德姆酒店网站时搜索结果加载不稳定问题排查
问题
尝试使用Puppeteer对温德姆酒店官网进行爬取,但搜索结果无法稳定加载,已尝试更换UserAgent、不拦截资源,均无效,询问该网站是否存在反爬机制。

原代码:
const puppeteer = require("puppeteer"); // ^21.0.2 const {setTimeout} = require("node:timers/promises"); const url = "https://www.wyndhamhotels.com/wyndham"; const navClick = async (page, sel) => { const nav = page.waitForNavigation(); await page.click(sel); return nav; }; let browser; (async () => { browser = await puppeteer.launch({headless: false}); const [page] = await browser.pages(); await page.goto(url); await page.type('[name="destination"]', "Chicago, IL, USA"); await navClick(page, ".search-btn.btn-primary"); const el = await page.waitForSelector("#chk-show-avail"); await page.waitForFunction(` !document.querySelector("#chk-show-avail").disabled `); await el.evaluate(el => el.click()); // not part of the logic; temporarily added so you can see // the box was ticked (screenshots can be hidden by modals) await setTimeout(60_000); })() .catch(err => console.error(err)) .finally(() => browser?.close());
回答
温德姆酒店官网确实存在反爬机制,针对自动化工具(如Puppeteer)有特征检测,同时你的代码在等待逻辑、交互模拟上也存在问题,导致搜索结果无法稳定加载。以下是具体解决思路和优化方案:
核心问题分析
- Puppeteer特征暴露:默认情况下,Puppeteer会留下
window.navigator.webdriver等特征,仅更换UserAgent不足以绕过检测。 - 错误的等待逻辑:搜索属于单页应用的AJAX请求,并非页面跳转,原代码中
waitForNavigation会导致不必要的等待甚至超时,错过结果加载时机。 - 机械操作被识别:输入、点击操作过于迅速,不符合正常用户的操作节奏,容易触发反爬规则。
解决方案
1. 隐藏自动化特征
使用puppeteer-extra搭配stealth插件,一键消除Puppeteer的常见识别特征,这是绕过这类反爬的关键。
2. 优化等待逻辑
放弃waitForNavigation,改用waitForNetworkIdle等待搜索请求完成,或直接等待结果列表元素出现,确保页面完全加载。
3. 模拟人类操作节奏
在输入、点击之间添加合理延迟,避免操作过于机械。
4. 处理前置弹窗
网站通常会有Cookie授权弹窗,需先处理避免阻塞后续操作。
优化后的代码
const puppeteer = require('puppeteer-extra'); const StealthPlugin = require('puppeteer-extra-plugin-stealth'); const { setTimeout } = require('node:timers/promises'); puppeteer.use(StealthPlugin()); const url = "https://www.wyndhamhotels.com/wyndham"; let browser; (async () => { browser = await puppeteer.launch({ headless: false, defaultViewport: null, args: [ '--start-maximized', '--disable-blink-features=AutomationControlled' ] }); const [page] = await browser.pages(); // 设置真实浏览器环境参数 await page.setExtraHTTPHeaders({ 'Accept-Language': 'en-US,en;q=0.9' }); await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'); await page.goto(url, { waitUntil: 'networkidle2' }); // 处理Cookie弹窗 try { await page.waitForSelector('#onetrust-accept-btn-handler', { timeout: 5000 }); await page.click('#onetrust-accept-btn-handler'); await setTimeout(1000); } catch (err) { console.log('无Cookie弹窗或已自动处理'); } // 模拟人类输入目的地 await page.click('[name="destination"]'); await setTimeout(500); await page.type('[name="destination"]', "Chicago, IL, USA", { delay: 100 }); await setTimeout(1000); // 点击搜索并等待请求完成 await page.click(".search-btn.btn-primary"); await page.waitForNetworkIdle({ idleTime: 2000, timeout: 30000 }); // 处理"仅显示可预订酒店"复选框 try { const el = await page.waitForSelector("#chk-show-avail", { timeout: 10000 }); await page.waitForFunction(el => !el.disabled, {}, el); await el.click(); await setTimeout(2000); // 等待结果列表加载完成 await page.waitForSelector('.hotel-list-item', { timeout: 15000 }); console.log('搜索结果已稳定加载'); } catch (err) { console.error('加载失败或超时:', err); } await setTimeout(60_000); })() .catch(err => console.error(err)) .finally(() => browser?.close());
额外注意事项
- 若仍不稳定,可尝试使用代理更换IP,避免频繁请求被封禁。
- 控制请求频率,不要短时间内重复发起大量请求。
- 网站反爬规则可能更新,需定期检查选择器和等待逻辑是否有效。
内容的提问来源于stack exchange,提问作者Nelson Mak
相关产品推荐
相关产品推荐

