偶然发现一个叫puppeteer的库,这样解释的:“谷歌浏览器在17年自行开发了Chrome Headless特性,并与之同时推出了puppeteer,可以理解成我们日常使用的Chrome的无界面版本以及对其进行操控的js接口套装”。简单说就是模拟浏览器行为的第三方库,再说直白点,只要用浏览器访问的页面,它都可以去操作,强大之至。
想起朋友在做一个学校教务系统爬虫,苦于该校登陆界面是用js渲染的,登陆接口会拼接上js加密的字符串,这个字符串只有在浏览器下才会动态生成,js又是瑞数加密,破解成本极高,极难,便来尝试利用puppeteer来模拟登陆获取用户数据。
现在开始,打开登陆界面开始分析
用puppeteer其实就是人怎么操作的,将它写成代码即可
1:打开网址
2:输入学号
3:输入密码
4:点击登陆
实现成代码便是:
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({
ignoreDefaultArgs: ["--enable-automation"],
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
}); //去除自动化测试的提醒
const page = await browser.newPage();
//为了像一个浏览器,伪造更多数据
await page.evaluateOnNewDocument(() => { //在每个新页面打开前执行以下脚本
const newProto = navigator.__proto__;
delete newProto.webdriver; //删除navigator.webdriver字段
navigator.__proto__ = newProto;
window.chrome = {}; //添加window.chrome字段,为增加真实性还需向内部填充一些值
window.chrome.app = {
"InstallState": "hehe",
"RunningState": "haha",
"getDetails": "xixi",
"getIsInstalled": "ohno"
};
window.chrome.csi = function () {};
window.chrome.loadTimes = function () {};
window.chrome.runtime = function () {};
Object.defineProperty(navigator, 'userAgent', { //userAgent在无头模式下有headless字样,所以需覆写
get: () => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36",
});
Object.defineProperty(navigator, 'plugins', { //伪装真实的插件信息
get: () => [{
"description": "Portable Document Format",
"filename": "internal-pdf-viewer",
"length": 1,
"name": "Chrome PDF Plugin"
}]
});
Object.defineProperty(navigator, 'languages', { //添加语言
get: () => ["zh-CN", "zh", "en"],
});
const originalQuery = window.navigator.permissions.query; //notification伪装
window.navigator.permissions.query = (parameters) => (
parameters.name === 'notifications' ?
Promise.resolve({
state: Notification.permission
}) :
originalQuery(parameters)
);
})
await page.goto('http://xxx/Login.html')
await page.waitForNavigation();//等待加载完成
await page.type('#txtUser', "20190xxxxx");
await page.type('#txtPWD', '42052120xxxxx');
await page.click('#ibtnLogin');
await page.waitForNavigation();
登陆成功,接着点击【成绩单】
const [response] = await Promise.all([
page.waitForNavigation(),
page.click('a[href="SearchInfo/Score/ScoreList.aspx"]')
]);
let res = await page.$eval('ul.listUl', el => el.outerHTML)//拿到dom
await browser.close();
如果输出res则是这个表格的所有代码
这玩意还可以截屏,没错,你看不到界面,还可以用方法截屏
await page.screenshot({
path: 'c:/temp/temp.png'
})
加上这段就可以截屏并存在C盘下temp文件夹里,如果没有文件夹不会自动创建,需要自己提前创建好
是不是感觉好用到爆,可是这玩意儿就好比后台打开了一个浏览器运行的,如果多个请求进来,后台也会打开多个任务,分别执行,但是并发高了岂不是服务器都要炸了。那能不能放到云函数里呢?答案是可以的。
放到微信小程序云函数中可以直接运行,无需安装库,官方nodejs10版本已经默认安装了,直接引用就可以用
再来看耗时和所占内存大小
耗时2789ms,内存135.74MB完全可以接受。
总结:puppeteer模拟登陆可以做任何形式的网站,但是速度有点慢
优点:最大优点也是优势就是可以做那些做了反爬虫的网站,和js渲染的网站
缺点:内存黑洞,放服务器上不能应对高并发(用云函数可以解决),速度慢,因为是模拟人工操作,输入内容都是一个个值输入,还要等待加载,所以比常规的请求接口方式慢不少