命令行应用程序:使用 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)
当我运行程序时node index.js,控制台会显示:
[ '/usr/local/Cellar/node/11.4.0/bin/node',
'/Users/Damien/Desktop/javascript/scraping/index.js' ]
你会得到不同的结果,但你会得到两个元素。第一个是使用的运行时(这里是 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()
}
好的,那我们接下来需要做什么?
- 创建一个函数来封装此功能。
- 从命令行调用该函数
- 为该功能提供正确的数据(页面网址、用户名)
我的应用程序将采用以下约定:第一个参数是函数名,第二个参数是用户名。因此,代码可能是:
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!')
}
首先,我们导入 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()
}
前三行始终相同:初始化、新页面、跳转到…… 然后,我们有一个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!')
}
完成!现在,我们可以运行以下命令:
node index.js getPDF damcossetnode index.js getPDF bennode 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)
锵锵锵锵锵锵!
代码可以在这里找到。
结论
好了,第一部分就到这里。Puppeteer 真的是个很有趣的工具,我一定会回来向大家展示更多它能实现的精彩功能。
玩得开心哦 <3
文章来源:https://dev.to/damcosset/a-command-line-application-scraping-dev-to-with-puppeteer-149k


