博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用Node.js爬取动态网页,这可能是最简洁的方式
阅读量:6216 次
发布时间:2019-06-21

本文共 4620 字,大约阅读时间需要 15 分钟。

现在网页大多是动态网页,如果只是单纯地通过爬取网页HTML文件,根本爬取不到需要后续加载的商品价格或图片等重要信息,更别谈那些丧心病狂的登录限制,对于小爬虫来说,去分析那些复杂的脚本得不偿失,更别谈网站还会与时俱进地更新,好不容易破解了,人家一更新又得从头来,这都大大提高了小爬虫的难度。

但幸好,在Node.js里有那么一款神器,无惧网站的登录限制和反爬虫措施,以不变应万变,通过一招简单的模拟用户操作就能破解绝大部分限制,它就是由谷歌出品的爬取动态网页神器Puppeteer。

1.Puppeteer的好处和坏处

Puppeteer本质上是一个chrome浏览器,只不过可以通过代码进行各种操控。比如模拟鼠标点击、键盘输入等操作,有点像按键精灵,网页很难分清这是人类用户还是爬虫,所以限制也就无处谈起。

它的好处在于简单,非常简单,可能是在所有可以爬取动态网页的库里最简单的一个。

但坏处也很明显,那就是速度慢,效率有点低。它等于每次运行都会启动一个Chrome浏览器,所以运行效率上远远比不过其它库,并不适合爬取大数据。但对于小爬虫来说已经绰绰有余了。

接下来以我写过的爬取jd商品页面的小爬虫为例,来看看这款有多简单。 当初写这个爬虫是为了买苹果的妙控板,找了一圈后发现jd夺宝岛里的价格很诱人,这也应该是夺宝岛里唯一值得抢的商品,但是数量稀少,很久才会出现一个。

于是就想到了监控商品页面,一旦发现新的妙控板就弹出提醒。甚至还可以实现自动竞拍,但我没写,毕竟除了触控板以外我都不想买,没办法测试能否成功拍到。

OK,开始吧!

2.首先第一步要安装Puppeteer:

先安装Puppeteer库,用到的也就只有这个库:

npm install puppeteer复制代码

3.第二步链接网页

链接网页也非常简单,只需要几行代码:

//启动浏览器const browers = await puppeteer.launch()//启动新页面const page = await browers.newPage()//链接网址await page.goto(url)复制代码

这样子就链接成功了!Puppeteer.launch()还可以接收很多参数,但这里我们用到的只有headless,默认为ture,如果是false的话会显示浏览器界面。我们可以利用这个特性实现弹出窗口提醒,一旦发现有符合条件的商品就将headless改成false。

4.爬取商品信息

在链接网页后接下来就是爬取商品信息,然后进行分析。

网址:

4.1获取相对应的元素标签

通过页面可以看到,一旦有同类商品会出现在旁边的同类夺宝里,我们只需要爬取那里的信息就行了,有两种方式:

一种是$eval,相当于js里的document.querySelector,只爬取符合的第一个元素;

另一种为$$eval,相当于js里的document.querySelectorAll,爬取所有符合的元素;

它们接收的第一个参数是元素地址,第二个参数是回调函数,操作和document.querySelector一样,来看代码:

//我们拿到同类夺宝里的所有子元素const goods = page.$$eval('#auctionRecommend > div.mc > ul > li', ele => ele)复制代码

4.2.分析商品信息

现在已经拿到了同类夺宝里所有商品的标签信息,接下来开始分析信息。 获取里面所有商品的名称,然后对照关键字是否存在,如果存在则将headless改为false弹出窗口提醒,如果不存在则在半小时后再次链接。

Puppeteer提供了一个等待命令page.waitFor(),不仅可以按时间等待,也可以按某个元素的加载进度进行等待。

const goods = page.$$eval('#auctionRecommend > div.mc > ul > li', el => {	  //错误和关键字不存在都会返回false,接着循环    try {        for (let i = 0; i < el.length; i++) {            let n = el[i].querySelector('div.p-name').textContent            if(n.includes('妙控板')){                return true            } else {                return false            }        }    } catch (error) {        return false    }})if(!bool){    return console.log('网页已打开,不再监控')}await goods.then(async (b) => {    if(b){        console.log('有货了!')        await page.waitFor(2000)        await browers.close()        return requestUrl(false)    }  else {        console.log('还没货')        console.log('三十分钟后再尝试')        await page.waitFor(1800000)        await browers.close()        return requestUrl(true)    }})复制代码

5.优化代码

对于这个小爬虫来说,损失的效率并不多,没什么优化的必要,但作为一个强迫症,还是希望能去掉的尽量去掉。

5.1拦截图片

在这个爬虫里,我们根本不用看任何图片信息,所以所有图片都没有加载的必要,为了提升一点点运行效率,将所有图片拦截掉:

//开启拦截器await page.setRequestInterception(true)await page.on('request',interceptedRequest => {    //判断url是否以jpg或png结尾,符合条件将不再加载    if(interceptedRequest.url().endsWith('.jpg') || interceptedRequest.url().endsWith('.png')){        interceptedRequest.abort();    }else{        interceptedRequest.continue();    }})复制代码

5.2调整窗口大小

在浏览器弹出时,会发现打开的窗口显示范围很小,不仅不方便浏览,可能还会导致点击或输入等操作出错,所以还是有必要进行调整:

await page.setViewport({    width: 1920,    height: 1080,})复制代码

至此,所有代码已经完成了,快试试效果吧!

6.完整代码

const puppeteer = require('puppeteer')const url = 'https://paipai.jd.com/auction-detail/114533257?entryid=p0120003dbdnavi'const requestUrl = async function(bool){    const browers = await puppeteer.launch({
headless:bool}) const page = await browers.newPage() await page.setRequestInterception(true) await page.on('request',interceptedRequest => { if(interceptedRequest.url().endsWith('.jpg') || interceptedRequest.url().endsWith('.png')){ interceptedRequest.abort(); }else{ interceptedRequest.continue(); } }) await page.setViewport({ width: 1920, height: 1080, }) await page.goto(url) const goods = page.$$eval('#auctionRecommend > div.mc > ul > li', el=>{ try { for (let i = 0; i < el.length; i++) { let n = el[i].querySelector('div.p-name').textContent if(n.includes('妙控板')){ return true } else { return false } } } catch (error) { return false } }) if(!bool){ return console.log('网页已打开,不再监控') } await goods.then(async (b)=>{ if(b){ console.log('有货了!') await page.waitFor(2000) await browers.close() return requestUrl(false) } else { console.log('还没货') console.log('三十分钟后再尝试') await page.waitFor(1800000) await browers.close() return requestUrl(true) } })}requestUrl(true)复制代码

也可以通过Github获取完整代码: 如果对你有帮助,欢迎关注我,我会持续输出更多好文章!

转载于:https://juejin.im/post/5d04d7b4e51d4510617210bc

你可能感兴趣的文章
StringBuilder用法小结
查看>>
对‘初学者应该选择哪种编程语言’的回答——计算机达人成长之路(38)
查看>>
如何申请开通微信多客服功能
查看>>
Sr_C++_Engineer_(LBS_Engine@Global Map Dept.)
查看>>
非监督学习算法:异常检测
查看>>
《OSPF和IS-IS详解》一2.7 BGP-IGP的路由交换
查看>>
App开发中甲乙方冲突会闹出啥后果?H5 APP 开发可以改变现状吗
查看>>
python知识点总结---函数
查看>>
jquery的checkbox,radio,select等方法总结
查看>>
Linux coredump
查看>>
Ubuntu 10.04安装水晶(Mercury)无线网卡驱动
查看>>
unity3d中布娃娃系统
查看>>
Myeclipes快捷键
查看>>
[译] 将 Kotlin 应用于现有 Java 代码的策略
查看>>
JavaScript 包管理器工作原理简介
查看>>
sysbench测试
查看>>
Linux_NIS+NFS+Autofs
查看>>
Git工具的安装和日常使用
查看>>
这10个州是美国网络和系统管理员最佳选择
查看>>
5miles:这家电商公司,想做推动区块链技术落地的标杆
查看>>