PHP多URL数据解析问题:循环页面数据无法完整抓取
解决PHP循环抓取动态分页代课数据的问题
听起来你遇到的情况很常见——面对这种自动循环刷新、每日页数不固定的分页内容,很容易因为终止条件判断不准、请求逻辑疏漏或者HTML解析不完整导致漏抓数据。我帮你拆解几个核心问题点,再给你一个可参考的完整脚本:
常见的错误原因排查
- 硬编码分页数量:如果脚本里写死了只抓固定页数,那页数变化时就会漏抓;更糟的是,页面循环后你可能会重复抓取旧数据。
- 没有处理页面循环逻辑:当到达最后一页后,网站会跳回
subst_001.htm,如果你没检测到内容重复,会陷入无限循环,同时可能覆盖或漏抓中间数据。 - 请求没有规避缓存/反爬:服务器可能对频繁请求做限制,或者返回缓存页面,导致你拿到的不是最新内容。
- HTML解析逻辑不完善:代课数据通常在表格里,如果你的解析代码只提取了部分行/列,或者没处理换行、空格等格式问题,就会丢失数据。
完整的改进版脚本示例
下面是一个兼顾分页检测、循环终止、数据解析和数据库存储的脚本,你可以对照自己的代码找差异:
<?php // 初始化配置 $baseUrl = 'http://plan.regenbogen-gesamtschule.de/vertretung/lehrer/subst_'; $pageSuffix = '.htm'; $visitedHashes = []; $maxRetries = 3; // 单页面重试次数 $dbHost = 'localhost'; $dbName = '你的数据库名'; $dbUser = '用户名'; $dbPass = '密码'; // 连接数据库 try { $pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(PDOException $e) { die("数据库连接失败: " . $e->getMessage()); } // 准备插入语句(根据你的表结构调整字段) $insertStmt = $pdo->prepare("INSERT INTO vertretung_data (page_num, datum, lehrer, vertretung, stunde, fach, raum) VALUES (:page_num, :datum, :lehrer, :vertretung, :stunde, :fach, :raum) ON DUPLICATE KEY UPDATE vertretung=VALUES(vertretung), stunde=VALUES(stunde)"); $pageNum = 1; while(true) { $currentUrl = $baseUrl . sprintf('%03d', $pageNum) . $pageSuffix; $pageContent = ''; $retryCount = 0; // 带重试的请求逻辑,模拟浏览器请求头 while($retryCount < $maxRetries) { $context = stream_context_create([ 'http' => [ 'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36\r\n" . "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" . "Cache-Control: no-cache\r\n", 'timeout' => 10 ] ]); $pageContent = @file_get_contents($currentUrl, false, $context); if($pageContent !== false) break; $retryCount++; sleep(2); // 重试前等待2秒 } if($pageContent === false) { echo "页面 $pageNum 请求失败,跳过\n"; $pageNum++; continue; } // 计算页面哈希,检测是否循环回到已抓取的页面 $pageHash = md5($pageContent); if(in_array($pageHash, $visitedHashes)) { echo "检测到页面循环,已抓取所有唯一页面,停止爬取\n"; break; } $visitedHashes[] = $pageHash; // 解析HTML表格数据(假设数据在<table>标签内,根据实际结构调整) $dom = new DOMDocument(); @$dom->loadHTML($pageContent); $tables = $dom->getElementsByTagName('table'); if($tables->length === 0) { echo "页面 $pageNum 未找到数据表格,跳过\n"; $pageNum++; continue; } $table = $tables->item(0); // 假设第一个表格是数据表格 $rows = $table->getElementsByTagName('tr'); foreach($rows as $index => $row) { // 跳过表头行(根据实际表头位置调整) if($index === 0) continue; $cols = $row->getElementsByTagName('td'); if($cols->length < 6) continue; // 确保列数足够 // 提取数据(根据实际列顺序调整) $datum = trim($cols->item(0)->nodeValue); $lehrer = trim($cols->item(1)->nodeValue); $vertretung = trim($cols->item(2)->nodeValue); $stunde = trim($cols->item(3)->nodeValue); $fach = trim($cols->item(4)->nodeValue); $raum = trim($cols->item(5)->nodeValue); // 插入数据库 try { $insertStmt->execute([ ':page_num' => $pageNum, ':datum' => $datum, ':lehrer' => $lehrer, ':vertretung' => $vertretung, ':stunde' => $stunde, ':fach' => $fach, ':raum' => $raum ]); } catch(PDOException $e) { echo "插入数据失败: " . $e->getMessage() . "\n"; } } echo "页面 $pageNum 数据抓取完成\n"; $pageNum++; sleep(1); // 避免请求过于频繁 } echo "所有数据抓取并存储完成\n"; ?>
关键改进点说明
- 动态终止条件:通过计算页面内容的MD5哈希值,检测到重复页面时停止爬取,避免无限循环和重复抓取。
- 健壮的请求逻辑:添加了重试机制和浏览器请求头,降低请求失败概率,避免被服务器拦截。
- 可靠的HTML解析:使用
DOMDocument代替正则表达式解析HTML,更稳定地提取表格数据。 - 数据库安全:使用预处理语句插入数据,防止SQL注入,同时通过
ON DUPLICATE KEY UPDATE处理重复数据。
你可以对照这个脚本检查自己的代码:比如是不是没处理页面循环?解析表格时是不是漏了某些行?数据库插入是不是没做异常处理?
内容的提问来源于stack exchange,提问作者Musayyab Naveed
相关产品推荐
相关产品推荐

