不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:Albert.Wen  添加时间:2023-07-16 13:49:22  修改时间:2024-04-24 22:05:26  分类:数据采集/桌面自动化  编辑

创建项目文件夹,安装 puppeteer 包

mkdir crawler && cd crawler
pnpm i puppeteer-core

运行并连接到 Chrome 浏览器

在 package.json 设置属性 type 为 "module" 以使用 ES 模块开发:

{
  "type": "module",
  "dependencies": {
    "puppeteer-core": "^19.10.0"
  }
}

创建 server.js 文件,添加以下代码:

import puppeteer from "puppeteer-core";

const browser = await puppeteer.launch({
  executablePath: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
  headless: false,
  defaultViewport: null,
  args: ["--start-maximized"],
});

选项 headless 值为 false 表示非无头模式,即显示浏览器界面。选项 defaultViewport 值为 null 不去影响 Chrome 浏览器视口大小。选项 args 传递了标记 --start-maximized 以最大化浏览器窗口。

打开微博首页

获取浏览器的第一个标签页,然后访问 https://weibo.com ,等待导航完成(导航到 https://weibo.com ,然后重定向到一个新地址),等待页面网络空闲:

const [page] = await browser.pages();
await page.goto("https://weibo.com");
await page.waitForNavigation();
await page.waitForNetworkIdle();

获取微博名称

使用 Chrome DevTools 获取一条微博的 XPath //*[@id="scroller"]/div[1]/div[1]/div/article ,然后删除最后一个 [1] ,我们就得到了可以获取界面上所有微博的 XPath //*[@id="scroller"]/div[1]/div/div/article 。

同样使用 Chrome DevTools 获取一条微博中的帐户名称的 XPath //*[@id="scroller"]/div[1]/div[1]/div/article/div/header/div[1]/div/div[1]/a/span ,移除与微博 XPath 相同的部分,然后在前面加上 xpath ,就得到了帐户名称的 XPath xpath/div/header/div[1]/div/div[1]/a/span 。

以下代码获取当前页面所有的微博的帐户名称:

const articles = await page.$x('//*[@id="scroller"]/div[1]/div/div/article');
const names = await Promise.all(
  articles.map(
    async article =>
      await article.$eval(
        "xpath/div/header/div[1]/div/div[1]/a/span",
        node => node.innerText
      )
  )
);

无限滚动

微博在会在用户滚动页面时获取下一组微博,我们需要编程实现滚动。Puppeteer 没有这个功能,不过我非常幸运的找到了一个 NPM 库 puppeteer-autoscroll-down 。

let scrollCount = 0;
let start;
let end;
let allNames = [];
do {
  // 获取微博名称部分在此省略

  await scrollPageToBottom(page, { size: 500 });
  start = performance.now();
  await page.waitForNetworkIdle();
  end = performance.now();
  scrollCount++;
} while (end - start > 100 && scrollCount < 5);

微博在滚动后会自动加载下一组微博。我们会等待滚动导致的微博加载完成,并统计加载时间,以此判断是否有网络活动(没有网络活动可能说明全部内部容都已加载)。和滚动次数一起作为判断是否再次滚动的依据。

全部代码

import puppeteer from "puppeteer-core";
import { scrollPageToBottom } from "puppeteer-autoscroll-down";

// 运行并连接到浏览器
const browser = await puppeteer.launch({
  executablePath: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
  protocolTimeout: 99999999,
  headless: false,
  defaultViewport: null,
  args: ["--start-maximized"],
});

// 获取第一个标签页
const [page] = await browser.pages();

// 访问微博并等待导航完成和网络空闲
await page.goto("https://weibo.com");
await page.waitForNavigation();
await page.waitForNetworkIdle();

let scrollCount = 0;
let start;
let end;
let allNames = [];
do {
  // 获取所有微博的发布帐户名称
  const articles = await page.$x('//*[@id="scroller"]/div[1]/div/div/article');
  const names = await Promise.all(
    articles.map(
      async article =>
        await article.$eval(
          "xpath/div/header/div[1]/div/div[1]/a/span",
          node => node.innerText
        )
    )
  );

  allNames = allNames.concat(names);

  // 滚动页面并等待下一组微博加载完毕
  await scrollPageToBottom(page, { size: 500 });
  start = performance.now();
  await page.waitForNetworkIdle();
  end = performance.now();
  scrollCount++;
} while (end - start > 500 && scrollCount < 5);

console.log(allNames);

参见

 

 

摘自:https://elementalgrady.com/posts/developing-crawlers-with-puppeteer/