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

Laravel+Goutte爬取URL时关联数组重复及性能问题求助

问题分析与解决方案

一、重复数据问题解决

问题根源

  1. 闭包变量绑定错误:原代码闭包each内部依赖$this属性,循环中$this的属性会被后续循环覆盖,导致闭包最终引用的都是最后一次循环的值。
  2. 索引遍历错误:原循环从$i=1开始,若数组为0索引会漏掉第一个元素;且每次循环重复计算count($allOrders),浪费性能。
  3. 冗余数据生成:页面中所有<a>标签都会生成一条结果,导致同一订单被多次记录到结果数组中。

修复方案

  1. 改用foreach遍历数组:直接获取当前循环的订单数据,避免索引错误。
  2. 闭包用use捕获变量:将当前循环的订单变量传入闭包,确保闭包使用的是当前循环的正确值,而非被覆盖的$this属性。
  3. 优化结果记录逻辑:仅在找到匹配锚文本时记录到result_ok,整个页面未匹配时才记录一次到result_ko,避免冗余数据。

修复后的核心代码片段

$result_ok = [];
$result_ko = [];
// 提前初始化Client,复用实例
$client = new Client();

foreach ($allOrders as $order) {
    $postUrl = $order["post_url"];
    $anchorText = trim($order["anchor_text"]);
    $orderNumber = $order["order"];
    $orderName = $order["order_name"];
    $typeLink = $order["type_link"];

    // 跳过空URL,避免无效请求
    if (empty($postUrl)) {
        continue;
    }

    try {
        $website = $client->request('GET', $postUrl);
        $matched = false;

        $website->filter('a')->each(function ($node) use ($orderNumber, $orderName, $postUrl, $anchorText, $typeLink, &$result_ok, &$matched) {
            $nodeText = trim($node->text());
            if ($anchorText === $nodeText) {
                $result_ok[] = [
                    'pedido' => $orderNumber,
                    'articulo' => $orderName,
                    'url' => $postUrl,
                    'anchor_text' => $anchorText,
                    'tipo' => $typeLink,
                    'result_test' => $nodeText,
                    'link_rel' => $node->attr('rel'),
                ];
                $matched = true;
                return false; // 找到匹配项后停止遍历,提升效率
            }
        });

        // 未找到匹配项时,仅记录一次到失败数组
        if (!$matched) {
            $result_ko[] = [
                'pedido' => $orderNumber,
                'articulo' => $orderName,
                'url' => $postUrl,
                'anchor_text' => $anchorText,
                'tipo' => $typeLink,
                'result_test' => '未找到匹配的锚文本',
                'link_rel' => '',
            ];
        }
    } catch (\Exception $e) {
        // 捕获请求异常,记录错误信息
        $result_ko[] = [
            'pedido' => $orderNumber,
            'articulo' => $orderName,
            'url' => $postUrl,
            'anchor_text' => $anchorText,
            'tipo' => $typeLink,
            'result_test' => "请求失败:{$e->getMessage()}",
            'link_rel' => '',
        ];
    }
}

// 循环结束后统一输出结果,避免循环内输出消耗资源
echo "<pre>";
var_dump($result_ok);
echo "</pre>";

二、响应速度慢问题优化

优化方向

  1. 复用Client实例:原代码每次循环新建Client,重复初始化增加开销,提前初始化一次即可复用。
  2. 设置请求超时:为Guzzle客户端设置超时时间,避免单个请求卡住导致整体流程停滞:
    $guzzleClient = new \GuzzleHttp\Client([
        'timeout' => 10,         // 请求超时时间(秒)
        'connect_timeout' => 5,  // 连接超时时间(秒)
    ]);
    $client = new Client();
    $client->setClient($guzzleClient);
    
  3. 异步请求/队列处理:300+URL串行请求耗时极长,可改用Laravel队列将爬取任务异步执行,或使用Guzzle并发请求(控制并发数在10-20之间,避免触发反爬或耗尽资源)。
  4. 缓存已爬取结果:若存在重复的post_url,将爬取结果缓存到Redis或数据库,避免重复请求同一页面。
  5. 移除循环内输出:原循环内每次var_dump占用大量IO资源,改为循环结束后统一输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 19:34:58