当前位置:首页 > 短网址资讯 > 正文内容

实战Guzzle抓取

www.ft12.com5年前 (2017-08-24)短网址资讯1259

虽然早就知道很多人用 Guzzle 爬数据,但是我却从来没有真正实践过,因为在我的潜意识里,抓取是 Python 的地盘。不过前段时间,当我抓汽车之家数据的时候,好心人跟我提起 Goutte 搭配 Guzzle 是最好的爬虫,让我一直记挂在心上,加上最近打算更新一下车型数据,于是我便重写了抓取汽车之家数据的脚本。

因为我是通过接口抓取,而不是网页,所以暂时用不上 Goutte,只用 Guzzle 就可以了,抓取过程中需要注意两点:首先需要注意的是通过并发节省时间,其次需要注意的是失败重试的步骤。算了,我不想说了,直接贴代码吧。

<?php

require "vendor/autoload.php";

use GuzzleHttp\Pool;
use GuzzleHttp\Client;
use GuzzleHttp\Middleware;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;

// 品牌
$brands = [];
// 车系
$series = [];
// 车型
$models = [];

// 配置
$configs = [];

$timeout = 10;
$concurrency = 100;

ini_set("memory_limit", "512M");

$stack = HandlerStack::create();
$stack->push(Middleware::retry(
    function($retries) { return $retries < 3; },
    function($retries) { return pow(2, $retries - 1); }
));

$client = new Client([
    "debug" => true,
    "timeout" => $timeout,
    "base_uri" => "https://cars.app.autohome.com.cn",
    "headers" => [
        "User-Agent" => "Android\t6.0.1\tautohome\t8.3.0\tAndroid",
    ],
    "handler" => $stack,
]);

// 品牌列表页
$url = "/cars_v8.3.0/cars/brands-pm2.json";

$response = $client->get($url);
$contents = $response->getBody()->getContents();
$contents = json_decode($contents, true);
$contents = $contents["result"]["brandlist"];

foreach ($contents as $values) {
    $initial = $values["letter"];

    foreach ($values["list"] as $v) {
        $brands[$v["id"]] = [
            "id" => $v["id"],
            "name" => $v["name"],
            "initial" => $initial,
        ];
    }
}

$brands = array_values($brands);

###

$requests = function ($brands) {
    foreach ($brands as $v) {
        $id = $v["id"];
        // 品牌介绍页
        $url = "/cars_v8.3.0/cars/getbrandinfo-pm2-b{$id}.json";
        yield new Request("GET", $url);
    }
};

$pool = new Pool($client, $requests($brands), [
    "concurrency" => $concurrency,
    "fulfilled" => function ($response, $index) use(&$brands) {
        $contents = $response->getBody()->getContents();
        $contents = json_decode($contents, true);
        $contents = $contents["result"]["list"];
        $contents = $contents ? $contents[0]["description"] : "暂无";
        $contents = trim(str_replace(["\r\n", ","], ["\n", ","], $contents));

        $brands[$index]["description"] = $contents;
    },
]);

$pool->promise()->wait();

$requests = function ($brands) {
    foreach ($brands as $v) {
        $id = $v["id"];
        // 车系列表页
        $url = "/cars_v8.3.0/cars/seriesprice-pm2-b{$id}-t16-v8.3.0.json";
        yield new Request("GET", $url);
    }
};

$pool = new Pool($client, $requests($brands), [
    "concurrency" => $concurrency,
    "fulfilled" => function ($response, $index) use(&$series, $brands) {
        $contents = $response->getBody()->getContents();
        $contents = json_decode($contents, true);
        $contents = $contents["result"];

        $brand_id = $brands[$index]["id"];
        
        foreach (["fctlist", "otherfctlist"] as $field) {
            $values = $contents[$field];

            foreach ($values as $value) {
                $factory = $value["name"];

                foreach ($value["serieslist"] as $v) {
                    list($min, $max) = explode("-", $v["price"]) + [1 => 0];

                    $min_price = $min * 10000;
                    $max_price = $max * 10000;

                    if ($max_price == 0) {
                        $max_price = $min_price;
                    }

                    $series[$v["id"]] = [
                        "id" => $v["id"],
                        "name" => $v["name"],
                        "level" => $v["levelname"],
                        "factory" => $factory,
                        "min_price" => $min_price,
                        "max_price" => $max_price,
                        "brand_id" => $brand_id,
                    ];
                }
            }
        }
    },
]);

$pool->promise()->wait();

$series = array_values($series);

###

$requests = function ($series) {
    foreach ($series as $v) {
        $id = $v["id"];
        // 车型列表页
        $url = "/carinfo_v8.3.0/cars/seriessummary-pm2-s{$id}-t-c110100-v8.3.0.json";
        yield new Request("GET", $url);
    }
};

$pool = new Pool($client, $requests($series), [
    "concurrency" => $concurrency,
    "fulfilled" => function ($response, $index) use(&$models, $series) {
        $contents = $response->getBody()->getContents();
        $contents = json_decode($contents, true);
        $contents = $contents["result"]['enginelist'];

        $series_id = $series[$index]["id"];

        foreach ($contents as $values) {
            if (in_array($values["yearvalue"], [0, 1])) {
                continue;
            }

            foreach ($values["yearspeclist"] as $value) {
                foreach ($value["speclist"] as $v) {
                    if (isset($models[$v["id"]])) {
                        continue;
                    }

                    $price = $v["price"] * 10000;

                    $description = trim($v["description"]);

                    if (!$description) {
                        $description = "暂无";
                    }

                    $models[$v["id"]] = [
                        "id" => $v["id"],
                        "name" => $v["name"],
                        "status" => $v["state"],
                        "price" => $price,
                        "description" => $description,
                        "series_id" => $series_id,
                    ];
                }
            }
        }
    },
]);

$pool->promise()->wait();

$models = array_values($models);

###

$requests = function ($models) {
    foreach ($models as $v) {
        $id = $v["id"];
        // 车型参数页
        $url = "/cfg_v8.3.0/cars/speccompare.ashx?pm=2&type=1&specids={$id}&cityid=110100&site=2&pl=2";
        yield new Request("GET", $url);
    }
};

$pool = new Pool($client, $requests($models), [
    "concurrency" => $concurrency,
    "fulfilled" => function ($response, $index) use(&$models, &$configs) {
        $contents = $response->getBody()->getContents();
        $contents = json_decode($contents, true);
        $contents = $contents["result"];

        $models[$index]["config"] = [];

        foreach (["paramitems", "configitems"] as $key) {
            $values = $contents[$key];

            foreach ($values as $value) {
                $category = $value["itemtype"];

                foreach ($value["items"] as $v) {
                    $id = $v["id"];

                    if ($id < 1) {
                        continue;
                    }

                    $name = $v["name"];
                    $value = $v["modelexcessids"][0]["value"];

                    if ($value != "-") {
                        $models[$index]["config"][$id] = $value;
                    }

                    if (!isset($configs[$category][$id])) {
                        $configs[$category][$id] = [
                            "id" => $id,
                            "name" => $name,
                            "category" => $category,
                        ];
                    }
                }
            }
        }

        $models[$index]["config"] = json_encode(
            $models[$index]["config"], JSON_UNESCAPED_UNICODE
        );
    },
]);

$pool->promise()->wait();

$configs = call_user_func_array("array_merge", $configs);

// todo: 保存数据

?>

编写此类工具性质的脚本无需考虑面向对象之类的弯弯绕,一马平川的流水账往往是最好的选择。运行前记得先通过 composer 安装 guzzle,整个运行过程大概会执行三万次抓取请求,可以抓取汽车之家完整的品牌,车系,车型及配置等相关数据,总耗时大概十分钟左右,效率还是可以接受的。

扫描二维码推送至手机访问。

版权声明:本文由短链接发布,如需转载请注明出处。

本文链接:https://www.ft12.com/article_429.html

分享给朋友:

相关文章

微信小程序新增推广功能:支持自定义关键词,扩大搜索结果

微信为方便用户找到所需小程序,并帮助小程序更准确地触达用户,向小程序的开发者提供了自定义关键词的功能。  关键词的搜索策略,将于6月9日正式生效。届时,开发者完成关键词设置后,微信用户可以通过搜索关键词,找到相关的小程序。  开发者可在小程...

百度:我们不造车,Apollo只是人工智能的搬运工

百度:我们不造车,Apollo只是人工智能的搬运工

[ FT12短网址 ] 百度在Apollo生态中承担的角色更像是供应商,但是与传统零部件供应商不同的是,百度将不主要依赖于车企采购产品或技术的方式盈利,百度的盈利模式还有进一步的想象空间,而且在百度内部也还没完全考虑成熟...

摩拜单车发布“摩拜+”战略 过半新用户来自微信小程序

5月19日消息,摩拜单车在北京举行发布会,正式宣布“摩拜+”开放平台战略,全面布局“生活圈”、“大数据”和“物联网”三大开放平台。中国联通(微博)、招商银行、中国银联、百度地图等8大品牌成为摩拜“生活圈”的首批战略合作伙伴。  此外,摩拜单...

58速运“里程计算”优化与演进

58速运“里程计算”优化与演进

58速运货物运输,滴滴快递网约车,司机端都是按照行驶公里数收费的,所以“里程”的准确性,是这类业务的一个核心难题,“里程计算”方案演进,以及其中优化思想,是本文要讨论的问题 一、直接调用地图API这是最容易想到的方法,最省事,但司...

麦当劳惊曝丑闻刷爆全球!你一定没想到,我们吃的冰淇淋竟然.......

麦当劳惊曝丑闻刷爆全球!你一定没想到,我们吃的冰淇淋竟然.......

近几年,麦当劳和肯德基的食品丑闻,和他们总是毗邻相开的店面一样,此起彼伏,接二连三。而最近,麦当劳又摊上事儿了……具体什么事呢?故事还要从一位,在麦当劳打工的小哥说起……他叫Nick,18岁的他在麦当劳,路易斯安那州拉普拉斯店,找了一份后厨...

信息流广告崛起自媒体时代互联网营销新玩法层出不穷

信息流广告崛起自媒体时代互联网营销新玩法层出不穷

目前,信息流广告已经覆盖绝大多数网民朋友,信息流广告基本是基于移动互联网终端的,最简单直观地信息流广告出现在各大门户网站的移动APP中,如腾讯新闻、天天快报、今日头条、百度新闻、网易新闻、搜狐新闻、一点资讯等等移动资讯APP。根据国内知名数...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。