在之前的系列文章中介绍了如何使用httpclient抓取页面html以及如何用jsoup分析html源文件内容得到我们想要的数据,但是有时候通过这两种方式不能正常抓取到我们想要的数据,比如看如下例子。

1.需求场景:

想要抓取股票的最新价格,页面F12信息如下:



按照前面的方式,爬取的代码如下:

/**
* @description: 爬取股票的最新股价
* @author: JAVA开发老菜鸟
* @date: 2021-10-16 21:47
*/
public class StockPriceSpider { Logger logger = LoggerFactory.getLogger(this.getClass()); public static void main(String[] args) { StockPriceSpider stockPriceSpider = new StockPriceSpider();
String html = stockPriceSpider.httpClientProcess();
stockPriceSpider.jsoupProcess(html);
} private String httpClientProcess() {
String html = "";
String uri = "http://quote.eastmoney.com/sh600036.html";
//1.生成httpclient,相当于该打开一个浏览器
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
//2.创建get请求,相当于在浏览器地址栏输入 网址
HttpGet request = new HttpGet(uri);
try {
request.setHeader("user-agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36");
request.setHeader("accept", "application/json, text/javascript, */*; q=0.01"); // HttpHost proxy = new HttpHost("3.211.17.212", 80);
// RequestConfig config = RequestConfig.custom().setProxy(proxy).build();
// request.setConfig(config); //3.执行get请求,相当于在输入地址栏后敲回车键
response = httpClient.execute(request); //4.判断响应状态为200,进行处理
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
//5.获取响应内容
HttpEntity httpEntity = response.getEntity();
html = EntityUtils.toString(httpEntity, "utf-8");
logger.info("访问{} 成功,返回页面数据{}", uri, html);
} else {
//如果返回状态不是200,比如404(页面不存在)等,根据情况做处理,这里略
logger.info("访问{},返回状态不是200", uri);
logger.info(EntityUtils.toString(response.getEntity(), "utf-8"));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//6.关闭
HttpClientUtils.closeQuietly(response);
HttpClientUtils.closeQuietly(httpClient);
}
return html;
} private void jsoupProcess(String html) {
Document document = Jsoup.parse(html);
Element price = document.getElementById("price9");
logger.info("股价为:>>> {}", price.text());
} }

运行结果:

纳尼,股价为"-" ?不可能。

之所以爬不到正确的结果,是因为这个值在网站上是通过异步加载渲染的,因此不能正常获取。

2.java爬取异步加载的数据的方法

那如何爬取异步加载的数据呢?通常有两种做法:

2.1内置浏览器内核

内置浏览器就是在抓取的程序中启动一个浏览器内核,使我们获取到 js 渲染后的页面就和静态页面一样。常用的内核有

  • Selenium
  • PhantomJs
  • HtmlUnit

这里我选了Selenium,它是一个模拟浏览器,是进行自动化测试的工具,它提供一组 API 可以与真实的浏览器内核交互。当然,爬虫也可以用它。

具体做法如下:

  • 引入pom依赖
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
  • 配置对应浏览器的驱动

    要使用selenium,需要下载浏览器的驱动,根据不同的浏览器要下载的驱动程序也不一样,下载地址为:https://npm.taobao.org/mirrors/chromedriver/

    我用的是谷歌浏览器,因此下载了对应版本的windows和linux驱动。



    下载后需要配置进java环境变量里面,指定驱动的目录:

    System.getProperties().setProperty("webdriver.chrome.driver", "F:/download/chromedriver_win32_1/chromedriver.exe");

  • 代码实现:

    Logger logger = LoggerFactory.getLogger(this.getClass());
    
      public static void main(String[] args) {
    
          StockPriceSpider stockPriceSpider = new StockPriceSpider();
    stockPriceSpider.seleniumProcess();
    } private void seleniumProcess() { String uri = "http://quote.eastmoney.com/sh600036.html"; // 设置 chromedirver 的存放位置
    System.getProperties().setProperty("webdriver.chrome.driver", "F:/download/chromedriver_win32_1/chromedriver.exe"); // 设置浏览器参数
    ChromeOptions chromeOptions = new ChromeOptions();
    chromeOptions.addArguments("--no-sandbox");//禁用沙箱
    chromeOptions.addArguments("--disable-dev-shm-usage");//禁用开发者shm
    chromeOptions.addArguments("--headless"); //无头浏览器,这样不会打开浏览器窗口
    WebDriver webDriver = new ChromeDriver(chromeOptions); webDriver.get(uri);
    WebElement webElements = webDriver.findElement(By.id("price9"));
    String stockPrice = webElements.getText();
    logger.info("最新股价为 >>> {}", stockPrice);
    webDriver.close();
    }

    执行结果:

    爬取成功!

2.2反向解析法

反向解析法就是通过F12查找到 Ajax 异步获取数据的链接,直接调用该链接得到json结果,然后直接解析json结果获取想要的数据。

这个方法的关键就在于找到这个Ajax链接。这种方式我没有去研究,感兴趣的可以百度下。这里略。

3.结束语

以上即为如何通过selenium-java爬取异步加载的数据的方法。通过本方法,我写了一个小工具:

持仓市值通知系统,他会每日根据自己的持仓配置,自动计算账户总市值,并邮件通知到指定邮箱。

用到的技术如下:

相关代码已经上传到我的码云,感兴趣可以看下。

Java爬虫系列四:使用selenium-java爬取js异步请求的数据的更多相关文章

  1. 爬虫系列4:Requests+Xpath 爬取动态数据

    爬虫系列4:Requests+Xpath 爬取动态数据 [抓取]:参考前文 爬虫系列1:https://www.cnblogs.com/yizhiamumu/p/9451093.html [分页]:参 ...

  2. Java爬虫系列二:使用HttpClient抓取页面HTML

    爬虫要想爬取需要的信息,首先第一步就要抓取到页面html内容,然后对html进行分析,获取想要的内容.上一篇随笔<Java爬虫系列一:写在开始前>中提到了HttpClient可以抓取页面内 ...

  3. Java爬虫学习(1)之爬取新浪微博博文

    本次学习采用了webmagic框架,完成的是一个简单的小demo package com.mieba.spiader; import us.codecraft.webmagic.Page; impor ...

  4. 爬虫系列3:Requests+Xpath 爬取租房网站信息并保存本地

    数据保存本地 [抓取]:参考前文 爬虫系列1:https://www.cnblogs.com/yizhiamumu/p/9451093.html [分页]:参考前文 爬虫系列2:https://www ...

  5. 爬虫系列2:Requests+Xpath 爬取租房网站信息

    Requests+Xpath 爬取租房网站信息 [抓取]:参考前文 爬虫系列1:https://www.cnblogs.com/yizhiamumu/p/9451093.html [分页]:参考前文 ...

  6. 爬虫系列1:Requests+Xpath 爬取豆瓣电影TOP

    爬虫1:Requests+Xpath 爬取豆瓣电影TOP [抓取]:参考前文 爬虫系列1:https://www.cnblogs.com/yizhiamumu/p/9451093.html [分页]: ...

  7. 爬虫系列(1)-----python爬取猫眼电影top100榜

    对于Python初学者来说,爬虫技能是应该是最好入门,也是最能够有让自己有成就感的,今天在整理代码时,整理了一下之前自己学习爬虫的一些代码,今天先上一个简单的例子,手把手教你入门Python爬虫,爬取 ...

  8. scrapy爬虫系列之二--翻页爬取及日志的基本用法

    功能点:如何翻页爬取信息,如何发送请求,日志的简单实用 爬取网站:腾讯社会招聘网 完整代码:https://files.cnblogs.com/files/bookwed/tencent.zip 主要 ...

  9. (转)Python网络爬虫实战:世纪佳缘爬取近6万条数据

    又是一年双十一了,不知道从什么时候开始,双十一从“光棍节”变成了“双十一购物狂欢节”,最后一个属于单身狗的节日也成功被攻陷,成为了情侣们送礼物秀恩爱的节日. 翻着安静到死寂的聊天列表,我忽然惊醒,不行 ...

随机推荐

  1. GUI容器之布局管理器

    布局管理器 布局管理器:frame.setLayout(); 默认值为new flowLayout() 流式布局 frame.setLayout(new FlowLayout(FlowLayout.R ...

  2. git跟踪忽略规则文件.gitignore

    在使用Git的过程中,我们希望有的文件比如临时文件,编译的中间文件等不要被跟踪,也不需要提交到代码仓库,这时就要设置相应的忽略规则,来忽略这些文件的提交. 配置语法 以斜杠"/"开 ...

  3. Mysql常用sql语句(10)- is null 空值查询

    测试必备的Mysql常用sql语句系列 https://www.cnblogs.com/poloyy/category/1683347.html 前言 is null是一个关键字来的,用于判断字段的值 ...

  4. MongoDB(14)- 查询 null 字段或缺少某个字段的文档

    插入测试数据 db.inventory.insertMany([ { _id: 1, item: null }, { _id: 2 } ]) 后面的栗子都会用到这里的测试数据 查询匹配包含值为 nul ...

  5. java 多线程Thread和Runnable的区别

    如果一个类继承Thread,则不适合资源共享.但是如果实现了Runable接口的话,则很容易的实现资源共享 实现Runnable接口比继承Thread类所具有的优势:1. 适合多个相同的程序代码的线程 ...

  6. 模拟BS服务器

    一.模拟BS服务器分析 二.BS模拟服务器代码实现 图片都是单独请求,后台单独线程,这边是通过构造方法传入的Runable接口的实现类匿名对象创建线程: 创建本地输入流读取到网络输出流传过来的信息再放 ...

  7. MySQL(3)-日志

    3. InnoDB日志 3.1 InnoDB架构 分为 内存区域架构 buffer pool log buffer 磁盘区域架构 redo log undo log 2.1.1 内存区域架构 1)Bu ...

  8. TypeScript 中命名空间与模块的理解?区别?

    一.模块 TypeScript 与ECMAScript 2015 一样,任何包含顶级 import 或者 export 的文件都被当成一个模块 相反地,如果一个文件不带有顶级的import或者expo ...

  9. NPOI相关资料

    http://blog.csdn.net/heyangyi_19940703/article/details/52292755 http://www.cnblogs.com/zhengjuzhuan/ ...

  10. 计算字符串的长度.len,RuneCountInString

    内置函数len(),可以返回字符串/数组/切片/map/channel的长度. unicode/utf8包 函数:RuneCountInString(输入一个字符串),返回int类型的字符串长度.由于 ...