PHP 本身無內建瀏覽器渲染引擎,無法直接將網頁轉為圖片。常見做法是呼叫第三方截圖服務 API(如 Urlbox、ScreenshotAPI、PageCapture 等)或自架 Headless Chrome(Puppeteer/Playwright)服務。本文以原文提供的 ppt.cc API 為例,說明整合流程、錯誤處理與現代替代方案。
<?php
/**
* 取得網頁快照圖片網址或儲存至本地
*
* @param string $site 目標網址(需含 http(s)://)
* @param string $path 本地儲存路徑,留空則只回傳圖片 URL
* @param int $delay 渲染延遲秒數
* @return string|int|false 圖片 URL / 儲存位元組數 / 失敗回傳 false
*/
function snapshot(string $site, string $path = '', int $delay = 0): string|int|false
{
// 驗證網址格式
if (!filter_var($site, FILTER_VALIDATE_URL)) {
trigger_error("無效網址: $site", E_USER_WARNING);
return false;
}
$apiEndpoint = 'https://ppt.cc/yo2/catch.php';
$query = http_build_query([
'url' => $site,
'delay' => $delay,
'rnd' => mt_rand(1, 9)
]);
$ch = curl_init($apiEndpoint);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $query,
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?? 'PHP Snapshot Bot',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_FOLLOWLOCATION => true,
]);
$data = curl_exec($ch);
$err = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($err || $code !== 200) {
trigger_error("API 請求失敗: $err (HTTP $code)", E_USER_WARNING);
return false;
}
// API 回傳 32 字元雜湊碼
if (strlen($data) !== 32) {
trigger_error("API 回傳異常: $data", E_USER_WARNING);
return false;
}
// 組裝圖片 URL(依 API 規則:前 3 字元為目錄層級)
$hash = $data;
$dir = "{$hash[0]}/{$hash[1]}/{$hash[2]}/";
$imageUrl = "https://cache.ppt.cc/{$dir}src_{$hash}.png";
if ($path !== '') {
$imgData = @file_get_contents($imageUrl);
if ($imgData === false) {
trigger_error("圖片下載失敗: $imageUrl", E_USER_WARNING);
return false;
}
$bytes = file_put_contents($path, $imgData);
return $bytes ?: false;
}
return $imageUrl;
}
// 使用範例
$url = 'https://www.baidu.com';
// 1. 只取得圖片網址
$imgUrl = snapshot($url);
echo "快照圖片: $imgUrl\n";
// 2. 直接儲存至本地
$bytes = snapshot($url, __DIR__ . '/snapshots/baidu.png');
echo $bytes ? "儲存成功 ($bytes bytes)" : "儲存失敗";
ppt.cc 服務,若服務停止或變更規則將失效。生產環境建議自架或採用商業 SLA 服務。rnd 參數防止快取,確保取得最新快照。不依賴第三方,完全掌控渲染品質、等待邏輯、驗證碼處理等。
// screenshot-service.js
const express = require('express');
const puppeteer = require('puppeteer');
const app = express();
app.use(express.json());
let browser;
puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] }).then(b => browser = b);
app.post('/snapshot', async (req, res) => {
const { url, delay = 1000, fullPage = true } = req.body;
if (!url) return res.status(400).json({ error: 'url required' });
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 720 });
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
await new Promise(r => setTimeout(r, delay));
const buffer = await page.screenshot({ fullPage, type: 'png' });
await page.close();
res.set('Content-Type', 'image/png');
res.send(buffer);
});
app.listen(3000, () => console.log('Screenshot service on :3000'));
function snapshotLocal(string $url, string $savePath = '', int $delay = 1000): string|false
{
$ch = curl_init('https://localhost:3000/snapshot');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['url' => $url, 'delay' => $delay]),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
]);
$img = curl_exec($ch);
if (curl_errno($ch)) return false;
curl_close($ch);
if ($savePath) return file_put_contents($savePath, $img) ?: false;
return 'data:image/png;base64,' . base64_encode($img); // 或存檔後回傳 URL
}
快速原型可用第三方 API;正式專案建議自架 Headless Chrome 服務,配合佇列非同步處理,兼具穩定性、彈性與成本控制。PHP 僅負責發起請求與儲存,渲染工作交給專業工具。