JAVA爬虫实战项目——OKX解析
文章目录
- 前言
- 介绍
- 主要技术
- 初始配置
- JSoup主程序
- Selenium+ChromeDriver程序
- 进一步开发
前言
因为项目的需要,所以需要学习Java爬虫,我是参照B站教程:【【狂神说Java】Jsoup爬虫入门实战】这一视频教程,进行学习的,在这里记录一下自己的学习经验和问题解答,以供后续复盘回看。
介绍
爬虫的生命周期:
- 页面下载
- 链接提取
- URL管理
- 内容抽取及持久化
通用爬虫:宽度遍历,对网页进行无差别的进行抓取,但是效率不高,因为是爬取的网页上的所有内容
垂直型爬虫:关注内容与准确率,仅仅抓取到有效信息的数据
例如webmagic
主要技术
模拟浏览器:HttpClient
html解析:jsoup
初始配置
新建Maven项目直接使用Project,不需要选下面的Maven Archetype
项目建好之后,pom.xml
中添加Jsoup的依赖,Jsoup 是一款专为 Java 打造的 HTML 解析利器,它能轻松从 URL、文件或字符串中解析 HTML 内容。其具备易用、快速、灵活的特性,可通过类似 CSS 选择器的语法精准定位元素,方便地提取元素文本与属性值,还能自如地进行 DOM 操作,如添加、删除、修改元素等,在网页数据抓取、信息提取及 HTML 文档处理等场景中广受 Java 开发者青睐,是处理 HTML 数据的得力助手。
我这里用的是1.10.2版本的
<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.10.2</version></dependency>
JSoup主程序
因为需要访问的是OKX,所以我们需要在代码中添加代理服务器,在这里先打印的是document,因为还不确定,能不能找到我们需要的类,所以先测试页面的读取效果。
package com.tuling.utils;import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;public class HtmlParse {public static void main(String[] args) throws IOException {// 设置要解析的urlString url = "https://www.okx.com";// 设置代理服务器Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("代理服务器地址", 代理服务器端口));// 解析网页Document document = Jsoup.connect(url).proxy(proxy).userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko)Chrome/91.0.4472.124 Safari/537.36").get();// 获取指定class的元素Elements element = document.getElementsByClass("要查找的类");System.out.println(document.html());}
}
这返回的结果并没有找到我们想要的数据,问题就在于jsoup读取的是静态的界面,而对于动态更新的数据,不能捕获到。
后来我们通过F12开发者工具,监控Network,看看能不能找到我们想要的东西
可以看到,有一个filter-list的信息,里面储存的信息刚好可以和界面对应上,需要我们点击按钮才能获取到这一信息,所以在程序开发的时候,需要有点击这一操作,所以只靠Jsoup肯定是不能满足我们的需求,这时就需要找到另一个工具——Selenium。
Selenium+ChromeDriver程序
程序启动后,会调用ChromeDriver启动模拟浏览器,Selenium点击按钮并读取json数据,给他保存起来。
为了更好的满足可读性,需要从json中获取我们需要的数据,假如字段中含有price,则把price后的数据打印出来。
依赖安装
<dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.9.0</version></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.0.1-jre</version></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.14</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.14</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.8.0</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.14.2</version></dependency>
package com.tuling;import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.v85.network.Network;
import org.openqa.selenium.devtools.v85.network.model.RequestId;
import java.io.FileWriter;
import java.io.IOException;
import java.time.Instant;
import java.util.Optional;public class Main {public static void main(String[] args) {// 设置chromedriver.exe的路径System.setProperty("webdriver.chrome.driver", "C:\\Program Files\\Google\\Chrome\\Application\\chromedriver.exe");WebDriver chromeDriver = new ChromeDriver();DevTools devTools = ((ChromeDriver) chromeDriver).getDevTools();devTools.createSession();final RequestId[] targetRequestId = new RequestId[1];boolean found = false;// 启用网络监控devTools.send(Network.enable(Optional.of(100000000), Optional.empty(), Optional.empty()));// 先访问页面//https://www.okx.com/zh-hans/web3/detail/501/5QS7RcHfGUa2ZtrovPvEJMB9coqroiT7H48dPSwFpumpString url = "https://www.okx.com/zh-hans/web3/detail/501/GorvUUSkJGSzJUebM5W6berUkZ5pG8nmnn7tmk74pump";chromeDriver.get(url);try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}// 添加响应监听器,捕获特定URL的响应devTools.addListener(Network.responseReceived(), responseReceived -> {if (responseReceived.getResponse().getUrl().contains("filter-list")) {targetRequestId[0] = responseReceived.getRequestId();}});while (true) {// 点击交易历史按钮WebElement button = chromeDriver.findElement(By.className("index_tab-item__z34vL"));button.click();try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}// 获取响应体if (targetRequestId[0]!= null) {try {String jsonResponse = devTools.send(Network.getResponseBody(targetRequestId[0])).getBody();System.out.println("历史数据json已获取");System.out.println("解析json......获取价格数据");ObjectMapper mapper = new ObjectMapper();JsonNode root = mapper.readTree(jsonResponse);JsonNode dataNode = root.get("data");if (dataNode!= null && dataNode.has("list")) {JsonNode listNode = dataNode.get("list");int count = 0;System.out.println("==========价格=============");for (JsonNode item : listNode) {if (count >= 10) {break;}if (item.has("price")) {System.out.println(item.get("price"));count++;}}System.out.println("=========================");}long timestamp = Instant.now().getEpochSecond();String fileName = timestamp + ".json";try (FileWriter fileWriter = new FileWriter(fileName)) {fileWriter.write(jsonResponse);} catch (IOException e) {e.printStackTrace();}} catch (Exception e) {e.printStackTrace();}}try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}
}
进一步开发
上面我们已经完成了对动态数据的获取,那对于在Network中找不到json数据的,我们只能采用静态获取的方式,这方法的优点是易于寻找数据,但是缺点是数据不灵活,就是已经写死的。
package com.tuling;import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.v85.network.Network;
import org.openqa.selenium.devtools.v85.network.model.RequestId;
import java.io.FileWriter;
import java.io.IOException;
import java.time.Instant;
import java.util.Optional;public class Main {public static void main(String[] args) {// 设置chromedriver.exe的路径System.setProperty("webdriver.chrome.driver", "C:\\Program Files\\Google\\Chrome\\Application\\chromedriver.exe");WebDriver chromeDriver = new ChromeDriver();DevTools devTools = ((ChromeDriver) chromeDriver).getDevTools();devTools.createSession();final RequestId[] targetRequestId = new RequestId[1];boolean found = false;// 启用网络监控devTools.send(Network.enable(Optional.of(100000000), Optional.empty(), Optional.empty()));// 先访问页面//https://www.okx.com/zh-hans/web3/detail/501/5QS7RcHfGUa2ZtrovPvEJMB9coqroiT7H48dPSwFpumpString url = "https://www.okx.com/zh-hans/web3/detail/501/GorvUUSkJGSzJUebM5W6berUkZ5pG8nmnn7tmk74pump";chromeDriver.get(url);try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}// 添加响应监听器,捕获特定URL的响应devTools.addListener(Network.responseReceived(), responseReceived -> {if (responseReceived.getResponse().getUrl().contains("filter-list")) {targetRequestId[0] = responseReceived.getRequestId();}});while (true) {// 点击交易历史按钮WebElement button = chromeDriver.findElement(By.className("index_tab-item__z34vL"));button.click();try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("币种名称:ZAILGO");//获取MarketCap和Liquidity,静态获取方法,和VPN所在地区有关,在哪即为哪里的货币java.util.List<WebElement> elementList = chromeDriver.findElements(By.className("index_value__QnvWy"));if (elementList.size() >= 2) {// 通过索引0获取第一个元素WebElement firstElement = elementList.get(0);System.out.println("Market Cap: " + firstElement.getText());// 通过索引1获取第二个元素WebElement secondElement = elementList.get(1);System.out.println("Liquidity: " + secondElement.getText());}// 获取响应体if (targetRequestId[0]!= null) {try {String jsonResponse = devTools.send(Network.getResponseBody(targetRequestId[0])).getBody();//System.out.println("历史数据json已获取");//System.out.println("解析json......获取价格数据");ObjectMapper mapper = new ObjectMapper();JsonNode root = mapper.readTree(jsonResponse);JsonNode dataNode = root.get("data");if (dataNode!= null && dataNode.has("list")) {JsonNode listNode = dataNode.get("list");int count = 0;System.out.println("==========价格=============");for (JsonNode item : listNode) {if (count >= 10) {break;}if (item.has("price")) {System.out.println(item.get("price"));count++;}}System.out.println("=========================");}long timestamp = Instant.now().getEpochSecond();String fileName = timestamp + ".json";try (FileWriter fileWriter = new FileWriter(fileName)) {fileWriter.write(jsonResponse);} catch (IOException e) {e.printStackTrace();}} catch (Exception e) {e.printStackTrace();}}try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}
}