摘要
国内量化系统、行情大屏开发中,A 股沪深京市场数据是核心需求。很多 PHP 技术栈团队对接行情时,容易踩 curl 无连接复用、无重试、批量请求效率低等坑。本文基于 PHP 实现 A 股批量行情采集方案,支持上证指数、深证成指、创业板指以及多只个股的批量拉取,附带 Guzzle 连接池、异常重试、定时任务部署,数据源采用脉动行情数据 API。
一、业务背景
A 股市场覆盖沪市主板、深市主板、创业板、科创板、北交所,核心指数包括上证指数、深证成指、创业板指、沪深 300 等。 很多 PHP 开发者直接用 file_get_contents 或原生 curl 写行情采集,上线后暴露几个问题:
- 每次请求新建 curl 句柄,没有连接复用,性能差;
- 没有超时控制,接口慢导致进程卡住;
- 单只个股循环请求,没有利用批量查询能力;
- 没有重试机制,网络抖动直接丢数据。
二、品种编码说明
脉动行情数据 API A 股市场覆盖:
- 指数:HX_SH000001(上证指数)、HX_SZ399001(深证成指)、HX_SZ399006(创业板指)
- 个股:HX_SH_600519(贵州茅台)、HX_SZ_300750(宁德时代)、HX_SH_601318(中国平安)、HX_SZ_002594(比亚迪)
- 接口地址:http://39.107.99.235:1008/getQuote.php
- K 线接口:http://39.107.99.235:1008/redis.php
三、Composer 依赖
composer require guzzlehttp/guzzle
四、完整 PHP 采集类
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
/**
* A股行情批量采集类
* 支持沪深京指数、个股
*/
class AStockFetcher
{
private Client $httpClient;
private string $quoteUrl = 'http://39.107.99.235:1008/getQuote.php';
private string $klineUrl = 'http://39.107.99.235:1008/redis.php';
private int $maxRetry = 3;
public function __construct()
{
// 全局单例Guzzle客户端,连接池复用
$this->httpClient = new Client([
'timeout' => 8.0,
'connect_timeout' => 5.0,
'headers' => [
'Accept-Encoding' => 'gzip',
],
]);
}
/**
* 批量获取A股实时行情
* @param string $codes 多个编码英文逗号分隔
* @return array
*/
public function getBatchQuote(string $codes): array
{
for ($i = 0; $i < $this->maxRetry; $i++) {
try {
$resp = $this->httpClient->get($this->quoteUrl, [
'query' => ['code' => $codes],
]);
$data = json_decode($resp->getBody()->getContents(), true);
if (isset($data['code']) && $data['code'] == 200) {
return $data['data']['body'];
}
} catch (RequestException $e) {
error_log("第" . ($i + 1) . "次请求失败: " . $e->getMessage());
sleep(1);
}
}
return [];
}
/**
* 获取单标的K线
*/
public function getKline(string $code, string $cycle = '1d', int $rows = 100): string
{
try {
$resp = $this->httpClient->get($this->klineUrl, [
'query' => [
'code' => $code,
'time' => $cycle,
'rows' => $rows,
],
]);
return $resp->getBody()->getContents();
} catch (RequestException $e) {
error_log("K线请求失败: " . $e->getMessage());
return '';
}
}
/**
* 执行一次采集并打印
*/
public function run(string $codes): void
{
echo "[" . date('Y-m-d H:i:s') . "] 开始采集A股行情\n";
$items = $this->getBatchQuote($codes);
foreach ($items as $item) {
echo sprintf(
"[%s] %s 最新价:%s 涨跌:%s 涨跌幅:%s%%\n",
$item['Time'],
$item['StockCode'],
$item['Price'],
$item['Diff'],
$item['DiffRate']
);
}
echo "本次采集完成,共" . count($items) . "条\n";
}
}
// 测试运行
$fetcher = new AStockFetcher();
// 指数 + 多只热门个股
$codes = 'HX_SH000001,HX_SZ399001,HX_SZ399006,HX_SH_600519,HX_SZ_300750,HX_SZ_002594';
$fetcher->run($codes);
// 单独获取贵州茅台日K线
$kline = $fetcher->getKline('HX_SH_600519', '1d', 50);
echo "\n贵州茅台日K线数据:\n" . $kline . "\n";
五、定时任务部署
Linux crontab 每 30 秒执行一次:
# 编辑crontab
crontab -e
# 每30秒执行一次采集
* * * * * /usr/bin/php /path/to/a_stock_fetch.php >> /path/to/logs/fetch.log 2>&1
* * * * * sleep 30 && /usr/bin/php /path/to/a_stock_fetch.php >> /path/to/logs/fetch.log 2>&1
或者用 Workerman 常驻进程,更稳定:
use Workerman\Worker;
use Workerman\Lib\Timer;
require __DIR__ . '/vendor/autoload.php';
$worker = new Worker();
$worker->onWorkerStart = function () {
$fetcher = new AStockFetcher();
$codes = 'HX_SH000001,HX_SZ399001,HX_SH_600519,HX_SZ_300750';
// 启动立即执行一次
$fetcher->run($codes);
// 每30秒定时执行
Timer::add(30, function () use ($fetcher, $codes) {
$fetcher->run($codes);
});
};
Worker::runAll();
六、返回字段说明
表格
| 字段 | 说明 |
|---|---|
| StockCode | 标的编码 |
| Price | 最新价 |
| Open | 开盘价 |
| LastClose | 昨收价 |
| High | 日内最高 |
| Low | 日内最低 |
| Diff | 涨跌额 |
| DiffRate | 涨跌幅 |
| TotalVol | 成交量 |
| Time | 更新时间 |
七、开发注意事项
- Guzzle 全局单例复用连接池,不要每次 new Client;
- 批量请求一次拼接多个编码,减少 HTTP 连接次数;
- A 股交易时段为国内时间,非交易时段价格不更新;
- 开启 Gzip 压缩,减少传输体积;
- 生产环境建议日志按天切分,方便排查问题。
八、总结
PHP 技术栈做 A 股行情采集非常方便,Guzzle 连接池性能足够。本文实现指数与个股批量采集、重试、定时部署,修改标的编码列表即可扩展到更多沪深京个股,适合 PHP 后端团队快速搭建行情服务。
