Java爬虫案例

wen java案例 1

本文目录导读:

Java爬虫案例

  1. 文章标题:从零到一:Java爬虫实战案例深度剖析——以电商数据采集为例
  2. 目录导读

从零到一:Java爬虫实战案例深度剖析——以电商数据采集为例


目录导读

  1. 引言:为什么Java仍是企业级爬虫的首选?
  2. 环境搭建与核心依赖(Maven + HttpClient + Jsoup)
  3. 实战案例一:静态页面数据抓取(京东商品标题与价格)
    • 1 目标分析与反爬初探
    • 2 代码实现与解析
  4. 实战案例二:动态渲染页面抓取(携程酒店评论 - Selenium/Playwright介入)
    • 1 识别动态加载机制
    • 2 无头浏览器与等待策略
  5. 实战案例三:登录态与Cookie维持(模拟GitHub登录)

    3 表单提交与会话保持

  6. 数据清洗与持久化(多线程 + MyBatis/Redis)
  7. 高频面试问答环节(Q&A)
  8. 爬虫的法律边界与Robots协议

引言:为什么Java仍是企业级爬虫的首选?

尽管Python在数据科学领域炙手可热,但Java凭借其高并发处理能力、强类型语言的安全性以及丰富的生态(如HttpClient、Jsoup、WebMagic),在企业级数据采集场景中依然占据主导地位,尤其针对需要处理千万级商品数据、需与Spring Cloud微服务架构无缝集成的系统,Java爬虫的稳定性与可维护性远超脚本语言。

本篇文章将摒弃枯燥的理论,直接通过三个由浅入深的Java爬虫案例,带您剖析从静态页面到动态渲染、从无状态请求到会话保持的完整技术链路,所有代码均基于JDK 11+,并针对百度与Google的SEO关键词密度(如Java爬虫案例Jsoup解析HttpClient模拟登录)进行自然融合。


环境搭建与核心依赖

在IDEA中创建一个Maven工程,我们只需要三个核心库:

<dependencies>
    <!-- HTTP客户端:负责发送请求 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.14</version>
    </dependency>
    <!-- HTML解析器:像JS一样操作DOM -->
    <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.17.2</version>
    </dependency>
    <!-- WebDriver:驱动真实浏览器 -->
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.21.0</version>
    </dependency>
</dependencies>

实战案例一:静态页面数据抓取(京东商品标题与价格)

1 目标分析与反爬初探

访问京东搜索页面,按下F12观察网络请求,我们发现:

  • 反爬点:京东绝大分部价格是动态加载,但、链接以及部分标签(如“自营”标识)存在初始HTML中。
  • 请求头:必须携带User-Agent(模拟浏览器)和Referer(来源页),否则返回403空页面。

2 代码实现与解析

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class JdStaticCrawler {
    public static void main(String[] args) throws Exception {
        // 1. 构建HttpClient
        try (CloseableHttpClient client = HttpClients.createDefault()) {
            HttpGet request = new HttpGet("https://search.jd.com/Search?keyword=Java编程思想");
            // 2. 关键:伪装浏览器头
            request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36");
            request.setHeader("Referer", "https://www.jd.com/");
            try (CloseableHttpResponse response = client.execute(request)) {
                String html = EntityUtils.toString(response.getEntity(), "UTF-8");
                // 3. Jsoup解析
                Document doc = Jsoup.parse(html);
                // 4. 选择器:抓取商品名称为 .p-name em
                Elements items = doc.select(".gl-item");
                for (Element item : items) {
                    String title = item.select(".p-name em").text();
                    System.out.println("商品标题: " + title);
                }
            }
        }
    }
}

要点:此案例展示了最简单的Jsoup选择器用法,若遇到404,请检查keyword参数是否需要URL编码。


实战案例二:动态渲染页面抓取(携程酒店评论 - Selenium介入)

1 识别动态加载机制

携程的评论数据通过AJAX异步加载,接口具有加密签名(_fxpcqlniredt),纯HttpClient无法模拟,此时需引入Selenium WebDriver,驱动一个真实的Chrome浏览器。

2 无头浏览器与等待策略

为了追求性能,使用--headless模式(无头浏览器),但必须注意:部分站点会通过window.navigator.webdriver属性检测Selenium,我们需通过executeCdpCommand隐藏特征。

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.chrome.ChromeOptions;
import java.util.List;
public class CtripDynamicCrawler {
    public static void main(String[] args) {
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless");
        options.addArguments("--disable-gpu");
        options.setExperimentalOption("excludeSwitches", new String[]{"enable-automation"}); // 隐藏特征
        WebDriver driver = new ChromeDriver(options);
        driver.get("https://hotels.ctrip.com/hotel/123456.html");
        // 核心:显式等待,确保元素加载完成
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        wait.until(d -> d.findElements(By.cssSelector(".hotel-comment-list-item")).size() > 0);
        List<WebElement> comments = driver.findElements(By.cssSelector(".hotel-comment-list-item"));
        comments.forEach(comment -> {
            String text = comment.findElement(By.cssSelector(".comment-content")).getText();
            System.out.println(text);
        });
        driver.quit();
    }
}

质变:通过等待策略,解决因网速慢导致的NoSuchElementException,对于大量数据的翻页,可循环点击“下一页”按钮。


实战案例三:登录态与Cookie维持(模拟GitHub登录)

1 表单提交与会话保持

很多数据需登录后才能查看,我们用HttpClient手动模拟提交表单,核心是维护CookieStore

import org.apache.http.client.CookieStore;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.client.methods.HttpGet;
public class GitHubLoginCrawler {
    public static void main(String[] args) throws Exception {
        CookieStore cookieStore = new BasicCookieStore();
        try (CloseableHttpClient client = HttpClients.custom()
                .setDefaultCookieStore(cookieStore) // 自动管理Cookie
                .build()) {
            // 第一步:伪造登录POST请求
            HttpPost post = new HttpPost("https://github.com/session");
            post.setHeader("Content-Type", "application/json");
            // 注意:此处token需提前从登录页的HTML中获取 authenticity_token
            String json = "{\"login\":\"你的用户名\",\"password\":\"你的密码\",\"authenticity_token\":\"xxx\"}";
            post.setEntity(new StringEntity(json));
            client.execute(post); // 执行后Cookie自动存入store
            // 第二步:带Cookie访问私有页面
            HttpGet get = new HttpGet("https://github.com/settings/profile");
            client.execute(get);
        }
    }
}

坑点:GitHub有CSRF令牌机制,需先GET登录页解析隐藏域authenticity_token,实际工作中建议使用HttpClientResponseHandler简化处理。


数据清洗与持久化

爬虫不是抓完就完了,为了追求效率,推荐使用多线程配合ExecutorService

ExecutorService pool = Executors.newFixedThreadPool(8);
List<Future<?>> futures = new ArrayList<>();
for (String url : urlList) {
    futures.add(pool.submit(() -> {
        // 采集逻辑...
        // 存储至Redis List或MySQL
    }));
}

针对数据去重,可先将URL在布隆过滤器中校验;对于价格波动数据,利用时间戳字段做增量更新。


高频面试问答环节(Q&A)

Q1:HttpClient和Jsoup的区别是什么? A:HttpClient负责网络传输(获取输入流),Jsoup负责解析DOM(提取节点),两者相辅相成,但Jsoup也可直接发送请求,不过功能较为单一。

Q2:遇到强反爬的网站(如字体反爬、CSS偏移)该怎么办? A:对于字体反爬,需要下载自定义字体文件(.woff),使用FontBox库解析映射关系;对于CSS偏移,则需计算坐标位置,还原真实文本顺序。

Q3:如何保证爬虫代码的性能? A:核心是连接池(HttpClient自带PoolingHttpClientConnectionManager)+ 多线程 + 异步IO(可改用HttpAsyncClient),同时避免频繁创建新连接,复用主机的Context

Q4:Java爬虫如何应对IP被封? A:三层策略:①降低请求频次(Thread.sleep(随机时间));②使用代理池(快代理、讯代理的API接口动态提取);③构建分布式爬虫(利用Redis的POP命令分发URL)。

Q5:你如何处理前端加密参数(如sign/TOKEN)? A:首选逆向JS文件(用RhinoGraalJS执行);若加密复杂,则使用Selenium绕过;但注意性能成本。


爬虫的法律边界与Robots协议

在编写任何Java爬虫案例时,请务必遵守目标网站的robots.txt协议(例如淘宝明确禁止抓取),我国《刑法》285条与《数据安全法》已明确,非法获取计算机信息系统数据将承担刑事责任,建议只爬取公开、非敏感数据,并通过官方API获取更为稳妥,技术无罪,但使用之人才是决定善恶的钥匙。


:本文所有域名及示例均基于技术研究目的,请勿对真实网站进行高并发压测,如果你对某个阶段的代码细节有疑问,欢迎在评论区留言,我将逐一进行解答。

抱歉,评论功能暂时关闭!