前言

本教程将通过调用 YouTube Data API 获取频道的上传视频列表,并提取每个视频的标题和链接。最终,数据将被保存到 Excel 文件中。

  1. 打开Google Cloud Console
  2. 创建一个新的项目,或者选择一个现有项目。
  3. 在左侧导航栏中,选择 API 和服务 > 启用API和服务
  4. 搜索并启用 YouTube Data API v3
  5. 创建API密钥:
    • API和服务 > 凭据 中,点击 创建凭据,选择 API密钥
    • 保存生成的API密钥,你将需要在程序中使用它。

如何获取 YouTube 频道 ID

代码

使用教程:

  1. 设备配置好python环境
  2. 安装第三方库
1
pip install requests pandas openpyxl

windows的话:新建文本文档,改后缀为.py,复制下面的代码保存退出,双击运行,输入api和id,点击提交。会在脚本同级目录下生成youtube_video_links.xlsx

仅提取链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import requests
import pandas as pd
import time
import tkinter as tk
from tkinter import simpledialog

# 获取上传视频的播放列表 ID
def get_uploads_playlist_id(api_key, channel_id):
url = 'https://www.googleapis.com/youtube/v3/channels'
params = {
'key': api_key,
'id': channel_id,
'part': 'contentDetails'
}
response = requests.get(url, params=params)
data = response.json()

if 'items' in data and len(data['items']) > 0:
return data['items'][0]['contentDetails']['relatedPlaylists']['uploads']
else:
print("无法获取上传视频的播放列表 ID。")
return None

# 获取播放列表中的所有视频链接
def get_video_urls_from_playlist(api_key, playlist_id):
base_url = 'https://www.googleapis.com/youtube/v3/playlistItems'
video_data = [] # 用于存储视频链接
next_page_token = None
retries = 3 # 设置重试次数

while True:
params = {
'key': api_key,
'playlistId': playlist_id,
'part': 'snippet',
'maxResults': 50, # 每次请求最多可获取 50 个视频
'pageToken': next_page_token
}

for attempt in range(retries):
try:
response = requests.get(base_url, params=params)
response.raise_for_status() # 检查 HTTP 请求是否成功
break # 请求成功则退出重试循环
except requests.exceptions.RequestException as e:
print(f"请求失败(尝试 {attempt + 1}/{retries} 次):{e}")
time.sleep(2) # 等待一段时间后重试
else:
print("所有重试均失败,停止请求。")
return video_data

data = response.json()

if 'error' in data:
print(f"API 错误:{data['error'].get('message', '未知错误')}")
break

# 提取视频链接
for item in data.get('items', []):
video_id = item['snippet']['resourceId']['videoId']
video_data.append(f"https://www.youtube.com/watch?v={video_id}") # 只添加视频链接

print(f"已提取视频数量:{len(video_data)}")

next_page_token = data.get('nextPageToken')
if not next_page_token:
break

time.sleep(1) # 延时,避免请求过快

return video_data

# 将视频链接保存到 Excel 文件
def save_to_excel(video_data):
df = pd.DataFrame(video_data, columns=["Video URL"]) # 只有视频链接一列
df.to_excel("youtube_video_links.xlsx", index=False, engine='openpyxl')
print("视频链接已成功输出到 'youtube_video_links.xlsx'")

# 主程序:创建输入框弹窗
def main():
# 创建Tkinter根窗口
root = tk.Tk()
root.title("批量提取YouTube视频信息") # 设置窗口标题
root.geometry("450x250") # 设置窗口大小,增加一些空间

# 设置窗口背景颜色
root.config(bg="#f4f4f9")

# 创建框架,用于放置输入框和标签
frame = tk.Frame(root, bg="#f4f4f9")
frame.pack(pady=20)

# 设置标签和输入框
label_font = ('Arial', 10, 'bold')
entry_font = ('Arial', 10)

tk.Label(frame, text="请输入API密钥:", font=label_font, bg="#f4f4f9", anchor="w").grid(row=0, column=0, padx=20, pady=10, sticky="w")
api_key_entry = tk.Entry(frame, font=entry_font, width=35)
api_key_entry.grid(row=0, column=1, padx=10, pady=10)

tk.Label(frame, text="请输入频道ID:", font=label_font, bg="#f4f4f9", anchor="w").grid(row=1, column=0, padx=20, pady=10, sticky="w")
channel_id_entry = tk.Entry(frame, font=entry_font, width=35)
channel_id_entry.grid(row=1, column=1, padx=10, pady=10)

# 创建按钮框架
button_frame = tk.Frame(root, bg="#f4f4f9")
button_frame.pack(pady=20)

# 设置按钮
def on_submit():
api_key = api_key_entry.get()
channel_id = channel_id_entry.get()

if api_key and channel_id:
# 获取上传视频的播放列表 ID
playlist_id = get_uploads_playlist_id(api_key, channel_id)
if playlist_id:
video_data = get_video_urls_from_playlist(api_key, playlist_id)

if video_data:
save_to_excel(video_data)
print(f"总共提取到的视频数量:{len(video_data)}")
else:
print("没有提取到任何视频链接")
else:
print("无法获取播放列表 ID")
else:
print("API密钥和频道ID不能为空")

root.quit() # 关闭窗口

def on_cancel():
root.quit() # 关闭窗口

submit_button = tk.Button(button_frame, text="提交", command=on_submit, font=('Arial', 10), width=12, bg="#4CAF50", fg="white", relief="raised")
submit_button.pack(side="left", padx=15)

cancel_button = tk.Button(button_frame, text="取消", command=on_cancel, font=('Arial', 10), width=12, bg="#f44336", fg="white", relief="raised")
cancel_button.pack(side="left", padx=15)

# 启动Tkinter事件循环
root.mainloop()

# 等待用户手动退出
input("程序运行完毕,请按回车键退出...")

if __name__ == "__main__":
main()

视频标题+链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import requests
import pandas as pd
import time
import tkinter as tk
from tkinter import simpledialog

# 获取上传视频的播放列表 ID
def get_uploads_playlist_id(api_key, channel_id):
url = 'https://www.googleapis.com/youtube/v3/channels'
params = {
'key': api_key,
'id': channel_id,
'part': 'contentDetails'
}
response = requests.get(url, params=params)
data = response.json()

if 'items' in data and len(data['items']) > 0:
return data['items'][0]['contentDetails']['relatedPlaylists']['uploads']
else:
print("无法获取上传视频的播放列表 ID。")
return None

# 获取播放列表中的所有视频
def get_video_urls_from_playlist(api_key, playlist_id):
base_url = 'https://www.googleapis.com/youtube/v3/playlistItems'
video_data = [] # 用于存储视频链接和标题
next_page_token = None
retries = 3 # 设置重试次数

while True:
params = {
'key': api_key,
'playlistId': playlist_id,
'part': 'snippet',
'maxResults': 50, # 每次请求最多可获取 50 个视频
'pageToken': next_page_token
}

for attempt in range(retries):
try:
response = requests.get(base_url, params=params)
response.raise_for_status() # 检查 HTTP 请求是否成功
break # 请求成功则退出重试循环
except requests.exceptions.RequestException as e:
print(f"请求失败(尝试 {attempt + 1}/{retries} 次):{e}")
time.sleep(2) # 等待一段时间后重试
else:
print("所有重试均失败,停止请求。")
return video_data

data = response.json()

if 'error' in data:
print(f"API 错误:{data['error'].get('message', '未知错误')}")
break

# 提取视频链接和标题
for item in data.get('items', []):
video_id = item['snippet']['resourceId']['videoId']
title = item['snippet']['title'] # 获取视频标题
video_data.append([title, f"https://www.youtube.com/watch?v={video_id}"])

print(f"已提取视频数量:{len(video_data)}")

next_page_token = data.get('nextPageToken')
if not next_page_token:
break

time.sleep(1) # 延时,避免请求过快

return video_data

# 将视频链接和标题保存到 Excel 文件
def save_to_excel(video_data):
df = pd.DataFrame(video_data, columns=["Title", "Video URL"]) # 两列:视频标题和视频链接
df.to_excel("youtube_video_links.xlsx", index=False, engine='openpyxl')
print("视频链接和标题已成功输出到 'youtube_video_links.xlsx'")

# 主程序:创建输入框弹窗
def main():
# 创建Tkinter根窗口
root = tk.Tk()
root.title("批量提取YouTube视频信息") # 设置窗口标题
root.geometry("450x250") # 设置窗口大小,增加一些空间

# 设置窗口背景颜色
root.config(bg="#f4f4f9")

# 创建框架,用于放置输入框和标签
frame = tk.Frame(root, bg="#f4f4f9")
frame.pack(pady=20)

# 设置标签和输入框
label_font = ('Arial', 10, 'bold')
entry_font = ('Arial', 10)

tk.Label(frame, text="请输入API密钥:", font=label_font, bg="#f4f4f9", anchor="w").grid(row=0, column=0, padx=20, pady=10, sticky="w")
api_key_entry = tk.Entry(frame, font=entry_font, width=35)
api_key_entry.grid(row=0, column=1, padx=10, pady=10)

tk.Label(frame, text="请输入频道ID:", font=label_font, bg="#f4f4f9", anchor="w").grid(row=1, column=0, padx=20, pady=10, sticky="w")
channel_id_entry = tk.Entry(frame, font=entry_font, width=35)
channel_id_entry.grid(row=1, column=1, padx=10, pady=10)

# 创建按钮框架
button_frame = tk.Frame(root, bg="#f4f4f9")
button_frame.pack(pady=20)

# 设置按钮
def on_submit():
api_key = api_key_entry.get()
channel_id = channel_id_entry.get()

if api_key and channel_id:
# 获取上传视频的播放列表 ID
playlist_id = get_uploads_playlist_id(api_key, channel_id)
if playlist_id:
video_data = get_video_urls_from_playlist(api_key, playlist_id)

if video_data:
save_to_excel(video_data)
print(f"总共提取到的视频数量:{len(video_data)}")
else:
print("没有提取到任何视频链接")
else:
print("无法获取播放列表 ID")
else:
print("API密钥和频道ID不能为空")

root.quit() # 关闭窗口

def on_cancel():
root.quit() # 关闭窗口

submit_button = tk.Button(button_frame, text="提交", command=on_submit, font=('Arial', 10), width=12, bg="#4CAF50", fg="white", relief="raised")
submit_button.pack(side="left", padx=15)

cancel_button = tk.Button(button_frame, text="取消", command=on_cancel, font=('Arial', 10), width=12, bg="#f44336", fg="white", relief="raised")
cancel_button.pack(side="left", padx=15)

# 启动Tkinter事件循环
root.mainloop()

# 等待用户手动退出
input("程序运行完毕,请按回车键退出...")

if __name__ == "__main__":
main()

运行结果

仅提取视频链接

运行情况

爬取结果

提取视频链接和标题

运行情况

结果

配额计算:

YouTube Data API v3 的配额是按请求数量来计算的。不同类型的请求消耗的配额也不同。以下是常见请求的配额消耗情况:

  1. channels.list 请求(用于获取频道详细信息):
    • 消耗 1 单位配额。
  2. playlistItems.list 请求(用于获取播放列表中的视频):
    • 每个请求消耗 1 单位配额(最多获取 50 个视频链接)。即使每次请求返回最多 50 个视频,仍然是 1 单位配额。
  3. search.list 请求(用于获取基于某些条件的搜索结果):
    • 每个请求消耗 100 单位配额。

假设您提取了 737 个视频链接,每次请求返回最多 50 个视频:

  • 总请求数 = 737 / 50 ≈ 15 请求
  • 每次请求消耗 1 单位配额,因此总配额消耗约为 15 单位

  • 15 单位 是针对所有 playlistItems.list 请求的配额消耗。如果每次请求成功且配额充足,您应该不会超过配额限制。

  • YouTube API 的免费配额每日为 10,000 单位,即使您提取多个频道的视频链接,通常也不会轻易用完这个配额。

playlistItems.list请求测试

仅提取视频链接

第一次测试,提取737个

配额使用用情况

再次提取737个视频后

image

视频链接+标题

同样提取737个视频,怎加标题提取后额度消耗翻倍

image

按照每天10000个配额,理论上如果只是提取视频链接,每天可以提取50万个视频;如果提取视频链接+视频标题,每天可以提取25万个。