开发者问题收集

如何使用 puppeteer 获取 Google 地图上某个地点的所有评论/评价?(由于页面是可滚动的,因此我无法获取所有评论/评价)

2018-10-31
1928

我正在尝试使用 Puppeteer 从我搜索的地方抓取评论/评价。我有两个问题:

  1. 我从当前页面只获得了 16 条评论/评价,而实际上我想要的是所有评论/评价(在这种情况下,根据我的搜索,是 62 条评论,甚至更多),但我认为问题出在页面可滚动。
  2. 当我在谷歌地图中抓取没有评论的评论时,我收到一条错误消息:

    “(node:13184) UnhandledPromiseRejectionWarning: 错误:评估失败:TypeError:无法读取 __puppeteer_evaluation_script__:9:38 处的 null 属性‘innerText’”
    

    ,每次有评论带有 NULL 评论时,我都不确定如何摆脱这种情况(我有一些代码几乎在最后尝试解决 NULL 评论,但没有奏效,我尝试了其他几种方法,但没有奏效也可以)。

以下是我的代码:

const puppeteer = require('puppeteer'); // Require the Package we need...

let scrape = async () => { // Prepare scrape...

    const browser = await puppeteer.launch({args: ['--no-sandbox', '--disabled-setuid-sandbox']}); // Prevent non-needed issues for *NIX
    const page = await browser.newPage(); // Create request for the new page to obtain...

    const busqueda = 'Alitas+del+Cadillac+Tumbaco';
    const Url = `https://www.google.com/maps/search/${busqueda}`;

    const buscar = '.section-result';
    const click1 = '.widget-pane-link';
    const cajaTexto = '#searchboxinput';

    const comentarioLength = 'section-review-text';
    const comentarios = 'div.section-review:nth-child(Index) > div:nth-child(1) > div:nth-child(3) > div:nth-child(2) > div:nth-child(1) > span:nth-child(4)';

    console.log(comentarioLength);

    //const comentario = 'div.section-review:nth-child(INDEX) > div:nth-child(1) > div:nth-child(3) > div:nth-child(2) > div:nth-child(1) > span:nth-child(4)';

    // Replace with your Google Maps URL... Or Test the Microsoft one...
    //await page.goto('https://www.google.com/maps/place/Microsoft/@36.1275216,-115.1728651,17z/data=!3m1!5s0x80c8c416a26be787:0x4392ab27a0ae83e0!4m7!3m6!1s0x80c8c4141f4642c5:0x764c3f951cfc6355!8m2!3d36.1275216!4d-115.1706764!9m1!1b1');

    await page.goto(Url); // Define the Maps URL to Scrape...
    await page.waitFor(2*1000); // In case Server has JS needed to be loaded...

    await page.click(buscar); //busco caja de texto*/

    await page.waitForNavigation();
    await page.waitFor(2*1000);

    await page.click(click1);

    await page.waitForNavigation();
    await page.waitFor(2*1000);

    console.log(page.url());

    console.log("3");

    await page.evaluate(_ => { // This is just a test, don't really need this!

    });

    await page.waitFor(2*1000);

    console.log('how many?', (await page.$$('.section-review-text')).length);

    //div.section-result:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > h3:nth-child(1) > span:nth-child(1)

    let listLength = await page.evaluate((sel) => {
        window.scrollBy(0, window.innerHeight);
        return document.getElementsByClassName(sel).length;
    }, comentarioLength);

    console.log(listLength);

    for (let i = 1; i <= listLength; i++) {

        let selectorComentarios = comentarios.replace("Index", i);

        const result = await page.evaluate((sel) => { // Let's create variables and store values...

            return document.querySelector(sel).innerText;

        }, selectorComentarios);

        if(!result){
            continue;
        }

        console.log(i+result);

    }

    /*await page.evaluate(_ => {
        window.scrollBy(0, window.innerHeight)
    })*/

    browser.close(); // Close the Browser...
    return result; // Return the results with the Review...
};

scrape().then((value) => { // Scrape and output the results...


console.log(value); // Yay, output the Results...
});
2个回答

要解决第一个问题,您需要通过添加如下函数来处理无限滚动:

async function scrollPage(page, scrollContainer) {
  let lastHeight = await page.evaluate(`document.querySelector("${scrollContainer}").scrollHeight`);
  while (true) {
    await page.evaluate(`document.querySelector("${scrollContainer}").scrollTo(0, document.querySelector("${scrollContainer}").scrollHeight)`);
    await page.waitForTimeout(2000);
    let newHeight = await page.evaluate(`document.querySelector("${scrollContainer}").scrollHeight`);
    if (newHeight === lastHeight) {
      break;
    }
    lastHeight = newHeight;
  }
}

在此函数中,第一个参数是 Puppeteer page ,第二个参数是 可滚动的 HTML 元素(在本例中,它的类名为 .DxyBCb )。此函数检查元素的当前 scrollHeight ,然后滚动到此高度并再次检查 scrollHeight 。如果通过加载新元素更改了高度,则函数将再次重复滚动。

此部分代码中的第二个问题:

return document.querySelector(sel).innerText;

您需要处理在页面上找不到带有选择器 sel 的元素的情况。您可以使用 可选 chining ,它返回 undefined ,而不是抛出错误,并在发生错误时添加一些文本:

return document.querySelector(sel)?.innerText || `Element with selector ${sel} not found on the page`;

现在,如果搜索选择器不存在,您的评估函数将返回 || 之后的文本。

博客文章包含超出您问题范围的更详细信息: web Scraping Google Maps reviews with Nodejs

Mikhail Zub
2023-01-29

这违反了 Google Maps Platform 的服务条款。

请查看第 3.2.4 段(禁止滥用服务的限制)。内容如下

(a) No Scraping . Customer will not extract, export, scrape, or cache Google Maps Content for use outside the Services. For example, Customer will not:(i) pre-fetch, index, store, reshare, or rehost Google Maps Content outside the services; (ii) bulk download geocodes; (iii) copy business names, addresses, or user reviews; or (iv) use Google Maps Content with text-to-speech services. Caching is permitted for certain Services as described in the Maps Service Specific Terms.

来源: https://cloud.google.com/maps-platform/terms/#3-license

很抱歉带来坏消息。

xomena
2018-11-01