本文目录导读:

在 PHP 中抓取动态页面(即页面内容由 JavaScript 渲染生成)时,传统的 file_get_contents() 或 curl 只能获取到未执行 JavaScript 的原始 HTML 源码,无法获取到渲染后的数据。
以下是几种常见的解决方案,从简单到复杂:
使用无头浏览器(推荐)
这是目前最主流的做法,通过 Headless Chrome(无头浏览器)来执行页面 JS,然后返回渲染后的 DOM 内容。
使用 Puppeteer + Node.js(跨语言调用)
虽然这是 Node.js 工具,但 PHP 可以通过 exec() 或 shell_exec() 调用它,这是最稳定的方案。
-
步骤 1:安装 Node.js 环境,并安装 Puppeteer(内含 Chromium 浏览器)。
-
步骤 2:编写一个 Node.js 抓取脚本
scrape.js:// scrape.js const puppeteer = require('puppeteer'); async function scrape(url) { const browser = await puppeteer.launch({ headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); await page.goto(url, { waitUntil: 'networkidle0', timeout: 60000 }); // 获取渲染后的 HTML 内容 const content = await page.content(); await browser.close(); return content; } // 接收命令行参数 const url = process.argv[2]; scrape(url).then(html => { console.log(html); }).catch(err => { console.error('Error:', err.message); process.exit(1); }); -
步骤 3:在 PHP 中调用该脚本:
<?php function fetchDynamicPage($url) { $scriptPath = '/path/to/scrape.js'; // 注意使用 escapeshellarg 防止注入 $cmd = 'node ' . $scriptPath . ' ' . escapeshellarg($url) . ' 2>&1'; $output = shell_exec($cmd); return $output; } $html = fetchDynamicPage('https://example.com/dynamic-page'); // 接下来可以用 DOMDocument 或正则解析 $html ?>
使用 PHP 库:facebook/webdriver 或 php-webdriver/php-webdriver
这需要一个独立的 Selenium Server 来驱动浏览器。
-
步骤 1:下载 Selenium Server(Java 包)并启动。
-
步骤 2:通过 Composer 安装库:
composer require php-webdriver/webdriver -
步骤 3:PHP 代码示例:
<?php require_once 'vendor/autoload.php'; use Facebook\WebDriver\Remote\RemoteWebDriver; use Facebook\WebDriver\Remote\DesiredCapabilities; use Facebook\WebDriver\Chrome\ChromeOptions; function fetchWithSelenium($url) { // 启动 Chrome 选项,启用 headless 模式 $options = new ChromeOptions(); $options->addArguments(['--headless', '--disable-gpu', '--no-sandbox']); $caps = DesiredCapabilities::chrome(); $caps->setCapability(ChromeOptions::CAPABILITY, $options); // 连接 Selenium 服务器 $driver = RemoteWebDriver::create('http://localhost:9515', $caps); $driver->get($url); // 等待特定元素加载(可选,提高稳定性) // $driver->wait(10)->until( // WebDriverExpectedCondition::presenceOfElementLocated(WebDriverBy::cssSelector('selector')) // ); // 获取页面源代码 $html = $driver->getPageSource(); $driver->quit(); return $html; } $html = fetchWithSelenium('https://example.com'); ?>需要注意的是,这种方式需要 Java 环境,配置较繁琐。
使用 symfony/panther(纯 PHP 方案,基于 ChromeDriver)
Panther 是 Symfony 的一个组件,它封装了 ChromeDriver 的交互,API 类似于 Facebook WebDriver,但不需要独立的 Selenium Server,直接驱动 ChromeDriver。
<?php
require_once 'vendor/autoload.php';
use Symfony\Component\Panther\Client;
$client = Client::createChromeClient(); // 自动下载 Chromedriver(如果必要)
// 或使用已有的 Chrome 安装:Client::createChromeClient(null, ['--headless' => true]);
$crawler = $client->request('GET', 'https://example.com');
// 等待异步请求完成(等待某个元素出现)
$client->waitFor('.dynamic-content');
// 获取完整 HTML
$html = $client->getCrawler()->html();
// 也可以使用 DOMXPath 提取数据
// $values = $crawler->filter('.item')->extract(['_text']);
?>
优点:配置相对简单,如果是抓取单页面内容,效率较高。
分析 XHR/API 接口(最佳实践)
很多“动态页面”实际上是通过 AJAX 请求后端 JSON 接口渲染的。优先尝试在浏览器 F12 -> Network 面板中查找 XHR 请求,如果能找到数据接口,直接模拟该接口请求即可,性能最优。
// 假设页面渲染数据来自 /api/data?page=1
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://example.com/api/data?page=1');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'); // 伪装浏览器
// 处理 Cookie 或添加 Referer 等 Header
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Requested-With: XMLHttpRequest',
'Referer: https://example.com/page'
]);
$response = curl_exec($ch);
curl_close($ch);
if ($response !== false) {
$data = json_decode($response, true); // 直接处理 JSON 数据
print_r($data);
}
寻找伪静态或服务器端渲染(SSR)后备方案
如果页面是 Vue/React 同构渲染(SSR),服务器会直接返回渲染后的 HTML,curl 就能获取到内容,如果页面是 Vue 的 SPA(单页应用) 且历史模式为 history,可以通过添加特殊参数(如 )或查看是否有 ?_escaped_fragment_= 的 SEO 版本。
总结与建议
| 方案 | 技术难度 | 性能消耗 | 稳定性 | 适用场景 |
|---|---|---|---|---|
| API 直连 | ⭐ | 极低 | 高 | 能找到数据接口时 |
| Puppeteer | ⭐⭐⭐ | 高(需Node) | 高 | 复杂渲染、需要执行大量 JS 或需要登录 |
| Panther | ⭐⭐⭐ | 高 | 中 | 彻底使用 PHP 技术栈且需要模拟交互 |
| Selenium | ⭐⭐⭐⭐ | 高 | 中 | 需要复杂的浏览器交互操作(点击、滑动) |
推荐路线:
- 先看请求:优先使用
curl模拟 AJAX 接口。 - 再看页面:如果你的页面是纯 JS 渲染且没有可逆的 API,推荐使用 Puppeteer + Node.js 配合 PHP 调用,这是目前生态最好、抗检测能力最强的方案。
- 最后才是:如果强制要求纯 PHP,使用
symfony/panther(需要服务器安装 Chrome 和 ChromeDriver)。