如何在Puppeteer网页爬取中管理会话数据?
解决Puppeteer爬取immobilienscout24.de的会话管理与反爬问题
现有代码的核心问题
- 冗余Cookie操作:每次页面跳转后重复获取并设置Cookie完全没必要——同一个页面实例会自动维护会话Cookie,重复操作反而可能干扰正常会话。
- 用户目录冲突:直接使用Chrome默认的
userDataDir,可能和日常使用的Chrome会话冲突,自动化场景应该用独立的用户数据目录,避免被网站识别为异常会话。 - 等待策略不合理:
waitUntil: "load"仅等待页面所有资源加载完成,对于动态渲染的内容,应该用networkidle2(等待网络请求空闲)或等待关键元素出现,避免抓取到空内容。 - 固定延迟太僵化:硬编码10秒等待时间既浪费效率又不一定能避开反爬,应该根据页面加载状态动态等待。
- 并行处理触发反爬:
tiles.map里用async会导致所有链接请求并行发起,短时间内大量请求很容易被识别为机器人。
优化后的解决方案
以下是调整后的代码,重点优化会话管理、反爬规避和请求逻辑:
const puppeteer = require('puppeteer-extra') const StealthPlugin = require('puppeteer-extra-plugin-stealth') const cheerio = require('cheerio') // 启用Stealth插件做基础反爬伪装 puppeteer.use(StealthPlugin()) // 独立的用户数据目录,避免和日常Chrome会话冲突 const USER_DATA_DIR = './puppeteer-chrome-profile' puppeteer.launch({ headless: false, args: [ "--disable-setuid-sandbox", "--disable-blink-features=AutomationControlled", // 隐藏自动化标识 "--start-maximized" // 模拟正常窗口大小 ], ignoreHTTPSErrors: true, executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', userDataDir: USER_DATA_DIR, // 用独立目录保存会话数据,重启可复用 defaultViewport: null // 使用当前窗口视口,避免固定尺寸暴露机器人特征 }).then(async browser => { const page = await browser.newPage() const baseURL = 'https://www.immobilienscout24.de' // 模拟真实用户代理(Stealth已处理,额外增强伪装) await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36') for (let p = 1; p <= 10; p++) { const targetURL = `https://www.immobilienscout24.de/Suche/de/neubauwohnung-mieten?pagenumber=${p}` try { await page.goto(targetURL, { waitUntil: 'networkidle2', // 等待网络请求空闲,确保动态内容加载完成 timeout: 30000 // 超时时间设为30秒,避免因网络问题卡死 }) // 等待关键元素出现,确认页面渲染完成 await page.waitForSelector('.result-list__listing', { timeout: 20000 }) // 模拟人类滚动行为,触发页面动态加载逻辑 await page.evaluate(() => { window.scrollTo(0, document.body.scrollHeight) }) await page.waitForTimeout(Math.floor(Math.random() * 3000) + 2000) // 随机延迟2-5秒 const html = await page.content() const $ = cheerio.load(html) const tiles = $('.result-list__listing') // 串行处理每个链接,避免并行请求触发反爬 for (const item of tiles.toArray()) { let link = $(item).find('a.result-list-entry__brand-title-container').attr('href') if (link && link.includes("expose")) { link = baseURL + link console.log(link) // 若需进入详情页,可打开新页面处理(示例仅打印链接) // const detailPage = await browser.newPage() // await detailPage.goto(link, { waitUntil: 'networkidle2' }) // // 处理详情页逻辑... // await detailPage.close() // await page.waitForTimeout(Math.floor(Math.random() * 4000) + 1000) } } // 页面间随机延迟3-8秒,模拟人类浏览间隔 await page.waitForTimeout(Math.floor(Math.random() * 5000) + 3000) } catch (error) { console.error(`第${p}页加载失败:`, error.message) // 失败后可重试或跳过当前页面 continue } } await browser.close() })
关键优化点说明
- 独立会话目录:
userDataDir指定本地独立目录,Puppeteer会自动保存Cookie、localStorage等会话数据,重启后可复用,避免每次启动重新建立会话。 - 增强反爬伪装:添加
--disable-blink-features=AutomationControlled参数隐藏Chrome的自动化标识,配合Stealth插件进一步降低被识别概率。 - 动态等待逻辑:用
networkidle2和waitForSelector确保页面内容完全渲染,替代固定延迟,同时加入随机延迟模拟人类行为。 - 串行请求控制:将
tiles.map改为for...of循环,串行处理每个链接,避免短时间内大量请求触发反爬机制。 - 错误处理:添加try-catch捕获页面加载失败的情况,避免整个爬虫因单页出错而崩溃。
内容的提问来源于stack exchange,提问作者uditha
相关产品推荐
相关产品推荐

