如何使用PHP XPath实现主页面链接对应子页内关联链接的抓取?
实现可行性说明
完全可以用和现有逻辑类似的XPath操作实现该进阶需求,核心思路是将单页抓取逻辑封装为可复用函数,遍历主页面提取到的子页链接,逐个请求子页后用XPath提取目标google.com链接即可,和你现有写法的底层逻辑完全一致。
具体实现建议
- 第一步:封装公共抓取函数,将curl请求、DOM初始化、XPath查询的通用逻辑抽离,避免重复编码,同时要额外处理相对链接转绝对链接的逻辑,避免子页内的相对路径无法正常请求。
- 第二步:保留现有主页面抓取逻辑,提取到所有子页链接后先做去重、有效性校验,过滤掉无效链接、重复链接,减少不必要的请求。
- 第三步:遍历子页链接调用公共抓取函数,针对子页的结构写对应的XPath规则,提取所有指向google.com的链接即可,目标链接的XPath可以直接写为
//a[contains(@href, "google.com")]/@href,不需要从根路径一层层写,匹配效率更高。
完整示例代码
<?php ini_set('display_errors', 1); // 忽略HTML不规范导致的解析警告 libxml_use_internal_errors(true); /** * 通用页面抓取函数,传入页面URL和目标XPath规则,返回匹配结果 * @param string $url 目标页面URL * @param string $xpathRule 匹配规则 * @return array 匹配到的节点值数组 */ function scrapePage(string $url, string $xpathRule): array { $result = []; $ch = curl_init(); curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); // 加超时设置,避免请求卡住 curl_setopt($ch, CURLOPT_TIMEOUT, 10); $data = curl_exec($ch); // 跳过请求失败的页面 if(curl_errno($ch) || curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200) { curl_close($ch); return $result; } curl_close($ch); $dom = new DOMDocument(); $dom->loadHTML($data); $xpath = new DOMXPath($dom); $nodes = $xpath->query($xpathRule); if($nodes->length > 0) { foreach ($nodes as $node) { $result[] = trim($node->nodeValue); } } return $result; } // 1. 抓取主页面,获取所有子页链接 $mainUrl = 'http://test123cxqwq12.000webhostapp.com/mainpage.php'; $subPageLinks = scrapePage($mainUrl, '/html/body/a/@href'); // 这里可以加子页链接转绝对路径、去重的逻辑 $subPageLinks = array_unique($subPageLinks); // 2. 遍历所有子页,抓取google.com链接 $targetLinks = []; foreach ($subPageLinks as $subLink) { // 这里如果是相对路径,需要先拼接成绝对URL再请求,示例省略拼接逻辑 $googleLinks = scrapePage($subLink, '//a[contains(@href, "google.com")]/@href'); $targetLinks = array_merge($targetLinks, $googleLinks); } // 输出结果 foreach ($targetLinks as $link) { echo "<a href='{$link}' target='_blank'>{$link}</a><br>"; } ?>
额外注意事项
- 正式使用时建议给curl加User-Agent头,避免被目标站点识别为爬虫拦截
- 可以在循环请求子页的时候加1~2秒的延迟,避免请求频率过高被封IP
- 如果子页内的链接是相对路径,需要根据主域名拼接成绝对路径再做请求,避免请求失败
内容的提问来源于stack exchange,提问作者Arkadio
相关产品推荐
相关产品推荐

