1. DOM 解析基础
1.1 DOM 树结构与节点类型
在网页爬取中,DOM 树结构负责把 HTML 转换成树状对象,便于程序遍历与提取。使用 PHP 的 DOMDocument 可以把文档加载为 DOM,并对元素、属性、文本节点进行定位。掌握这一点是实现高效数据提取的基础。
核心要点包括节点类型、文本节点与元素节点的访问方式,以及如何处理非法 HTML 的容错加载。以下示例展示了将 HTML 字符串转换为 DOM 的基本流程,帮助你理解 DOMDocument 的工作原理。
示例1.2 DOMXPath 定位与查询
在复杂的选择场景,XPath 提供强大定位能力,能够通过路径表达式提取节点文本、属性值等。结合 DOMXPath,可以用一个查询完成多种定位需求,显著提升数据提取的灵活性。
应用要点包括如何组合表达式进行筛选、如何取得文本内容以及在循环中高效地遍历节点。下面给出一个简单的示例,演示如何通过 XPath 提取链接文本。
query("//a[@class='title']/text()");
foreach ($nodes as $node) {echo $node->nodeValue . PHP_EOL;
}
?>
2. 高效数据提取的核心技术
2.1 选择正确的工具:网络请求与并发
高效爬虫离不开高性能的网络请求与数据并发能力。cURL 多请求并发或 Guzzle 等客户端都能提升吞吐量。设计时要权衡并发数量、超时设置、代理与限流等因素,以确保稳定性与可扩展性。
在实现时,并发粒度要与目标站点的承载能力相匹配,避免触发反爬机制。下面示例展示了使用 curl_multi 实现简单的并发请求结构,帮助你理解并发控制的基本要点。
$url) {$ch = curl_init($url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0');curl_multi_add_handle($mh, $ch);$handles[$i] = $ch;
}
$active = null;
do {$status = curl_multi_exec($mh, $active);
} while ($status == CURLMSG_CALL_MULTI_PERFORM);while ($active && $status == CURLM_OK) {if (curl_multi_select($mh) == -1) {usleep(100);}do {$mrc = curl_multi_exec($mh, $active);} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
foreach ($handles as $ch) {$body = curl_multi_getcontent($ch);// 处理 body ...curl_multi_remove_handle($mh, $ch);
}
curl_multi_close($mh);
?>
实战要点是合理设定并发上限、统一处理错误与超时,以及结合队列进行限速,以实现稳定的抓取速率与高效数据收集。

2.2 提取策略:CSS 选择器、XPath 与正则的权衡
在提取阶段,应综合使用 CSS 选择器、XPath 与必要的 正则表达式。DOMXPath 适用于结构化的 HTML;CSS 选择器 更直观;对极端情况,正则表达式 可以解决文本中隐藏数据的问题。正确的组合能显著提升数据提取的准确性。
通过对不同页面结构的分析,选择器策略可以从条目节点快速定位到所需字段。下面示例展示了使用 XPath 提取产品名称元素的文本。
query("//div[@class='product-name']/a/text()");
foreach ($nodes as $n) {echo trim($n->nodeValue) . PHP_EOL;
}
?>
3. 实战爬虫架构设计
3.1 任务队列与速率限制
将抓取任务放入队列,并设置持续的速率限制,可以避免被目标网站封禁。任务队列 可以采用 Redis、RabbitMQ 等方案;速率限制 可通过计时器实现,确保持续稳定的抓取节奏。
设计要点包括分级队列、并发控制以及速率监控。下面给出一个简化的速率控制示例,帮助理解如何在循环中保持固定的抓取间隔。
3.2 去重与持久化
数据去重是高效爬虫的关键之一。通常以 URL+内容哈希 作为去重键,在 Redis 或数据库中进行判重;持久化则需要选择合适的存储结构,如关系型数据库、文档数据库或列式数据库,以支持后续分析。
去重策略应覆盖重复页面、重复字段和相似页面的组合,避免重复数据污染分析结果。下面示例展示了一个简单的哈希去重实现,结合 Redis 的集合功能进行判重。
sismember('crawled', $key);
$redis->sadd('crawled', $key);
?>
4. 抗反爬与稳定性
4.1 伪装与变换请求头
为降低检测概率,随机 User-Agent、代理池、Referer 等头信息的变换很重要。结合合规边界与合法性要求,动态调整这些信息能提升隐蔽性和稳定性。
在实现时,建议将头信息抽象成配置与策略模块,方便在运行时随机化与切换。下面展示一个简单的伪造请求头的代码片段,帮助理解如何应用变量化的请求头。
4.2 错误处理与重试策略
网络波动与目标站点的限流可能导致请求失败,需要健壮的重试策略。指数退避、超时控制、以及 断路保护 是常见做法,能在异常情况下保持系统的可用性与稳定性。
合适的重试策略应避免对目标造成二次压力,同时控制总的抓取成本。下面给出一个简单的重试框架,演示在失败时如何实现指数退避。
5. 实例解析与性能优化
5.1 从 DOM 到数据结构:实际提取示例
在具体案例中,先用 DOM 解析获取条目节点,再把数据映射到结构化数组。通过 批量处理与序列化缓存,可以显著提升处理吞吐,减少重复的 DOM 解析成本。将提取后的数据以结构化形式输出,便于后续分析和存储。
性能要点包括缓存 DOMTree、复用解析对象、以及并行处理与分段聚合。下面给出一个实际的示例,演示如何将提取出的条目映射为数组并输出为 JSON。
均为产品条目
$doc = new DOMDocument();
$doc->loadHTML($html);
$xpath = new DOMXPath($doc);
$nodes = $xpath->query("//div[@class='item']");
$products = [];
foreach ($nodes as $n) {$name = $xpath->query(".//h2[@class='name']", $n)->item(0)->nodeValue;$price = $xpath->query(".//span[@class='price']", $n)->item(0)->nodeValue;$products[] = ['name' => trim($name), 'price' => trim($price)];
}
echo json_encode($products);
?>


