You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Cron Job可靠运行长时PHP爬虫脚本并实现断点续爬

爬虫稳定性与断点续爬解决方案

一、断点续爬实现

  • 状态持久化存储:创建数据库表(如crawl_tasks),字段包含url、status(待爬/爬取中/已爬/失败)、last_attempt_time、price_data。每次脚本启动时,优先选取status='待爬'或status='失败'且距上次尝试超1小时的URL,避免重复或频繁重试失败链接。
  • 原子状态更新:请求前将URL标记为爬取中,防止多进程重复处理;请求成功则更新为已爬并存储价格,失败则标记为失败,后续可重试。
  • 核心代码片段:
// 初始化PDO连接
$pdo = new PDO('mysql:host=localhost;dbname=crawl_db', 'user', 'pass');

// 获取单个待爬URL
$stmt = $pdo->prepare("SELECT url FROM crawl_tasks WHERE status = 'pending' LIMIT 1 FOR UPDATE");
$stmt->execute();
$url = $stmt->fetchColumn();

if ($url) {
    // 标记为爬取中
    $pdo->prepare("UPDATE crawl_tasks SET status = 'processing' WHERE url = ?")->execute([$url]);
    
    // 执行爬取逻辑
    $price = fetchProductPrice($url);
    
    if ($price !== false) {
        $pdo->prepare("UPDATE crawl_tasks SET status = 'completed', price_data = ?, last_attempt_time = NOW() WHERE url = ?")->execute([$price, $url]);
    } else {
        $pdo->prepare("UPDATE crawl_tasks SET status = 'failed', last_attempt_time = NOW() WHERE url = ?")->execute([$url]);
    }
}

function fetchProductPrice($url) {
    try {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($httpCode !== 200) return false;
        // 此处替换为实际的价格解析逻辑
        preg_match('/<span class="price">\$(\d+\.\d+)<\/span>/', $response, $matches);
        return $matches[1] ?? false;
    } catch (Exception $e) {
        error_log("爬取失败 $url: " . $e->getMessage());
        return false;
    }
}

二、脚本稳定性保障

  • 拆分单次任务:放弃每周一次触发全量任务的方案,改为Cron Job每隔200秒执行一次,每次仅处理1个URL。即使服务器停机,也仅丢失当前未完成的单个任务,重启后自动从下一个待爬URL继续。
  • 错误捕获与重试:用try-catch包裹请求逻辑,捕获网络异常、解析错误;对标记为失败的URL,可设置每日定时重试3次,避免永久遗漏。
  • 资源限制:移除set_time_limit(0),改为设置合理的超时时间(如30秒),避免单个请求阻塞脚本;同时限制脚本内存使用,防止内存泄漏。

三、一周运行时长的合理性

3000个URL分散到一周,平均每201秒请求一次,这个频率远低于大多数网站的反爬阈值,完全合理,几乎不会触发防火墙或过载对方服务器。如果担心效率,可将间隔缩短至100-150秒,同时监控对方网站的响应状态(如是否返回429限流码)。

四、缩短至数小时的处理方案

若要压缩到6小时内完成,需将请求间隔调整至约7秒,此时需注意:

  • 动态间隔调整:根据对方响应状态自动调整间隔,若返回429则临时延长至30秒,连续3次失败则标记为待重试,1小时后再处理。
  • 并发控制:保持串行请求,禁止多进程并发,避免瞬间请求量过大被封禁。
  • 批次化管理:将URL拆分为多个批次(如每100个一批),数据库新增batch_id字段,脚本每次处理一个批次的剩余URL,便于跟踪进度。

内容的提问来源于stack exchange,提问作者Jethro Hazelhurst

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 20:42:51