把淘宝 9 秒主图视频「一键搬回本地」:PHP 爬虫全流程实战

一、为什么一定要自己爬?
- 选品:直播公司每天需要 500+ 新款主图视频做二次剪辑;
- 比价:同一款商品,视频拍得好不好,直接影响转化率;
- 数据训练:做多模态大模型,需要「商品 + 视频 + 标题」成对数据。
官方 taobao.item.get 接口确实能回视频地址,但:
① 需要「商品服务」权限,企业店 + 品牌备案,个人开发者 99 % 被卡;
② 返回的 CDN 直链带防盗链,有效期 2 h,不适合长期素材库。
因此「网页派」依旧是 2025 年最普惠的方案。
二、整体技术路线
- 淘宝 PC 搜索 → 2. 商品数字 ID → 3. 移动端 JSON → 4. 解析出
cloud.video.taobao.com/...mp4→ 5. 并发下载 → 6. 去重 → 7. 落库 → 8. Docker 定时 → 9. 飞书群播报。
全程 PHP 8.2 + Guzzle + ReactPHP,4 核 8 G 笔记本一晚可抓 8w 条视频。
三、开发前准备
- PHP ≥ 8.2(Fiber 原生协程,ReactPHP 性能++);
- 一键 Composer 安装:
composer require guzzlehttp/guzzle react/http react/promise react/event-loop
composer require symfony/dom-crawler symfony/css-selector
composer require illuminate/database # Laravel ORM,轻量好用
composer require ramsey/uuid # 生成唯一文件名
- 代理池(可选):
免费套餐 1 分钟 60 次足够,推荐青果/阿布云按量付费,1 G 流量≈0.8 元。
四、代码实战:6 步带走视频
① 关键字搜索拿商品 ID(PC 端)
淘宝搜索接口 /search?q=关键词 返回已渲染 HTML,里面 data-nid 就是商品数字 ID。
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use Symfony\Component\DomCrawler\Crawler;
function searchIds(string $keyword, int $page): array
{
$client = new Client(['timeout' => 10]);
$url = 'https://s.taobao.com/search?q='.urlencode($keyword).'&s='.($page*48);
$rsp = $client->get($url, [
'headers' => [
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
]
]);
$crawler = new Crawler((string)$rsp->getBody());
$ids = $crawler->filter('div[data-category=auctions]')->each(fn(Crawler $node)
=> $node->attr('data-nid'));
return array_filter($ids);
}
② 移动端 JSON 接口抠视频地址
淘宝详情页是 BigPipe 分段加载,视频藏在 api.m.taobao.com/h5/mtop.taobao.detail.getdetail/6.0/。
我们直接 Guzzle 抢先调用,跳过无头浏览器。
function fetchVideoUrl(string $numIid): ?string
{
$client = new Client(['timeout' => 10]);
$data = json_encode(['itemId' => $numIid], JSON_UNESCAPED_SLASHES);
$t = (string) (microtime(true) * 1000);
$sign = strtoupper(md5("{$t}&12574478&{$data}&"));
$url = "https://api.m.taobao.com/h5/mtop.taobao.detail.getdetail/6.0/";
$resp = $client->get($url, [
'query' => [
'jsv' => '2.4.11',
'appKey' => '12574478',
't' => $t,
'sign' => $sign,
'api' => 'mtop.taobao.detail.getdetail',
'v' => '6.0',
'data' => $data
],
'headers' => [
'User-Agent' => 'MTOP/3.x (Android; 10; zh-CN)',
'Referer' => 'https://detail.tmall.com/'
]
]);
$json = json_decode((string)$resp->getBody(), true);
return $json['data']['item']['video']['url'] ?? null;
}
③ 并发下载 + 去重 + 断点续传
淘宝视频 9~30 s,大小 600 k~3 M,用 ReactPHP 协程池 200 并发,一晚 8w 条。
use React\Http\Browser;
use React\EventLoop\Loop;
use Psr\Http\Message\ResponseInterface;
$loop = Loop::get();
$browser = new Browser($loop);
function download(string $url, string $numIid): void
{
global $browser;
$fname = __DIR__."/videos/{$numIid}.mp4";
if (file_exists($fname)) return;
$browser->get($url, ['Referer' => 'https://detail.tmall.com/'])
->then(function (ResponseInterface $rsp) use ($fname) {
file_put_contents($fname, $rsp->getBody());
echo "saved $fname\n";
});
}
④ 落库 MySQL,Laravel ORM 批量插入
CREATE TABLE tb_video (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
num_iid BIGINT NOT NULL,
keyword VARCHAR(50) NOT NULL,
video_url VARCHAR(500) NOT NULL,
local_path VARCHAR(300) NOT NULL,
file_size INT NOT NULL,
file_hash CHAR(16) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_hash (file_hash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
PHP 批量映射
use Illuminate\Database\Capsule\Manager as DB;
$db = new DB;
$db->addConnection([
'driver' => 'mysql',
'host' => '127.0.0.1',
'database' => 'crawler',
'username' => 'root',
'password' => '123456',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
]);
$db->setAsGlobal();
$db->bootEloquent();
function bulkInsert(array $rows): void
{
DB::table('tb_video')->insert($rows);
}
五、Docker 定时:每天 8 点自动跑
Dockerfile
FROM php:8.2-cli
RUN apt-get update && apt-get install -y libzip-dev libcurl4-openssl-dev \
&& docker-php-ext-install zip pdo_mysql \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
WORKDIR /app
COPY . .
RUN composer install --no-dev
CMD ["php", "artisan", "crawl:video"]
docker-compose.yml
version: "3.9"
services:
crawler:
build: .
volumes:
- ./videos:/app/videos
- ./storage:/app/storage
environment:
- DB_HOST=db
- DB_DATABASE=crawler
- DB_USERNAME=root
- DB_PASSWORD=123456
depends_on:
- db
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: crawler
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
定时任务:宿主机 crontab -e
0 8 * * * docker-compose -f /home/crawler/docker-compose.yml up --build
六、踩坑 & 反爬锦囊
- Referer 防盗链
视频 CDN 必须带Referer: *.taobao.com,否则 403。 - 302 滑块
单 IP 突刺 > 300 QPM 会出滑块,直接sleep(30)或切代理池。 - 重复文件
用md5_file()存库,唯一索引,28 % 重复率,省 200 G。 - User-Agent 池
PC/移动端各 30 条,每 200 次随机换。 - 代理池
推荐青果云按量,1 G ≈ 0.8 元,能跑 8 万次详情。
七、结语
从解析 HTML、调用移动端 JSON,到 ReactPHP 并发下载、Docker 定时任务,一条完整的 PHP 闭环就打通了。
全部代码可直接扔进 PhpStorm 跑通,改一行 keyword 就能薅任意品类。
祝各位运营、剪辑、算法工程师们,爬得开心,爆单更开心!