Jsoup 是 Java 最流行的 HTML 解析庫,提供類 jQuery 的選擇器 API。本文示範如何連線抓取網頁、解析 `` 與 ``,並處理編碼、重導向、錯誤重試等實務細節。
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version> <!-- 請檢查最新版 -->
</dependency>
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.SocketTimeoutException;
import java.util.Optional;
public class MetaExtractor {
private static final int TIMEOUT_MS = 10_000;
private static final String USER_AGENT = "Mozilla/5.0 (compatible; MetaBot/1.0)";
/** 提取關鍵字與描述 */
public static MetaInfo extract(String url) {
try {
Document doc = Jsoup.connect(url)
.userAgent(USER_AGENT)
.timeout(TIMEOUT_MS)
.followRedirects(true)
.ignoreHttpErrors(true) // 允許 4xx/5xx 仍解析內容
.get();
// 檢查 HTTP 狀態
if (doc.location() == null) {
return MetaInfo.error("連線失敗:無回應");
}
String title = doc.title();
String keywords = selectMetaContent(doc, "keywords");
String description = selectMetaContent(doc, "description");
// 兼容 Open Graph / Twitter Card
if (description.isEmpty()) description = selectMetaProperty(doc, "og:description");
if (description.isEmpty()) description = selectMetaProperty(doc, "twitter:description");
if (keywords.isEmpty()) keywords = selectMetaProperty(doc, "og:keywords");
return new MetaInfo(title, keywords, description, doc.location());
} catch (SocketTimeoutException e) {
return MetaInfo.error("連線逾時: " + e.getMessage());
} catch (IOException e) {
return MetaInfo.error("IO 錯誤: " + e.getMessage());
} catch (Exception e) {
return MetaInfo.error("解析異常: " + e.getMessage());
}
}
/** 通用 meta name 選擇 */
private static String selectMetaContent(Document doc, String name) {
Element meta = doc.selectFirst("meta[name=" + name + "]");
return meta != null ? meta.attr("content").trim() : "";
}
/** 兼容 property/og: / twitter: */
private static String selectMetaProperty(Document doc, String property) {
Element meta = doc.selectFirst("meta[property=" + property + "]");
return meta != null ? meta.attr("content").trim() : "";
}
/** 結果資料類別 */
public static record MetaInfo(
String title,
String keywords,
String description,
String finalUrl,
String error
) {
public static MetaInfo error(String msg) {
return new MetaInfo(null, null, null, null, msg);
}
public boolean isSuccess() { return error == null; }
}
// 測試主程式
public static void main(String[] args) {
String url = "https://example.com";
MetaInfo info = extract(url);
if (info.isSuccess()) {
System.out.println("標題: " + info.title());
System.out.println("關鍵字: " + info.keywords());
System.out.println("描述: " + info.description());
System.out.println("最終 URL: " + info.finalUrl());
} else {
System.err.println("失敗: " + info.error());
}
}
}
| 場景 | 解法 |
|---|---|
| 大量並發抓取 | 使用 ExecutorService + 連線池 (Jsoup.connect().connectionManager()),加入速率限制 (Token Bucket)。 |
| 動態渲染頁面 (SPA) | Jsoup 無法執行 JS,需改用 Selenium/WebDriver、Playwright 或 Puppeteer (Node.js) 配合 Headless 瀏覽器。 |
| 反爬蟲 (User-Agent, Cookie, JS 挑戰) | 輪換 UA、維護 Cookie Jar、處理 Cloudflare Turnstile 需引入瀏覽器自動化。 |
| 編碼偵測失敗 | Jsoup 通常自動偵測 ``,若失敗可手動指定 .parser(Parser.xmlParser()) 或設定 System.setProperty("jsoup.charset", "UTF-8")。 |
| 只需特定標籤 | 使用 ParseSettings 限制解析樹大小,或 :not(script):not(style) 選擇器減少記憶體。 |
robots.txt 與網站服務條款 (ToS)。Crawl-Delay,避免造成對方伺服器負載。Jsoup 以 簡潔 API、容錯能力強、零依賴 成為 Java 靜態網頁解析首選。掌握 selectFirst、屬性選擇器、連線參數調教,即可快速建立穩健的 Meta 資訊擷取服務。動態頁面請轉向 Headless 瀏覽器方案。