如何使用PHP从无人机竞速追踪软件的WebSocket中获取赛事编号(28/28)?
使用PHP获取无人机竞速网站的编号数据是可行的!
你遇到的核心问题是:目标数据不是静态HTML内容,而是通过Socket.IO(基于WebSocket的封装协议)动态推送的,所以静态网页抓取工具(比如你用的ultimate-web-scraper)根本拿不到数据;而你尝试的curl代码,因为Socket.IO的握手流程、事件订阅逻辑不对,也没能成功建立有效连接。
问题分析
- 静态抓取无效:你用web scraper抓取的是页面初始HTML,但
(28/28)这类实时计分数据是页面加载后通过Socket.IO从服务器推送过来的,初始HTML里没有这些内容。 - Socket.IO连接流程错误:你的curl代码错误地使用了页面URL作为Socket服务器地址,而实际的Socket.IO端点是
wss://aircrasher.livefpv.com/socket.io/;另外你发送的事件(比如chat message)和网站实际需要的订阅事件不匹配,服务器不会推送计分数据给你。
解决方案:用PHP正确处理Socket.IO连接
要拿到数据,你需要完成Socket.IO的完整握手流程,订阅正确的事件,再监听服务器推送的消息。下面是具体实现步骤和代码示例:
1. 准备依赖
首先安装PHP的WebSocket客户端库(用于处理Socket.IO的WebSocket传输):
composer require ratchet/pawl
2. 完整代码实现
<?php require __DIR__ . '/vendor/autoload.php'; use Ratchet\Client\WebSocket; use React\EventLoop\Factory; // 创建事件循环(Socket.IO需要异步处理) $loop = Factory::create(); // 第一步:Socket.IO握手,获取会话ID(sid) $handshakeUrl = 'https://aircrasher.livefpv.com/socket.io/?EIO=4&transport=polling'; $ch = curl_init($handshakeUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $handshakeResponse = curl_exec($ch); curl_close($ch); // 解析握手响应(格式:0{"sid":"xxx", ...}) $handshakeData = json_decode(substr($handshakeResponse, 1), true); if (!$handshakeData || !isset($handshakeData['sid'])) { die('握手失败:无法获取Socket.IO会话ID'); } $sid = $handshakeData['sid']; // 第二步:建立WebSocket连接 $wsUrl = "wss://aircrasher.livefpv.com/socket.io/?EIO=4&transport=websocket&sid={$sid}"; $loop->addTimer(0.1, function () use ($loop, $wsUrl, $handshakeData) { WebSocket\connect($wsUrl)->then(function (WebSocket $conn) use ($loop, $handshakeData) { // 发送Socket.IO连接包(v4版本用"40"表示CONNECT) $conn->send('40'); // 发送订阅计分事件(事件名需要对照网站实际流量调整,可通过浏览器开发者工具查看) // 这里假设网站需要订阅"joinScoring"事件,传递房间参数 $conn->send('42["joinScoring", {"room": "live-scoring"}]'); // 设置Ping定时器,保持连接(避免服务器断开) $pingTimer = $loop->addPeriodicTimer(($handshakeData['pingInterval'] / 1000) - 1, function () use ($conn) { $conn->send('2'); // Socket.IO的Ping包 }); // 监听服务器推送的消息 $conn->on('message', function ($msg) use ($conn, $loop, $pingTimer) { // Socket.IO消息格式示例:42["scoringUpdate", {"current":28, "total":28, ...}] if (str_starts_with($msg, '42')) { $eventData = json_decode(substr($msg, 2), true); if ($eventData[0] === 'scoringUpdate') { // 确认是计分更新事件 $scoringInfo = $eventData[1]; $targetNumber = "{$scoringInfo['current']}/{$scoringInfo['total']}"; echo "获取到目标编号:{$targetNumber}\n"; // 拿到数据后关闭连接、停止循环 $conn->close(); $loop->cancelTimer($pingTimer); $loop->stop(); } } }); // 处理连接关闭 $conn->on('close', function ($code = null, $reason = null) use ($loop, $pingTimer) { echo "连接已关闭:{$code} - {$reason}\n"; $loop->cancelTimer($pingTimer); $loop->stop(); }); }, function (\Exception $e) use ($loop) { echo "连接失败:{$e->getMessage()}\n"; $loop->stop(); }); }); // 启动事件循环 $loop->run(); ?>
3. 关键注意点
- 事件名确认:代码中的
joinScoring和scoringUpdate是假设的事件名,你需要打开浏览器开发者工具(Network -> WebSocket),查看网站实际发送和接收的事件名称,替换成对应的值。 - Socket.IO版本:代码中使用的是EIO=4(对应Socket.IO v4),如果网站用的是其他版本,需要调整握手URL和消息格式。
内容的提问来源于stack exchange,提问作者truemiro
相关产品推荐
相关产品推荐

