java爬虫抓取动态网页(优雅的使用WebMagic框架,爬取、PhantomJS、Selenium、JavaScriptEngine)

优采云 发布时间: 2022-01-23 02:07

  java爬虫抓取动态网页(优雅的使用WebMagic框架,爬取、PhantomJS、Selenium、JavaScriptEngine)

  优雅使用WebMagic框架爬取唐诗笔苑诗人诗歌数据

  同时在几种动态加载技术(HtmlUnit、PhantomJS、Selenium、JavaScriptEngine)中进行比较和选择

  虽然 WebMagic 已经快两年没有维护了,但它是一个优秀的爬虫框架的实现。源码中有很多参考,尤其是爬虫多线程的控制。另外,由于爬取的页面是非结构化数据,所以数据保存到MongoDB中。技术准备pom.xml文件中的依赖很简单,没有用到Spring系列框架,所以有些地方已经编码实现了Spring提供的功能。项目结构项目说明

  根据需要将数据保存到MongoDB数据库中,所以程序运行前必须设置resources/mongodb.properties文件

  最好确保 MongoDB 的版本为 4.0 或更高。另外,MongoDB的用户管理比较麻烦。流程大致如下:首先需要创建一个用于存储数据的数据库,比如user_tangpoem,并存储任意数据(集合)使数据库生效,然后创建数据库

  admin数据库的root用户,继续创建一个可以读写应用数据库user_tangpoem的用户,然后修改MongoDB配置文件以安全认证模式启动。重启数据库,选择admin数据库(使用admin)

  使用db.auth()用刚刚创建的用户(非root用户)登录,返回1表示认证成功,选择user_tangpoem数据库(使用user_tangpoem),输入show 采集s,如果看到数据库时采集是最初创建的,表示用户创建成功。

  详见MongoDB4.0.0远程连接和用户名密码认证登录配置——windows

  爬虫以多线程方式运行。线程数和线程休眠时间可以在 resources/spider.properties 文件中设置。设置好数据库配置后,直接运行Main.main(),爬虫就会开始爬取。

  线程休眠是WebMagic框架源码中每个线程爬取一个url后必须经过的一个过程,但是作者的文档并没有说明这个,请根据实际情况调整动态加载技术的选择1.PhantomJS和硒

  WebMagic 的底层很好地使用了 HttpClient 来加载静态页面。对于动态页面,还有两种常用的 PhantomJSDownloader 和 SeleniumDownloader。

  浏览器内核模拟浏览器行为的实现。其中,PhantomJS需要指定phantomjs.exe和要爬取的JS文件,seleniumDownloader需要指定chromedriver.exe,需要自己下载对应操作系统的版本。

  使用起来并不难,在本项目中不会过多讨论。这里的关键是解释HtmlUnit

  2. HtmlUnit

  一款开源的Java页面分析工具,阅读完页面后,可以有效的使用HtmlUnit对页面内容进行分析。纯Java实现的模拟浏览器,无需指定外部文件。

  虽然它对 JS 的支持还不完善,但总的来说 HtmlUnit 的内存消耗、CPU 消耗和效率都比 PhantomJS 和 Selenium 好,值得使用

  本项目使用2.25版本的HtmlUnit,没有JS添加不成功的问题,但是使用2.3x版本会加载失败3. JavaScriptEngine

  因为JavaScriptEngine有局限性,最明显的就是不支持jquery的语法,因为jquery使用的是浏览器的内置对象,而JS引擎本身并没有浏览器对象

  当然,只要分析了页面的加载逻辑,如果不涉及浏览器对象的使用,或者转换了 JS 逻辑,仍然可以使用 JS 引擎,但是牺牲了通用性。本项目分析后使用JS引擎加载

  4. 横向比较

  经测试,三者对比如下

  PhantomJS使用外部程序,所以JVM无法管理这部分硬件资源,需要打开任务管理器来爬取进程

  经过分析,爬取步骤分为4个步骤:

  抓取所有诗人的身份。一次调用接口即可获取所有诗人id,返回JSON格式的数据。接口地址为:抓取所有诗人信息。根据上一步的诗人id,逐一爬取对应诗人的详细信息。一共有2529条数据,然后调用接口2529次,返回JSON格式数据。接口地址为:{id} 抓取所有诗歌信息。根据上一步的诗人信息获取所有诗歌id,然后逐个调用接口获取诗歌的详细信息。总共大约有48000条数据,调用接口48000次返回html页面。需要模拟浏览器动态执行JS。接口地址为:{id}由于js是动态执行的,可能会超时,所以最后需要对还没有成功加载的诗歌信息进行处理,从数据库中读取这样的数据,形成url调用接口再次爬取直到所有数据都完成。这类数据约占1%,接口调用约480次。显然,如上所述,使用了广度优先遍历,所以当执行第三步时,数据将存储在数据库中。

  优化后使用Java 8的nashorn JS引擎执行JS代码,不需要动态加载JS,所以不会出现4的问题

  时间估计

  根据爬取过程分析,忽略程序启动时间和调用接口获取诗人id的时间

  在开启 8 个线程的并发模式下(使用 HtmlUnit 动态加载):

  所需总数:2529 / 8 5 + 48000 (1 + 0.01)/ 8 * 10 ≈ 62596 秒 ≈ 1043 分钟 ≈ 17.4 小时

  以上数据是本地测试得到的,配置为win10 8G i5-4210M 4核

  优化后,使用JS引擎代替模拟浏览器动态加载JS,获取诗歌信息所需时间由10秒大幅缩短至6秒左右。因此,重新计算时间如下:

  2529 / 8 5 + 48000 / 8 6 ≈ 37185 秒 ≈ 620 分钟 ≈ 10.3 小时

  绩效评估

  使用模拟浏览器动态加载JS时,观察JVM的使用情况,发现诗歌爬取阶段频繁出现Minor GC(新生代GC),大约每10秒一次,如下图所示。

  后来发现多线程模拟浏览器加载页面行为非常占用内存(指同时打开8个浏览器加载网页),对象频繁创建和消费频繁。

  建议在运行时通过-Xms -Xmx将JVM内存设置得更大,至少1G,然后将新生代的比例设置为更大的值,如-Xms2048M -Xmx2048M -XX:+UseParallelOldGC -XX:NewRatio =1

  

  后来用JS引擎代替模拟浏览器动态加载JS,不仅速度明显提升,内存消耗也大大降低。Minor GC 平均每分钟发生一次,如下图所示。

  

  最后附上GitHub项目地址:

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线