在進行網頁資料採集時,PHP 的 preg_match_all 是提取結構化資料的利器。本文整理了從採集到清洗、匹配、去標籤的完整流程,並分享編碼轉換與正則優化的實戰經驗,幫助開發者快速構建穩健的採集腳本。
採集的第一步是獲取目標頁面原始碼。推薦使用 cURL 庫,支援 HTTPS、Cookie、User-Agent 等完整 HTTP 特性。詳細實作可參考筆者先前文章:PHP cURL 採集基礎教學。
目標網站可能使用 GBK、Big5、UTF-8 等不同編碼。採集前務必檢視 Content-Type 或頁面 <meta charset> 確認原始編碼,再用 mb_convert_encoding() 統一轉為 UTF-8:
// 已知原編碼為 GBK
$str = mb_convert_encoding($str, 'UTF-8', 'GBK');
// 未知編碼,啟用自動偵測
$str = mb_convert_encoding($str, 'UTF-8', 'auto');
提示:若 mbstring 擴展未啟用,可改用 iconv(),但 mb_convert_encoding 對中文支援更佳。
HTML 原始碼常含換行、Tab、多餘空格,導致正則匹配失敗。建議在匹配前統一清理:
$contents = str_replace(["\r\n", "\n", "\t", " "], '', $contents);
$contents = preg_replace('/[\r\n\t ]+/', '', $contents);
實測下,str_replace 在大型字串處理上速度快約 2-3 倍,建議優先使用。
preg_match_all 可一次擷取所有符合模式的片段,語法如下:
int preg_match_all(string $pattern, string $subject, array &$matches, int $flags = 0)
常用旗標:
PREG_PATTERN_ORDER(預設):$matches[0] 為完整匹配(含邊界),$matches[1] 為第一個捕獲組……PREG_SET_ORDER:$matches[0][0] 為第一筆完整匹配,$matches[0][1] 為第一筆第一捕獲組,結構更直觀,推薦採集場景使用。PREG_OFFSET_CAPTURE:回傳位移資訊,適合需定位原文位置的場景。// 目標:實際內容
preg_match_all('/<p class="content">(.*?)<\/p>/s', $contents, $out, PREG_SET_ORDER);
// $out[0][0] → 含標籤完整字串
// $out[0][1] → 僅捕獲組內容(即「實際內容」)
// 第 n 筆資料:$out[n-1][1]
// 若有多個捕獲組:$out[n-1][m] 取第 m 組
關鍵技巧:
s 修飾符讓 . 匹配換行,避免跨行匹配失敗。class="content"),減少誤匹配。DOMDocument 或 QueryList 等解析器,比純正則更穩健。提取到帶標籤的片段後,使用 PHP 內建 strip_tags() 一鍵淨化:
$cleanText = strip_tags($out[0][1]);
// 保留特定標籤(如
, ):
$cleanText = strip_tags($out[0][1], '
');
(.*?)、加 s 修飾符。掌握這套流程,面對多數靜態頁面採集需求皆能游刃有餘。動態渲染頁面則需引入 Headless Chrome(如 Puppeteer/PHP-WebDriver)處理,另文探討。