网页爬虫 JavaScript 页面渲染技术与应用

廖雨寒 杨彦松 张斌
睿哲科技股份有限公司
一、背景
随着网络的迅速发展,万维网成为大量信息的载体,如何有效地提取并利用这些信息成为一个巨大的挑战,网络爬虫(web crawler)随之而生。但是现在的网站很少有纯静态网页,大部分网站都通过javascript渲染、ajax异步等实现网页数据加载。对于目前的爬虫框架来说,基本都是爬取到的未渲染过的html源码,所以对于爬虫来说没真正做到浏览器的所见即所爬。对于scrapy来说,官方有一个scrapy-splash项目支持页面渲染解析,然而scrapy-splash在高并发状态下极其不稳定。一种方案是通过webkit浏览器引擎直接渲染,另一种方案是通过调用浏览器渲染。通过几种方案比较,最终选择了google chrome devtools protocol开发渲染功能。
二、渲染方案可行性分析
1.scrapy-splash
scrapy-splash是scrapy官方团队提供的一个解决js渲染问题的方案。splash是处理网页渲染的模块,它内部使用的开源的webkit浏览器引擎,通过http api来使用渲染服务。在scrapy中通过downloadermiddleware处理网页请求,实际是去请求splash接口并得到渲染后的数据。
splash优点:
可以并行处理多个网页
获取html结果和/或截取屏幕截图
关掉加载图片或使用 adblock plus规则使得渲染速度更快
使用javascript处理网页内容
使用lua脚本
能够获得具体的har格式的渲染信息
然而在实际使用scrapy-splash的过程中,也遇到过一些splash的问题:
对于一些特殊网站,并不能很好的处理重定向,比如一些需要通过js渲染cookie的页面,需要通过location对象重定向好几次才能到真正的页面,但是splash只是处理了第一次的渲染cookie后并没有实现跳转
在并发的情况下,获取到的数据有时候是没有内容的
2.scrapy-qtwebkit
splash是采用webkit引擎实现的网页渲染,可直接采用webkit对接scrapy实现渲染网页。在qt库中有相应的qtwebkit模块,但是在qt5.6版本以上qtwebkit就被淘汰了,代替它的是qtwebengine,因此,选择qtwebkit时,建议使用qt4或者qt5.6之前的版本。
webkit大致通过view-->page-->frame的流程来加载网页。通过这些模块,对于上面splash遇到重定向问题和空页面问题都能得到解决。
webkit优点:
细粒度处理网页渲染
资源加载可控
直接交与webkit处理更效率
webkit缺点:
内存资源占用较大
高并发处理网页过多容易引起c底层错误
三、google chrome devtools protocol
以上两种方案,都存在各自的缺点, 那有没有可能直接通过调用浏览器加载网页呢?事实上selenium可以操作浏览器。selenium操作chrome是使用chromedriver,chromedriver底层应用的是chrome devtools protocol,因此,何不直接使用chrome devtools protocol。
chrome devtools protocol介绍
在chrome/chromium浏览器中,按f12会弹出调试工具,它是通过chrome devtools protocol的协议来进行数据通讯。chrome devtools protocol用来与浏览器页面(pages)交互和调试的协议通道。它采用websocket来与页面建立通信通道,由发送给页面的commands和它所产生的events组成。
在chrome devtools protocol中,有很多不同的功能模块域(domains),类似于chrome开发这工具的个功能模块。
然而对于此例的爬虫来说,只需要用到network,page,runtime等几个功能模块域:
network允许跟踪页面的网络活动,它公开http, file, data,网络请求和响应等数据信息
page会检查与页面相关的动作和事件
runtime主要用作运行javascript操作代码
启动调试实例
要使用chrome devtools protocol,需要开启调试。我们的项目运行在服务器中,所以需要开启无头模式 --headless --disable-gpu --no-sandbox。
chrome.exe --remote-debugging-port=9222 --headless --disable-gpu --no-sandbox
如果需要远程调试,可以加上参数--remote-debugging-address='0.0.0.0'。
操作chrome devtools protocol
通过以上命令可以启动一个chrome调试实例,通过http调用接口:
http://loacalhost:9222/json
会得到接口返回的json数据:


"description": "",
"devtoolsfrontendurl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/a6c1f7b23dff222a87143acb37cbf7c4",
"id": "a6c1f7b23dff222a87143acb37cbf7c4",
"title": "about:blank",
"type": "page",
"url": "about:blank",
"websocketdebuggerurl": "ws://localhost:9222/devtools/page/a6c1f7b23dff222a87143acb37cbf7c4"


默认开启实例后会有一个空的标签页(tab),要创建新的tab,只需要通过调用接口
http://loacalhost:9222/json/new
或者在后面直接添加网址,tab将会创建并加载网页
http://loacalhost:9222/json/new?http://www.example.com/
在每个tab中有一个websocketdebuggerurl字段,它提供了一个websocket接口与chrome交互。比如要启用page模块域,通过websocket发送以下命令开启:
{"id":1, "method":"page.enable", "params":{}}
关闭tab
http://loacalhost:9222/json/close/a6c1f7b23dff222a87143acb37cbf7c4
四、scrapy实现chrome protocol下载渲染页面
scrapy是一个爬虫框架,它使用了twisted异步网络库来处理网络通讯,他的大致架构流程如图
图 1  scrapy框架
在图1中我们看到scrapy处理网络请求的是downloader模块,他通过downloaderhandler下载处理器完成下载网络请求,下载处理器使用的是twisted网络库实现的,对于chrome protocol来说我们通过接口操作命令实现网页加载本质上是chrome加载网页,也就是说下载处理是通过chrome protocol接口操作chrome浏览器请求加载并渲染网页,因此我们需要拿到渲染后的网页html源码,需要改写downloaderhandler。
然而原生操作chrome protocol太繁琐,我们需要封装它。在github上有多种语言实现了chrome devtools protocol的封装。对于python来说,我们使用pychrome库来操作chrome protocol,可以直接用pip安装模块:
pip install pychrome
当然要使用该模块得要启动chrome调试实例,通过实例的地址和端口号连接:
browser = pychrome.browser(url="http://<your_ip>:9222")
通过new_tab()方法创建tab标签页,通过tab.start()启动当前tab的websocket链接,例如需要启动network功能域,通过调用tab.network.enable()来启动,通过tab.page.navigate(url="http://www.example.com")加载网页,该页面加载时的一切网络活动都可以通过接收websocket响应得到json数据。
这时会有一个问题,何时才算是真正渲染完成页面?
图 2  js渲染流程
在图2中是浏览器从输入网址到页面加载完成的处理流程,loadeventend是真正加载完成,但是在一些使用ajax异步请求的网页使用loadeventend并不能很好的判断为渲染结束,所以这里我们使用javascript命令获取readystate状态判断页面是否加载完成:
document.readystate
readystate一共有五种状态:
uninitialized - xml 对象被产生,但没有任何文件被加载
loading - 加载程序进行中,但文件尚未开始解析
loaded - 部分的文件已经加载且进行解析,但对象模型尚未生效
interactive - 仅对已加载的部分文件有效,在此情况下,对象模型是有效但只读的
complete - 文件已完全加载,代表加载成功
通过document.readystate == complate来判断数据的加载进度。当加载完成时通过javascript命令获取渲染后的html源码:
document.documentelement.outerhtml
在这里我们就可以使用pychrome替代scrapy的twisted下载处理。
五、结语
使用chrome devtools protocol来操作chrome浏览器来渲染页面,能基本完成渲染页面的需求,一些需要与浏览器交互的页面也大致能使用javascript操作命令,重点是使用chrome操作页面渲染会很占用资源,因此不宜过多的开启渲染进程,多线程下载尽量重用tab标签页,因为每多创建一个tab相当于开了一个进程,最后,爬虫结束时,应该调用相关接口关闭所有tab,释放资源。

三星GalaxyS11高清渲染图公布 屏占比进一步增大
2020年新能源汽车销量:特斯拉年度销量遥遥领先 上通五菱杀出重围
智能电网中照明和能源控制IC与传感器的重要性
芯片如今受限于硅技术难以突破
AI/VR/AR等“八大核心科技”你都了解吗?
网页爬虫 JavaScript 页面渲染技术与应用
物联网新型基础设施智慧灯杆标准化白皮书
英伟达加大VR显卡产量 但是要求零售商优先游戏玩家
中国的半导体和国际差距
接口模块的组合应用方案
美信推出推出MAX11102系列系列低功耗ADC
D君讲堂|为碳中和贡献力量的HV电池修理
选购RVS双绞线时需要掌握的三个小技巧
梦之墨“工程实践与创新能力”系列课程成功落地华中科技大学
惠普电脑扎心了 被曝80款受影响隐藏键盘记录器
UPS将部分AirPods Max退还给苹果:含有 “危险材料”
VVT-i、VTEC是什么,发动机的可变气门正时与升程技术
国星光电持续丰富新能源车载光应用产品
基于三权分立的去中心化模型将解决人类相信机器的问题
沉浮:中国大飞机的三十年