通過 fopen 獲取 https://finance.yahoo.com/q?s=SYMBOL 頁面源碼,定位 rowspan=3 標記作為數據區塊起始,提取至 ichart.yahoo.com 圖表鏈接結束,輸出原始 HTML 片段。
<?php
function getYahooQuote($stockSymbol = "CCR") {
$targetURL = "https://finance.yahoo.com/q?s=$stockSymbol&d=t";
$fd = fopen($targetURL, "r");
if (!$fd) return false;
$startExtract = $stopExtract = false;
$capturedHTML = '';
while (!feof($fd)) {
$buffer = fgets($fd, 4096);
if (strstr($buffer, "rowspan=3")) {
$startExtract = true;
}
if ($startExtract && !$stopExtract) {
// 截取最後一個 ")) {
$stopExtract = true;
echo $capturedHTML;
break;
}
}
fclose($fd);
}
// 批量抓取示例
$symbols = ['CCR', 'IIXL', 'SAPE', 'WBVN'];
foreach ($symbols as $sym) {
echo "$sym
";
getYahooQuote($sym);
}
?>
| 問題 | 影響 | 修復方案 |
|---|---|---|
| 依賴特定 HTML 結構 | 頁面改版即失效 | 改用官方 API(Yahoo Finance API、Alpha Vantage、Twelve Data) |
| 無錯誤處理/重試 | 網絡波動導致數據缺失 | 加入超時、重試、熔斷機制 |
| 同步阻塞請求 | 批量抓取慢、併發差 | 使用 cURL 多句柄 / Swoole / Guzzle 異步並發 |
| 直接輸出 HTML 片段 | 難以結構化存儲/計算 | 解析為標準數組/JSON,入庫或緩存 |
| 無 User-Agent/Referer | 易被反爬封禁 | 設置合理 Header、IP 池、驗證碼識別 |
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use Symfony\Component\DomCrawler\Crawler;
$client = new Client(['timeout' => 10, 'headers' => ['User-Agent' => 'Mozilla/5.0...']]);
function fetchQuote(Client $client, string $symbol): ?array {
try {
$resp = $client->get("https://finance.yahoo.com/quote/$symbol");
$html = (string)$resp->getBody();
$crawler = new Crawler($html);
// Yahoo 現代頁面使用 data-test 屬性
$price = $crawler->filter('[data-test="qsp-price"]')->text();
$change = $crawler->filter('[data-test="qsp-price-change"]')->text();
$time = $crawler->filter('[data-test="qsp-market-time"]')->text();
return [
'symbol' => $symbol,
'price' => (float)str_replace(',', '', $price),
'change' => $change,
'time' => $time,
'fetched_at' => date('c'),
];
} catch (\Exception $e) {
error_log("Fetch $symbol failed: " . $e->getMessage());
return null;
}
}
// 並發批量(需 Guzzle 7+ Promise)
$promises = [];
foreach (['AAPL', 'GOOGL', 'MSFT', 'TSLA'] as $sym) {
$promises[$sym] = $client->getAsync("https://finance.yahoo.com/quote/$sym");
}
$results = [];
foreach ($promises as $sym => $promise) {
$resp = $promise->wait();
$results[$sym] = fetchQuote($client, $sym); // 簡化:實際應在 then 中解析
}
print_r($results);
本文代碼僅作爬蟲入門教學演示。生產環境金融數據獲取請務必走官方 API 或授權渠道,並建立完善的錯誤重試、監控告警、合規審計體系。