使用Requests和BeautifulSoup无法获取YouTube视频时长的问题求助
问题描述:
我想通过YouTube视频链接获取视频时长,在浏览器开发者工具里能看到页面上有一个class为ytp-time-duration的span标签(显示着视频时长内容)。
我尝试用requests和BeautifulSoup来抓取这个标签,但不管用find_all还是find都返回空结果,完全找不到这个标签。我的代码如下:
import requests from bs4 import BeautifulSoup url = "https://www.youtube.com/watch?v=ANYyoutubeLINK" response = requests.get(url) response.raise_for_status() soup = BeautifulSoup(response.text, 'html.parser') duration_span = soup.find_all('span', class_='ytp-time-duration') print(duration_span)
请问这是哪里出问题了?
我的解答:
嘿,这个问题我之前也碰到过!核心原因其实很简单:YouTube的很多页面元素是通过JavaScript动态渲染出来的,你用requests.get()获取的只是服务器返回的初始静态HTML,这个阶段那个ytp-time-duration的span标签还不存在——它是等页面加载完成后,由前端JS动态生成的,所以BeautifulSoup自然找不到它。
给你几个可行的解决方案,按推荐程度排序:
方案1:从页面静态脚本中提取视频信息(无额外依赖)
YouTube会把视频的核心数据(包括时长)打包在页面的<script>标签里,比如ytInitialPlayerResponse这个变量,我们可以直接从静态HTML里匹配提取,不需要依赖动态渲染:
import requests import re import json url = "https://www.youtube.com/watch?v=ANYyoutubeLINK" response = requests.get(url) response.raise_for_status() # 匹配页面中的ytInitialPlayerResponse变量内容 pattern = re.search(r'var ytInitialPlayerResponse = ({.*?});', response.text) if pattern: player_response = json.loads(pattern.group(1)) # 提取时长(单位为秒) duration_seconds = player_response['videoDetails']['lengthSeconds'] # 转换为友好的时分秒格式 hours = int(duration_seconds) // 3600 minutes = (int(duration_seconds) % 3600) // 60 seconds = int(duration_seconds) % 60 if hours > 0: duration = f"{hours}:{minutes:02d}:{seconds:02d}" else: duration = f"{minutes}:{seconds:02d}" print(f"视频时长:{duration}") else: print("无法从页面中提取视频信息")
方案2:用Selenium模拟浏览器加载(适配动态渲染)
如果YouTube更新了页面结构导致方案1失效,可以用Selenium模拟真实浏览器的加载过程,这样就能获取到动态渲染后的元素:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC url = "https://www.youtube.com/watch?v=ANYyoutubeLINK" # 初始化Chrome浏览器(需提前安装对应版本的ChromeDriver) driver = webdriver.Chrome() driver.get(url) try: # 等待时长元素加载完成(最多等10秒) duration_element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, 'ytp-time-duration')) ) print(f"视频时长:{duration_element.text}") finally: # 用完记得关闭浏览器 driver.quit()
方案3:使用YouTube Data API(最稳定的长期方案)
如果你的需求是长期稳定获取视频信息,官方的Data API是最佳选择,不过需要先申请API密钥(免费额度足够个人使用)。示例代码如下(需先安装google-api-python-client库):
from googleapiclient.discovery import build import isodate API_KEY = "你的API密钥" VIDEO_ID = "替换为视频链接中的v参数值" # 初始化API客户端 youtube = build('youtube', 'v3', developerKey=API_KEY) # 请求视频的内容详情 request = youtube.videos().list( part="contentDetails", id=VIDEO_ID ) response = request.execute() # 提取ISO 8601格式的时长并转换为秒 duration_iso = response['items'][0]['contentDetails']['duration'] duration_seconds = isodate.parse_duration(duration_iso).total_seconds() # 转换为友好格式 hours = int(duration_seconds) // 3600 minutes = (int(duration_seconds) % 3600) // 60 seconds = int(duration_seconds) % 60 if hours > 0: duration = f"{hours}:{minutes:02d}:{seconds:02d}" else: duration = f"{minutes}:{seconds:02d}" print(f"视频时长:{duration}")
总结一下:用requests+BeautifulSoup抓不到是因为元素是动态生成的,优先试试方案1(无需额外工具),如果不行再用Selenium,长期项目推荐用官方API。
备注:内容来源于stack exchange,提问作者Mark K

