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

Cron调度PHP循环刷新问题及PHP爬虫执行逻辑改造咨询

解决方案:两个PHP调度与爬虫优化问题的处理指南

一、解决Cron调度的PHP循环需页面刷新的问题

首先,你需要页面刷新才能让循环继续,大概率是因为原来的脚本靠浏览器访问、依赖Web服务器运行——这种模式下脚本的执行受限于HTTP请求的生命周期,必须刷新页面才能触发下一轮循环。要彻底解决这个问题,核心是切换到CLI(命令行)模式运行脚本,让Cron直接调用PHP解释器执行,完全脱离Web环境:

  • 适配CLI环境修改脚本:去掉脚本中依赖Web的全局变量(比如$_SERVER、$_POST),所有文件路径改用绝对路径(比如/var/www/html/your_script.php而非./script.php),避免CLI环境下路径解析出错。
  • 配置Cron任务:直接在Crontab中添加调用命令,比如每分钟执行一次脚本:
    * * * * * /usr/bin/php /path/to/your/loop_script.php >> /var/log/loop_script.log 2>&1
    
    这里>> /var/log/loop_script.log 2>&1是把脚本的输出和错误都写入日志文件,方便后续排查问题。
  • 取消Web触发逻辑:删掉原来需要页面刷新的循环触发方式,让Cron完全接管调度,脚本会在后台按计划持续执行,不需要任何人工或浏览器干预。

二、PHP爬虫改为每次处理单个站点并自动触发下一次执行的实现方案

把原来的批量循环改成单次处理+自动续跑,核心是用数据库跟踪处理状态,避免重复处理,同时实现自动触发下一轮任务。下面是具体步骤和示例代码:

1. 数据库表结构调整

先给你的站点表(假设表名为sites)加两个字段,用来跟踪处理状态:

ALTER TABLE sites ADD COLUMN processing_status ENUM('pending', 'processing', 'completed', 'failed') DEFAULT 'pending';
ALTER TABLE sites ADD COLUMN last_processed_at DATETIME NULL;
  • processing_status:标记站点的处理状态,防止多个进程同时处理同一个站点;
  • last_processed_at:记录最后一次处理的时间,方便后续排查或重试。

2. 修改爬虫脚本逻辑(crawler.php)

脚本核心流程变为:锁定一个未处理的站点 → 执行检测 → 更新状态 → 触发下一次执行。我写了个简化版示例,你可以根据实际情况调整:

<?php
set_time_limit(0); // 取消脚本超时限制,确保curl检测能完成

// 1. 连接数据库(这里用PDO示例,你可以换成自己的连接方式)
$dsn = 'mysql:host=localhost;dbname=your_db;charset=utf8mb4';
$pdo = new PDO($dsn, 'username', 'password', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);

try {
    // 2. 用行锁获取一个未处理的站点,防止并发冲突
    $pdo->beginTransaction();
    $stmt = $pdo->prepare("SELECT * FROM sites WHERE processing_status = 'pending' LIMIT 1 FOR UPDATE");
    $stmt->execute();
    $site = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if (!$site) {
        // 没有未处理的站点,直接退出
        $pdo->commit();
        exit("No pending sites to process.\n");
    }

    // 3. 标记该站点为处理中
    $stmt = $pdo->prepare("UPDATE sites SET processing_status = 'processing' WHERE id = ?");
    $stmt->execute([$site['id']]);
    $pdo->commit();

    // 4. 执行curl检测逻辑(替换成你原有的检测代码即可)
    $ch = curl_init($site['url']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 设置30秒超时
    $html = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    // 5. 根据检测结果更新站点状态
    $status = ($httpCode >= 200 && $httpCode < 300) ? 'completed' : 'failed';
    $stmt = $pdo->prepare("UPDATE sites SET processing_status = ?, last_processed_at = NOW() WHERE id = ?");
    $stmt->execute([$status, $site['id']]);

    echo "Processed site {$site['url']} - Status: {$status}\n";

    // 6. 自动触发下一次脚本执行(后台运行,不阻塞当前进程)
    $scriptPath = __FILE__;
    exec("/usr/bin/php {$scriptPath} > /dev/null 2>&1 &");

} catch (Exception $e) {
    // 异常处理:把站点状态改回pending,避免死锁
    if (isset($site['id'])) {
        $pdo->rollBack();
        $stmt = $pdo->prepare("UPDATE sites SET processing_status = 'pending' WHERE id = ?");
        $stmt->execute([$site['id']]);
    }
    error_log("Error processing site: " . $e->getMessage());
    exit("Error occurred: " . $e->getMessage() . "\n");
}
?>

3. 更稳妥的触发方式:用Cron定期触发

如果担心exec()调用导致进程堆积,也可以用Cron每分钟触发一次脚本,脚本执行时如果有未处理的站点就处理,没有就直接退出。这种方式更可控,避免意外的进程爆炸:

* * * * * /usr/bin/php /path/to/crawler.php >> /var/log/crawler.log 2>&1

4. 额外优化建议

  • 重试机制:对于failed状态的站点,可以加个定时任务定期重置为pending,比如每天凌晨执行:UPDATE sites SET processing_status = 'pending' WHERE processing_status = 'failed' AND last_processed_at < DATE_SUB(NOW(), INTERVAL 1 DAY);
  • 分布式锁:如果后续需要多进程处理,建议用Redis做分布式锁,比数据库行锁性能更好;
  • 资源监控:可以在脚本中添加内存使用监控,避免内存泄漏;或者用ulimit在Cron命令中限制进程资源。

内容的提问来源于stack exchange,提问作者David Sword

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:26:18