【技术分享】从零构建YouTube评论爬虫:TKinter + Pandas + 反反爬全解析

本文对应仓库:https://github.com/mashukui/ytb_cmt_spider

引言

YouTube 作为全球最大的视频平台,其评论区蕴藏着大量用户反馈和舆情数据。然而 YouTube 官方 API 有严格的配额限制,每天仅提供 10,000 单位的配额,对于大规模数据采集需求来说远远不够。

本文将详细介绍如何利用 Python 构建一个完整的、可直接交付用户的 YouTube 评论采集工具,涵盖 GUI 界面、数据流水线、软件保护与授权管理等关键技术点。

技术架构总览

复制代码
┌─────────────────────────────────────────────────┐
│                  TKinter GUI                     │
│  ┌──────────┐  ┌──────────┐  ┌───────────────┐  │
│  │ 登录窗口  │  │ 参数配置  │  │ 实时日志输出    │  │
│  └──────────┘  └──────────┘  └───────────────┘  │
├─────────────────────────────────────────────────┤
│                业务逻辑层                         │
│  ┌──────────┐  ┌──────────┐  ┌───────────────┐  │
│  │ 用户认证  │  │ 评论爬取  │  │ 数据转换导出   │  │
│  └──────────┘  └──────────┘  └───────────────┘  │
├─────────────────────────────────────────────────┤
│                 数据存储层                        │
│  ┌──────────┐  ┌──────────┐  ┌───────────────┐  │
│  │  MySQL   │  │   JSON   │  │  CSV / XLSX   │  │
│  │ (远程认证) │  │ (临时存储) │  │  (最终输出)    │  │
│  └──────────┘  └──────────┘  └───────────────┘  │
└─────────────────────────────────────────────────┘

软件界面概览

一、GUI 层:TKinter 双窗口设计

整个应用采用双窗口模式 :登录窗口 → 主工作窗口,两者通过 root_login.destroy()create_spider_root() 实现平滑切换。

1.1 登录窗口

相关代码:

python 复制代码
def create_login_root():
    root_login = tk.Tk()
    root_login.title('爬油管评论软件v3.3')
    root_login.minsize(width=400, height=300)

    # 记住上次登录的用户信息
    if os.path.exists(cfg_path + 'userinfo.txt'):
        with open(cfg_path + 'userinfo.txt', 'r') as f:
            userinfos = f.readlines()
            entry_username.insert(0, str(userinfos[0]).strip())
            entry_password.insert(0, str(userinfos[1]).strip())

这里有几个设计细节值得注意:

  • 凭据持久化 :将用户名和密码明文写入本地 userinfo.txt,下次启动时自动填充,减少用户重复输入。虽然从安全角度看明文存储不够严谨,但对于面向非技术用户的桌面工具,这是一个务实的取舍。
  • 配置文件隐藏 :配置路径选择在 node_modules/jsdom/lib/jsdom/living/xhr/ 下,利用 node_modules 目录"不会被普通用户轻易翻看"的特点来做简单隐藏。

1.2 主工作窗口

主窗口使用 ttk.LabelFrame 进行分区布局,核心控件包括:

控件 类型 用途
comment_num tk.Spinbox 爬取数量,-1 表示全量
order_type ttk.Combobox 排序方式(按日期/按热门)
txt_msglist tk.Text + Scrollbar 实时滚动日志

1.3 多线程防止 UI 冻结

爬取任务是 IO 密集型操作,如果在主线程中执行会导致界面卡死。这里自定义了 MyThread 类:

python 复制代码
class MyThread(threading.Thread):
    def __init__(self, func, *args):
        super().__init__()
        self.func = func
        self.args = args
        self.setDaemon(True)   # 守护线程,主程序退出时自动销毁
        self.start()           # 创建即启动

    def run(self):
        self.func(*self.args)

将耗时任务放入子线程,通过 setDaemon(True) 确保主窗口关闭时爬取线程不会成为僵尸进程。线程间的 UI 更新通过共享的 txt_msglist Text 控件完成,使用 delete + insert 的方式刷新日志内容。

二、爬虫核心:第三方库的使用与优化

2.1 基础调用

python 复制代码
from youtube_comment_downloader import YoutubeCommentDownloader, SORT_BY_POPULAR, SORT_BY_RECENT

downloader = YoutubeCommentDownloader()
comments = downloader.get_comments_from_url(
    'https://www.youtube.com/watch?v={}'.format(video_id),
    sort_by=SORT_BY_RECENT  # 或 SORT_BY_POPULAR
)

youtube_comment_downloader 是一个轻量级的第三方库,它模拟浏览器请求直接从 YouTube 页面抓取评论,不需要 YouTube Data API key,也因此不受API配额限制。这是选择它而非官方API的核心原因。

2.2 数量控制与惰性求值

python 复制代码
from itertools import islice

if self.comment_num != '-1':
    comment_num2 = int(self.comment_num)
    comments = islice(comments, comment_num2)  # 取出 TOP N

这里使用了 itertools.islice 而非直接切片。因为 get_comments_from_url 返回的是生成器 ,数据按需加载。islice 在达到指定数量后直接停止迭代,不会浪费流量拉取多余的评论,这对于网络请求来说至关重要------每一条额外的评论都是一次 HTTP 往返。

2.3 逐条落盘策略

python 复制代码
for comment in comments:
    with open('./jsons/{}.json'.format(video_id), 'a+', encoding='utf-8') as f:
        f.write(json.dumps(comment, ensure_ascii=False))
        f.write('\n')

关键设计决策:边爬边写 。如果将评论全部收集到内存中再一次写入,一旦程序中途崩溃,之前爬取的数据将全部丢失。逐条追加写入的方式实现了断点续传的效果------即使程序异常退出,已写入的 JSON 行也不会丢失。

这里每条评论写成一行 JSON(JSONL 格式),便于后续用 pd.read_json(lines=True) 快速读取。

三、数据流水线:JSON → CSV → XLSX

3.1 JSONL 到 DataFrame

python 复制代码
df = pd.read_json('./jsons/{}.json'.format(video_id), lines=True)
df.drop(['photo', 'heart', 'reply'], axis=1, inplace=True)
if 'paid' in df.columns:
    df.drop(['paid'], axis=1, inplace=True)

原始评论数据中包含了 photo(头像)、heart(点赞)、reply(回复内容)等字段,这些对大多数分析场景无用,而且 reply 是嵌套结构,直接导出到二维表格会引发格式问题,因此提前剔除。

特别注意 paid 字段的处理:它只在某些特定评论中出现(如付费置顶评论),如果直接 drop 可能导致 KeyError,所以使用 if 'paid' in df.columns 先做判断。

3.2 时间戳转换的坑

python 复制代码
def trans_time(self, v_timestamp):
    """10位时间戳转换为时间字符串"""
    v_timestamp = int(str(v_timestamp)[:10])
    timeArray = time.localtime(v_timestamp)
    otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
    return otherStyleTime

df['time2'] = df['time_parsed'].apply(
    lambda x: self.trans_time(x) if pd.notna(x) else x
)

这里有两个细节值得展开:

  1. 为什么取前10位? YouTube 返回的时间戳可能是毫秒级(13位)也可能是秒级(10位),[:10] 统一截断为 10 位秒级时间戳。
  2. 为什么要判断 pd.notna(x) 某些评论的 time_parsed 字段可能为 NaN,此时 int(str(x)[:10]) 会抛出 ValueError: invalid literal for int() with base 10: 'nan'。v3.3 版本正是修复了这个 bug。

3.3 追加模式写入 CSV

python 复制代码
if os.path.exists(self.result_file1):
    header = False
else:
    header = True
df.to_csv(self.result_file1, index=False, mode='a+', encoding='utf_8_sig', header=header)

多个视频的评论写入同一个 CSV 文件时,只在第一次写入时写表头 ,后续写入仅追加数据行。utf_8_sig 编码确保 Excel 打开时中文不乱码。

3.4 CSV → XLSX 最终输出

python 复制代码
df2 = pd.read_csv(self.result_file1)
df2['replies'].fillna(0, inplace=True)
df2.to_excel(self.result_file2, index=False)

replies(回复数)字段的空值填充为 0,使数据在 Excel 中展示更整洁。

四、软件保护与授权

授权逻辑采用三层验证

  1. 用户凭证验证:用户名 + 密码匹配
  2. 有效期验证 :当前时间在 end_time 之前
  3. 机器码绑定:首次登录时绑定 CPU 序列号,后续登录验证机器码一致性,防止账号共享

获取 CPU 序列号的方式跨平台兼容:

  • Windows:通过 wmi 库读取 Win32_Processor.ProcessorId
  • macOS:通过 system_profiler SPHardwareDataType 命令提取 Serial Number

五、日志系统:按天滚动

python 复制代码
info_handler = TimedRotatingFileHandler(
    filename=case_dir + info_file_name,
    when='MIDNIGHT',
    interval=1,
    backupCount=7,
    encoding='utf-8'
)

使用 TimedRotatingFileHandler 而非 RotatingFileHandler,以时间为维度而非文件大小来切割日志。配置为每天午夜滚动一次,保留最近 7 天的日志。这样做的好处是出问题时可以快速定位到当天的日志文件,不会因为文件过大而难以排查。

总结

本文完整介绍了YouTube评论采集工具的工程实现,涵盖了从 TKinter GUI 到数据流水线再到软件保护的各个环节。核心的技术选型思路是用轻量级爬虫库替代官方 API,从而绕开配额限制实现大规模采集。代码虽然体量不大(不到 500 行),但包含了桌面应用开发中常见的关键模式:多线程调度、流式数据处理、跨平台兼容、以及商业软件的授权管理,希望对有类似需求的开发者有所启发。

更多交流或疑问,可前往 GitHub仓库Issue区 进行讨论。


作者声明:本文涉及的完整代码和数据仅供技术学习交流,请遵守目标网站的使用条款和相关法律法规。

相关推荐
zhougl9966 小时前
Java实现腾讯云开发者社区列表爬虫
java·爬虫·腾讯云
Experience-摆渡1 天前
Firecrawl深度调研报告:功能、实现原理、架构与自研落地建议
爬虫
深蓝电商API1 天前
Hook XMLHttpRequest 实战
爬虫·hook
quantdash_cc2 天前
告别自建 Requests/BS4 网页爬虫:基于 QuantDash 搭建零维保的高性能量化行情流水线
开发语言·爬虫·python·pandas·量化·quantdash
Patrick在香港2 天前
Python爬虫框架实战:优雅抓取香港政府公开API数据的通用方案
java·开发语言·爬虫·python·ai编程·数据可视化·可用性测试
鬼手点金3 天前
与LLM结合的主流智能爬虫框架
爬虫·python·llm·post·request·firecrawl·crawl4ai
天启HTTP4 天前
爬虫频繁弹验证码?解析网站反爬检测机制
网络·爬虫·tcp/ip
深蓝电商API4 天前
如何快速定位加密函数?
爬虫·加密函数