WebHarvest是基於XML配置的Java開源爬蟲框架,通過HttpClient獲取頁面,結合XPath/XQuery/正則提取結構化數據。本文以抓取天涯論壇版塊結構為例,詳細講解配置文件編寫與Java集成。
天涯版塊地圖:https://www.tianya.cn/bbs/index.shtml
<div class="backrgoundcolor">
<div class="bankuai_list">
<h3>社會民生</h3>
<ul>
<li><a href="..." id="item天涯雜談">天涯雜談</a></li>
<li><a href="..." id="item國際觀察">國際觀察</a></li>
...
</ul>
</div>
</div>
<div class="nobackrgoundcolor">
<div class="bankuai_list">
<h3>文學讀書</h3>
<ul>...</ul>
</div>
</div>
規律:每個大版塊在<div class="bankuai_list">中,包含<h3>標題和<ul><li><a>子版塊列表。
<?xml version="1.0" encoding="UTF-8"?>
<config charset="utf-8">
<!-- 1. 定義入口變量 -->
<var-def name="startPage">
<html-to-xml>
<http url="https://www.tianya.cn/bbs/index.shtml" charset="utf-8" />
</html-to-xml>
</var-def>
<!-- 2. 提取所有版塊列表容器 -->
<var-def name="categoryNodes">
<xpath expression="//div[@class='bankuai_list']">
<var name="startPage" />
</xpath>
</var-def>
<!-- 3. 遍歷生成XML輸出 -->
<file action="write" path="tianya/siteboards.xml" charset="utf-8">
<![CDATA[ <site> ]]>
<loop item="catNode" index="i">
<list><var name="categoryNodes"/></list>
<body>
<xquery>
<xq-param name="catNode"><var name="catNode"/></xq-param>
<xq-expression><![CDATA[
declare variable $catNode as node() external;
<category name="{normalize-space($catNode//h3/text())}">
{
for $item in $catNode//li return
<board name="{normalize-space($item//a/text())}"
url="{normalize-space($item/a/@href)}" />
}
</category>
]]></xq-expression>
</xquery>
</body>
</loop>
<![CDATA[ </site> ]]>
</file>
</config>
| 元素 | 作用 | 關鍵屬性 |
|---|---|---|
<var-def> | 定義變量 | name: 變量名 |
<http> | 發送HTTP請求 | url, charset, method, headers |
<html-to-xml> | HTML轉標準XML | 容錯解析 |
<xpath> | XPath提取節點 | expression: XPath表達式 |
<xquery> | XQuery轉換輸出 | 支持循環/條件/構造XML |
<loop> | 遍歷集合 | item: 迭代變量名, list: 數據源 |
<file> | 寫入文件 | path, action(write/append), charset |
public class TianyaCrawler {
public static void main(String[] args) {
String configFile = "tianya.xml";
String targetDir = "c:/crawler_output";
try {
// 1. 載入配置
ScraperConfiguration config = new ScraperConfiguration(configFile);
// 2. 創建爬蟲實例
Scraper scraper = new Scraper(config, targetDir);
// 3. 設置代理(可選)
scraper.getHttpClientManager().setHttpProxy("proxy.example.com", 8080);
// 4. 啟用調試
scraper.setDebug(true);
// 5. 動態注入變量(如分頁參數)
// scraper.addVariableToContext("pageNum", 1);
// 6. 執行爬取
scraper.execute();
System.out.println("爬取完成,輸出: " + targetDir + "/tianya/siteboards.xml");
} catch (Exception e) {
System.err.println("爬取失敗: " + e.getMessage());
e.printStackTrace();
}
}
}
<config>
<var-def name="pageNum">1</var-def>
<loop item="page" index="p">
<list><var name="pageNum"/></list> <!-- 這裡需配合script生成頁碼列表 -->
<body>
<var-def name="pageUrl">
<script><![CDATA[
"https://example.com/list?page=" + page
]]></script>
</var-def>
<var-def name="pageDoc">
<html-to-xml>
<http url="${pageUrl}" charset="utf-8"/>
</html-to-xml>
</var-def>
<!-- 提取數據 -->
<xpath expression="//div[@class='item']">
<var name="pageDoc"/>
</xpath>
</body>
</loop>
</config>
<site>
<category name="社會民生">
<board name="天涯雜談" url="https://www.tianya.cn/publicforum/articleslist/0/free.shtml"/>
<board name="國際觀察" url="https://www.tianya.cn/publicforum/articleslist/0/worldlook.shtml"/>
<board name="天涯時空" url="https://www.tianya.cn/publicforum/articleslist/0/news.shtml"/>
</category>
<category name="文學讀書">
<board name="蓮蓬鬼話" url="https://www.tianya.cn/techforum/articleslist/0/16.shtml"/>
<board name="煮酒論史" url="https://www.tianya.cn/publicforum/articleslist/0/no05.shtml"/>
</category>
...
</site>
Thread.sleep(random(1000,3000))或配置<delay><http header="User-Agent" value="${uaList[random()]}"/>HttpClientManager.setCookieStore()持久化會話HttpException指數退避重試3次Last-Modified/ETag,配合If-Modified-Since| 框架 | 配置方式 | Java版本 | 維護狀態 | 適用場景 |
|---|---|---|---|---|
| WebHarvest | XML | 1.5+ | 停止更新 | 簡單靜態頁、遺留項目 |
| Jsoup | 代碼API | 1.8+ | 活躍 | HTML解析、輕量爬取 |
| WebMagic | 代碼/註解 | 1.8+ | 活躍 | 分佈式爬蟲、結構化項目 |
| Crawler4j | 代碼 | 1.8+ | 活躍 | 大規模遞歸爬取 |