Java无内置爬虫框架,但可用HttpURLConnection、HttpClient配合Jsoup实现简单爬虫;需处理反爬、编码、连接复用等卡点,Jsoup适合静态页面,不支持JS渲染。

Java 本身没有内置的“爬虫框架”,但用 HttpURLConnection 或 Apache HttpClient 配合 Jsoup 就能快速写出稳定、可控的简单爬虫——关键不是“能不能写”,而是别绕开反爬、编码、连接复用这些实际卡点。
用 Jsoup 发起 GET 请求并解析 HTML
这是最轻量也最常用的组合:Jsoup.connect() 底层封装了连接管理,自动处理重定向和基础 User-Agent,适合静态页面抓取。
常见错误:直接 Jsoup.connect("http://example.com").get() 在无网络/超时/403 时抛 IOException 或 HttpStatusException,不加 try-catch 会崩。
- 务必设置超时:
.timeout(5000)(单位毫秒),否则默认无限等待 - 显式声明 User-Agent:
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"),很多站点会拦截默认 UA - 检查响应状态码:
Connection.Response response = Jsoup.connect(...).execute(); if (response.statusCode() == 200) { ... }
Document doc = Jsoup.connect("https://httpbin.org/html")
.userAgent("Mozilla/5.0")
.timeout(5000)
.get();
Elements titles = doc.select("h1");
System.out.println(titles.text());
处理中文乱码与字符集识别
网页 <meta charset="gb2312"> 或 HTTP Header 中的 Content-Type: text/html; charset=GBK 没被正确读取,就会导致 doc.title() 返回问号或方块。
立即学习“Java免费学习笔记(深入)”;
Jsoup 默认按 UTF-8 解析,不自动 fallback 到页面声明的编码——必须手动指定。
- 优先从响应头获取 charset:
response.charset(),再传给Jsoup.parse(response.body(), "", response.charset()) - 若响应头缺失,可先用
response.bodyAsBytes()+CharsetDetector(需引入icu4j)探测编码 - 简单场景下可暴力指定:
Jsoup.parse(htmlBytes, "GB2312", url),但仅限明确知道目标站编码时
绕过基础反爬:请求头与连接池控制
被返回空内容、302 跳转到验证码页、或直接 Connection refused,大概率是服务端根据请求特征做了拦截。
单纯换 UA 不够,还需模拟真实浏览器的完整头部链路,并避免高频短连。
- 至少补全:
Accept、Accept-Language、Accept-Encoding、Referer(尤其有 Referer 白名单的站点) - 用
HttpClient替代 Jsoup 原生连接,以便复用连接池:PoolingHttpClientConnectionManager控制最大并发和空闲回收 - 加随机延迟:
Thread.sleep(1000 + new Random().nextInt(2000)),避免请求间隔整齐暴露脚本行为
Jsoup 不适合什么场景
当目标页面依赖 JavaScript 渲染(如 Vue/React 单页应用)、需要登录态维持(含复杂 Cookie + Token 更新)、或存在滑动验证/点击验证时,Jsoup 无法执行 JS,也就拿不到最终 DOM。
这时候得换方案:
- 用
Selenium + ChromeDriver:能真实运行 JS,但启动慢、内存高、部署麻烦 - 用
Playwright for Java:比 Selenium 更轻量,支持无头、截屏、自动等待,API 更现代 - 直接调用接口:F12 看 Network → 找 XHR/Fetch 请求,绕过前端,直击后端 API(最稳最快)
真正难的从来不是“怎么发请求”,而是判断该不该发、发什么、以及发完之后怎么应对服务端的层层设防——尤其是那些没写在文档里、只靠试错才能摸清的规则。










