你没有碰到过要将网页转换为PDF文件的需求? 图片呢?
如何保存一个网页的快照? 截图? 页面过长怎办,系统自带的截图工具无法滚屏
海外网页被墙,经过在香港服部署一PDF生成服务能够免除无谓的浏览其它垃圾/非法网址。
公司管理层须要在公司集成办公系统上查看其它系统的资料,好比hr系统上员工简历,营销系统上的某报表...html
需求仍是很多的。前端
我的前后使用过 wkhtmltopdf , phantomjs ,puppeteer。
wkhtmltopdf 使用起来最简单,功能比较单一,看名字就知道了,在windows下直接就是一个exe,可在本身的项目中fork/startProcess 开一个新进程,并传入网页和保存的路径地址,经过 wkhtmltopdf -h 能够查看各参数和说明。若是你要生成的页面是集团内部服务,那么使用它是最方便的。node
phantomjs ,一样经过名字就知道它和js 结合的很是紧密。它使用webkit内核,同pupp,用户在可在页面内注入js,像在浏览器上操做页面和元素同样操做它,但它已中止更新了。linux
puppeteer 是google 提供的 node 库用来操做chrome,有些人称之为无头浏览器,在linux上使用会配套下载chromium。功能上彻底能够做为phantomjs 替代品,正由于它操做的实际是chrome浏览器,对于分析页面数据,模拟和破解前端逻辑很是给力。web
除此以外,其实还有不少工具,网上能搜出一大把,这里就很少说了。chrome
使用puppeteer实现windows
'use strict' const Service = require("egg").Service; const puppeteer = require('puppeteer'); class pupp extends Service { async Create(tourl, savePath,ops) { ops = ops || {}; //console.log("pupp.js ", tourl, savePath,ops); const browser = await puppeteer.launch({ args : [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu', '--disable-dev-shm-usage', '--no-first-run', '--no-zygote', //'--single-process' ], ignoreHTTPSErrors:true }); const page = await browser.newPage(); // console.log("before go to:"); await page.setUserAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"); /* await page.setExtraHTTPHeaders({ "accept-language":"zh-CN,zh;q=0.9,en;q=0.8", "cookie":"csrfToken=V04e-JSpCtM_BEr_VH2-73N9", "cache-control":"max-age=0" });*/ if(ops.cookies) { console.log("setcookies return :",await page.setCookie(...ops.cookies)); await page.waitFor(200); } await page.goto(tourl, { waitUntil : 'networkidle2', timeout : 60000 }); if(ops.navigation) await page.waitForNavigation({waitUntil:'networkidle2'});//没有这个,weibo 相关页面会是空白 //await page.screenshot({path: 'screenshot.png'}); //否则会有href 显示在页面 page.emulateMedia('screen'); let scrollEnable = true; let scrollStep = 1000; //每次滚动的步长 let scrollCount = 0; while (scrollEnable) { if (scrollCount++ > 5) //最多容许滚动5次 { console.error("too many scroll, jumpout ", tourl); scrollEnable = false; break; } scrollEnable = await page.evaluate((scrollStep) => { let scrollTop = document.scrollingElement.scrollTop; document.scrollingElement.scrollTop = scrollTop + scrollStep; return document.body.clientHeight > scrollTop + scrollStep ? true : false }, scrollStep); await page.waitFor(200) } page.waitFor(2000); await page.pdf({ path : savePath, width : "1280px", height : "1960px", printBackground:true }); await browser.close(); } /** 将cookie string 转换为cookie object 的数组 */ cvtCookie(ck_line) { let arr = fs.readFileSync("./old_cookies.txt").toString().split(";"); let objcks = arr.map(aline=>{ let ind = aline.indexOf("="); return {name:aline.substr(0,ind),value:aline.substr(ind+1)}; }); } } //延时 function waitMs(ms) { if (typeof ms != "number") return Promise.resolve(0); return new Promise((resolve, rej) => { setTimeout(() => { resolve(1); }, ms); }); } module.exports = pupp;
以上是生成pdf文件的核心代码,使用的是egg框架,不熟悉并不影响阅读。有以下几点可留意一下数组
1:有些注释掉的代码没有去掉,有些代码也能够注释,取决于你真实的测试状况,许多网页的渲染方式不同,有些还混合着多种js异步渲染。浏览器
2:文中的一些具体参数都是通过反复屡次验证效果后添加或调整过的,官网上并不会有这些内容,有时一个默认参数就会让效果迥异。服务器
3:代码中有段cookie设置,这是由于不少网页用户不登录不能看,因此须要前置的用户登录,并获取登录成功后的cookie,进而在此步使用。须要留意模拟登录后的cookie(甚至手动在浏览器登录后保存cookie)使用的浏览器请求头应尽可能保持一致,以避免服务端将它区分为两个不一样用户。
4:文中有个滚动页面的逻辑,由于有些页面仅显示一屏,当用户滚动鼠标到文末时才会继续加载,就像一些手机app,最多往下拉五次,拉太多没多大意义还浪费时间。
puppeteer用做生成pdf有些大材小用了,用做分析和操做页面元素获取数据都十分方便的。我的认为它不太适合大批量的爬取数据,效率较低。
另外,要获取别人的页面就绕不开须要登录和验证码,若是不是过于频繁的请求,如上文所述,直接用户手动在浏览器输入账密,而后再将浏览器的cookie保存下来,交由上面代码处理就够了。但若是是将服务部署在服务器,这种办法就失去了做用。笔者就用过一个网上下载的新浪微博登录库,并使用一种很“挫”的方式填充验证码,工做良好,有兴趣的同窗能够留言向我了解。
下一篇,将讲述使用 selinum 获取股票数据