代码之家  ›  专栏  ›  技术社区  ›  TheProgrammer

使用puppeter,我如何打开一个页面,获取数据,然后返回到上一页以获取列表中的下一页?

  •  0
  • TheProgrammer  · 技术社区  · 6 年前

    情况:

    我想做的是:

    1)我加载第0页。第0页包含指向不同页的可单击链接。我想加载所有这些页面的内容。所以:

    2)单击第一个链接。加载第1页。获取数据。返回上一页(第0页)

    3)点击加载第2页的第二个链接。等。。无限期直到所有链接都被点击。

    使用当前代码,加载第0页,然后单击第一个链接并加载第1页,然后出现崩溃,错误如下:

    (node:2629) UnhandledPromiseRejectionWarning: Error: Protocol error (Runtime.callFunctionOn): Execution context was destroyed.
    

    问题:

    我做错了什么?我怎样才能让我的剧本表现出我想要的样子?


    代码:

    const puppeteer = require('puppeteer');
    const fs = require('fs');
    
    let getData = async () => {
        const browser = await puppeteer.launch({headless: false});
        const page = await browser.newPage();
    
        await page.goto('url', { waitUntil: 'networkidle2' });
        await page.setViewport({width: ..., height:...});
    
        const result = await page.evaluate(async () => {
            let data = []; 
            let elements = document.querySelector('.items').querySelectorAll('.item'); 
    
            for (const element of elements) {
    
                element.click();
                await new Promise((resolve) => setTimeout(resolve, 2000));
    
                // GETTING THE DATA THEN PUSHING IT INTO THE DATA ARRAY
    
                await page.goBack();
            }
    
            return data; // Return our data array
    
        });
    
        browser.close();
        return result; // Return the data
    };
    
    4 回复  |  直到 6 年前
        1
  •  5
  •   AJC24    6 年前

    好吧,这是我的看法。首先,你用的是 evaluate 方法不正确。主要是因为你实际上并不需要它,但也因为你要求它做一些它做不到的事情。只是解释一下 评价 方法在 网页 只有。它几乎只允许您在远程浏览器中直接在当前页面上执行Javascript指令。它没有您在外部向该函数声明的变量的概念,因此在本例中,当您执行此操作时:

    await page.goBack();
    

    这个 评价 方法不知道 page 也不是怎么用的。现在有办法注射 第页 进入 评价 但这也不能解决你的问题。puppeter API调用在 评价 方法(我自己尝试过,它总是返回一个异常)。

    所以现在让我们回到你的问题-你在做什么 评价 函数正在检索一个带有类的UI元素 .items 然后使用类搜索该UI元素中的每个UI元素 .item . 然后循环浏览所有找到的UI元素,单击每个元素,获取某种数据,然后返回到单击下一个元素。

    你不需要使用 评价 方法,而是使用puppeter API调用,如下所示:

    const itemsList = await page.$('.items'); // Using '.$' is the puppeteer equivalent of 'querySelector'
    const elements = await itemsList.$$('.item'); // Using '.$$' is the puppeteer equivalent of 'querySelectorAll'
    
    const data = [];
    elements.forEach(async (element) => {
      await element.click();
      // Get the data you want here and push it into the data array
      await page.goBack();
    });
    

    希望这能帮到你!

        2
  •  2
  •   Grant Miller    6 年前

    与其来回导航以单击第一页中的下一个链接,不如将第一页中的链接存储到数组中,然后使用 page.goto() .

    换句话说,可以使用以下示例完成此任务:

    await page.goto('https://example.com/page-1');
    
    const urls = await page.evaluate(() => Array.from(document.querySelectorAll('.link'), element => element.href));
    
    for (let i = 0, total_urls = urls.length; i < total_urls; i++) {
      await page.goto(urls[i]);
    
      // Get the data ...
    }
    
        3
  •  1
  •   gemart    6 年前

    @AJC24对我不起作用。问题是,当单击并返回到原始页面时,页面上下文被破坏。

    我最后不得不做的事情与格兰特的建议类似。我收集了一个数组中的所有按钮标识符,当返回到原始页面时,我将再次单击。

        4
  •  1
  •   Hunter    5 年前

    通过使用@Grant的迭代

    执行上下文被破坏,很可能是因为导航。

    然后我让它在迭代中打开一个新的选项卡,它解决了问题!

    for (let i = 0, total_urls = urls.length; i < total_urls; i++) {
      const page = await browser.newPage();
      await page.goto(url), { waitUntil: 'networkidle0', timeout: 0 };
    
      await page.goto(urls[i]);
    
      // Get the data ...
    }