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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 09:35:37