发布于 2026-01-06 0 阅读
0

命令行应用程序:使用 Puppeteer 抓取 dev.to 简介 结论 DEV 全球展示挑战赛,由 Mux 呈现:展示你的项目!

命令行应用程序:使用 Puppeteer 抓取 dev.to 数据

介绍

结论

由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!

介绍

在我的发票制作应用程序中,我使用Puppeteer生成 PDF 文件。我非常喜欢这个工具的简洁性,为了展示 Puppeteer 的众多功能,我决定制作一个小应用程序。

木偶师能做什么?

根据 GitHub README 文档,Puppeteer 可以帮助您完成以下一些事情:

Puppeteer 是一款可以简化网页抓取操作的工具。它是一个无头 Chrome 浏览器实例(也就是没有用户界面的 Chrome 浏览器)。网页抓取是指访问网站并从中提取数据。

我们将建造什么

所以,我们将构建一个简单的命令行应用程序。本文将确保我们目前能够完成两项任务:

  • 给定用户名,生成该用户个人页面的屏幕截图。
  • 给定用户名,检索该用户撰写的最后一篇文章并将其生成为 PDF 文件。

设置

所以,我们先创建一个名为cli-scraping 的文件夹。进入该文件夹后,运行`yarn init`(或者`npm init`,但这里我将使用 yarn)。接受默认设置并创建一个index.js文件。然后,运行`yarn add puppeteer` 。最后,在cli-scraping 文件夹内创建两个子文件夹`screenshots-users``pdfs`。开始编写代码吧!

获取命令行参数

我们将使用process.argv来获取我们提供的参数。它会返回一个至少包含两个元素的数组。让我们来试试:

console.log(process.argv)
Enter fullscreen mode Exit fullscreen mode

当我运行程序时node index.js,控制台会显示:

[ '/usr/local/Cellar/node/11.4.0/bin/node',
  '/Users/Damien/Desktop/javascript/scraping/index.js' ]
Enter fullscreen mode Exit fullscreen mode

你会得到不同的结果,但你会得到两个元素。第一个是使用的运行时(这里是 Node v11.4.0),第二个是脚本的路径。因此,我们给出的每个参数都会从 `process.argv[2]` 开始。如果我运行 `process.argv[2]` node index.js blabla,那么 `process.argv[2]` 就是 `process.argv[2]` blabla。明白了吗?很简单。现在我们知道如何获取参数了。接下来我们来看看 Puppeteer。

生成屏幕截图

要生成屏幕截图,我们可以使用以下代码:


(async () => {
    // Launching an instance of a headless Chrome browser
    const browser = await puppeteer.launch()

    // Create a new page
    const page = await browser.newPage()

    // Move to the specified url
    await page.goto('urlToThePage')

    // Take a screenshot and save it at the specified path
    await page.screenshot({ path: 'screenshot.png' })

    // Close the browser
    await browser.close()
}
Enter fullscreen mode Exit fullscreen mode

好的,那我们接下来需要做什么?

  • 创建一个函数来封装此功能。
  • 从命令行调用该函数
  • 为该功能提供正确的数据(页面网址、用户名)

我的应用程序将采用以下约定:第一个参数是函数名,第二个参数是用户名。因此,代码可能是:

const puppeteer = require('puppeteer')

const getScreenshot = async username => {
    const browser = await puppeteer.launch()
    const page = await browser.newPage()
    await page.goto(`https://dev.to/${username}`)
    await page.screenshot({ path: `screenshots-users/${username}.png`, fullPage: true })
    await browser.close()
}

switch (process.argv[2]) {
    case 'getScreen':
        getScreenshot(process.argv[3])
        break
    default:
        console.log('Wrong argument!')
}
Enter fullscreen mode Exit fullscreen mode

首先,我们导入 puppeteer。然后,我们创建getScreenshot函数,该函数将负责生成屏幕截图。该函数的框架之前已经展示过。请注意以下几处改动:

  • page.goto会接收带有提供的用户名的正确的 dev.to URL。
  • `page.screenshot` 函数会将 PNG 文件保存到 screenshots 文件夹中,文件名为用户名。请注意,`fullPage: true` 参数用于获取完整页面。

最后,我们来看一个 switch 语句。我使用getScreen作为参数名来生成屏幕截图。

太好了,现在我可以去node index.js getScreen damcosset截取我的个人资料屏幕截图了。我可以在 screenshots-users 文件夹中找到名为 damcosset.png 的屏幕截图:

注:为了节省空间,我截取了部分屏幕截图,但截图中包含了整个页面 ;)

现在运行程序node index.js getScreen ben,我们会在名为 ben.png 的文件夹中得到以下屏幕截图:

生成 PDF

为此,我们有三个不同的步骤:

1. 进入用户个人页面
2. 点击她最近发表的文章进入该页面
3. 获取一个属性以确保 PDF 文件名唯一(可选)
4. 生成 PDF

我们创建一个名为getPDF 的函数。其内部代码如下所示:


const getPDF = async username => {
    const browser = await puppeteer.launch()
    const page = await browser.newPage()
    await page.goto(`https://dev.to/${username}`)

    await Promise.all([page.waitForNavigation(), page.click('.single-article')])
    const dataPath = await page.evaluate(() =>
        document.querySelector('.article').getAttribute('data-path')
    )
    await page.pdf({ path: `pdfs/${dataPath.split('/')[2]}.pdf` })

    await browser.close()
}
Enter fullscreen mode Exit fullscreen mode

前三行始终相同:初始化、新页面、跳转到…… 然后,我们有一个Promise.all。这里我们等待两个操作:

  • 点击文章卡片。
  • 然后,需要加载该文章所在的页面。

我们需要查看一下这个页面的 HTML 内容。在开发者工具中,我可以看到用户个人页面中的每篇文章都有一个名为single-article 的类。所以,这就是我们要定位的目标。为此,我们将使用page.click函数,并传入这个选择器。

这将选中具有该选择器的第一个元素,而且由于 dev.to 会优先显示较新的文章,这正是我想要的。

接下来,当我研究 HTML 结构时,我发现每篇文章都包含在一个带有article类的 div 元素中。这个元素有一个data-path属性。通过使用page.evaluate 方法,我可以获取到该节点并检索到这个属性。这将确保在保存 PDF 文件时不会出现冲突。

最后,我会调用page.pdf函数,并在选项中指定路径。我获取到的数据路径类似于这样/username/title-article-000,所以我将其拆分以获取最后一部分。

最后,别忘了在 switch 语句中添加一个 case:

switch (process.argv[2]) {
    case 'getScreen':
        getScreenshot(process.argv[3])
        break
    case 'getPDF':
        getPDF(process.argv[3])
        break
    default:
        console.log('Wrong argument!')
}
Enter fullscreen mode Exit fullscreen mode

完成!现在,我们可以运行以下命令:

node index.js getPDF damcosset
node index.js getPDF ben
node index.js getPDF jess

这样,就会创建一个无头Chrome浏览器实例,跳转到我的页面,点击我最近写的文章,跳转到该页面,然后生成一个包含该页面内容的PDF文件。对Jess和Ben也一样。

现在我的pdf文件夹中有3个PDF文件,分别名为:

start-before-you-are-ready-393e.pdf (Mine)

what-advice-would-you-give-someone-looking-to-use-their-keyboard-more-and-their-mouse-less-1lea.pdf (Ben)

what-was-your-win-this-week-3a9k.pdf (Jess)
Enter fullscreen mode Exit fullscreen mode

锵锵锵锵锵锵!

代码可以在这里找到。

结论

好了,第一部分就到这里。Puppeteer 真的是个很有趣的工具,我一定会回来向大家展示更多它能实现的精彩功能。

玩得开心哦 <3

文章来源:https://dev.to/damcosset/a-command-line-application-scraping-dev-to-with-puppeteer-149k