你遇到的错误:
> **"The media could not be loaded, either because the server or network failed or because the format is not supported."**
说明视频标签(`<video>`)已经存在,但浏览器无法加载媒体资源。这通常意味着:
- 视频 URL 请求被阻止或返回失败(403/404/CORS)
- 媒体格式不支持(如 `.m3u8` 未启用 HLS 支持)
- 缺少必要的请求头(如 `Referer`, `Origin`, `Authorization`)
- 被 CDN 或后端识别为自动化访问并拒绝服务
- 浏览器未正确启用媒体解码功能(尤其是无头模式下)
---
### ✅ 深度分析与解决方案
#### ✅ 1. **检查真实视频请求是否发出?**
使用 Playwright 监听网络请求,确认是否发起了对 `.mp4`、`.m3u8`、`.ts` 等视频资源的请求,并查看状态码。
```python
def run(playwright):
browser = playwright.chromium.launch(
headless=False,
args=[
"--disable-blink-features=AutomationControlled",
"--mute-audio",
"--no-sandbox",
"--allow-media-stream", # 允许媒体流
"--use-fake-ui-for-media-stream",
"--use-fake-device-for-media-stream"
]
)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
locale="zh-CN",
timezone_id="Asia/Shanghai"
)
# 绕过 webdriver 检测
context.add_init_script("""
Object.defineProperty(navigator, 'webdriver', { get: () => false });
window.chrome = { runtime: {}, loadTimes: () => {}, csi: () => {} };
Object.defineProperty(navigator, 'languages', { get: () => ['zh-CN', 'zh', 'en'] });
Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
""")
page = context.new_page()
# === 监听网络请求 ===
def log_request(req):
if "m3u8" in req.url or ".mp4" in req.url or "video" in req.resource_type:
print(f"📹 Video request: {req.url} | Type: {req.resource_type}")
def log_response(res):
if "m3u8" in res.url or ".mp4" in res.url or "ts" in res.url:
print(f"📥 Response: {res.url} | Status: {res.status}")
if res.status != 200:
print(f"❌ Failed to load video resource! Status: {res.status}")
page.on("request", log_request)
page.on("response", log_response)
# 设置额外请求头(模拟正常浏览)
page.set_extra_http_headers({
"Referer": "https://www.google.com/",
"Origin": "https://your-video-site.com",
})
try:
page.goto("https://example.com/video-page.html", wait_until="networkidle")
page.wait_for_selector("video", timeout=10000)
# 尝试播放
page.eval_on_selector("video", """
video => {
video.muted = true;
video.autoplay = true;
video.playsInline = true;
video.play().catch(e => console.error('Play error:', e));
}
""")
# 等待一段时间观察结果
page.wait_for_timeout(15000)
# 检查播放状态
is_playing = page.eval_on_selector("video", """
video => ({
paused: video.paused,
ended: video.ended,
currentTime: video.currentTime,
error: video.error ? video.error.code : null
})
""")
print("Playback state:", is_playing)
except Exception as e:
print(f"Error: {e}")
context.close()
browser.close()
with sync_playwright() as p:
run(p)
```
---
### ✅ 2. **常见修复方法汇总**
| 问题类型 | 解决方案 |
|--------|---------|
| ❌ 自动播放被禁用 | 添加 `--mute-audio` 启动参数 + JS 设置 `muted=true` |
| ❌ 缺少 Referer 导致 403 | 使用 `set_extra_http_headers({"Referer": "..."})` |
| ❌ 请求被 CORS 阻止 | 使用 `--disable-web-security`(仅测试)或代理中转 |
| ❌ 视频是 HLS (.m3u8) 格式 | Chromium 默认支持有限,需确保启用 Media Source Extensions |
| ❌ 被服务器封禁 IP/User-Agent | 使用真实 User-Agent + 国内代理 |
| ❌ DRM 加密视频(如 Widevine) | 必须启用支持且设备已授权(Playwright 默认可能不支持) |
---
### ✅ 3. 强制启用媒体支持的启动参数(重要!)
```python
args=[
"--autoplay-policy=no-user-gesture-required", # 允许无交互自动播放
"--disable-blink-features=AutomationControlled",
"--mute-audio",
"--use-fake-device-for-media-stream",
"--use-fake-ui-for-media-stream",
"--allow-media-stream",
"--enable-media-stream",
"--no-sandbox",
"--start-maximized"
]
```
---
### ✅ 4. 手动获取视频源地址并验证
有时 `<video>` 的 `src` 是空的,实际通过 JavaScript 动态添加。你可以手动提取:
```python
# 获取 video 元素的所有 source
sources = page.eval_on_selector("video", """
video => Array.from(video.querySelectorAll("source")).map(s => s.src)
""")
print("Video sources:", sources)
# 或直接读取 .src 属性
src = page.eval_on_selector("video", "video => video.src")
print("Direct src:", src)
```
然后在浏览器中新开标签页访问这个链接,看是否能播放。
---
### ✅ 5. 替代方案:使用 FFmpeg + requests 下载测试
如果只是想“下载”或“检测”视频是否可访问,可以直接用 Python 发起带 Headers 的请求:
```python
import requests
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Referer": "https://example.com/video-page.html",
"Origin": "https://example.com"
}
response = requests.get("https://example.com/path/to/video.m3u8", headers=headers, stream=True)
if response.status_code == 200:
print("✅ 视频可以访问!")
else:
print("❌ 被拒绝:", response.status_code, response.text)
```
---