从Python爬虫到热度分析:短视频平台数据采集全链路实战

发布时间:2026/10/5 11:08:49
从Python爬虫到热度分析:短视频平台数据采集全链路实战 说实话干数据采集这行差不多六年了被问得最多的一句话就是“短视频平台那些播放量、点赞数都是公开的怎么才能批量拿下来做分析”单独要某一个视频的数据并不难难的是把成百上千条内容稳定地抓下来、清洗成统一结构、再算出一个能指导运营决策的热度值。这篇内容就围绕“短视频平台数据的爬虫采集与热度分析”整套链路展开从需求拆解、技术选型到代码实现、指标建模把我在实际项目里踩过的坑和最终沉淀下来的方案一次性聊清楚。如果你正准备做内容投放、竞品监控、账号矩阵分析或者单纯想练手 Python 爬虫这套思路可以直接抄作业。1. 开篇之前先把需求拆透你采集短视频数据到底为了什么1.1 从业务场景倒推数据指标我接触过的绝大多数项目拿到手的原始需求都类似“我想监控一下竞品账号的数据”。但这个描述太宽泛了直接开干十有八九会做成一堆没用的数。先想清楚场景数据指标才谈得上设计。常见的短视频数据采集场景大致有三类内容运营复盘运营人员想知道自己账号哪些视频有爆款潜力需要持续采集发布后的播放、点赞、评论、转发数据观察涨势。竞品监控与分析盯住同类账号的内容节奏看对方发的什么选题哪些内容互动突出从而调整自己的选题方向。投流与选品参考投放人员需要估算某个视频的传播价值判断该不该加热、跟不跟量这时候热度指标就成了核心决策依据。你会发现同样是采集数据三类场景关注的重点完全不同。运营复盘更看重“趋势变化”竞品监控更看重“内容特征对比”投流更看重“热度绝对值与性价比”。所以第一步根本不是写爬虫而是把指标定义清楚再反推需要采集哪些字段。1.2 “热度”不是一个数而是一组指标很多人一听到“热度分析”就觉得要算一个综合评分。短视频平台本身没有公开的热度分我们只能用量化手段逼近它。我通常把热度拆成三个层次基础热度播放量、点赞量、评论量、转发量。这四个数是最直观的腰封指标。互动质量点赞率点赞/播放、评论率评论/播放、转发率转发/播放。同一个播放量下互动率越高说明内容质量越好越容易进推荐池。传播趋势单位时间内的增速比如发出24小时内的平均每小时播放增长量。爆款视频和普通视频最重要的区别就是增长速度而不是最终总数。把这三个层次想明白了你要爬的字段就清晰了视频ID、标题、发布时间、播放数、点赞数、评论数、转发数、收藏数、作者信息、采集时间。这样每一条记录才具备分析意义。1.3 数据采集合规红线入门就先划清这部分容易被忽略但我建议所有想长期做数据采集的人第一课就学这个。合法合规的采集有几个基本前提只采集平台公开展示的数据不碰需要登录后才能看的非公开内容不采集用户个人敏感信息手机号、私信、通讯录这类控制请求频率不冲击目标服务器不做破解、绕过验证机制的行为收集到的数据仅用于个人学习、行业分析或正当商业用途。这些都是我执行项目的默认底线。了解清楚边界之后再谈技术项目的寿命才能长久。上面的规则也决定了下面的技术方案为什么我不优先考虑复杂的登录态采集、高并发采集原因就在这里。2. 技术选型requests XPath 的组合为什么够用2.1 技术栈清单与各自分工很多新手一上来就想着上 Selenium、上 Playwright觉得能渲染 JavaScript 的浏览器自动化工具才靠谱。但实际做公开页面数据采集Python 的 requests 配合 lxml 就够了。先说结论我常用的技术栈是Python 3.10主语言生态成熟写起来快。requests发起 HTTP 请求比 urllib 好用得多支持会话保持、超时控制、重试机制。lxml解析 HTML用 XPath 定位节点速度和稳定性都比正则表达式强很多。pandas清洗数据、做初步统计分析。SQLite / MySQL持久化存储前者适合个人项目后者适合团队协作。可视化部分则用 matplotlib 或直接把数据导入到 BI 工具根据需要来。这个组合的好处是轻量、可控、不烧机器。短视频平台的 Web 页面在 PC 端往往还是服务端渲染部分数据的播放数、点赞数这类指标可以通过接口或页面标签拿到并不一定需要完整的浏览器环境。2.2 页面结构分析与 XPath 定位重点谈 text()写爬虫最花时间的不是请求代码而是页面结构分析。短视频平台的前端页面结构虽然各家不同但规律比较接近视频详情页里会有标题、作者信息、数据统计区域。我们拿到 HTML 源码后先不要急着写代码先用浏览器开发者工具定位目标元素确认数据在哪个标签下。这里就要说到热搜词里的“python xpath 爬虫 text函数”。XPath 里面text()的核心作用是帮你精确匹配“某个文本内容正好是什么”的节点。我用一个很典型的场景说明假设页面上有一段结构div classvideo-stats span classstat-item播放/span span classstat-num12.3万/span span classstat-item点赞/span span classstat-num4567/span /div如果用//div[classvideo-stats]//span去取一下会把“播放”“12.3万”“点赞”“4567”全部取出来很难对应。这时候用text()函数做锚点play_label tree.xpath(//span[contains(class, stat-item) and text()播放])找到“播放”这个标签后再取它旁边的兄弟节点就能稳定拿到播放数play_count tree.xpath(//span[contains(class, stat-item) and text()播放]/following-sibling::span[contains(class, stat-num)]/text())following-sibling::是 XPath 里的兄弟轴表示当前节点后面同级的节点。这种“先用text()锚定语义标签再取邻近数值”的思路比单纯匹配 class 名要稳得多因为页面改版时数值标签的 class 经常变但“播放”“点赞”这种文案一般不变。2.3 请求策略UA、Cookie、频率控制与错误重试不懂请求策略的人写爬虫通常写完就挂因为你第一次请求可能就触发了风控。我的基础请求头至少包含这几项HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9, Referer: https://www.example.com/, }User-Agent 最好维护一个小池子随机轮换。原因很简单一个固定的 UA 发出高频请求太容易被识别成脚本。至于 Cookie不同的平台要求不一。我只采集完全公开的页面一般不需要登录 Cookie如果某些页面必须带基础 Cookie 才能请求成功我也会确认是否有权限访问该公开内容再在代码里维护。频率控制是另一个容易被忽略的点。我的经验是单 IP 请求间隔不要低于 2 秒采集任务量大的时候随机在 2 到 5 秒之间波动。频繁请求看似效率高实际上等你触发临时封禁再处理浪费的时间远多于省下来的时间。请求失败也很常见代码里要做指数退避重试第一次失败等 1 秒、第二次等 2 秒、第三次等 4 秒最多重试 3 次就放弃避免死循环。3. 采集模块实操从单条数据到批量链路3.1 字段设计一条短视频数据记录什么采集代码开工前建议先把数据表结构定下来。我常用的表结构大概长这样CREATE TABLE video_stats ( video_id TEXT PRIMARY KEY, title TEXT, author_name TEXT, publish_time TIMESTAMP, play_count BIGINT, like_count BIGINT, comment_count BIGINT, share_count BIGINT, collect_count BIGINT, collect_time TIMESTAMP );video_id一定要当主键同一个视频多次采集后用于更新数据。collect_time记录本次采集时间这列是为热度趋势分析准备的没有它你只能看到最终数据看不出增长速度。3.2 核心代码请求、解析、清洗、落库一次跑通直接上一段我在实战中用过的基础框架可以当模板改import requests from lxml import etree import pandas as pd import time import random import sqlite3 from datetime import datetime HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9, Referer: https://www.example.com/, } def fetch_html(url: str) - str: 请求页面 HTML带超时和重试。 for attempt in range(3): try: resp requests.get(url, headersHEADERS, timeout8) resp.raise_for_status() resp.encoding resp.apparent_encoding return resp.text except requests.RequestException as e: print(f请求失败 {url}第 {attempt 1} 次重试{e}) time.sleep(attempt 1) return def parse_video_info(html: str) - dict: 从详情页解析出核心字段。 tree etree.HTML(html) # 标题 title tree.xpath(//h1[contains(class, video-title)]/text()) title title[0].strip() if title else # 播放数利用 text() 锚定“播放”标签 play_count tree.xpath( //span[contains(class, stat-item) and text()播放]/ following-sibling::span[contains(class, stat-num)]/text() ) play_count parse_number(play_count[0]) if play_count else 0 # 点赞数其他字段同理 like_count tree.xpath( //span[contains(class, stat-item) and text()点赞]/ following-sibling::span[contains(class, stat-num)]/text() ) like_count parse_number(like_count[0]) if like_count else 0 return { title: title, play_count: play_count, like_count: like_count, } def parse_number(text: str) - int: 把 12.3万、4567 这类展示文本转成整数。 text text.strip() if text.endswith(万): return int(float(text[:-1]) * 10000) if text.endswith(亿): return int(float(text[:-1]) * 100000000) return int(text.replace(,, )) def save_to_sqlite(data_list: list, db_path: str video_data.db): conn sqlite3.connect(db_path) conn.execute( CREATE TABLE IF NOT EXISTS video_stats ( video_id TEXT PRIMARY KEY, title TEXT, play_count INTEGER, like_count INTEGER, collect_time TIMESTAMP ) ) for item in data_list: conn.execute( INSERT OR REPLACE INTO video_stats (video_id, title, play_count, like_count, collect_time) VALUES (?, ?, ?, ?, ?) , ( item[video_id], item[title], item[play_count], item[like_count], datetime.now().isoformat(), ), ) conn.commit() conn.close() if __name__ __main__: # 示例批量采集视频列表页里的几个链接 video_urls [ https://www.example.com/video/1001, https://www.example.com/video/1002, ] results [] for url in video_urls: html fetch_html(url) if html: info parse_video_info(html) if info[title]: results.append(info) time.sleep(random.uniform(2, 5)) save_to_sqlite(results) print(f采集完成共保存 {len(results)} 条记录)这里有两个细节值得说。第一个是解码问题我在代码里主动设置了resp.encoding resp.apparent_encoding。不设置的话某些页面返回的是gbk编码requests 默认按utf-8解析你的标题字段就会变成乱码。遇到乱码不要先怀疑 XPath 写错先查编码这是顺序问题。第二个是数字清洗。短视频平台页面显示的“12.3万”“1.2亿”看起来可读但没法直接参与计算。我在parse_number里做了统一转换“万”乘10000“亿”乘100000000再转成整数这样后面热度评分时才能直接做数学运算。3.3 增量采集与去重逻辑做监控项目最怕的是每次全量采集不仅慢还会把数据库撑爆。增量采集的思路很简单只去采集上次采集之后新增或发生变化的视频。具体做法是启动任务时先查一下数据库里最新的collect_time和已有的video_id列表然后只对超出这个范围的内容发起请求。另一种思路是定时采集同一批视频的更新数据。每天固定时间跑一遍用INSERT OR REPLACE更新已有记录保留新的collect_time。这样数据库里每个视频有多条历史快照为后面的热度趋势分析提供了原材料。去重这块除了数据库主键约束我还会在采集列表中加一层内存去重。比如从列表页解析出视频链接后先和数据库现有 ID 比对不在集合里的才进入详情页采集队列避免重复请求。3.4 采集并发与分布式扩展思路单协程按顺序爬速度确实慢。一个视频平均 3 秒1000 个视频就是近一个小时。所以我做大规模采集的时候会用concurrent.futures.ThreadPoolExecutor开线程池同时维持一个简单的频率限制器。线程数不建议开太大。个人经验是 5 个线程、每线程请求间隔 1 到 2 秒已经能跑得比较舒服。再往上加容易被平台的风控系统盯上。至于分布式爬虫只有两种情况才需要考虑目标视频量达到几十万级或者采集频率要求非常高。一般中小型项目单机多线程完全够用。真要做分布式思路也就是把采集任务拆成队列用 Redis 做任务队列多台机器各自消费任务数据统一写入同一个数据库。这套架构写起来不复杂但维护成本高没有足够需求不要轻易上。4. 热度分析模块把采集结果变成可决策的分数4.1 三个热度维度基础播放、互动质量、传播速度数据采回来不分析那就是白采。热度分析第一步是把采集到的原始指标映射到具体的业务含义上。基础播放维度播放量是视频触达人群的底数播放量高说明平台推荐给的流量大。互动质量维度点赞率、评论率、转发率这组比率衡量的是内容勾住用户的程度。一个播放 10 万、点赞 2 万的视频内容质量显著高于一个播放 100 万、点赞 2 万的视频。传播速度维度用相邻两次采集的数据算差值除以时间间隔得到单位时间增量。例如昨天 20 点采集播放是 10 万今天 20 点采集是 15 万日均增量就是 5 万。增速越高越可能是正在被推上热门的短视频。我一般会在数据入库之后写一条 SQL 或多写一段 pandas 脚本先算出每个视频在这三个维度的原始值再进入下一步归一化评分。4.2 热度评分公式与归一化处理有了三个维度的指标怎么合成一个分数我常用的公式是线性加权hot_score 0.4 * norm(play_count) 0.3 * norm(interact_rate) 0.3 * norm(growth_rate)norm表示归一化。为什么要归一化因为播放量是几十万级别的数互动率是零点几的小数增速又是另一个量级直接加权会被播放量一票定生死其他维度完全失去意义。最简单的归一化方法是Min-Max 归一化norm(x) (x - min(x)) / (max(x) - min(x))把全体数据里的最大值映射为 1最小值映射为 0这样所有维度的取值都落在 0 到 1 之间量纲问题就解决了。权重怎么定没有标准答案取决于业务偏好。如果团队更在意内容质量就把互动率的权重调高比如 0.3、0.4、0.3。如果做投流会更看重传播速度那就给增速更大权重。我的建议是权重先按经验设定然后拿历史爆款视频回测看分数前 10% 的视频和真实爆款重合率有多高再迭代调整。4.3 趋势判断与简单可视化热度评分算出来以后我们通常会画两条曲线单条视频的播放量随时间变化曲线以及整个账号视频的平均热度变化曲线。画曲线用 pandas 加 matplotlib 就够了。先把 SQLite 里的历史快照读出来按video_id和collect_time排序import pandas as pd import matplotlib.pyplot as plt df pd.read_sql_query(SELECT * FROM video_stats ORDER BY video_id, collect_time, conn) df[collect_time] pd.to_datetime(df[collect_time]) video_1 df[df[video_id] 1001] plt.plot(video_1[collect_time], video_1[play_count], markero) plt.title(视频1001播放量变化) plt.xlabel(采集时间) plt.ylabel(播放量) plt.show()趋势判断有个实用技巧连续多次采集的播放量增量逐渐放大说明视频正在被推荐算法加持可以考虑趁热出同选题内容反过来增量持续收窄说明流量红利接近尾声这时候再投钱就不划算了。很多团队有个误区只看单日数据就下结论。短视频的推荐机制是波动的某天播放量掉了下来可能只是平台算法在做流量池切换不代表内容不行。至少要看 3 到 5 天的连续数据趋势面的判断才稳。5. 实测中常见的问题与排查实录5.1 XPath 失效页面结构变了怎么办这种情况我遇到太多次了。头天晚上跑得好好的脚本第二天早上起来全挂了一看报错XPath 取出来的列表是空的。原因无非两种页面 DOM 结构改版class 名变了或者页面加入了动态渲染目标数据是通过 JavaScript 异步加载的初始 HTML 里根本没有。排查步骤很简单把当前页面的 HTML 保存下来用浏览器开发者工具重新定位一次字段看看新的结构长什么样。如果数据是异步加载的就需要去分析 XHR 接口。开发者工具里切到 Network 面板找返回 JSON 数据的请求直接请求那个接口拿数据比解析 HTML 要稳得多。这也是我说“会解析 HTML 只是基本功会找接口才是进阶”的原因。公开数据的接口通常接口参数不复杂只要不涉及加密签名改造起来并不难。5.2 请求被限制状态码 403、验证码弹窗采集时最怕遇到 403。遇到这个状态码通常说明请求头的伪装程度不够或者 IP 被临时标记了。我先检查 User-Agent 是不是太老旧再检查 Referer 是否正确。有些平台会校验 Referer不带的话直接拒绝。如果这两项都正常那就是频率问题短时间内请求数太多让程序停下来冷却 10 到 30 分钟期间不要发任何请求。再往深一层说动态页面还可能弹出行为验证组件。遇到这种情况我的处理原则是立即停止脚本放弃对这种页面的批量采集。原因很简单绕过验证已经越过了合规边界而且技术上性价比极低。换一个数据源或者通过官方 API 申请数据授权才是更健康的方案。5.3 中文乱码与字段缺失人民币符号、中文引号、特殊空格这类字符处理不及时轻则数据看起来别扭重则 JSON 解析直接报错。乱码优先查编码前面已经说过。字段缺失的问题则更常见有些视频禁止评论评论数为空有些视频是合作内容作者信息不完全。我的处理方式是把解析函数写成防御式写法每个字段都做空值兜底解析不到就给默认值 0 或空字符串绝不让单条数据解析失败拖垮整个采集流程。养成一个习惯每次写解析函数先跑三个不同类型的视频页面验证一个热门视频、一个普通视频、一个发布完没多久的新视频。这三种页面的结构差异往往最大能用它们验证过的解析逻辑上线后才不会频繁报错。5.4 数据重复和中断续采长时间采集最怕断点。机器重启、网络闪断、目标平台短暂不可用都可能让任务中断。我的做法是设计断点续采机制采集进度单独存到一个本地progress.json文件里每处理完一条就写入已经完成的视频 ID。下次启动任务时先读这个文件已经完成的直接跳过。数据重复的问题主要由并发造成。两个线程同时拿到同一个视频链接就会产生重复请求。解决办法是在进入详情页请求之前用线程安全的集合做去重把正在处理中的视频 ID 也加进去。数据库里则靠video_id主键和INSERT OR REPLACE兜底即使有个别重复最终表里也只保留一份最新记录。我把平时最容易踩的问题整理成一个速查表方便排查现象可能原因排查思路标题中文乱码页面编码不是 UTF-8设置resp.encoding resp.apparent_encodingXPath 取到空列表页面改版或数据异步加载用开发者工具重新定位或改找 XHR 接口请求返回 403UA 过于简陋或频率过高完善请求头降低频率冷却后继续采集几千条后中断触发了临时限流做断点续采下一次从断点继续同一个视频多次入库缺少主键约束表结构加video_id主键使用INSERT OR REPLACE播放数显示为“12.3万”展示格式未清洗写转换函数统一转成整数6. 一点个人体会这活儿真正值钱的地方不在爬虫在分析我不太建议只把眼光放在“怎么绕过反爬”上。做了这么多采集项目最深的体会是爬虫本身只是数据管道的前半段真正产生价值的是后半段的热度分析和业务决策。有一次我给一个做短剧投放的团队做监控前期用同样的采集架构拿到了几百个视频的每日数据。单看播放量爆款和普通内容的差距并不算大差距真正拉开是在互动率上。投手根据我给的互动率排序调整了投放策略把资源集中到“播放量中等但互动率很高”的几部短剧上次月整体 ROI 比上个月提升了将近两成。那次之后我更加确认数据采集的终局是帮助人做判断。合规的边界也值得从业者反复自省。公开数据的采集、正当的分析使用、适度的请求频率、尊重平台服务条款这几个原则我会一直坚持。数据项目要做成长期工程靠的是稳和准不是猛和快。最后再分享一个小技巧采集脚本跑一段时间后记得定期抽查数据质量。我会在每个批次里随机选 5 条视频手动打开页面核对采集到的播放数、点赞数有没有偏差。这个方法朴实地有点笨但能帮你尽早发现页面改版和解析逻辑失效的问题比盯着日志里的异常更直接。希望这篇基于“短视频平台数据的爬虫采集与热度分析”的整套实操笔记能让你少走一些我当年走过的弯路。