Java 爬虫数据异步加载如何解决

这是 Java 爬虫系列博文的第三篇,在上一篇Java 爬虫如何爬取需要登录的网站,该怎么办? 中,我们简单的讲解了爬虫时遇到登录问题的解决办法,在这篇文章中我们一起来聊一聊爬虫时遇到数据异步加载的问题,这也是爬虫中常见的问题。

现在很多都是前后端分离项目,这会使得数据异步加载问题更加突出,所以你在爬虫时遇到这类问题不必惊讶,不必慌张。对于这类问题的解决办法总体来说有以下两种:

1、内置一个浏览器内核

内置浏览器就是在抓取的程序中,启动一个浏览器内核,使我们获取到 js 渲染后的页面,这样我们就跟采集静态页面一样了。这种工具常用的有以下三种:

  • Selenium
  • HtmlUnit
  • PhantomJs

这些工具都能帮助我们解决数据异步加载的问题,但是他们都存在缺陷,那就是效率不高而且不稳定。

2、反向解析法

什么是反向解析法呢?我们 js 渲染页面的数据是通过 Ajax 的方式从后端获取的,我们只需要找到对应的 Ajax 请求连接就 OK,这样我们就获取到了我们需要的数据,反向解析法的好处就是这种方式获取的数据都是 json 格式的数据,解析起来也比较方便,另一个好处就是相对页面来说,接口的变化概率更小。同样它有两个不足之处,一个是在 Ajax 时你需要有耐心有技巧,因为你需要在一大推请求中找到你想要的,另一个不足的地方就是对 JavaScript 渲染的页面束手无策。

上面就是异步数据加载的两种解决办法,为了加深大家的理解和在项目中如何使用,我以采集网易要闻为例,网易新闻地址:https://news.163.com/ 。利用上诉的两种方式来获取网易要闻的新闻列表。网易要闻如下:

内置浏览器 Selenium 方式

Selenium 是一个模拟浏览器,进行自动化测试的工具,它提供一组 API 可以与真实的浏览器内核交互。在自动化测试上使用的比较多,爬虫时解决异步加载也经常使用它,我们要在项目中使用 Selenium ,需要做两件事:

1、引入 Selenium 的依赖包,在 pom.xml 中添加

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

2、下载对应的 driver,例如我下载的 chromedriver,下载地址为:https://npm.taobao.org/mirrors/chromedriver/,下载后,需要将 driver 的位置写到 Java 的环境变量里,例如我直接放在项目下,所以我的代码为:

System.getProperties().setProperty("webdriver.chrome.driver", "chromedriver.exe");

完成上面两步之后,我们就可以来编写使用 Selenium 采集网易要闻啦。具体代码如下:

/**
 * selenium 解决数据异步加载问题
 * https://npm.taobao.org/mirrors/chromedriver/
 *
 * @param url
 */
public void selenium(String url) {
 // 设置 chromedirver 的存放位置
 System.getProperties().setProperty("webdriver.chrome.driver", "chromedriver.exe");
 // 设置无头浏览器,这样就不会弹出浏览器窗口
 ChromeOptions chromeOptions = new ChromeOptions();
 chromeOptions.addArguments("--headless");

 WebDriver webDriver = new ChromeDriver(chromeOptions);
 webDriver.get(url);
 // 获取到要闻新闻列表
 List<WebElement> webElements = webDriver.findElements(By.xpath("//div[@class='news_title']/h3/a"));
 for (WebElement webElement : webElements) {
 // 提取新闻连接
 String article_url = webElement.getAttribute("href");
 // 提取新闻标题
 String title = webElement.getText();
 if (article_url.contains("https://news.163.com/")) {
  System.out.println("文章标题:" + title + " ,文章链接:" + article_url);
 }
 }
 webDriver.close();
}

运行该方法,得到结果如下:

我们使用 Selenium 已经正确的提取到了网易要闻的列表新闻。

反向解析法

反向解析法就是获取到 Ajax 异步获取数据的链接,直接获取到新闻数据。如果没有技巧的话,查找 Ajax 的过程将非常痛苦,因为一个页面加载的链接太多了,看看网易要闻的 network:

有几百条的请求,该如何查找到是哪条请求获取的要闻数据呢?你不嫌麻烦的话,可以一个一个的去点,肯定能够查找到的,另一种快捷的办法是利用 network 的搜索功能,如果你不知道搜索按钮,我在上图已经圈出来啦,我们在要闻中随便复制一个新闻标题,然后检索一下,就可以获取到结果,如下图所示:

这样我们就快速的获取到了要闻数据的请求链接,链接为:https://temp.163.com/special/00804KVA/cm_yaowen.js?callback=data_callback,访问该链接,查看该链接返回的数据,如下图所示:

从数据我们可以看出,我们需要的数据都在这里啦,所以我们只需要解析这段数据接可以啦,要从这段数据中解析出新闻标题和新闻链接,有两种方式,一种是正则表达式,另一种是将该数据转成 json 或者 list。这里我选择第二种方式,利用 fastjson 将返回的数据转换成 JSONArray 。所以我们是要引入 fastjson ,在 pom.xml 中引入 fastjson 依赖:

<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>fastjson</artifactId>
 <version>1.2.59</version>
</dependency>

除了引入 fastjson 依赖外,我们在转换前还需要对数据进行简单的处理,因为现在的数据并不符合 list 的格式,我们需要去掉 data_callback( 和最后面的 )。具体反向解析获取网易要闻的代码如下:

/**
 * 使用反向解析法 解决数据异步加载的问题
 *
 * @param url
 */
public void httpclientMethod(String url) throws IOException {

 CloseableHttpClient httpclient = HttpClients.createDefault();
 HttpGet httpGet = new HttpGet(url);
 CloseableHttpResponse response = httpclient.execute(httpGet);
 if (response.getStatusLine().getStatusCode() == 200) {
 HttpEntity entity = response.getEntity();
 String body = EntityUtils.toString(entity, "GBK");
 // 先替换掉最前面的 data_callback(
 body = body.replace("data_callback(", "");
 // 过滤掉最后面一个 )右括号
 body = body.substring(0, body.lastIndexOf(")"));
 // 将 body 转换成 JSONArray
 JSONArray jsonArray = JSON.parseArray(body);
 for (int i = 0; i < jsonArray.size(); i++) {
  JSONObject data = jsonArray.getJSONObject(i);
  System.out.println("文章标题:" + data.getString("title") + " ,文章链接:" + data.getString("docurl"));
 }
 } else {
 System.out.println("处理失败!!!返回状态码:" + response.getStatusLine().getStatusCode());
 }

}

编写 main 方法,执行上面的方法,需要注意的地方是:这时候传入的链接为https://temp.163.com/special/00804KVA/cm_yaowen.js?callback=data_callback 而不是 https://news.163.com/。得到如下结果:

两种方法都成功的获取到了网易要闻异步加载的新闻列表,对于这两种方法的选取,我个人的倾向是使用反向解析法,因为它的性能和稳定是都要比内置浏览器内核靠谱,但是对于一些使用 JavaScript 片段渲染的页面,内置浏览器又更加靠谱。所以根据具体情况选择吧。

源代码:源代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java 网络爬虫新手入门详解

    这是 Java 网络爬虫系列文章的第一篇,如果你还不知道 Java 网络爬虫系列文章,请参看Java 网络爬虫基础知识入门解析.第一篇是关于 Java 网络爬虫入门内容,在该篇中我们以采集虎扑列表新闻的新闻标题和详情页为例,需要提取的内容如下图所示: 我们需要提取图中圈出来的文字及其对应的链接,在提取的过程中,我们会使用两种方式来提取,一种是 Jsoup 的方式,另一种是 httpclient + 正则表达式的方式,这也是 Java 网络爬虫常用的两种方式,你不了解这两种方式没关系,后面会有相应

  • Java 网络爬虫基础知识入门解析

    前言 说起网络爬虫,大家想起的估计都是 Python ,诚然爬虫已经是 Python 的代名词之一,相比 Java 来说就要逊色不少.有不少人都不知道 Java 可以做网络爬虫,其实 Java 也能做网络爬虫而且还能做的非常好,在开源社区中有不少优秀的 Java 网络爬虫框架,例如 webmagic .我的第一份正式工作就是使用 webmagic 编写数据采集程序,当时参与了一个舆情分析系统的开发,这里面涉及到了大量网站的新闻采集,我们就使用了 webmagic 进行采集程序的编写,由于当时不知

  • 分享一个简单的java爬虫框架

    反复给网站编写不同的爬虫逻辑太麻烦了,自己实现了一个小框架 可以自定义的部分有: 请求方式(默认为Getuser-agent为谷歌浏览器的设置),可以通过实现RequestSet接口来自定义请求方式 储存方式(默认储存在f盘的html文件夹下),可以通过SaveUtil接口来自定义保存方式 需要保存的资源(默认为整个html页面) 筛选方式(默认所有url都符合要求),通过实现ResourseChooser接口来自定义需要保存的url和资源页面 实现的部分有: html页面的下载方式,通过Htt

  • hadoop中实现java网络爬虫(示例讲解)

    这一篇网络爬虫的实现就要联系上大数据了.在前两篇java实现网络爬虫和heritrix实现网络爬虫的基础上,这一次是要完整的做一次数据的收集.数据上传.数据分析.数据结果读取.数据可视化. 需要用到 Cygwin:一个在windows平台上运行的类UNIX模拟环境,直接网上搜索下载,并且安装: Hadoop:配置Hadoop环境,实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS,用来将收集的数据直接上传保存到HDFS,然后用MapReduce

  • Java多线程及分布式爬虫架构原理解析

    这是 Java 爬虫系列博文的第五篇,在上一篇Java 爬虫服务器被屏蔽的解决方案中,我们简单的聊反爬虫策略和反反爬虫方法,主要针对的是 IP 被封及其对应办法.前面几篇文章我们把爬虫相关的基本知识都讲的差不多啦.这一篇我们来聊一聊爬虫架构相关的内容. 前面几章内容我们的爬虫程序都是单线程,在我们调试爬虫程序的时候,单线程爬虫没什么问题,但是当我们在线上环境使用单线程爬虫程序去采集网页时,单线程就暴露出了两个致命的问题: 采集效率特别慢,单线程之间都是串行的,下一个执行动作需要等上一个执行完才能

  • JAVA超级简单的爬虫实例讲解

    爬取整个页面的数据,并进行有效的提取信息,注释都有就不废话了: public class Reptile { public static void main(String[] args) { String url1=""; //传入你所要爬取的页面地址 InputStream is=null; //创建输入流用于读取流 BufferedReader br=null; //包装流,加快读取速度 StringBuffer html=new StringBuffer(); //用来保存读取页

  • java实现一个简单的网络爬虫代码示例

    目前市面上流行的爬虫以python居多,简单了解之后,觉得简单的一些页面的爬虫,主要就是去解析目标页面(html).那么就在想,java有没有用户方便解析html页面呢?找到了一个jsoup包,一个非常方便解析html的工具呢. 使用方式也非常简单,引入jar包: <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.8.

  • Java 爬虫如何爬取需要登录的网站

    这是 Java 网络爬虫系列博文的第二篇,在上一篇 Java 网络爬虫新手入门详解 中,我们简单的学习了一下如何利用 Java 进行网络爬虫.在这一篇中我们将简单的聊一聊在网络爬虫时,遇到需要登录的网站,我们该怎么办? 在做爬虫时,遇到需要登陆的问题也比较常见,比如写脚本抢票之类的,但凡需要个人信息的都需要登陆,对于这类问题主要有两种解决方式:一种方式是手动设置 cookie ,就是先在网站上面登录,复制登陆后的 cookies ,在爬虫程序中手动设置 HTTP 请求中的 Cookie 属性,这

  • Java 爬虫数据异步加载如何解决

    这是 Java 爬虫系列博文的第三篇,在上一篇Java 爬虫如何爬取需要登录的网站,该怎么办? 中,我们简单的讲解了爬虫时遇到登录问题的解决办法,在这篇文章中我们一起来聊一聊爬虫时遇到数据异步加载的问题,这也是爬虫中常见的问题. 现在很多都是前后端分离项目,这会使得数据异步加载问题更加突出,所以你在爬虫时遇到这类问题不必惊讶,不必慌张.对于这类问题的解决办法总体来说有以下两种: 1.内置一个浏览器内核 内置浏览器就是在抓取的程序中,启动一个浏览器内核,使我们获取到 js 渲染后的页面,这样我们就

  • Android 3.0引入的异步加载机制Loader

    Loader是谷歌在Android 3.0引入的异步加载机制,能够对数据异步加载并显示到Activity或Fragment上,使用者不需要对数据的生命周期进行管理,而是交给Loader机制来管理. 使用Loader的优点 假如我们需要从网络上获取数据,通常的做法是使用子线程Thread+Handler或者是使用AsyncTask来处理. Thread+Handler方法实现起来简单直观,不过会麻烦点,需要自己实现Handler子类,创建线程,还要管理Handler的生命周期. AsyncTask

  • Javascript 异步加载详解(浏览器在javascript的加载方式)

    一.同步加载与异步加载的形式 1. 同步加载 我们平时最常使用的就是这种同步加载形式: <script src="http://yourdomain.com/script.js"></script> 同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止了后续的解析,因此停止了后续的文件加载(如图像).渲染.代码执行. js 之所以要同步执行,是因为 js 中可能有输出 document 内容.修改dom.重定向等行为,所以默认同步执行才是安全的. 以前的一般建议

  • 详解JS异步加载的三种方式

    一:同步加载 我们平时使用的最多的一种方式. <script src="http://yourdomain.com/script.js"></script> <script src="http://yourdomain.com/script.js"></script> 同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止后续的解析,只有当当前加载完成,才能进行下一步操作.所以默认同步执行才是安全的.但这样如果js中有输

  • 关于layui的下拉搜索框异步加载数据的解决方法

    思路分析:当我使用layui默认的下拉搜索框的时候,layui会默认渲染出一个HTML结构,所以我把渲染出来的这个结果直接给复制出来,这样css的样式就不用从头到尾写一遍了, 前端代码(我用的是jsp): <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC &quo

  • jquery使用EasyUI Tree异步加载JSON数据(生成树)

    这几天因为工作需要,要做一个支持无限级的菜单. 我也是菜鸟一只,能想到的东西不多,所以用了Easy UI的tree组件. 不得不说,easyui确实很强大. 因为是无限级菜单,数据量可能有点大,所以考虑采用异步加载. 但是因为后台默认传来的数据是 一个实体,所以又在后台进行了JSON字符串拼接. 最后,在网上找了N多代码,然后又去问了好几个群里的网友,终于搞出来这个小东西. 一.HTML部分代码 <div id="categoryChooseDiv" title="请选

  • 使用加载图片解决在Ajax数据加载中页面出现短暂空白的问题(推荐)

    在项目中用ajax异步获取数据后有时会因为数据问题或者网络问题,页面一直显示空白,现在用加载图片来过渡这种状态: <script> $(function(){ $.ajax({ url:'',//提供接口的文件地址链接 dataType:'json', type:'POST', beforeSend: function(){ $('#loading').html("<img src=\"images/loading.gif\" width=\"15

  • vue awesome swiper异步加载数据出现的bug问题

    本人第一次使用vue awesome. 踩到的坑确实不少.官网上面的用法写的很简单,按照上面做法基本会遇到如下这个问题 轮播第二次之后,首屏会自动跳过 网上找了很多资料,都不能解决这个问题.于是自己琢磨了很久终于灵光一闪.一个小技巧解决了这个问题. 使用方法应该很简单,去官网可以查看到方法.基本步骤如下 npm install vue-awesome-swiper --save-dev 在main.js中 import VueAwesomeSwiper from 'vue-awesome-swi

  • Ajax实现异步加载数据

    本文实例为大家分享了Ajax实现异步加载数据的具体代码,供大家参考,具体内容如下 项目结构如下 (需要导入一个JQuery的包,配置文件web.xml和springmvc-servlet.xml,不在写了,不知道的可以看一下我其它的博客,上边都有) 异步加载数据 首先创建一个实体类 package com.zkw.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;

  • 详解Vue-Cli 异步加载数据的一些注意点

    刚开始学vue的时候没有使用脚手架,现在用脚手架写法有点不同,今天遇到的问题是使用豆瓣api异步加载数据的时候,会一直在命令行上报错,基本上错误都是xxx 未定义. 例子 <template> <div v-if="moviesData"> <!-- 正在上映的电影-北京 --> <h1>{{ moviesData.title }}</h1> </div> </template> <script&

随机推荐