当前位置: 首页 > 后端技术 > Node.js

Puppeteer系列踩坑日志—1—批量截图变白

时间:2023-04-03 15:32:43 Node.js

Puppeteer最常用于截图(每日批量自动截图)。我的目标是Xbao的一些商店主页。我这次遇到的问题是在一些无线终端上,商户的页面很长,甚至达到6万到10万像素。经常出现白屏或残缺现象。第一个坑:加载问题?遇到了类似的问题。首先我想到的是页面的资源加载问题。puppeteer截图的时候不是加载了吗?于是我根据页面的滚动查看,确实有类似的情况。首先,如果无线端的页面是无状态登录(没有设置userdata、cookie等),会弹出登录。简单看了一下dom,其实就是一个浮层,清理一下就好了。第二步,截图了半天,发现有时候页面在loading,所以对于这种类型,我让promise直接reject(还能怎么办,放到error队列里,稍后再试!)第三步,当然,作为一个强迫症患者,看不惯乱七八糟的字体,所以就统一了,全部设置为微软雅黑,这样就有了风格。第四步,在做滚动的时候,发现PC端其实很简单,body.scrollBy就可以直接滚动,但是到了无线端,就坑了!这个XXX怎么也动不了,我打开调试器一看,Xbao无线端的DOM全是DIVDIVDIVDIVDIV(写一个selector很麻烦,后面会讲到如何快速找到我想要的元素,掌握棍子,但瞄准院子)。还好后来看到了曙光。其实滚动的关键就在.rax-scrollviewdiv中,所以解决了滚动问题。好了,说了这么多,何不来个更全面的代码:asyncfunctionscrollToBottom(page){returnawaitpage.evaluate(()=>{returnnewPromise((res,rej)=>{if(document.querySelector('body>div>span')){if(document.querySelector('body>div>span').innerText==='加载中...'){rej('页面截图太快了,天猫加载不出来')}}varstyle=document.createElement('style');style.innerHTML='*{font-family:"MicrosoftYahei"!important;}';document.body.appendChild(style);vartotalH=0;vardistance=250;varclientWidth=document.body.clientWidth;varselectorEle='.rax-scrollview';//淘宝独家滑动domvarscrollEle=document.querySelector(selectorEle)!=null?document.querySelector(selectorEle):document.body;varscrollEleS=document.querySelector(selectorEle)!=null?document.querySelector(selectorEle):窗户;vartimer=setInterval(()=>{varscrollHeight=scrollEle.scrollHeight;scrollEleS.scrollBy(0,distance);totalH+=distance;if(document.querySelector('body>div.J_MIDDLEWARE_FRAME_WIDGET>div>a')){document.querySelector('body>div.J_MIDDLEWARE_FRAME_WIDGET>div>a').click();}if(totalH>=scrollHeight){clearInterval(timer);scrollEleS.scrollTo(0,0);//返回top再截图防止从底部截图,如果一些活跃的页面不回头就会出问题。res({w:clientWidth,h:scrollHeight})}},250)})})}第二个坑:截图问题?再次运行服务器后,发现又出现了空白,而且这次的空白明显比上次多了……考虑了各方面的问题(我不喜欢绕太多),终于把问题锁定在page.screenshot这个链接里,因为headless是关掉的,所以我只是看着页面完全加载!还有一个空白,如果不是你的API,还能是谁?在一次操作中(搜索引擎+github+官网),发现一个有趣的代码块:https://github.com/ChromeDevT...其实可能是截图的高度受限了。过高也会出现类似的问题。当然,搞代码的我们也要讲究严谨。为了证明这一点,我用一些高度在1000-2000像素的网站进行了测试。大约100个网站,截图完美。这几乎也被证明是这里的问题。既然问题找到了,那就来解决吧。先祭出代码,让我们按照代码“事后诸葛亮”:letImageHeight=5000;//pc限制截图的高度if(!!!pageInfo.type){ImageHeight=1000;//mob限制截图的高度}lettempLength=Math.ceil(data.h/ImageHeight)//计算截了多少张letimagesArr=[]//存储我们截图的顺序for(leti=0,j=1;i{console.log('screenshot',i,'picture');imagesArr.push(`yourpath`)}).catch(err=>{console.log('截图失败!');})}逻辑是一张一张截图。这样的话,问题就基本解决了!代码很简单,简单到我觉得不用解释了,不过还是多说几句,让我判断一下pc是不是无线的(pc就不用截图了,因为pc大概率不会有问题,除非很长,可以根据实际情况修改,我觉得5000差不多)。另外需要提醒的是:这里5000和1000的阈值需要根据你的电脑/服务器情况来确定。如果配置不好,您可能需要降低它。之前遇到过在一台很老的笔记本上测试,发现1000会=-=黑屏...(是0202,你问我,为什么要用很老的笔记本跑这个?因为写文章一定是严格!多测试!)我们再存到数组中(方便,后面会合并)。因为涉及到多任务处理(不能真的只开一个进程),如果不存储,那么合并删除这些小片段后,还要区分不同进程的片段截图,可能会有点蛋疼。所以每个进程都保留一个“小笔记本”~后面我们就知道删除哪些片段截图了。这里需要注意的是GM图片库是用来合并图片的(我用了不到一个小时就解决了这个问题,本来想省事找个轻量级的,结果发现没有GM.简单粗暴,唉,花花我整整一个下午,最后切换回GM库,5分钟搞定。)代码很简单,但是要安装GM,需要安装它的提前准备好软件,百度一下就可以找到。Windows一步步来,linux基本一样,wget需要的安装包,然后tar解压XXXXX进行常规操作,差不多就搞定了,这里就不多说了。演示一些GM代码:letdeleteFile=[...imagesArr];等待gmMerge(foldname,pathName,imagesArr)functiongmMerge(foldname,pathName,imagesArr){returnnewPromise((res,rej)=>{gm(imagesArr).shift()).append(...imagesArr).write(`./screenshot/${foldname}/${pathName}.png`,function(err){if(err){console.log('截图合并失败!')rej('截图合并失败!')}else{console.log('全屏生成成功!');res('全屏生成成功!');}});})}简单解释接下来把要删除的小复制一份,因为我们要用第一个截图作为初始画布,然后把后面的都加进去。我这里比较懒,用的是shift方式,以后删了,就漏了。所以我做了。你可以有更简单的方法,这里就不多说了。主要是我放在promise里面,这样awaitgmmerge函数就够了。这里需要提醒一下,promise一定要密封,否则会直接async执行,很有可能任务队列结束,这些片段的截图不会被删除,而是会留在里面,因为它会在不等待操作完成的情况下结束。其他坑:除了截图,puppeteer还提供element截图(部分dom),当页面确实复杂冗余时可以使用。具体可以参考下面的代码:letfooter=awaitpage.$('#footer');//获取DOM对象,有截图方法footer.screenshot({path:'demo.png'});其实关于puppeteer截图空白的问题其实是一个由来已久的问题。偶尔你会发现明明很正常的PC截图也会出现类似的问题。如果出现意外情况(截图被绘制),建议使用viewportresolution/screenshotsize开始思考,几乎大部分问题都出在这些地方。如果遇到其他问题,可以在评论底部联系我,我们一起学习如何解决这个坑。