背景
最近有项目需求,改用无头浏览器进行微信文章得爬取,并对文章进行相应得改动,我虽并非爬虫方向开发,在需求开发过程中也有所总结,踩了不少坑,特此记录一下。
目前业界比较公认的无头浏览器是selenium+chrome的方式,除此之外也可以firefox,safari,ie等等,但是这些方案都有前提就是你的服务器上需要安装相应的浏览器,所以最初的时候,我尝试的是另一个浏览器引擎JBrowser(可被坑惨了....),下面我会相应介绍两种方案的不同,以及我所遇到的问题
selenium + jbrowser
jbrowser这个项目是github上的一个开源项目,基于javaFx技术实现网站爬取,无需在服务器上安装额外的浏览器,只需要安装带有javaFx的jdk8即可,不过据说在jdk10中javaFx被移除了,所以使用的话请注意一下
项目网址:https://github.com/MachinePublishers/jBrowserDriver
README中明确的介绍和使用案例,有三种模式,单机模式,独立节点模式和网格模式,个人理解大概就是单节点还是多节点的区别吧,我这里主要介绍单机模式
maven依赖
<dependency>
<groupId>com.machinepublishers</groupId>
<artifactId>jbrowserdriver</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
jbrowser demo
public static void main(String[] args){
String url = "https://www.baidu.com";
// 创建jBrowser driver
JBrowserDriver driver = new JBrowserDriver(Settings.builder()
// 设置时区
.timezone(Timezone.ASIA_SHANGHAI)
// 设置1s连接超时
.connectionReqTimeout(1000)
// 设置socket超时时间2s
.socketTimeout(2000)
.build());
// 获取网页内容
driver.get(url);
// 获取网页状态码
System.out.println(driver.getStatusCode());
// 获取网页代码
System.out.println(driver.getPageSource());
// 关闭jBrowser进程(与close方法有别,close是关闭标签页)
driver.quit();
}
问题一:linux环境缺少javafx环境
这段代码在window上是可以完美运行的,但是在Linux上就会报错

我在GitHub项目下边找到了这个issue,地址:https://github.com/MachinePublishers/jBrowserDriver/issues/344
大概的意思就是说,window环境的jdk8是带有jfx功能的,但是在linux环境是被移除的,linux环境更期望用户自己安装javaFx功能。
后来,我对比了一下window环境和linux环境的jdk,Linux缺少少了jfxrt.jar这个包


然后我在linux安装openjfx这个东西发现找不到,实在没办法,我就将jfxrt.jar这个包上传这个ext这个目录下,试试吧,看能不能生效。
问题二:Graphics Device initialization failed for : sw
又重新试了一下,报了一个新的问题:

同样,在github上也找到了issue,地址:https://github.com/MachinePublishers/jBrowserDriver/issues/87
大概的意思就是说,我的linux环境缺少了环境,文章有推荐安装xxx什么的,我跟着都装了一遍,发现没用,还是报这个错误,然后我觉得可能你把那个jar包拷贝进来根本没用,思考了一下绝地本质问题就是linux没有javaFx功能导致的,然后我寻寻觅觅终于找到的答案。
解决方案
参考链接:
https://blog.csdn.net/haoranhaoshi/article/details/102892216 https://blog.csdn.net/huangdeijia/article/details/79445046
通过这两篇文章,我得知linux环境的openjdk1.8是不带javaFx功能,如果你想要让系统中javaFx功能,有两种方法:一种是你直接安装oracelJDK8就好了;另一种是你要手动编译openJFX,我想图省事,选用了第一种方案,安装OracleJDK地址贴在下方,安装之后成功解决
安装OracleJDK8:https://blog.csdn.net/wen524/article/details/88104688

上边的是jbrowser自带的debug信息,不必在意,另外请各位注意一个问题:我访问https协议的网页时, 拿到的返回码是499,查了一下是说需要令牌,并且https协议的网页速度也很慢

总结
jbrowser无需安装额外的浏览器,但是需要本地jdk带有javaFx功能;Linux环境下的openJdk自身不带有javaFx模块,你可以通过自行编译openJFX或者更换为OracleJDK解决jbrowser无法启动的问题
另外,关于https协议的网页报499和https网页爬取速度优化的问题,我没有继续探究,感兴趣的同学可以研究一下,在评论中交流一下,jbrowser这个开源项目在网上的信息比较少,遇到问题的时候我在GitHub下边留言也没有人搭理我(可能我英文不太好/(ㄒoㄒ)/~~),但是确实不同于现在市面上的浏览器方案,根据需要进行选用吧。
selenium + chrome
这个方案算是网页爬取上比较常见方案,也是我最终选用的方案,我会分享一下我的使用过程。
前提条件
- 安装chrome浏览器(Debian7的apt源安装有点问题,会缺少依赖,建议升级Debian8的源)
- 将对应版本的chromedriver文件上传到服务器上,附下载地址:http://npm.taobao.org/mirrors/chromedriver/
maven依赖
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
chrome driver demo
public static void main(String[] args) throws IOException {
// 设置环境变量,指定chromedriver位置
System.setProperty("webdriver.chrome.driver","d://chromedriver.exe");
ChromeDriver driver = null;
try {
// 设置chrome浏览器的启动参数
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless", "--disable-gpu", "--no-sandbox");
Map<String, Object> prefs = new HashMap<>(2);
prefs.put("pageLoadStrategy","eager");
options.setCapability("prefs",prefs);
// 构建chrome driver
driver = new ChromeDriver(options);
// 打开指定链接
driver.get("https://mp.weixin.qq.com/s/jx0cygGoPsmASa5TUjtdfg");
// 使用Jsoup解析网页内容
Document document = Jsoup.parse(driver.getPageSource());
// 关闭Jsoup的空格打印
document.outputSettings().prettyPrint(false);
// 将网页内容输出到文件中
File file = new File("./chrome.html");
FileWriter writer = new FileWriter(file);
writer.write(document.toString());
writer.flush();
writer.close();
} finally {
// 关闭chrome driver 进程
if (driver != null){
driver.quit();
}
}
}
chrome启动参数说明
- --headless:启用chrome浏览器的无头模式,使用此参数时,不会有可视化浏览器弹出
- --disable-gpu:禁用gpu渲染功能
- --no-sandbox:关闭沙盒模式。chrome浏览器在Linux环境下会有一个问题,就是root用户必须关闭沙盒模式才能启动。
- pageLoadStrategy=eager:设置页面加载策略是加载完html即可返回。关于这个参数是用来提高get(url)的返回速度的。pageLoadStrategy这个参数有三个值,分别是none、eager、normal,含义分别是不加载、加载完html、加载完整个页面,chrome浏览器默认策略是normal,因此会导致get(url)的时间很长,要将近20s。具体请参考:https://blog.csdn.net/ouyanggengcheng/article/details/83036680
另外请注意,网上有的帖子是在setCapability方法中设置这个参数,是不对的,会报一个无效参数的错误,在java中,请使用map去存放这个参数(我忘记我在哪里找到的)
如果是新版本的selenium包,可以直接使用setPageLoadStrategy方法设置
options.setPageLoadStrategy(PageLoadStrategy.EAGER);
使用技巧
- 保证dirver.quit()方法的执行,用以关闭chrome driver进程,不然服务器上会有很多chrome driver进程的,他的进程不会复用
- ChromeDriver还有很多方法,比如获取窗口大小,运行js脚本进行页面滑动之类
- jsoup可以操作document对象,进行元素、属性的操作,配合chrome driver进行网页的处理。在使用过程中发现jsoup的解析结果中加入空格导致页面样式改变,所以使用prettyPrint(false)关闭
结果展示:成功


项目不足及提醒
我最终选用了这个方案,对微信公众号文章进行解析和爬取,图片、部分视频需要进行特殊处理才能正常显示,有一部分的公众号文章样式有问题,部分视频是无法展示的,但是95%以上的文章在处理完之后是可以正常显示的(市面上有产品时支持那些有问题的文章的,但是我暂时并没有去完善)
文章到这里就结束了,欢迎各位在评论区进行交流和指正。
网友评论