javascript - 如何使用 Puppeteer 解决分页网页抓取问题?正确定位 Node ,但在 for 循环中单击它不断抛出错误

标签 javascript node.js web-scraping puppeteer

因此,我尝试从一个网站(这是一个包含公共(public)信息的网站)中获取一些基本的公司数据。我正在使用 Node 和 Puppeteer 来执行此操作。下面的工作代码成功地抓取了第一页,但是当点击第二页时,我收到错误:执行上下文被破坏,很可能是因为导航。,现在我'我收到错误消息,说我的函数不是函数。

谁能指出我做错了什么以及抓取所有 28 页的最佳方法是什么?

成功抓取第一页

const puppeteer = require("puppeteer");
// var fs = require("fs");
const fsp = require("fs").promises;
const fs = require("fs");

let pageCount = 1; // 21 full pages of content
let companyRows;
function delay(time) {
  return new Promise(function(resolve) {
    setTimeout(resolve, time);
  });
}

(async () => {
  try {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    page.on("console", msg => {
      for (let i = 0; i < msg.args().length; ++i)
        console.log(`${i}: ${msg.args()[i]}`);
    });

    await page.goto(
      "http://dpsstnet.state.or.us/IRIS_PublicInquiry/PrivateSecurity/SMSAgcyTable.aspx"
    );

    //Clicks a tag by id

    await page.click("#btnNaLL");

    await page.waitFor(1000);

    const result = await page.evaluate(() => {
      let row = document.querySelectorAll("tr");
      let companyData = [];

      row.forEach(el => {
        let company = {};
        let count = 0;
        for (data of el.cells) {
          switch (count) {
            case 0:
              company.name = data.innerText.trim();
            case 1:
              company.primaryContact = data.innerText.trim();
            case 2:
              company.address = data.innerText.trim();
            case 3:
              company.phone = data.innerText.trim();
            case 4:
              company.county = data.innerText.trim();
            case 5:
              company.status = data.innerText.trim();
            default:
              company.default = data.innerText.trim();
          }
          count++;
          companyData.push(company);
          //GOT SOME STUUFFFF
          console.log(JSON.stringify(companyData));
        }
      });

      // await page.waitFor(3000);
      // await fsp.writeFile("./json/file.json", result.stringify());
      companyData = companyData.filter((a, b) => companyData.indexOf(a) === b);
      companyData = companyData.filter(e => e.status === "Active");
      return companyData;
    });

    // fsp.writeFile(
    //   "./json/file.json",
    //   JSON.stringify(companyData, null, 2),
    //   err =>
    //     err
    //       ? console.error("Data not written!", err)
    //       : console.log("Data Written")
    // );

    await fsp.writeFile(
      "./json/file.json",
      JSON.stringify(result, null, 2),
      err =>
        err
          ? console.error("Data not written!", err)
          : console.log("Data Written")
    );
    await page.screenshot({
      path: "./screenshots/page1.png"
    });
    await page.pdf({ path: "./pdfs/page1.pdf" });
    await browser.close();
    return result;
  } catch (error) {
    console.log(error);
  }
})();

重写代码以浏览页面(不起作用) 目前,我在运行此程序时收到“clickLink 不是函数”。

const puppeteer = require("puppeteer");
const fsp = require("fs").promises;
const fs = require("fs");

let pageCount = 1; // 21 full pages of content
let companyRows;
let pageToClick;
function delay(time) {
  return new Promise(function(resolve) {
    setTimeout(resolve, time);
  });
}

(async () => {
  try {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    const clickLink = link => {
      page.click(link);
      page.waitFor(1000);
    };
    page.on("console", msg => {
      for (let i = 0; i < msg.args().length; ++i)
        console.log(`${i}: ${msg.args()[i]}`);
    });

    await page.goto(
      "http://dpsstnet.state.or.us/IRIS_PublicInquiry/PrivateSecurity/SMSAgcyTable.aspx"
    );

    //Clicks a tag by id

    await page.click("#btnNaLL");

    await page.waitFor(1000);
    let fullResult = [];
    let result;

    result = await page.evaluate(
      (fullResult, clickLink => {
        let row = document.querySelectorAll("tr");
        let companyData = [];
        let pageList = document.querySelectorAll("b > a");

        for (let step = 0; step < 2; step++) {
          row.forEach(el => {
            let company = {};
            let count = 0;
            for (data of el.cells) {
              switch (count) {
                case 0:
                  company.name = data.innerText.trim();
                case 1:
                  company.primaryContact = data.innerText.trim();
                case 2:
                  company.address = data.innerText.trim();
                case 3:
                  company.phone = data.innerText.trim();
                case 4:
                  company.county = data.innerText.trim();
                case 5:
                  company.status = data.innerText.trim();
                default:
                  company.default = data.innerText.trim();
              }
              count++;
              companyData.push(company);
              //GOT SOME STUUFFFF
              console.log(JSON.stringify(companyData));
            }
          });

          companyData = companyData.filter(
            (a, b) => companyData.indexOf(a) === b
          );
          companyData = companyData.filter(e => e.status === "Active");
          fullResult = [...fullResult, ...companyData];
          // console.log(JSON.stringify(pageList[step].innerText));
          clickLink(pageList[step]);
        }
        return fullResult;
      },
      fullResult,
      clickLink
    );

    await fsp.writeFile(
      "./json/file.json",
      JSON.stringify(result, null, 2),
      err =>
        err
          ? console.error("Data not written!", err)
          : console.log("Data Written")
    );
    //*
    await page.screenshot({
      path: "./screenshots/page1.png"
    });
    await page.pdf({ path: "./pdfs/page1.pdf" });
    await browser.close();
    return result;
  } catch (error) {
    console.log(error);
  }
})();

我想我只是不理解 puppeteer 最佳实践背后的一些概念。

上面的代码只是我最近的分页和抓取尝试。我还尝试使用 pageList[step].click() 而不是 clickLink 函数。我还尝试将 for 循环移到评估之外,并使用“await page.click(nextPageNode)”多次重新运行代码,但这很困惑,而且也不起作用。

如果您想测试该项目:它已上传到 https://github.com/jIrwinCline/scrapeDPSST 。没有自述文件仅供引用,但它非常简单。只需下拉代码,然后运行“node index.js”即可。

请帮忙!我最后一天一直在做分页的事情。

最佳答案

第一个问题是你不能像这样将函数传递给evaluate。为了向网页上下文公开函数,您应该使用 exposeFunction

即使您公开 clickLink,它也会引发 page 错误。了解不同的执行上下文非常重要。 Puppteer 的执行上下文与网页的执行上下文不同。 page 是 puppeteer 上下文中的对象,而 evaluate 在网页的执行上下文中运行您的函数。所以你不能通过 page 来评估。

对于分页,我建议将该 pageList 取出到 puppeteer 上下文,因为当您导航时执行上下文将被破坏:

const pageList = await page.$$('b > a');

现在循环访问 puppeteer 上下文中的链接并单击它们并运行 page.evaluate 以获取数据。

for (let link of pageList){
    await Promise.all([
        page.waitForNavigation(),
        link.click()
    ]);
    //get result
    await page.evaluate(() => ...);
}

确保链接不会在新选项卡中打开,因为页面引用当前页面/选项卡。

关于javascript - 如何使用 Puppeteer 解决分页网页抓取问题?正确定位 Node ,但在 for 循环中单击它不断抛出错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60408776/

相关文章:

javascript - 使用 add :true parameter 获取集合时重新渲染主干 View

javascript - 路由器应该负责网络请求吗? ( Backbone .js)

node.js - 如何将 npm 包的目录公开给 monorepo 中的其他包

java - Jsoup:如何获取与特定类关联的所有 href

python - 使用 pandas 进行网页抓取时在列表中显示 0 个元素

php - 如何将动态 (PHP) 网站存档为静态 HTML?

javascript - Array.apply 实际上在做什么

javascript - 永久改变 Canvas 图像的尺寸并且不保持纵横比

node.js - MongoDB 聚合和组嵌套字段

node.js - JestJS 测试不会因错误而失败