如何用 Python 抓取 YouTube

学习如何使用 Python 抓取 YouTube 的循序渐进指南。
7 min read
如何用 Python 抓取 YouTube

在这个循序渐进的指南中,您将学习如何使用 Python 在 YouTube 上进行网页抓取

本教程将涵盖:

YouTube API vs YouTube 抓取

YouTube Data API 是从平台获取数据的官方途径,包括关于视频、播放列表和内容创作者的信息。然而,至少有三个理由表明抓取 YouTube 比单纯依赖 API 更好:

  • 灵活性和自定义:使用 YouTube 爬虫,您可以定制代码以选择所需的数据。这种自定义程度有助于您收集特定使用案例所需的精确信息。相比之下,API 仅提供预定义的数据访问。
  • 获取非官方数据:API 提供 YouTube 选择的特定数据集。这意味着您目前依赖的一些数据在未来可能不再可用。通过网页抓取,您可以获取 YouTube 网站上的任何其他信息,即使这些信息没有通过 API 暴露出来。
  • 无限制:YouTube API 受速率限制。这种限制规定了在特定时间范围内您可以发出的请求频率和数量。通过直接与平台交互,您可以绕过任何限制。

从 YouTube 抓取的主要数据字段

  • 视频元数据
    • 标题
    • 描述
    • 观看次数
    • 点赞数
    • 时长
    • 发布日期
    • 频道
  • 用户资料
    • 用户名
    • 用户描述
    • 订阅者数
    • 视频数量
    • 播放列表
  • 其他
    • 评论
    • 相关视频

如前所述,获取这些数据的最佳方式是通过自定义爬虫。但是选择哪种编程语言呢?

Python 是用于网页抓取的最流行语言之一,因为它的语法简单和丰富的库生态系统。其多功能性、可读性和广泛的社区支持使其成为一个很好的选择。请查看我们的深入指南以开始 使用 Python 进行网页抓取

用 Selenium 抓取 YouTube

按照本教程,学习如何构建一个用于抓取 YouTube 的 Python 脚本。

步骤 1:设置

在编写代码之前,您需要满足以下先决条件:

您可以使用以下命令初始化一个 Python 项目并创建一个虚拟环境:

mkdir youtube-scraper
cd youtube-scraper
python -m venv env

上面创建的 youtube-scraper 目录代表您的 Python 脚本的项目文件夹。在 IDE 中打开它,创建一个 scraper.py 文件,并进行初始化如下:

print('Hello, World!')

现在,该文件只是一个打印“Hello, World!”的示例脚本,但它很快将包含抓取逻辑。

通过按 IDE 的运行按钮或使用以下命令验证脚本是否工作:

python scraper.py

在终端中,您应该看到:

Hello, World!

完美,您现在有了一个用于 YouTube 爬虫的 Python 项目。

步骤 2:选择和安装抓取库

如果您花时间浏览 YouTube,您会发现它是一个高度互动的平台。根据点击和滚动操作,网站会动态加载和渲染数据。这意味着 YouTube 很大程度上依赖于 JavaScript。

抓取 YouTube 需要一个可以在浏览器中渲染网页的工具,就像 Selenium!这个工具使得您可以抓取动态网站,允许您在浏览器中对网站执行自动化任务。

使用以下命令将 SeleniumWebdriver Manager 包添加到您的项目依赖项中:

pip install selenium webdriver-manager

安装任务可能需要一些时间,请耐心等待。

webdriver-manager 不是严格必要的,但它使得在 Selenium 中管理 Web 驱动程序更加容易。多亏了它,您不必手动下载、安装和配置 Web 驱动程序。

scraper.py 中开始使用 Selenium:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options

# initialize a web driver instance to control a Chrome window
# in headless mode
options = Options()
options.add_argument('--headless=new')

driver = webdriver.Chrome(
    service=ChromeService(ChromeDriverManager().install()),
    options=options
)

# scraping logic...

# close the browser and free up the resources
driver.quit()

这个脚本创建了一个 Chrome WebDriver 实例,通过它可以程序化地控制一个 Chrome 窗口。

默认情况下,Selenium 会启动带有 UI 的浏览器。虽然这对调试很有用,因为您可以实时体验自动化脚本在页面上的操作,但它会占用大量资源。因此,您应该配置 Chrome 以无界面模式运行。多亏了 --headless=new 选项,受控的浏览器实例将在后台启动,没有 UI。

完美!现在定义抓取逻辑!

步骤 3:连接到 YouTube

要在 YouTube 上进行网页抓取,您首先需要选择一个视频来提取数据。在本指南中,您将看到如何抓取 Bright Data 的 YouTube 频道中的最新视频。请记住,任何其他视频都可以。

以下是所选的目标 YouTube 页面:

https://www.youtube.com/watch?v=kuDuJWvho7Q

这是一个关于网页抓取的视频,标题为“Introduction to Bright Data | Scraping Browser”。

将 URL 字符串存储 在一个 Python 变量中:

url = 'https://www.youtube.com/watch?v=kuDuJWvho7Q'

现在,您可以指示 Selenium 连接到目标页面:

driver.get(url)

get() 函数告诉受控浏览器访问由参数传递的 URL 标识的页面。

这就是您的 YouTube 爬虫目前的样子:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options

# initialize a web driver instance to control a Chrome window
# in headless mode
options = Options()
options.add_argument('--headless=new')

driver = webdriver.Chrome(
    service=ChromeService(ChromeDriverManager().install()),
    options=options
)

# the URL of the target page
url = 'https://www.youtube.com/watch?v=kuDuJWvho7Q'
# visit the target page in the controlled browser
driver.get(url)

# close the browser and free up the resources
driver.quit()

如果您运行该脚本,它将打开浏览器窗口片刻,然后由于 quit() 指令关闭它:

注意“Chrome 正在被自动测试软件控制”消息,确保 Selenium 正在 Chrome 上正常工作。

步骤 4:检查目标页面

看看上一个截图。当您第一次打开 YouTube 时,会出现一个同意对话框。要访问页面上的数据,您必须首先点击“全部接受”按钮来关闭它。让我们学习如何做到这一点!

创建新的浏览器会话,在隐身模式下打开 YouTube。右键单击同意模态框,选择“检查”。这将打开 Chrome DevTools 部分:

注意对话框有一个id属性。这对于在 Selenium 中定义有效的选择器策略非常有用。

同样,检查“全部接受”按钮:

它是由以下 CSS 选择器识别的第二个按钮:

.eom-buttons button.yt-spec-button-shape-next

将其组合在一起,使用这些代码行在 Selenium 中处理 YouTube 的 Cookie 政策:

try:
    # wait up to 15 seconds for the consent dialog to show up
    consent_overlay = WebDriverWait(driver, 15).until(
        EC.presence_of_element_located((By.ID, 'dialog'))
    )

    # select the consent option buttons
    consent_buttons = consent_overlay.find_elements(By.CSS_SELECTOR, '.eom-buttons button.yt-spec-button-shape-next')
    if len(consent_buttons) > 1:
        # retrieve and click the 'Accept all' button
        accept_all_button = consent_buttons[1]
        accept_all_button.click()
except TimeoutException:
    print('Cookie modal missing')

同意模态框是动态加载的,可能需要一些时间显示。因此,您需要使用WebDriverWait等待预期条件发生。如果在指定的超时时间内没有发生任何事情,它会引发TimeoutException。YouTube 非常慢,因此建议使用超过 10 秒的超时时间。

由于 YouTube 不断更改其政策,该对话框可能在特定国家或情况下不会出现。因此,使用try-catch处理异常,以防模态框不存在时防止脚本失败。

要使脚本正常工作,请记住添加以下导入:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common import TimeoutException

按下“全部接受”按钮后,YouTube 需要一段时间来动态重新渲染页面:

在此期间,您无法在 Selenium 中与页面进行交互。如果您尝试选择一个 HTML 元素,您将收到“过时的元素引用”错误。这是因为在这个过程中 DOM 发生了很多变化。

如您所见,标题元素包含一条灰色线条。如果您检查该元素,您会看到:

判断页面何时加载的一个好指标是等待标题元素可见:

# wait for YouTube to load the page data
WebDriverWait(driver, 15).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, 'h1.ytd-watch-metadata'))
)

您已经准备好用 Python 抓取 YouTube。继续在 DevTools 中分析目标站点并熟悉其 DOM。

步骤 5:提取 YouTube 数据

首先,您需要一个数据结构来存储抓取的信息。使用以下代码初始化一个 Python 字典:

video = {}

正如您在上一步中应该注意到的那样,一些最有趣的信息位于视频播放器下方的部分:

使用h1.ytd-watch-metadata CSS 选择器,您可以获取视频标题:

title = driver \
    .find_element(By.CSS_SELECTOR, 'h1.ytd-watch-metadata') \
    .text

标题下方是包含频道信息的 HTML 元素:

通过“owner”id属性标识,您可以使用以下代码获取所有数据:

# dictionary where to store the channel info
channel = {}

# scrape the channel info attributes
channel_element = driver \
    .find_element(By.ID, 'owner')

channel_url = channel_element \
              .find_element(By.CSS_SELECTOR, 'a.yt-simple-endpoint') \
              .get_attribute('href')
channel_name = channel_element \
              .find_element(By.ID, 'channel-name') \
              .text
channel_image = channel_element \
              .find_element(By.ID, 'img') \
              .get_attribute('src')
channel_subs = channel_element \
              .find_element(By.ID, 'owner-sub-count') \
              .text \
              .replace(' subscribers', '')

channel['url'] = channel_url
channel['name'] = channel_name
channel['image'] = channel_image
channel['subs'] = channel_subs

更靠下方是视频描述。此组件有一个棘手的行为,因为它在关闭或打开时显示不同的数据。

点击它可以访问完整的数据:

driver.find_element(By.ID, 'description-inline-expander').click()

您应该能够访问扩展的描述信息元素:

使用以下代码检索视频观看次数和发布日期:

info_container_elements = driver \
    .find_elements(By.CSS_SELECTOR, '#info-container span')

views = info_container_elements[0] \
    .text \
    .replace(' views', '')
publication_date = info_container_elements[2] \
    .text

与视频关联的文本描述包含在以下子元素中:

使用以下代码抓取它:

description = driver \
    .find_element(By.CSS_SELECTOR, '#description-inline-expander .ytd-text-inline-expander span') \
    .text

接下来,检查点赞按钮:

使用以下代码收集点赞数:

likes = driver \
    .find_element(By.ID, 'segmented-like-button') \
    .text

最后,不要忘记将抓取的数据插入到video字典中:

video['url'] = url
video['title'] = title
video['channel'] = channel
video['views'] = views
video['publication_date'] = publication_date
video['description'] = description
video['likes'] = likes

太棒了!您刚刚用 Python 进行了网页抓取!

步骤 6:将抓取的数据导出为 JSON

感兴趣的数据现在存储在一个 Python 字典中,这不是与其他团队共享它的最佳格式。您可以使用以下两行代码将收集到的信息转换为 JSON 并导出到文件:

with open('video.json', 'w') as file:
    json.dump(video, file)

这个代码片段使用open()初始化一个video.json文件。然后,它使用json.dump()video字典的 JSON 表示写入输出文件。查看我们的文章以了解更多关于如何用 Python 解析 JSON 数据的信息。

您无需额外的依赖项来实现目标。您只需要 Python 标准库json包,您可以使用以下代码导入它:

import json

太棒了!您从一个动态 HTML 页面中的原始数据开始,现在有了半结构化的 JSON 数据。是时候查看整个 YouTube 爬虫了。

步骤 7:把它们放在一起
以下是完整的scraper.py脚本:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common import TimeoutException
import json

# enable the headless mode
options = Options()
# options.add_argument('--headless=new')

# initialize a web driver instance to control a Chrome window
driver = webdriver.Chrome(
    service=ChromeService(ChromeDriverManager().install()),
    options=options
)

# the URL of the target page
url = 'https://www.youtube.com/watch?v=kuDuJWvho7Q'
# visit the target page in the controlled browser
driver.get(url)

try:
    # wait up to 15 seconds for the consent dialog to show up
    consent_overlay = WebDriverWait(driver, 15).until(
        EC.presence_of_element_located((By.ID, 'dialog'))
    )

    # select the consent option buttons
    consent_buttons = consent_overlay.find_elements(By.CSS_SELECTOR, '.eom-buttons button.yt-spec-button-shape-next')
    if len(consent_buttons) > 1:
        # retrieve and click the 'Accept all' button
        accept_all_button = consent_buttons[1]
        accept_all_button.click()
except TimeoutException:
    print('Cookie modal missing')

# wait for YouTube to load the page data
WebDriverWait(driver, 15).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, 'h1.ytd-watch-metadata'))
)

# initialize the dictionary that will contain
# the data scraped from the YouTube page
video = {}

# scraping logic
title = driver \
    .find_element(By.CSS_SELECTOR, 'h1.ytd-watch-metadata') \
    .text

# dictionary where to store the channel info
channel = {}

# scrape the channel info attributes
channel_element = driver \
    .find_element(By.ID, 'owner')

channel_url = channel_element \
              .find_element(By.CSS_SELECTOR, 'a.yt-simple-endpoint') \
              .get_attribute('href')
channel_name = channel_element \
              .find_element(By.ID, 'channel-name') \
              .text
channel_image = channel_element \
              .find_element(By.ID, 'img') \
              .get_attribute('src')
channel_subs = channel_element \
              .find_element(By.ID, 'owner-sub-count') \
              .text \
              .replace(' subscribers', '')

channel['url'] = channel_url
channel['name'] = channel_name
channel['image'] = channel_image
channel['subs'] = channel_subs

# click the description section to expand it
driver.find_element(By.ID, 'description-inline-expander').click()

info_container_elements = driver \
    .find_elements(By.CSS_SELECTOR, '#info-container span')
views = info_container_elements[0] \
    .text \
    .replace(' views', '')
publication_date = info_container_elements[2] \
    .text

description = driver \
    .find_element(By.CSS_SELECTOR, '#description-inline-expander .ytd-text-inline-expander span') \
    .text

likes = driver \
    .find_element(By.ID, 'segmented-like-button') \
    .text

video['url'] = url
video['title'] = title
video['channel'] = channel
video['views'] = views
video['publication_date'] = publication_date
video['description'] = description
video['likes'] = likes

# close the browser and free up the resources
driver.quit()

# export the scraped data to a JSON file
with open('video.json', 'w') as file:
    json.dump(video, file, indent=4)

您可以用大约 100 行代码构建一个抓取 YouTube 视频数据的网络爬虫!

启动脚本,以下video.json文件将出现在您的项目根文件夹中:

{
    "url": "https://www.youtube.com/watch?v=kuDuJWvho7Q",
    "title": "Introduction to Bright Data | Scraping Browser",
    "channel": {
        "url": "https://www.youtube.com/@BrightData",
        "name": "Bright Data",
        "image": "https://yt3.ggpht.com/_Q-FPPjoMEH_3ocfi1lTy1HBwdh7CqUfehS7G9silsQcPZX11yAGffubPO1haKyFtbxKBURT=s48-c-k-c0x00ffffff-no-rj",
        "subs": "4.65K"
    },
    "views": "116",
    "publication_date": "Jun 14, 2023",
    "description": "Welcome to our comprehensive guide on setting up and using Bright Data's Scraping Browser for efficient web data extraction. This video walks you through the process of setting up the Scraping Browser, highlighting its unique features and benefits.\n\n- Introduction to Bright Data's Scraping Browser\n- Navigating the 'Proxies and Scraping Infrastructure' page\n- Creating and Naming Your Scraping Browser\n- Explaining User Interaction, Geo-Restrictions, and IP Rate Limits\n- Breakdown of Costs for Using the Scraping Browser\n- Access Parameters and Their Importance\n- Integration Examples: Puppeteer in Node.js and Playwright in Python\n- Introduction to Web Scraping 'Today's Deals' from Amazon.com\n- Automated Data Extraction Process\n- Statistics of Data Usage\n- Benefits of Automated Web Scraping\n\nWhether you're looking to extract data behind user interactions, dealing with geo-restrictions, or IP rate limits, Bright Data's Scraping Browser provides comprehensive solutions for your needs. In this video, we also delve into a practical demonstration using Puppeteer and Python, illustrating how this browser can help you access and extract data efficiently.\n\n#BrightData #ScrapingBrowser #WebScraping #Puppeteer #Python #Nodejs #Playwright #DataExtraction",
    "likes": "3"
}

恭喜!您刚刚学习了如何用 Python 抓取 YouTube!

结论

在本指南中,您了解了为什么抓取 YouTube 比使用其数据 API 更好。特别是,您看到了一个如何构建 Python 爬虫以获取 YouTube 视频数据的逐步教程。如本文所证,构建并不复杂,只需要几行代码。

同时,YouTube 是一个不断发展的动态平台,因此这里构建的爬虫可能不会永远工作。为了应对目标站点的变化,维护它需要耗费大量时间和精力。这就是我们构建 YouTube Scraper的原因,它是一个可靠且易于使用的解决方案,可让您无忧地获取所有想要的数据!

另外,不要忽视 Google 的反机器人系统。Selenium 是一个很好的工具,但在面对如此先进的技术时无能为力。如果 Google 决定保护 YouTube 免受机器人的侵害,大多数自动化脚本将被切断。如果发生这种情况,您需要一个可以渲染 JavaScript 并能自动处理指纹识别、验证码和反抓取的工具。嗯,它存在,叫做 Scraping Browser

不想处理 YouTube 网页抓取但对项目数据感兴趣?请求一个 YouTube 数据集