使用Puppeteer爬取Google Maps时滚动加载不全如何解决?
问题描述
我想获取家附近搜索词为eletricista的Google Maps数据,但无法让搜索结果区域的滚动条完全加载以获取全部数据,尝试实现区域加载但失败,现有代码如下:
const pup = require("puppeteer"); const { scrollPageToBottom } = require("puppeteer-autoscroll-down"); const fs = require("fs"); const url = "https://www.google.com/maps/"; const searchFor = "eletricista"; /* let c = 1; const list = []; */ const scrapeInfiniteScrollItems = async (page) => { while (true) { previousHeight = await page.evaluate(`document.querySelector('body > div:nth-child(3) > div > div > div > div').scrollHeight`); await page.evaluate(`window.scrollTo(0, document.querySelector('body > div:nth-child(3) > div > div > div > div').scrollHeight)`); await page.waitForFunction(`document.querySelector('body > div:nth-child(3) > div > div > div > div').scrollHeight > ${previousHeight}`); await new Promise((resolve) => setTimeout(resolve, 1000)); } }; (async () => { const browser = await pup.launch({ headless: false }); const page = await browser.newPage(); console.log("Iniciei!"); await page.goto(url); console.log("Fui para a URL!"); await page.waitForSelector("#searchboxinput"); await page.type("#searchboxinput", searchFor); await Promise.all([ page.waitForNavigation(), page.click("#searchbox-searchbutton"), ]); await scrapeInfiniteScrollItems(page); const links = await page.$$eval(".ecceSd > div > div > div > a", (el) => el.map((link) => link.href) ); console.log(links); await page.waitForTimeout(20000); await browser.close(); })();
修复方案
核心问题是滚动容器定位错误和无限循环无终止条件,以下是具体修改点:
替换不稳定的容器选择器
不要用body > div:nth-child(3)这种依赖页面结构顺序的选择器,改用Google Maps搜索结果容器的稳定类选择器,比如.m6QErb.DxyBCb.kA9KIf.dS8AEf(可通过浏览器开发者工具确认当前页面的容器类名)。针对结果容器滚动而非全局窗口
搜索结果是在独立的滚动容器内,不是整个页面,需要对容器本身执行scrollTop操作,而非全局的window.scrollTo。添加循环终止条件
原代码while(true)会无限滚动,可通过判断滚动后容器高度是否不再变化,或等待新内容加载超时来终止循环,确认所有结果已加载。优化选择器与等待逻辑
结果卡片的链接用.hfpxzc更稳定,这是Google Maps结果链接的标准类名;用waitForFunction带超时的方式替代固定延时,更适配不同网络速度。
修改后的完整代码
const pup = require("puppeteer"); const fs = require("fs"); const url = "https://www.google.com/maps/"; const searchFor = "eletricista"; const scrapeInfiniteScrollItems = async (page) => { // 定位搜索结果滚动容器 const resultsContainerSelector = ".m6QErb.DxyBCb.kA9KIf.dS8AEf"; await page.waitForSelector(resultsContainerSelector); let previousHeight = 0; let currentHeight = await page.evaluate(selector => { const container = document.querySelector(selector); return container.scrollHeight; }, resultsContainerSelector); while (currentHeight > previousHeight) { previousHeight = currentHeight; // 滚动容器到底部 await page.evaluate(selector => { const container = document.querySelector(selector); container.scrollTop = container.scrollHeight; }, resultsContainerSelector); // 等待新内容加载,超时则判定无更多结果 try { await page.waitForFunction((selector, prevHeight) => { const container = document.querySelector(selector); return container.scrollHeight > prevHeight; }, { timeout: 3000 }, resultsContainerSelector, previousHeight); } catch (err) { console.log("所有结果已加载完成"); break; } currentHeight = await page.evaluate(selector => { const container = document.querySelector(selector); return container.scrollHeight; }, resultsContainerSelector); } }; (async () => { const browser = await pup.launch({ headless: false }); const page = await browser.newPage(); console.log("Iniciei!"); await page.goto(url); console.log("Fui para a URL!"); await page.waitForSelector("#searchboxinput"); await page.type("#searchboxinput", searchFor); await Promise.all([ page.waitForNavigation(), page.click("#searchbox-searchbutton"), ]); // 等待结果列表加载完成 await page.waitForSelector(".m6QErb.DxyBCb.kA9KIf.dS8AEf"); await scrapeInfiniteScrollItems(page); // 获取结果链接 const links = await page.$$eval(".hfpxzc", (el) => el.map((link) => link.href) ); console.log(`共获取到${links.length}条结果`); console.log(links); // 可选:将结果保存到本地文件 fs.writeFileSync("eletricista_links.json", JSON.stringify(links, null, 2)); await browser.close(); })();
额外提示
如果运行时容器选择器失效,打开浏览器开发者工具,手动查看搜索结果列表的容器元素,更新resultsContainerSelector的值即可;滚动超时时间可根据网络情况调整,网络较慢时适当延长。
内容的提问来源于stack exchange,提问作者Felipe Tavares
相关产品推荐
相关产品推荐

