如何使用PHP Selenium WebDriver抓取并保存流媒体MP4文件
解决方案
注意你原有代码中的XPath写法不规范,需要调整为可匹配到video元素的正确XPath表达式,否则会无法获取到src属性
失败原因排查
直接使用fopen/file_get_contents无法获取视频流,通常是以下几个原因:
- 视频服务端做了请求校验,需要携带浏览器当前会话的Cookie、Referer、User-Agent等请求头,裸请求会被服务器拦截
- 获取到的
src是blob:开头的浏览器本地对象地址,对应资源存在于浏览器内存中,服务端PHP无法直接访问 - 视频是分片加载的HLS流(地址为.m3u8后缀),单个
src不是完整MP4文件地址
对应实现方案
场景1:src为http/https开头的真实远程MP4地址
复用Selenium当前浏览器的会话信息发起请求即可,推荐用CURL实现,示例代码如下:
// 接你原有代码 $vid = $driver->findElement(WebDriverBy::xpath('//video[@id="videojs_html5"]'))->getAttribute("src"); // 1. 获取浏览器当前会话的Cookie、User-Agent、当前页面地址(用作Referer) $cookies = $driver->manage()->getCookies(); $cookieStr = ''; foreach ($cookies as $cookie) { $cookieStr .= $cookie->getName() . '=' . $cookie->getValue() . '; '; } $userAgent = $driver->executeScript("return navigator.userAgent;"); $referer = $driver->getCurrentURL(); // 2. 用CURL携带头信息请求视频流,分片写入避免大视频占满内存 $savePath = __DIR__ . '/save_video.mp4'; $ch = curl_init($vid); $fp = fopen($savePath, 'w+'); curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "User-Agent: $userAgent", "Referer: $referer", "Cookie: $cookieStr" ]); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_exec($ch); // 错误检查 if(curl_errno($ch)) { unlink($savePath); throw new Exception('视频下载失败:' . curl_error($ch)); } curl_close($ch); fclose($fp);
场景2:src为blob:开头的本地对象地址
此时需要在浏览器端执行JS触发下载,提前配置Chrome的默认下载路径即可,示例代码如下:
use Facebook\Webdriver; use Facebook\WebDriver\WebDriverBy; use Facebook\WebDriver\Chrome\ChromeOptions; use Facebook\WebDriver\Remote\DesiredCapabilities; use Facebook\WebDriver\Remote\RemoteWebDriver; require_once __DIR__ . '/vendor/autoload.php'; $host = 'http://localhost:4444/wd/hub'; // 配置Chrome下载路径,避免下载到默认位置找不到 $chromeOptions = new ChromeOptions(); $downloadPath = __DIR__; // 自定义本地保存路径,确保有写入权限 $chromeOptions->setExperimentalOption('prefs', [ 'download.default_directory' => $downloadPath, 'download.prompt_for_download' => false, 'download.directory_upgrade' => true, 'safebrowsing.enabled' => false ]); $capabilities = DesiredCapabilities::chrome(); $capabilities->setCapability(ChromeOptions::CAPABILITY, $chromeOptions); $driver = RemoteWebDriver::create($host, $capabilities); $driver->get("http://example.com"); $vid = $driver->findElement(WebDriverBy::xpath('//video[@id="videojs_html5"]'))->getAttribute("src"); // 执行JS触发浏览器下载 $driver->executeScript(" const a = document.createElement('a'); a.href = arguments[0]; a.download = 'save_video.mp4'; document.body.appendChild(a); a.click(); document.body.removeChild(a); ", [$vid]); // 等待下载完成,可优化为检测目标文件大小不再变化的逻辑 sleep(15); $driver->quit();
场景3:src为.m3u8后缀的HLS分片流
需要借助ffmpeg工具完成分片下载和合并,确保本地已经安装ffmpeg后,执行如下代码即可:
// 提前获取到带会话参数的m3u8地址$vid $savePath = __DIR__ . '/save_video.mp4'; $cmd = "ffmpeg -i \"$vid\" -c copy -bsf:a aac_adtstoasc \"$savePath\""; exec($cmd, $output, $returnVar); if($returnVar !== 0) { throw new Exception('视频合并失败:' . implode("\n", $output)); }
内容的提问来源于stack exchange,提问作者wnets webmaster
相关产品推荐
相关产品推荐

