美文网首页
selenium + chrome(jbrowser) + Js

selenium + chrome(jbrowser) + Js

作者: 咖啡不加糖HF | 来源:发表于2020-01-22 16:09 被阅读0次

背景

最近有项目需求,改用无头浏览器进行微信文章得爬取,并对文章进行相应得改动,我虽并非爬虫方向开发,在需求开发过程中也有所总结,踩了不少坑,特此记录一下。

目前业界比较公认的无头浏览器是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上就会报错

image.png
我在GitHub项目下边找到了这个issue,地址:https://github.com/MachinePublishers/jBrowserDriver/issues/344
大概的意思就是说,window环境的jdk8是带有jfx功能的,但是在linux环境是被移除的,linux环境更期望用户自己安装javaFx功能。
后来,我对比了一下window环境和linux环境的jdk,Linux缺少少了jfxrt.jar这个包
image.png
image.png
然后我在linux安装openjfx这个东西发现找不到,实在没办法,我就将jfxrt.jar这个包上传这个ext这个目录下,试试吧,看能不能生效。

问题二:Graphics Device initialization failed for : sw

又重新试了一下,报了一个新的问题:

image.png
同样,在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

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

总结

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)关闭

结果展示:成功

image.png image.png

项目不足及提醒

我最终选用了这个方案,对微信公众号文章进行解析和爬取,图片、部分视频需要进行特殊处理才能正常显示,有一部分的公众号文章样式有问题,部分视频是无法展示的,但是95%以上的文章在处理完之后是可以正常显示的(市面上有产品时支持那些有问题的文章的,但是我暂时并没有去完善)


文章到这里就结束了,欢迎各位在评论区进行交流和指正。

相关文章

网友评论

      本文标题:selenium + chrome(jbrowser) + Js

      本文链接:https://www.haomeiwen.com/subject/ixmqzctx.html