网页 抓取 innertext 试题(Chrome+Puppeteer+NodeJS)和HeadlessChrome一起装逼一起飞)
优采云 发布时间: 2022-03-13 02:13网页 抓取 innertext 试题(Chrome+Puppeteer+NodeJS)和HeadlessChrome一起装逼一起飞)
JavaScript自动化爬虫入门指南(Chrome + Puppeteer + Node JS)和Headless Chrome假装一起飞
Udemy 黑色星期五特卖 — 数以千计的 Web 开发和软件开发课程限时发售,仅售 10 美元!完整的详细信息和课程推荐可以在这里找到。
简单的介绍
本文将教您如何使用 JavaScript 自动化网络爬虫,技术上使用 Google 团队开发的 Puppeteer。Puppeteer 运行在 Node 上,可用于操作无头 Chrome。什么是无头 Chrome?通俗的讲,它使用提供的 API 来模拟用户的浏览行为,而无需打开 Chrome 浏览器。
如果你还是不明白,你可以想象 Chrome 浏览器使用 JavaScript 完全自动化。
前言
确保您已安装 Node 8 及更高版本。如果没有,可以先从官网下载安装。请注意,“当前”处显示的版本号必须大于 8。
如果您是 Node 新手,最好查看介绍性教程:Learn Node JS — The 3 Best Online Node JS Courses。
安装 Node 后,创建一个项目文件夹并安装 Puppeteer。安装 Puppeteer 的过程会附带下载匹配版本的 Chromium 指定浏览器的执行路径):
npm install --save puppeteer
复制代码
示例 1 - 网页截图
一旦安装了 Puppeteer,我们就可以开始编写一个简单的示例。这个例子是直接复制官方文档的,可以对给定的网站进行截图。
首先创建一个任意名字的js文件,这里我们以test.js为例,输入如下代码:
const puppeteer = require('puppeteer');
async function getPic() {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://google.com');
await page.screenshot({path: 'google.png'});
await browser.close();
}
getPic();
复制代码
让我们逐行分析上面的代码。
细心的读者会发现getPic()前面多了一个async前缀,表示getPic()方法是异步方法。async 和 await 成对出现,是 ES 2017 的新功能。由于它是异步方法,因此在调用后返回一个 Promise 对象。当 async 方法返回一个值时,对应的 Promise 对象会传递这个值来解析(如果抛出异常,则会将错误信息传递给 Reject)。
在 async 方法中,可以使用 await 表达式暂停方法的执行,直到表达式中的 Promise 对象完全解析,然后继续向下执行。看不懂也没关系,后面我会详细解释,到时候你就明白了。
接下来,我们将深入研究 getPic() 方法:
const browser = await puppeteer.launch();
复制代码
这段代码用于启动 puppeteer,本质上是打开一个 Chrome 实例,然后将这个实例对象赋给变量 browser。因为使用了 await 关键字,所以这里运行的代码会阻塞(暂停)直到 Promise 被解析(不管执行结果是否成功)
const page = await browser.newPage();
复制代码
接下来在上面得到的浏览器实例中新建一个页面,返回后将新创建的页面对象赋值给变量page。
await page.goto('https://google.com');
复制代码
使用上面得到的页面对象,用它来加载我们给的URL地址,然后暂停代码的执行,直到页面加载完毕。
await page.screenshot({path: 'google.png'});
复制代码
页面加载后,您可以对页面进行截图。screenshot() 方法接受一个对象参数,该参数可用于配置保存屏幕截图的路径。注意,不要忘记添加 await 关键字。
await browser.close();
复制代码
最后,关闭浏览器。
运行示例
在命令行输入以下命令执行示例代码:
node test.js
复制代码
这是示例的屏幕截图:
不是很棒吗?这只是一个热身,这里是如何在非无头环境中运行代码。
非无头?百闻不如一见,先自己试一试,把代码放在第4行:
const browser = await puppeteer.launch();
复制代码
用这句话替换它:
const browser = await puppeteer.launch({headless: false});
复制代码
然后再次运行:
node test.js
复制代码
是不是更酷了?配置了 {headless: false} 后,可以直观的看到代码是如何控制 Chrome 浏览器的。
这里还有一个小问题。之前我们的截图有点不完整,是因为页面对象的默认截图尺寸有点小。我们可以通过以下代码重置页面的视口大小,然后截图:
await page.setViewport({width: 1000, height: 500})
复制代码
这要好得多:
最终代码如下:
const puppeteer = require('puppeteer');
async function getPic() {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto('https://google.com');
await page.setViewport({width: 1000, height: 500})
await page.screenshot({path: 'google.png'});
await browser.close();
}
getPic();
复制代码
示例 2 - 抓取数据
通过上面的例子,你应该已经掌握了Puppeteer的基本用法,我们来看一个稍微复杂一点的例子。
在开始之前,先看看。你会发现 Puppeteer 可以做很多事情,比如模拟鼠标点击、填写表单数据、输入文本、读取页面数据等等。
在接下来的教程中,我们将爬取一本名为 Books To Scrape 网站 的书,这个网站 是专门为开发者做爬虫练习的。
还是在之前创建的文件夹中,新建一个js文件,这里以scrape.js为例,然后输入如下代码:
const puppeteer = require('puppeteer');
let scrape = async () => {
// Actual Scraping goes Here...
// Return a value
};
scrape().then((value) => {
console.log(value); // Success!
});
复制代码
有了前面例子的经验,这段代码应该不难理解。如果你还是不明白……没关系。
首先引入puppeteer依赖,然后定义一个scrape()方法来编写爬虫代码。这个方法返回一个值,然后我们会处理这个返回值(示例代码直接打印这个值)
首先在scrape方法中加入下面这行来测试:
let scrape = async () => {
return 'test';
};
复制代码
在命令行输入node scrape.js,如果没有问题,控制台会打印一个测试字符串。测试通过后,我们继续细化scrape方法。
第一步:前期准备
与示例 1 一样,首先获取浏览器实例,创建一个新页面,然后加载 URL:
let scrape = async () => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto('http://books.toscrape.com/');
await page.waitFor(1000);
// Scrape
browser.close();
return result;
};
复制代码
我们再分析一下上面的代码:
首先我们创建一个浏览器实例并将headless设置为false,这样我们就可以直接看到浏览器的运行情况:
const browser = await puppeteer.launch({headless: false});
复制代码
然后新建一个标签页:
const page = await browser.newPage();
复制代码
使用权:
await page.goto('http://books.toscrape.com/');
复制代码
以下步骤是可选的,让代码暂停 1 秒以确保页面可以完全加载:
await page.waitFor(1000);
复制代码
任务完成后,关闭浏览器,返回执行结果。
browser.close();
return result;
复制代码
步骤 1 结束。
第 2 步:攀爬
打开Books to Scrape 网站后,你一定发现里面有很多书,但数据都是假的。让我们开始简单,我们首先抓取页面上第一本书的数据并返回其标题和价格信息(红色边框选择的那个)。
检查文档,我注意到这个方法模拟了一个页面点击:
page.click(选择器[,选项])
在这里可以使用开发者工具查看元素的选择器,在图片上右击选择inspect:
上述操作会打开开发者工具栏,之前选中的元素也会高亮显示。这时候点击前面的三个小点,选择复制-复制选择器:
有了元素选择器后,再加上前面找到的元素点击方法,得到如下代码:
await page.click('#default > div > div > div > div > section > div:nth-child(2) > ol > li:nth-child(1) > article > div.image_container > a > img');
复制代码
然后你会观察到浏览器点击第一本书的图片,页面会跳转到详情页。
在详情页,我们只关心书名和价格信息——见图中红框。
要获取此数据,您需要使用 page.evaluate() 方法。此方法可用于执行浏览器内置的 DOM API,例如 querySelector()。
首先创建 page.evaluate() 方法并将其返回值保存在 result 变量中:
const result = await page.evaluate(() => {
// return something
});
复制代码
同样,要在方法中选择我们要使用的元素,再次打开开发者工具,选择需要检查的元素:
标题是一个简单的 h1 元素,使用以下代码获得:
let title = document.querySelector('h1');
复制代码
事实上,我们需要的只是元素的文本部分。我们可以在它之后添加 .innerText。代码如下:
let title = document.querySelector('h1').innerText;
复制代码
获取价格信息也是如此:
price 元素上只有一个 price_color 类。您可以使用该类作为选择器来获取与价格对应的元素:
let price = document.querySelector('.price_color').innerText;
复制代码
这样,标题和价格就在那里,将它们放入一个对象并返回:
return {
title,
price
}
复制代码
回看刚才的操作,我们得到了标题和价格信息,保存在一个对象中返回,并将返回的结果赋值给result变量。所以,现在你的代码应该是这样的:
const result = await page.evaluate(() => {
let title = document.querySelector('h1').innerText;
let price = document.querySelector('.price_color').innerText;
return {
title,
price
}
});
复制代码
然后只需要返回结果,返回的结果会打印到控制台:
return result;
复制代码
最后,合并后的代码如下:
const puppeteer = require('puppeteer');
let scrape = async () => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto('http://books.toscrape.com/');
await page.click('#default > div > div > div > div > section > div:nth-child(2) > ol > li:nth-child(1) > article > div.image_container > a > img');
await page.waitFor(1000);
const result = await page.evaluate(() => {
let title = document.querySelector('h1').innerText;
let price = document.querySelector('.price_color').innerText;
return {
title,
price
}
});
browser.close();
return result;
};
scrape().then((value) => {
console.log(value); // Success!
});
复制代码
在控制台中运行代码:
node scrape.js
// { title: 'A Light in the Attic', price: '£51.77' }
复制代码
如果操作正确,您将在控制台中看到正确的输出。至此,您已经完成了网络爬虫。
示例 3 - 后期完美
稍微想一想,你会发现标题和价格信息是直接显示在首页的,所以不需要进入详情页去抓取这些数据。在这种情况下,我们不妨进一步想一想,能不能抓取到所有书籍的书名和价格信息?
所以,爬取的方式其实有很多种,需要自己去发现。另外,上面提到的直接从主页抓取数据也不一定可行,因为某些标题可能无法完全显示。
提出问题
目标 - 抓取首页所有书籍的书名和价格信息,并以数组的形式保存并返回。正确的输出应如下所示:
去吧,伙计,实现几乎和上面的例子一样。如果你觉得太难,可以参考以下提示。
暗示:
其实最大的不同就是需要遍历整个结果集。代码的一般结构如下:
const result = await page.evaluate(() => {
let data = []; // 创建一个空数组
let elements = document.querySelectorAll('xxx'); // 选择所有相关元素
// 遍历所有的元素
// 提取标题信息
// 提取价格信息
data.push({title, price}); // 将数据插入到数组中
return data; // 返回数据集
});
复制代码
如果提示后还是不行,好吧,下面是参考答案。在以后的教程中,我会在以下代码的基础上做一些扩展,也会涉及到一些更高级的爬取技术。您可以在此处提交您的电子邮件地址进行订阅,我们会在有新内容更新时通知您。
参考答案:
const puppeteer = require('puppeteer');
let scrape = async () => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto('http://books.toscrape.com/');
const result = await page.evaluate(() => {
let data = []; // 创建一个数组保存结果
let elements = document.querySelectorAll('.product_pod'); // 选择所有书籍
for (var element of elements){ // 遍历书籍列表
let title = element.childNodes[5].innerText; // 提取标题信息
let price = element.childNodes[7].children[0].innerText; // 提取价格信息
data.push({title, price}); // 组合数据放入数组
}
return data; // 返回数据集
});
browser.close();
return result; // 返回数据
};
scrape().then((value) => {
console.log(value); // 打印结果
});
复制代码
结论:
感谢收看!如果您对学习 NodeJS 感兴趣,可以前往 Learn Node JS — The 3 Best Online Node JS Courses。
每周都会发布4篇关于web开发的技术文章文章,欢迎订阅!或者你可以在 Twitter 上关注我
掘金翻译项目是一个翻译优质互联网技术文章的社区,文章来源是掘金上的英文分享文章。内容涵盖、、、、、、等领域。如果想看到更多优质翻译,请继续关注掘金翻译计划、官方微博、知乎栏目。