Jsoup连接超时或返回空文档主因是默认超时过短(3秒)及反爬拦截;需显式设timeout≥8000ms、加User-Agent头、检查statusCode、JS渲染页须换工具。

Jsoup连接超时或返回空文档的常见原因
直接用 Jsoup.connect(url).get() 却拿不到 HTML,十有八九是网络或反爬导致的——不是代码写错了,而是默认配置太“天真”。Jsoup 默认连接超时 3 秒、读取超时 3 秒,多数真实网站响应慢一点就直接抛 IOException;更常见的是服务器返回 200 但内容为空(比如被 JS 渲染、或服务端校验了 User-Agent)。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 必须显式设置超时:
Jsoup.connect(url).timeout(10000)(单位毫秒,建议 ≥8000) - 加基础请求头绕过简单拦截:
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") - 检查响应状态码:
Document doc = Jsoup.connect(url).execute().parse();,再用response.statusCode()确认是不是 200 - 如果页面依赖 JS 渲染(如 Vue/React 路由页),Jsoup 无能为力,得换 Puppeteer 或 Selenium
用 CSS 选择器提取数据时 selector 写不对的典型表现
调用 doc.select("div.title") 却返回空集合,不是 Jsoup 有问题,而是 selector 和实际 HTML 结构对不上。CSS 选择器在 Jsoup 中支持良好,但不支持伪类(如 :nth-child)、不支持属性值正则匹配,且大小写敏感(HTML 是 case-insensitive,但 Jsoup 解析后按 DOM 树处理)。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 先用浏览器开发者工具复制“真正生效”的 selector(右键元素 → Copy → Copy selector),粘贴后手动删掉可能含动态 class 的部分(如
.title-abc123改成.title) - 避免用过于深层的路径:
body > div#wrap > section > article h1容易因前端微调而失效,优先用带语义的 class 或 id:h1.post-title - 提取文本前务必判空:
Elements titles = doc.select("h1.title"); if (!titles.isEmpty()) { String text = titles.get(0).text(); } - 注意
select()返回Elements(集合),selectFirst()才返回单个Element,别混用
解析含中文、特殊符号或乱码 HTML 的字符集问题
抓回来的页面显示“”或标题变成“”,基本是字符集没对上。Jsoup 默认按 UTF-8 解析,但很多国内网站声明的是 GBK 或 GB2312,甚至 meta 标签里写的 charset 是错的。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 强制指定编码比依赖自动探测更稳:
Jsoup.parse(htmlString, "https://example.com", Parser.htmlParser()).outputSettings().charset(Charset.forName("GBK")) - 如果用
connect(),在get()前加.parser(Parser.htmlParser()),再用.data("charset", "GBK")(注意:这是 hack 方式,仅当响应头没设 charset 时有效) - 最可靠的做法:先用
Connection.Response response = Jsoup.connect(url).execute()拿到原始字节流,用response.bodyAsBytes()自己按正确编码转字符串,再传给Jsoup.parse()
内存与性能:大页面或高频请求下的泄漏风险
循环爬 1000 个页面却没释放 Document 对象,JVM 堆内存会持续上涨,最终 OutOfMemoryError。Jsoup 的 Document 不小,尤其含大量 script/style 标签时;而且它内部缓存了节点关系,GC 不容易立刻回收。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 每次解析完立刻丢弃引用:
Document doc = Jsoup.connect(url).get(); // ... 处理逻辑; doc = null; - 禁用 Jsoup 内部的 HTML 解析缓存(默认开启):
Parser.htmlParser().setTrackPosition(false),减少对象开销 - 高频请求务必加延迟(哪怕 100ms),否则目标站封 IP 是分分钟的事,比内存问题来得更快
- 别在循环里反复 new Connection ——
Jsoup.connect()本身轻量,但底层 HTTP 连接复用靠的是 OkHttp(Jsoup 1.15+ 内置),只要不用.maxBodySize(0)这类极端配置,一般没问题
真正难的从来不是写出能跑的代码,而是看懂 response 的 statusCode、确认 charset 是否生效、以及在 selector 失效时快速定位是结构变了还是 selector 写窄了——这些细节不打日志,光靠 print 是看不出问题的。











