本文目录导读:

从零到一:Java爬虫实战案例深度剖析——以电商数据采集为例
目录导读
- 引言:为什么Java仍是企业级爬虫的首选?
- 环境搭建与核心依赖(Maven + HttpClient + Jsoup)
- 实战案例一:静态页面数据抓取(京东商品标题与价格)
- 1 目标分析与反爬初探
- 2 代码实现与解析
- 实战案例二:动态渲染页面抓取(携程酒店评论 - Selenium/Playwright介入)
- 1 识别动态加载机制
- 2 无头浏览器与等待策略
- 实战案例三:登录态与Cookie维持(模拟GitHub登录)
3 表单提交与会话保持
- 数据清洗与持久化(多线程 + MyBatis/Redis)
- 高频面试问答环节(Q&A)
- 爬虫的法律边界与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,实际工作中建议使用HttpClient的ResponseHandler简化处理。
数据清洗与持久化
爬虫不是抓完就完了,为了追求效率,推荐使用多线程配合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文件(用Rhino或GraalJS执行);若加密复杂,则使用Selenium绕过;但注意性能成本。
爬虫的法律边界与Robots协议
在编写任何Java爬虫案例时,请务必遵守目标网站的robots.txt协议(例如淘宝明确禁止抓取),我国《刑法》285条与《数据安全法》已明确,非法获取计算机信息系统数据将承担刑事责任,建议只爬取公开、非敏感数据,并通过官方API获取更为稳妥,技术无罪,但使用之人才是决定善恶的钥匙。
注:本文所有域名及示例均基于技术研究目的,请勿对真实网站进行高并发压测,如果你对某个阶段的代码细节有疑问,欢迎在评论区留言,我将逐一进行解答。