前言

因为项目的需要,所以需要学习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 {
        // 设置要解析的url
        String 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/5QS7RcHfGUa2ZtrovPvEJMB9coqroiT7H48dPSwFpump
        String 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/5QS7RcHfGUa2ZtrovPvEJMB9coqroiT7H48dPSwFpump
        String 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();
            }
        }
    }
}
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐