简单的 Puppeteer 网页抓取教程
这是使用 Node.js 库 Puppeteer 进行网页抓取的分步指南。
多年来,网站发生了很大变化。网页抓取现代网站或单页应用程序需要渲染整个页面。它们依赖于延迟加载和无限滚动等元素,因此 传统文字 没什么帮助。无头浏览器库(如 Puppeteer)在网页抓取动态网站方面发挥着关键作用。
在本分步指南中,您将了解为什么 Puppeteer 是用于网页抓取的如此流行的 Node.js 库。您还将找到一个如何抓取动态页面的真实示例。
什么是 Puppeteer 抓取?
使用 Puppeteer 进行网页抓取的优势
木偶戏 比其他无头浏览器库快得多 和 Selenium 类似。主要原因是它使用了 Chromium 内置的 DevTools 协议,可以直接控制浏览器。而且占用资源少,所以执行时间相对较快。
图书馆 有很棒的插件 喜欢 puppeteer-extra-plugin-stealth or puppeteer-extra-plugin-匿名化-ua 您可以使用这些方法来伪造浏览器指纹。例如,您可以旋转标头或用户代理。此外,您还可以 将代理与 Puppeteer 集成。
Puppeteer 相对容易使用。 例如,它没有内置的集成开发环境 (IDE)(如 Selenium)来编写脚本,因此您可以使用自己选择的 IDE。对您来说,这意味着编写更少的代码。此外,安装库不会花费太多时间 - 添加 npm 或 yarn 包管理器并下载包即可。
在所有功能中,Puppeteer 有精心制作的文档 以及庞大的用户社区。因此,如果您在使用该工具时遇到任何困难,很快就能找到答案。
Node.js 和 Puppeteer Web 抓取:分步教程
两个链接都包含动态元素。有什么区别?第二个页面用于处理延迟渲染。当页面需要时间加载,或者您需要等待特定条件满足才能提取数据时,这很有用。
硬件需求
导入库
步骤 1。 首先,让我们导入必要的元素。
1)导入Puppeteer库。
import puppeteer from 'puppeteer'
2) 由于我们将使用内置的 Node.js 文件系统模块,因此您也需要导入它。
import fs from 'fs'
3)然后,导入 URL,以便您可以抓取它们。
const start_url = 'http://quotes.toscrape.com/js/'
//const start_url = 'http://quotes.toscrape.com/js-delayed/'
设置CSS选择器
步骤 1。 通过右键单击页面上的任意位置并按“检查”来检查 quotes.toscrape.com/js 的页面源代码。
步骤 2。 您需要选择所有引用类对象,并在其中找到以下类的文本:文本、引用、作者和标签。
const quote_elem_selector = '.quote'
const quote_text_selector = '.text'
const quote_author_selector = '.author'
const quote_tag_selector = '.tag'
const next_page_selector = '.next > a'
步骤 3。 设置一个列表,用于写下所抓取的引文。
var quotes_list = []
准备刮擦
步骤 1。 我们将使用 headful 模式,这样您就可以看到正在发生的事情。
async function prepare_browser() {
const browser = await puppeteer.launch({
headless: false,
})
return browser
}
注意: 如果您想添加 puppeteer-extra-plugin-stealth 来隐藏您的数字指纹或设置代理以避免基于 IP 的限制,这里就是您要找的地方。如果您不知道如何使用 Puppeteer 设置代理,我们准备了一个分步教程。
了解如何使用 Puppeteer 设置代理服务器。
步骤 2。 然后,写下 主() 功能。
async function main() {
1)调用setup_browser函数获取浏览器对象。
var browser = await prepare_browser()
var page = await browser.newPage()
2)现在,让我们开始使用start_url字符串进行抓取。
await get_page(page, start_url)
3)抓取完成后关闭浏览器。
await browser.close()
4)在终端窗口打印出JSON输出。
console.log(quotes_list)
}
5)当代码开始运行时,您需要调用main()函数。
main()
使用 Node.js 和 Puppeteer 抓取多个页面
步骤 1。 让我们使用 get_page() 函数转到 URL,获取 HTML 输出,并转到下一页。
async function get_page(page, url) {
await page.goto(url)
1) 现在,我们将使用 quote_selector 告诉 Puppeteer 等待内容出现。一旦出现带有 class=quote 的元素,它就会开始抓取。我们将超时值设置为 20 秒。
await page.waitForSelector(quote_elem_selector, {timeout: 20_000})
2)然后,调用scrape函数来解析HTML。
await scrape(page)
3)检查下一页选择器并提取 href 属性以进行抓取。
try {
let next_href = await page.$eval(next_page_selector, el => el.getAttribute('href'))
let next_url = `https://quotes.toscrape.com${next_href}`
console.log(`Next URL to scrape: ${next_url}`)
4)再次调用get_page()函数并将new_url传递给scrape。
await get_page(page, next_url)
} catch {
// Next page button not found, end job
return
}
}
步骤 2。 现在,让我们进入解析部分。我们将使用 scrape() 函数。
async function scrape(page) {
1)找到所有quote元素并把它们放入quote_elements列表中。
let quote_elements = await page.$$(quote_elem_selector)
2)然后,遍历列表以查找每个报价的所有值。
for (let quote_element of quote_elements) {
3)现在,让我们使用选择器找到我们需要的元素并提取它们的文本内容。
let quote_text = await quote_element.$eval(quote_text_selector, el => el.innerText)
let quote_author = await quote_element.$eval(quote_author_selector, el => el.innerText)
let quote_tags = await quote_element.$$eval(quote_tag_selector, els => els.map(el => el.textContent))
//console.log(quote_text)
//console.log(quote_author)
//console.log(quote_tags)
// Putting the output into a dictionary
var dict = {
'author': quote_author,
'text': quote_text,
'tags': quote_tags,
}
5)将字典推入quotes_list以获取输出。
quotes_list.push(dict)
}
}
这是输出:
PS C:\node-projects\scraping> node .\index.js
Next URL to scrape: https://quotes.toscrape.com/js/page/2/
Next URL to scrape: https://quotes.toscrape.com/js/page/3/
Next URL to scrape: https://quotes.toscrape.com/js/page/4/
Next URL to scrape: https://quotes.toscrape.com/js/page/5/
Next URL to scrape: https://quotes.toscrape.com/js/page/6/
Next URL to scrape: https://quotes.toscrape.com/js/page/7/
Next URL to scrape: https://quotes.toscrape.com/js/page/8/
Next URL to scrape: https://quotes.toscrape.com/js/page/9/
Next URL to scrape: https://quotes.toscrape.com/js/page/10/
[
{
author: 'Albert Einstein',
text: '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”',
tags: [ 'change', 'deep-thoughts', 'thinking', 'world' ]
},
{
author: 'J.K. Rowling',
text: '“It is our choices, Harry, that show what we truly are, far more than our abilities.”',
tags: [ 'abilities', 'choices' ]
},
{
author: 'Albert Einstein',
text: '“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”',
tags: [ 'inspirational', 'life', 'live', 'miracle', 'miracles' ]
},
将输出保存到 CSV 文件
步骤 1。 创建一个函数来将我们抓取并解析的输出写入 CSV:
function write_to_csv(){
1) 从 quotes_list 对象获取键。这将是 csv 文件的第一行。
var csv = Object.keys(quotes_list[0]).join(', ') + '\n'
2)遍历每个引文字典元素。
quotes_list.forEach(function(quote) {
3)向 CSV 变量中添加新行,并在行末添加换行符。
csv += `${quote['author']}, ${quote['text']}, "${quote['tags']}"\n`
})
4)将输出写入output.csv文件
fs.writeFile('output.csv', csv, (err) => {
if (err)
console.log(err)
else {
console.log("Output written successfully")
}
})
}
步骤 2。 写 主() 功能。
async function main() {
1)通过调用setup_browser函数初始化浏览器设置并获取浏览器对象。
var browser = await prepare_browser()
var page = await browser.newPage()
2)开始刮擦。
await get_page(page, start_url)
3)抓取完成后关闭浏览器。
await browser.close()
4)在终端窗口打印出输出json。
console.log(quotes_list)
5)将输出写入 CSV。
write_to_csv()
}
这是完整的代码:
import puppeteer from 'puppeteer'
import fs from 'fs'
const start_url = 'http://quotes.toscrape.com/js/'
//const start_url = 'http://quotes.toscrape.com/js-delayed/'
const quote_elem_selector = '.quote'
const quote_text_selector = '.text'
const quote_author_selector = '.author'
const quote_tag_selector = '.tag'
const next_page_selector = '.next > a'
var quotes_list = []
async function prepare_browser() {
const browser = await puppeteer.launch({
headless: false,
})
return browser
}
async function get_page(page, url) {
await page.goto(url)
await page.waitForSelector(quote_elem_selector, {timeout: 20_000})
await scrape(page)
try {
let next_href = await page.$eval(next_page_selector, el => el.getAttribute('href'))
let next_url = `https://quotes.toscrape.com${next_href}`
console.log(`Next URL to scrape: ${next_url}`)
await get_page(page, next_url)
} catch {
return
}
}
async function scrape(page) {
let quote_elements = await page.$$(quote_elem_selector)
for (let quote_element of quote_elements) {
// Here we find the elements by using the selectors and extracting their text content
let quote_text = await quote_element.$eval(quote_text_selector, el => el.innerText)
let quote_author = await quote_element.$eval(quote_author_selector, el => el.innerText)
let quote_tags = await quote_element.$$eval(quote_tag_selector, els => els.map(el => el.textContent))
//console.log(quote_text)
//console.log(quote_author)
//console.log(quote_tags)
var dict = {
'author': quote_author,
'text': quote_text,
'tags': quote_tags,
}
quotes_list.push(dict)
}
}
function write_to_csv(){
var csv = Object.keys(quotes_list[0]).join(', ') + '\n'
quotes_list.forEach(function(quote) {
csv += `${quote['author']}, ${quote['text']}, "${quote['tags']}"\n`
})
//console.log(csv)
fs.writeFile('output.csv', csv, (err) => {
if (err)
console.log(err)
else {
console.log("Output written successfully")
}
})
}
async function main() {
var browser = await prepare_browser()
var page = await browser.newPage()
await get_page(page, start_url)
await browser.close()
console.log(quotes_list)
write_to_csv()
}
main()