YouTube 是网络上访问量第二大的网站,其公开展示的数据对任何从事内容和 SEO 工作的人来说都是极具价值的研究宝库。针对某个搜索词排名靠前的视频标题、掌握某个话题的频道、代表需求信号的播放量、显示热门结果新鲜度的发布日期:这些信息全部可以在公开结果页上看到,都在告诉你受众真正点击的是什么。本指南将演示如何使用 Python 抓取 YouTube 公开数据,并将其转化为可付诸行动的关键词和内容研究。
先明确一点:本文所有内容都限定在来自公开搜索和视频页面的公开数据范围内。你将采集视频标题、频道名称、播放量、发布日期和视频链接。你不会触碰任何需要登录的内容、个人用户评论、私人播放列表,或观看者的个人数据。如果你的项目需要大规模、结构化的授权访问,官方 YouTube Data API 才是正确的工具,文末的合法性部分会解释原因。本文是我们关于 YouTube 频道爬虫深度指南的补充;这里的重点是用于优化研究的搜索与视频层面的数据。
你将构建的内容
一个小型 Python 脚本,接受公开的 YouTube 搜索查询词或视频 URL,通过 Crawling API 使用 JavaScript token 获取完整渲染后的页面,并为每条结果解析以下几个公开的、非个人性质的字段:
- Title 视频标题,告诉你某个话题是如何被定位以获得排名和点击的。
- Channel 发布该视频的频道名称,有助于识别谁掌握着某个话题。
- Views 公开播放量,是某个关键词或主题的粗略需求信号。
- Publish date 相对或绝对的上传日期,显示排名结果的新鲜程度。
- Link 每个视频的标准观看 URL,以便之后回顾或丰富数据。
注意哪些内容是有意缺失的:没有评论者的账号、没有仅限订阅者的内容、没有观看者的个人数据。你收集的是关于内容的聚合信号,而非个人档案。
为什么直接请求在 YouTube 上会失败
向 YouTube 搜索 URL 发送裸 HTTP 请求,你将得到一个技术上成功、实际上却几乎为空的响应。YouTube 在客户端渲染其结果:初始 HTML 是一个薄外壳,真正的视频列表只有在浏览器执行页面 JavaScript 并填充结果后才会出现。此外,YouTube 还会监控自动化流量。数据中心 IP 段、缺乏浏览器行为以及重复的请求模式,在有趣的内容加载之前就会受到挑战或限速。
因此,一个可用的 YouTube 数据爬虫需要在同一次请求中满足两点:一个能真正渲染页面的浏览器,以及一个被平台识别为普通访客的 IP。你可以自行用无头浏览器加轮换住宅代理池来实现,但维护这套方案本身就是主要的工作量。Crawling API 将两者合并为一次调用:你用 JavaScript token 发送 URL,它在可信住宅 IP 背后渲染页面,并返回供你解析的完整 HTML。关于为何渲染很重要,请参阅我们的如何抓取 JavaScript 网站指南。
Crawlbase 提供两种 token 类型。普通 token 获取静态 HTML;JavaScript(JS)token 则先在真实浏览器中渲染页面。YouTube 搜索和视频页面是在客户端渲染的,因此这里需要使用 JS token。普通 token 返回的外壳与直接获取的结果相同,没有可解析的有用内容。
前提条件
首先需要准备好以下几项,每项都不会花太长时间。
Python 基础。 你应该能够运行脚本并使用 pip 安装包。如果你对解析 HTML 还不熟悉,我们的 BeautifulSoup 使用入门指南涵盖了提取方面的内容。
Python 3.8 或更高版本。 使用 python --version 确认。如果尚未安装,请从 python.org 进行安装。
Crawlbase 账号和 JS token。 注册后打开控制台,复制你的 JavaScript(JS)token。免费套餐包含 1,000 次请求,且只对成功的请求收费,对于本指南中的研究运行来说已绰绰有余。请像对待密码一样保护你的 token,不要将其提交到版本控制系统。
搭建项目
创建独立的虚拟环境,然后安装爬虫所需的库。
python --version python -m venv youtube_env source youtube_env/bin/activate pip install crawlbase beautifulsoup4
在 Windows 上,使用 youtube_env\Scripts\activate 替代 source 那行来激活环境。两个依赖各司其职:crawlbase 是 Crawling API 的官方客户端,beautifulsoup4 解析返回的 HTML 以便通过选择器提取各个字段。导出所需的标准库 json 和 csv 模块无需额外安装。
第 1 步:获取渲染后的搜索页面
首先获取完整的页面。导入 CrawlingAPI,用你的 JS token 初始化,然后请求一个公开的搜索结果 URL。将查询词构建到标准的 results?search_query= 路径中,并在解析前检查状态码,确保失败情况显式暴露而非悄无声息。
from urllib.parse import quote_plus from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def crawl(page_url): options = {"ajax_wait": "true", "page_wait": 5000} response = api.get(page_url, options) if response["status_code"] == 200: return response["body"].decode("utf-8") print(f"Request failed: {response['status_code']}") return None def search_url(query): return f"https://www.youtube.com/results?search_query={quote_plus(query)}" if __name__ == "__main__": query = "data scraping tutorial" html = crawl(search_url(query)) print(html[:500] if html else "No HTML returned")
两个等待选项对于客户端渲染的目标至关重要。ajax_wait 告知 API 等待异步内容加载完成,page_wait 在加载后固定等待一段毫秒数,确保延迟渲染的结果列表在页面被捕获前出现。五秒是合理的起点;如果结果返回为空,可以适当增加。查询词沿用原示例("data scraping tutorial"),方便你直接对比输出。运行脚本,你应该看到真实的 YouTube 标记,这证明在编写任何选择器之前,渲染已经正常工作。
YouTube 需要在可信 IP 背后渲染结果页面,在一次调用中完成。Crawling API 接受 JS token,在真实浏览器中运行页面,在服务端轮换住宅 IP,并返回解析就绪的 HTML,因此你无需自行运行无头浏览器集群和代理池。先在免费套餐上对公开搜索查询进行测试。
第 2 步:解析公开字段
拿到渲染后的 HTML,最稳定的信号是页面随脚本一起嵌入的 ytInitialData JSON 对象。它包含 YouTube 用于渲染结果列表的同样字段:标题、频道名称、播放量文本、发布时间文本和视频 ID。解析该对象远比追踪频繁改名的深层嵌套 CSS 类更持久。将 HTML 加载到 BeautifulSoup,找出定义 ytInitialData 的脚本,并从中提取视频渲染器。
import json import re from bs4 import BeautifulSoup def load_initial_data(html): soup = BeautifulSoup(html, "html.parser") for script in soup.find_all("script"): text = script.string or "" if "ytInitialData" in text: match = re.search(r"ytInitialData\s*=\s*(\{.*?\});", text, re.DOTALL) if match: return json.loads(match.group(1)) return {} def text_of(node): if not node: return None if "simpleText" in node: return node["simpleText"] runs = node.get("runs", []) return "".join(r["text"] for r in runs) if runs else None
load_initial_data 辅助函数用非贪婪正则表达式定位 JSON 对象并解析它。text_of 辅助函数处理 YouTube 的两种文本格式:某些字段是普通的 simpleText 字符串,另一些是需要拼接的 runs 列表。有了这两个辅助函数,提取每个视频就变成了简单遍历搜索渲染器的过程。
第 3 步:每个视频提取一条记录
YouTube 将搜索结果嵌套在一长串区域和条目渲染器下。每个可播放的结果都是一个 videoRenderer,包含标题、频道名称(ownerText 或 longBylineText)、viewCountText、publishedTimeText 和 videoId。遍历这个结构,收集所有 videoRenderer,并将每个映射到一条扁平记录。
def find_video_renderers(node, found): if isinstance(node, dict): if "videoRenderer" in node: found.append(node["videoRenderer"]) for value in node.values(): find_video_renderers(value, found) elif isinstance(node, list): for item in node: find_video_renderers(item, found) return found def parse_search(html): data = load_initial_data(html) renderers = find_video_renderers(data, []) results = [] for v in renderers: video_id = v.get("videoId") if not video_id: continue channel = text_of(v.get("ownerText")) or text_of(v.get("longBylineText")) results.append({ "title": text_of(v.get("title")), "channel": channel, "views": text_of(v.get("viewCountText")), "published": text_of(v.get("publishedTimeText")), "link": f"https://www.youtube.com/watch?v={video_id}", }) return results
递归的 find_video_renderers 遍历避免了硬编码确切的嵌套路径(YouTube 会不时调整),它只是收集所有出现的 videoRenderer。每条记录包含你预设要收集的五个公开字段:标题、频道、播放量、发布日期和链接。这些都是内容和需求信号,而非观看者的个人数据。
YouTube 会在不通知的情况下更改其标记和内部字段名,这正是本代码依赖 ytInitialData 对象和渲染器名称而非脆弱的嵌套 CSS 类的原因。当某个字段返回 None 时,请在浏览器开发者工具中重新检查实时页面并更新键名。定期维护对任何生产级爬虫来说都是正常的,并不意味着出了问题。
第 4 步:整合代码并导出 JSON 和 CSV
现在将获取、解析和导出整合到一个可运行的脚本中。它对一组研究查询词依次运行,为每个词收集公开字段,并将结果写入 JSON 文件和 CSV 文件,以便数据直接进入电子表格或 notebook。
import csv import json import time from urllib.parse import quote_plus from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def main(): queries = ["data scraping tutorial", "python web scraping"] rows = [] for query in queries: html = crawl(search_url(query)) if not html: continue for record in parse_search(html)[:10]: record["query"] = query rows.append(record) time.sleep(3) with open("youtube_research.json", "w", encoding="utf-8") as f: json.dump(rows, f, indent=2, ensure_ascii=False) fields = ["query", "title", "channel", "views", "published", "link"] with open("youtube_research.csv", "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fields) writer.writeheader() writer.writerows(rows) print(f"Saved {len(rows)} videos across {len(queries)} queries") if __name__ == "__main__": main()
查询词之间的 time.sleep(3) 不是装饰性的。控制节奏是决定抓取任务能否保持健康的最重要因素,我们稍后会再谈这个问题。切片 [:10] 与原始脚本打印的前 10 条结果保持一致,使演示保持聚焦。将此代码与前面的 crawl、search_url 和 parse_search 函数整合到一个文件中,即可端到端运行。
输出示例
运行完整脚本,你将获得每个视频的干净记录,可按播放量排序、按频道分组,或扫描某个查询词中获胜标题的模式。
[ { "title": "Web Scraping Tutorial | Data Scraping from Websites to Excel", "channel": "Data Analytics", "views": "1.2M views", "published": "2 years ago", "link": "https://www.youtube.com/watch?v=aClnnoQK9G0", "query": "data scraping tutorial" }, { "title": "Beginners Guide To Web Scraping with Python", "channel": "Coding Channel", "views": "480K views", "published": "1 year ago", "link": "https://www.youtube.com/watch?v=QhD015WUMxE", "query": "data scraping tutorial" } ]
播放量和发布时间字符串直接来自 YouTube 的显示文本("1.2M views"、"2 years ago")。若要进行分析,请在后续步骤中对其规范化:去掉"views"并将 M 和 K 后缀展开为整数,将相对日期转换为近似绝对日期。在导出中保留原始字符串意味着你永远不会丢失原始信号。
将数据转化为内容和 SEO 研究
这次抓取的意义不在于原始数据行,而在于它告诉你关于某个话题的信息。几个实用的解读角度:
- 排名靠前的标题模式。 将每个查询词的前几条结果分组,观察获胜标题的措辞:修饰语、方括号、数字、承诺。这就是受众在那个关键词上会点击的语言。
- 按播放量衡量需求。 按播放量排序,可以看到哪些子话题吸引了最多关注。对于较老却没有新竞争者的高播放量视频,往往意味着新内容的机会。
- 内容新鲜度缺口。 发布日期列显示排名结果的发布时间。如果某个查询词被多年前的视频主导,就可能是一个适合发布最新内容的机会。
- 话题归属。 统计每个频道在你的查询词中出现的频率,可以看出谁已经掌握了某个主题,这对竞争分析和合作机会都有参考价值。
这与你更广泛的关键词研究自然配合。如果你在构建研究数据管道,我们关于利用数据改进 SEO 和如何提取和分析 Google SEO 数据的指南涵盖了如何将此类数据源纳入完整的分析图景。
扩展和分页
单个搜索页面返回第一批结果,对于研究来说通常已经足够。如果需要更多深度,运行更广泛的查询词集合,而非试图对单个查询词翻页:相关关键词列表能提供比滚动单个结果集更宽泛的覆盖,也与你实际规划内容的方式更吻合。若要丰富某个特定视频,请用同一个 crawl 函数获取其观看 URL,并解析其页面以获取描述和精确元数据,同样利用嵌入的 JSON 而非脆弱的选择器。
保持抓取量与研究问题成比例。你很少需要一个查询词的所有结果;前几条结果包含了大部分信号,一组精心挑选的小型查询词集合在质量和良好的使用规范方面都优于穷举式抓取。
保持不被封锁
即使有 Crawling API 处理渲染,YouTube 仍然会监视爬虫形态的流量。以下几个习惯有助于保持抓取任务的顺利运行,这些习惯适用于任何受到严格防御的目标。
-
控制请求节奏。 在紧密循环中不断请求页面是被限速最快的方式。添加真实的延迟(如上面的
time.sleep),克制过度并行化的冲动。 - 善用轮换。 住宅 IP 池能将请求分散到许多真实用户地址,使单个 IP 不触发限速。Crawling API 为你处理这个问题;如果你自建技术栈,这是最需要做好的部分。
- 读懂状态码。 如果某次运行开始返回验证挑战或错误,这是在告诉你当前的请求频率或 IP 等级已不够用。选择退后重试,而非加大力度。
- 保持低量且多样化的查询词。 内容研究不需要穷举式抓取 YouTube。对重要的查询词取样并及时停止。
更完整的方法论,请参阅我们的如何在不被封锁的情况下抓取网站指南,以及关于如何用 Python 抓取 JavaScript 页面的深度讲解。
抓取 YouTube 是否合法?
这是在编写生产代码之前必须阅读的部分。YouTube 归 Google 所有,其服务条款限制了自动化访问,并对从平台采集数据设有明确限制。无论你的工具多么谨慎,自动化抓取都可能违反这些条款,上述任何代码都无法改变这一点。它只是使技术层面得以实现。请阅读 YouTube 的服务条款及其 robots.txt,遵守任何速率限制,并将两者视为你所采集内容的边界。
以下是值得严格遵守的规则。只采集任何人无需登录即可看到的公开数据:视频标题、频道名称、播放量、发布日期和视频链接,正是本指南收集的那些聚合的、内容层面的信号。不要抓取任何需要登录的内容、私密或不公开列出的视频、仅限会员的内容,以及个人用户评论及其附带的账号名称。将评论、用户名和任何观看者详情视为个人数据;涉及个人数据时,GDPR 和 CCPA 等隐私法规适用,这意味着你需要合法的处理依据,并且必须响应删除请求。切勿绕过身份验证,也不要下载受版权保护的媒体进行再发布。这些都是明确的界限,本指南在设计上就明确限定在所有这些界限的公开、非个人的一侧。
对于任何真实的、持续的或商业性的用途,正确的工具是官方 YouTube Data API。这是 Google 提供的授权路径,为标题、播放量、频道数据和搜索提供有保障的结构化数据,并通过清晰的配额将你保持在平台条款之内。本文是一篇技术指南,范围严格限定在用于内容和 SEO 研究的公开、非个人数据。它不认可大规模数据采集,也不涵盖需要登录的任何内容。如果你的项目需要的不止是一小批公开字段,Data API 或正式协议才是正确的途径,而非更高明的爬虫。
核心要点
- YouTube 是客户端渲染且有反爬虫防御的。 直接请求返回空外壳,因此必须在解析之前渲染页面。
-
渲染和可信 IP 需要在一次调用中完成。 带 JS token 的 Crawling API 同时满足这两点;
ajax_wait和page_wait控制等待内容的时长。 -
解析嵌入的 JSON。
ytInitialData对象和渲染器名称远比脆弱的嵌套 CSS 类更持久。 - 五个公开字段驱动研究。 标题、频道、播放量、发布日期和链接是内容和需求信号,而非观看者的个人数据。
- 控制节奏、善用轮换,并优先使用 Data API。 保持低量,依赖住宅轮换,并对任何真实或商业性用途使用官方 YouTube Data API。
常见问题
为什么直接请求从 YouTube 返回没有数据?
因为 YouTube 使用 JavaScript 在客户端渲染其搜索和视频内容。初始 HTML 是一个外壳,只有在浏览器执行页面脚本后才会填充内容,因此裸 HTTP 请求返回的正文几乎为空。要获取真实的公开结果,必须先渲染页面,这正是 Crawling API 的 JS token 为你处理的事情。
YouTube 需要普通 token 还是 JS token?
JS token。普通 token 获取静态 HTML,在 YouTube 上与直接请求返回的空外壳相同。JS token 在将 HTML 返回之前先在真实浏览器中渲染页面,因此嵌入的 ytInitialData 对象及其描述的结果在你解析时都已存在。
哪些 YouTube 数据可以安全地用于 SEO 研究?
公开的、非个人的、内容层面的字段:来自公开搜索和视频页面的视频标题、频道名称、公开播放量、发布日期和视频链接。这些是告诉你某个话题如何排名以及受众点击什么的信号。个人用户评论及其附带的账号名称、私密或仅限会员的内容,以及任何需要登录的内容都超出范围,因为它们属于个人数据或被平台条款限制。
如何将抓取的数据转化为关键词研究?
按查询词对结果分组并研究规律。前几条结果的标题措辞显示了某个关键词的排名语言;播放量按需求对子话题排序;发布日期揭示你可以填补的内容新鲜度缺口;频道频率显示了谁已经掌握了某个主题。导出为 CSV,分析就是几次电子表格排序的事。
我应该使用官方 YouTube Data API 还是抓取网站?
对于任何真实的、持续的或商业性的用途,请使用官方 YouTube Data API。它是授权路径,提供有保障的结构化数据,包含明确的配额,并将你保持在 Google 条款之内。在没有 API 访问权限的情况下,用这里描述的方法对一小批公开字段进行轻量级一次性内容研究是合适的,前提是遵守条款、robots.txt 和速率限制。
如何避免在抓取 YouTube 时被封锁?
保持每个 IP 的请求频率低,在请求之间添加真实的延迟,多样化你的查询词而非穷举式抓取一个,并通过轮换住宅 IP 路由,使单个地址不触发限速。Crawling API 为你管理轮换和可信 IP 池。密切关注状态码,一旦开始看到验证挑战就立即退后重试。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
