puppeteer是2017年5月,由GoogleChrome推出的node包,内置了headless chrome/chromium浏览器,并提供丰富API以全面操控浏览器。可用于截图、生成PDF、数据爬取、自动测试等。
较于phantomjs,puppeteer可以直接用node调用,且API更易用。
对于网站资源的爬取,往往会遇到一个困难:所需的资源并不直接存在,而是经过了加密。比如腾讯漫画的漫画图片就经过了RSA加密。
虽然前端不存在真正的加密,但要找到加密流程并重新实现还是比较复杂的。
利用headless browser进行模拟加载,对加载完成的页面进行资源的查找,是一种替代的方案。这种方法效率要低一些,但开发成本也更低。
下面是一个用puppeteer爬取腾讯漫画资源的例子:
const puppeteer = require('puppeteer');
const wait = (ms) => new Promise(resolve => setTimeout(() => resolve(), ms));
(async () => {
// 创建browser和page
const browser = await puppeteer.launch();
const page = await browser.newPage();
// 捕获异常,保证browser.close()有效执行
try {
// 转到《海贼王》最新一话页面
await page.goto('http://ac.qq.com/ComicView/chapter/id/505430/cid/915');
// 获取图片数目和高度
const imagesLen = await page.$$eval('#comicContain img[data-h]', imgs => imgs.length);
const imgHeight = await page.$eval('#comicContain img[data-h]', img => img.getAttribute('data-h'));
// 自动滚动,使懒加载图片加载
const step = 1;
for (let i=1; i < imagesLen / step; i ++) {
await page.evaluate(`window.scrollTo(0, ${i * imgHeight * step})`);
// 为确保懒加载触发,需要等待一下。实际需要的时间可能不止100ms
await wait(100);
}
// 获取图片url
const images = await page.$$eval('#comicContain img[data-h]', imgs => {
const images = [];
imgs.forEach(img => {
images.push(img.src);
});
return images;
});
console.log(images);
} catch (err) {
console.error(err);
}
// 关闭浏览器
await browser.close();
})();
输出:
[ 'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420441&dir_path=/&name=19_08_47_49d8b179d3b33a174c0a2e88fe694c03_25517.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420441&dir_path=/&name=19_08_47_ffce200ae6fd6362d62fc668fee04fda_25518.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420441&dir_path=/&name=19_08_47_61815ec742bb71fc229566af0ec540b6_25519.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420441&dir_path=/&name=19_08_47_e8cc16772500e82959d0d2d529747987_25520.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420441&dir_path=/&name=19_08_47_f87e8298efe6d0f53d9effa8d4fae632_25521.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420441&dir_path=/&name=19_08_47_fccdb06ce0fbdc541cabdc1c97dfa809_25522.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420441&dir_path=/&name=19_08_47_e15c61db90fd67b4363755006b35251e_25523.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420441&dir_path=/&name=19_08_47_4ee9f370bc0b6fa6d42a39b8333065c3_25524.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420457&dir_path=/&name=19_08_47_87ad69ffccf51eca8f4b7159ad9cdbae_25525.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420458&dir_path=/&name=19_08_47_5d1bfa6849dd409fa426a1c7dae64ee1_25526.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420458&dir_path=/&name=19_08_47_f3d5b55311f403808dd5fb98ddc54519_25527.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420458&dir_path=/&name=19_08_47_a9fcab335b0b1f7f36d935814c0066df_25528.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420458&dir_path=/&name=19_08_47_c5b18da4620c80cf40cc6d75e3c70327_25529.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420458&dir_path=/&name=19_08_47_4d0d0aaebeb19dfd70e711f8bf97906e_25530.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420458&dir_path=/&name=19_08_47_1b6e00039fe54a70e534c098931cb9b3_25531.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420458&dir_path=/&name=19_08_47_a95418c785bd950764422ccd8ca05269_25532.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420458&dir_path=/&name=19_08_47_5f32a58fe438ead1fb40c3cdd097a4e5_25533.jpg',
'http://ac.tc.qq.com/store_file_download?buid=15017&uin=1521420458&dir_path=/&name=19_08_47_d2dfe29b136c7c42e0ba1bb56a40e5b0_25534.jpg' ]
可见,对延迟加载资源的页面,还需要做一些模拟操作,来使资源加载出来。
* 实际上,如果对延迟加载部分的js代码有所了解,还可以通过page.request() API来使关键js替换为自己的本地文件。通过稍稍修改其中的逻辑,就可以使资源在一开始时全部加载出来了。