nodejs抓取动态网页(种种实际上cheeriocheerio对于网页操作是无能为力的(组图))
优采云 发布时间: 2021-09-12 11:09nodejs抓取动态网页(种种实际上cheeriocheerio对于网页操作是无能为力的(组图))
内容
0. 前言
这两天对爬虫产生了兴趣。一开始是天涯的一张房价贴纸,覆盖了几万层楼,看了很久。天涯网页上的“只看主持人”需要会员,而移动端可以“只看主持人”,但是体验不是很好,录音不方便,所以我决定自己爬下主持人的演讲,可以保存或检索。
这个想法一开始很简单。搜索每个页面的元素。如果发帖人姓名与主持人姓名相符,请复制里面的内容。
网上找到的第一个工具是cheerio插件。读取网站后,保存网站内容,通过元素选择器选择内容。使用递归后,还可以解决翻页问题。
事实上,确实如此。通过几个简单的步骤,主持人的发言就被保存了下来,这也让我对爬虫产生了兴趣。
问题
cheerio真的很简单好用,处理简单的静态网页没有问题。但是拥有一定防爬机制的网站无能为力。比如cheerio通过动态修改url链接解决翻页问题。但是有些网站,比如我最喜欢的煎蛋,网页链接页码乱码,无法实现自动翻页。另一个例子是一些房地产网站。在列出待售资源时,使用延迟加载来提升用户体验。页面滚动到底部后才能触发加载。
以上这些其实都是cheerio对网页操作的无能为力。
解决
我在网上寻找处理延迟加载的方法时发现了puppeteer插件。谷歌浏览器在 2017 年开发了自己的 Chrome Headless 功能,同时推出了 puppeteer,它本质上是一个没有界面的浏览器,有点像电脑终端,所有的操作都是通过代码来完成的。
这样我们就可以在搜索网站之前操作指定元素滚动到底部触发更多信息。或者当需要翻页时,操作码点击翻页按钮,然后在翻页后对页面进行相关处理。
1. 下载并引用包
// 下载
npm i puppeteer
// 引包
const puppeteer = require('puppeteer')
2.使用步骤
// 将整个操作放置在一个闭包的异步函数中,以便于进行异步操作
(async () => {
// 1. 使用puppetee插件启动一个浏览器,并开启一个新页面
const brower = await puppeteer.launch({
args: ['--no-sandbox'],
dumpio: false,
headless:false, // 默认为true,设为false时,可以显示可视化浏览器界面
})
const page = await brower.newPage() // 开启一个新页面
// 2. 打开指定网页
await page.goto('http://jandan.net/ooxx', {
waitUntil: 'networkidle2' // 网络空闲说明已加载完毕
});
// 3. 对动态网站进行自动化操作,这一步是其精髓所在
// 由于我们监控的是动态网页,刚打开网页时,所需元素也许还未出现,所以需要进行*敏*感*词*,例如“下一页按钮”
await page.waitForSelector('a.previous-comment-page'); // 括号内是元素选择器
// 当下一页按钮出现时,模拟点击
await page.click('a.previous-comment-page')
// 4. 这时我们可以执行爬取我们需要的数据了,我们可以去审查页面的dom结果,来循环遍历这些数据。
// page.evaluate() 为在浏览器中执行函数,相当于在控制台中执行函数,返回一个 Promise
const result = await page.evaluate(() => {
// 拿到页面上的jQuery
var $ = window.$;
// 在这里进行熟悉的 DOM 操作
// Do something
});
// 5. 关闭浏览器,在console里面打印我们需要的数据
brower.close();
// 6. 对结果进行处理
console.log(result);
})();
3.爬过的几个坑。给page.evaluate传递参数的问题
因为打开的页面只是一个傀儡,不是真正的浏览器页面,所以这个页面上的操作和一般页面上的操作是不一样的。
官方文档说这个参数是这样的。实际使用中,可以传递一个字符串变量,但是更复杂的,比如‘fs’,在自定义外部函数的时候是无法读取的。
这也是我在第6步的建议,页面操作完成后,统一处理结果。 (主要是我没解决这个问题,所以就随便逛了一下……)
元素操作问题
在 puppeteer 中,最重要的功能执行和元素选择与普通浏览器上的有些不同。爬这里有一些陷阱,现在我说不出来。