Java网络爬虫(十三)

Wesley13
• 阅读 312

先说点题外话吧,在我刚开始学习爬虫的时候,有一次一个学长给了我一个需求,让我把京东图书的相关信息抓取下来。恩,因为真的是刚开始学习爬虫,并且是用豆瓣练得手,抓取了大概500篇左右的影评吧,然后存放到了mysql中,当时觉得自己厉害的不行,于是轻松的接下了这个需求。。。

然后信心满满的开始干活。。首先查看网页源代码。。。???我需要的东西源代码里面没有!!!然后去问了学长。学长给我说,这是AJAX产生的数据,大概听完之后我就去查了资料。发现网上大片的资料都在阐述一个道理,对于动态页面,使用PhantomJs进行抓取,但是这样效率很低。作为一个优秀的程序员,当时看见效率很低这四个字,那在我心里是绝对不能被允许的,所以我就采用了抓包的方式,查看AJAX数据所在的URL,对于这个模拟浏览器的方法也就一直搁置到现在。

但是既然知道了这个东西,哪有不去学习的道理。所以我抽出了一点时间看了一下关于Java方面使用PhantomJs的资料,现在分享给大家。

对了,其实做网络爬虫,页面上90%的数据都可以使用抓包进行获取。所以我还是鼓励大家直接请求自己所需数据所在的URL。毕竟这种方式虽然方便,但是效率低下。


JS渲染与AJAX

在学习这个东西之前我们首先得了解什么是JS渲染、什么是AJAX以及为何这两种数据我们在网页源码里面获取不到。

依照我的理解,JS渲染与AJAX是一种相辅相成的关系,AJAX负责异步从服务器端获取数据,拿到数据后再使用JS进行渲染,最后呈现给用户。由于在Java中,HttpClient只能请求简单的静态页面,并不能请求到页面完全加载好后由JS调用相关代码产生的异步数据,所以我们不能直接通过HttpClient获取AJAX与JS渲染产生的数据。此时按上面所说的推荐大家直接进行网络抓包拿到AJAX数据所在的URL,或者使用本文所说的PhantomJs渲染引擎。


三大JS渲染引擎的比较

在网上进行资料查阅的时候,我们经常会因为五花八门的答案而不知所措,这时候一是要保持一颗平静的心情,二就要考虑搜索问题的相关姿势,必要的时候还需要科学上网。

先不说本文所说的JS渲染引擎,单说在Java爬虫中HTTP请求的库简直就可以用五花八门来形容,Java的原生HttpURLConnection类,HttpClient第三方库等等… …当然网络上提供了这么多方法,我们必须要进行选择,那么我们肯定想选择功能强大的,使用简单的类库。此时我们就应该在网上搜索对两个类库做相关比较的问题,来进行更好的选择,而不是随便挑一个学习就完事了,这样很有可能投入的学习成本与回报不成正比。

那么相信大家在准备使用JS引擎模拟浏览器的时候,在网上看过不仅有PhantomJs,还听说过Selenium,HtmlUnit这两个具有相同功能的东西。那么我们该如何选择呢?下图截选自其他网友的博客:

HtmlUnit

Selenium

PhantomJs

内置Rhinojs浏览器引擎,没有哪一款浏览器使用该内核,解析速度一般,解析JS/CSS差,无浏览器界面。

Seleninum + WebDriver = Selenium基于本地安装的浏览器,需打开浏览器,需要引用相应的WebDriver,正确配置webdriver的路径参数,在爬取大量js渲染页面时明显不合适。

神器,短小精悍,可本地化运行,也可作为服务端运行,基于webkit内核,性能及表现良好,完美解析绝大部分页面。

这也是我选择讲述PhantomJs的原因。

网上PhantomJs和Selenium还经常成对出现,原因是Selenium封装了PhantomJs的一部分功能,Selenium又提供了Python的接口模块,在Python语言中可以很好地去使用Selenium,间接地就可以使用PhantomJs。然而,是时候抛弃Selenium+PhantomJs了,原因之一此封装的接口很久没有更新了(没人维护了),原因之二Selenium只实现了一部分PhantomJs功能,且很不完善。


PhantomJs的使用

我使用的Ubuntu16.04的开发环境,至于PhantomJs + Selenium的环境部署,网络上有大篇资料,我就在这里给大家引入一个链接,也不详细说明了:ubuntu安装phantomjs

关于PhantomJs和Selenium的介绍我也就不再详说,大家直接百度就可以了。我们直接来看一下在Java中应该怎么使用PhantomJs~

如果你没有使用Maven的话,就在网上下载第三方jar包。我们所需要的Maven依赖如下:

<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>2.53.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.github.detro.ghostdriver/phantomjsdriver -->
    <dependency>
      <groupId>com.github.detro.ghostdriver</groupId>
      <artifactId>phantomjsdriver</artifactId>
      <version>1.1.0</version>
    </dependency>

接下来我们来看一下程序到底应该怎么写:

1.设置请求头

//设置必要参数
DesiredCapabilities dcaps = new DesiredCapabilities();
//ssl证书支持
dcaps.setCapability("acceptSslCerts", true);
//截屏支持
dcaps.setCapability("takesScreenshot", true);
//css搜索支持
dcaps.setCapability("cssSelectorsEnabled", true);
//js支持
dcaps.setJavascriptEnabled(true);
//驱动支持(第二参数表明的是你的phantomjs引擎所在的路径,使用whereis phantomjs可以查看)
dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, "/usr/local/bin/phantomjs");

2.创建phantomjs浏览器对象

//创建无界面浏览器对象
PhantomJSDriver driver = new PhantomJSDriver(dcaps);

3.设置隐性等待

//设置隐性等待
driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);

因为Load页面需要一段时间,如果页面还没加载完就查找元素,必然是查找不到的。最好的方式,就是设置一个默认等待时间,在查找页面元素的时候如果找不到就等待一段时间再找,直到超时。

以上三点是使用PhantomJs时需要注意的地方,大家可以看一下大致的整体程序:

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.util.concurrent.TimeUnit;

/**
 * Created by hg_yi on 17-10-11.
 */
public class phantomjs {
   
        
    public static void main(String[] args) {
        //设置必要参数
        DesiredCapabilities dcaps = new DesiredCapabilities();
        //ssl证书支持
        dcaps.setCapability("acceptSslCerts", true);
        //截屏支持
        dcaps.setCapability("takesScreenshot", true);
        //css搜索支持
        dcaps.setCapability("cssSelectorsEnabled", true);
        //js支持
        dcaps.setJavascriptEnabled(true);
        //驱动支持(第二参数表明的是你的phantomjs引擎所在的路径)
        dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
                "/usr/bin/phantomjs-2.1.1-linux-x86_64/bin/phantomjs");
        //创建无界面浏览器对象
        PhantomJSDriver driver = new PhantomJSDriver(dcaps);

        //设置隐性等待(作用于全局)
        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
        //打开页面
        driver.get("--------------------------------");
        //查找元素
        WebElement element = driver.findElement(By.id("img_valiCode"));

        System.out.println(element.getAttribute("src"));
    }
}

我成功的抓取到了网页源码里面没有的数据~

关于上面使用到的PhantomJSDriver类的相关API,大家直接看这篇资料即可,应该可以满足你的日常需求了:webdriver API中文版(WebDriver的API同样适用于PhantomJSDriver)。


PhantomJs的性能优化

我们都知道使用PhantomJs这种无头浏览器进行网页源码的抓取是非常费时的,所以当我们决定使用这个工具并且对抓取速度还有一定要求的时候,就需要掌握对PhantomJs进行性能优化的能力。

1.设置参数

Google,Baidu半天,还看了一点官方文档,还是找不到PhantomJs相关的Java调用API文档,好吧,先扔一篇Python的,以后找到这方面的内容再进行补充吧~~~

【phantomjs系列】Selenium+Phantomjs性能优化

题外话:其实对于Java网络爬虫。。。真想吐槽,建议刚开始准备学爬虫的人还是转战Python爬虫吧~~对于Java爬虫,只说一句话:学习难度大,学习周期长,投出与收入不成正比!!!

点赞
收藏
评论区
推荐文章
光头强的博客 光头强的博客
4个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
Jacquelyn38 Jacquelyn38
1年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
1年前
Java爬虫之JSoup使用教程
title:Java爬虫之JSoup使用教程date:201812248:00:000800update:201812248:00:000800author:mecover:https://imgblog.csdnimg.cn/20181224144920712(https://www.oschin
Easter79 Easter79
1年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
1年前
MySQL查询按照指定规则排序
1.按照指定(单个)字段排序selectfromtable_nameorderiddesc;2.按照指定(多个)字段排序selectfromtable_nameorderiddesc,statusdesc;3.按照指定字段和规则排序selec
Stella981 Stella981
1年前
Angular material mat
IconIconNamematiconcode_add\_comment_addcommenticon<maticonadd\_comment</maticon_attach\_file_attachfileicon<maticonattach\_file</maticon_attach\
Wesley13 Wesley13
1年前
PHP中的NOW()函数
是否有一个PHP函数以与MySQL函数NOW()相同的格式返回日期和时间?我知道如何使用date()做到这一点,但是我问是否有一个仅用于此的函数。例如,返回:2009120100:00:001楼使用此功能:functiongetDatetimeNow(){
Wesley13 Wesley13
1年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
helloworld_34035044 helloworld_34035044
7个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为