☰
短视频公开数据采集与热度分析:从爬虫到内容观察实验
2026/10/10 6:55:45 网站建设 项目流程

1. 项目定位与三条铁律:这不是爬虫教程,是数据观察实验

短视频平台数据的爬虫采集,说穿了就是一套"定期去看、把看到的记下来、再算一算"的流程。但我一开始做这个练手项目的动机,并不是为了什么流量黑产或者批量搬运,而是想回答一个我一直好奇的问题:在短视频平台上,到底什么样的内容更容易被大家喜欢?点赞过百万的视频,和那些几千赞的普通视频,差别究竟有多大?

这个问题的麻烦之处在于,人工刷几天首页根本看不出规律,你需要连续一段时间的、结构化的数据才能说话。所以我就做了一套小工具,把公开页面上能看到的视频元数据存下来,再自己建了一个热度评估公式,最后用图表把趋势呈现出来。如果你也想做类似的"内容观察实验",这篇文章应该能帮你少走不少弯路——无论你是想研究热门内容规律,还是想评估某个话题的生命周期,这套流程都能直接搬过去用。

在写任何代码之前,我得先亮出我给自己定的三条铁律。这不仅是道德问题,也是技术问题:凡是需要登录、需要输入验证码、需要破解签名才能拿到的数据,我一开始就主动绕开。因为能通过公开入口拿到的那部分数据,已经足够支撑热度分析了。边界守得越清楚,工具跑得越稳。

1.1 我为什么想做这件事

先说个背景。当时我在整理一批兴趣领域的创作者名单,想看看哪些创作者的内容风格更受欢迎。平台自带的统计数据并不是对所有内容维度开放的,也不太方便做历史对比。我试着手动记录了几条视频的点赞、评论、转发,发现一个视频一个视频地点开看,效率太低了。

于是问题就变成了:能不能用程序定期把"任何一个普通访客打开网页就能看到的那些信息"保存下来,比如发布时间、点赞数、评论数、转发数、收藏数、播放量,以及视频标题里的话题标签?答案是可以。这些数据本质上就是页面渲染时已经公开展示的内容,不存在任何"越权获取"的成分。我的工具只是把这些公开信息结构化,然后做统计。

项目范围也顺势定下来了:不做用户画像,不碰私信、通讯录、浏览历史这类隐私字段;不做批量下载视频文件;不做任何商业用途。项目代号就叫"观察者",它是一个只负责记录和计算的工具。

这一段经验我想先说清楚:一个采集项目的边界,应该在写第一行代码之前就划定,而不是在跑起来之后才想起来补救。划边界这件事,直接决定了后边你会不会和平台的风控机制纠缠不清。

1.2 采集前先划红线:三条必须守住的规矩

我自己总结下来就三条,分享给同样想做这件事的人。

第一条,只采集不需要登录就能看到的公开数据。如果某个数据入口要求你先登录账号,那就意味着它面向的是"登录用户"这个群体,默认不在公开共享范围内,不去碰它。很多页面上的数据是未登录状态就能完整看到的,这些才是我们的数据源。

第二条,访问频率必须低于正常人的刷新速度,并且要设置随机间隔。这不是教你"怎么伪装得更像真人",恰恰相反,这是对目标服务的尊重。如果每个人做采集都按每秒多少请求的速率去打,平台的服务器压力会指数级上升。我实际用的是每次请求间隔1.5到3秒随机值,跑一次全量拉取也就多花几分钟,换来的是长期稳定。

第三条,数据只用在自己看得见的分析与学习场景中,不向外传播原始明细。热度分析需要的只是聚合后的统计结论,比如"这一类内容平均点赞数是多少""发布时间对热度的影响有多大",原始明细数据留在本地数据库里就行,完全没必要公开展示。

这三条我建议你也写进自己的项目说明里,甚至写成代码头部注释,时刻提醒自己不要跑偏。

1.3 技术选型与整体架构

这套工具没有用复杂框架,整体思路就是一个轻量数据流水线,分四个环节:获取、存储、计算、呈现。

环节选型理由
网络请求Python + httpxhttpx支持HTTP/2,接口返回JSON时解析干净利落
数据解析内置json模块平台接口绝大多数直接返回JSON,无需额外解析器
存储SQLite单机项目不折腾数据库服务,一个文件搞定,随时可查
分析pandas表格化处理、时间序列重采样、分组聚合都顺手
可视化pyecharts输出HTML交互图表,观察趋势线很直观
调度系统定时任务不必引入消息队列,每天固定时间跑一次增量更新就够

这六项加起来,整个项目没有一处需要"重型基础设施",运行环境就是一台普通的个人电脑。你也可以用MySQL替换SQLite,用matplotlib替换pyecharts,核心逻辑不受影响。我选择这套组合的唯一标准是:上手快、出活快、后续维护成本低。

2. 公开数据从哪儿来:三个不用登录也能访问的入口

很多人一提到短视频数据采集,第一反应就是"要去逆向App客户端接口"。其实完全不必。Web端页面本身就是一座金矿,而且Web端有非常稳定的数据形态。我在浏览器的开发者工具里,把Network面板打开、刷新页面,就能看到大量XHR请求,这些请求基本都是JSON接口。我需要做的只是从中找出结构清晰、字段稳定的那几个入口。

我自己在项目中实际用到的入口有三个:热榜接口、话题搜索接口、单条视频详情接口。这三个入口合在一起,基本覆盖了热度分析需要的所有维度。下面分别说说它们的用途和注意点。

2.1 热榜接口:平台替你算好的热度风向标

热榜接口没有别的花头,它直接把"当前整个平台上最受关注的一批内容"端到你面前。这相当于平台已经帮我们做了一轮热度排序,我们能拿到的就是这份排序的快照。

这个接口返回的数据一般包含:榜单名称、上榜视频ID、排名、各互动指标数值、上榜时间等。我只需要定时去拉这份榜单,把每次的快照存入数据库,就能追踪"某条视频什么时候上榜、排名怎样波动、在榜单上停留了多久"。

这个维度特别适合回答一类问题:热点话题的生命周期有多长?有的视频上榜时会冲到第一,但两三天后就消失了;也有视频能在榜单里停留一周以上。把这些排名变化连成线,你就能看到内容热度的真实衰减规律,而不是只看到某个瞬间的静态排名。

这里要提醒一点:热榜接口返回的字段名称各家不同,有的叫"hot_value",有的叫"rank_score",还有的干脆就是一堆加密的数字。我的建议是,初次解析时先打印完整的JSON结构,把能对得上含义的字段先标记出来,再决定哪些字段进表。不要上来就猜字段名,很容易踩空。

2.2 话题搜索接口:按关键词批量拉取内容

热榜只能看头部内容,但热度分析不能只看头部。为了观察普通视频的分布规律,还需要一个入口,能够根据话题关键词拉取一批相关的视频列表。这就是话题搜索接口的用途。

比如我想观察"城市生活技巧"这个方向的内容情况,就在请求参数里带上这个话题标签,接口会返回一页视频列表,包含标题、作者昵称、发布时间、各互动数、封面图地址等。通过游标翻页,可以一页一页地把这个话题下最近的视频拉下来。

这个入口最需要注意的是翻页和时效性。搜索列表默认按时间倒序或综合排序,翻页参数通常是一个游标字符串,而不是简单的页码数字。游标会随着数据变化而失效,一旦失效,重新从第一页开始跑就行,不需要难受。还有,搜索接口有时候会智能过滤掉一些低质量内容,所以它返回的列表并不等于全量数据,这一点做统计时要记住:你的分析结论描述的是"平台推荐给你看的那部分内容",而不是平台上全部内容。

2.3 单条视频详情与作者主页:补充内容维度

有了榜单和搜索列表,数据量已经不小了,但还缺一些细粒度字段。比如视频具体的发布时间(精确到秒)、时长、封面分辨率、话题标签列表、背景音乐信息,这些在列表接口里往往被裁剪过。这时候就需要第三个入口:单条视频详情接口。

用视频ID拼接出详情页地址,请求返回的数据就会完整不少。视频时长可以用来分析"多长的视频更容易火";精确发布时间可以算出"发布后多少小时达到多少互动量",这是热度模型里很有价值的信息。作者主页同理,能拿到创作者公开发布的简介、认证信息、作品数量,用于后续的创作者分层观察。

这里我要强调一个细节:我只采集作者主页上公开展示、与内容统计相关的字段,比如作品数和获赞总数,不会去采集任何涉及个人联系方式、位置信息等敏感字段。公开数据不等于没有隐私,采集时做必要的克制,既是对数据主体的尊重,也是让自己睡得着觉的前提。

3. 采集脚本的开发过程:从单次请求到增量更新

架构聊完了,接下来是真正动手写代码的部分。我会按实际开发的顺序,讲清楚从最小Demo到增量更新的每一步。代码都做了脱敏处理,接口路径和参数名做了简化,因为真正的价值在于逻辑,而不在于某个具体的路径字符串。

3.1 最小请求Demo:先把一条数据拿到手

我习惯先写一个最简版本,目标只有一个:能从公开接口拿到一条JSON记录并打印出来。这个步骤看似简单,却能验证很多基础问题,比如网络是否通畅、请求头是否被拒、接口返回结构是什么。

import httpx import json BASE_HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/120.0 Safari/537.36", "Accept": "application/json, text/plain, */*", "Referer": "https://example.com/", } def fetch_hot_list(): url = "https://api.example.com/hot/list" params = {"limit": 20} with httpx.Client(headers=BASE_HEADERS, timeout=10) as client: resp = client.get(url, params=params) resp.raise_for_status() data = resp.json() print(json.dumps(data, ensure_ascii=False, indent=2))

第一次打印出来的JSON结构,可能和你预想的不太一样。比如互动数被包在嵌套对象里,字符串里混着转义符,甚至字段名是拼音缩写。这个阶段的任务不是急着写业务逻辑,而是把这份原始JSON完整地读一遍,搞清楚结构。我在项目里就是靠这一份打印,确定了后续所有解析代码的字段路径。

3.2 请求头与请求间隔的调优:慢就是快

最小Demo跑通之后,下一个动作是调优请求头和间隔。请求头并不需要堆满一堆字段,关键是User-Agent要像一个真实的浏览器版本,Referer要和当前页面对应,Accept声明我们只接受JSON。超出这三个字段之外的东西,往往只是噪音。

访问间隔方面,我实测下来比较稳的是1.5秒到3秒的随机延迟。一开始我用的是固定2秒,但后来发现固定间隔会产生某种节奏感,容易被识别;改为随机间隔之后,整个采集过程反倒更顺滑。这个细节也符合常理:正常人是不会像节拍器一样每隔正好两秒点一次页面的。

import time import random def polite_sleep(): time.sleep(random.uniform(1.5, 3.0))

在长时间采集任务里,这个随机延迟函数会在每次请求之间调用。看起来每一次都慢了一两秒,但换来的稳定性完全值得。有一句话我想放在这里:采集代码的高质量,不是体现在你能多快地跑完一个任务,而是体现在你能连续跑多少天不被打断。

3.3 游标翻页与去重存储:别让数据在手里溜走

列表接口的翻页通常用游标实现,伪代码逻辑如下:先请求第一页,从返回结果里取游标;然后用游标请求下一页,直到返回列表为空或游标为空。

def fetch_all_items(): cursor = "" page_size = 20 collected = [] while True: params = {"page_size": page_size, "cursor": cursor} data = fetch_data(params) items = data.get("items", []) if not items: break collected.extend(items) cursor = data.get("cursor", "") if not cursor: break polite_sleep() return collected

拿到列表之后,紧接着就要考虑存储。我用的SQLite表结构大致是:视频ID作为主键,标题、作者名、发布时间、时长、播放数、点赞数、评论数、转发数、收藏数、抓取时间。视频ID做主键的好处是天然去重,同一视频重复抓取只需要更新互动数值即可。

CREATE TABLE IF NOT EXISTS video_stats ( video_id TEXT PRIMARY KEY, title TEXT, author TEXT, publish_time DATETIME, duration_seconds INTEGER, play_count INTEGER, like_count INTEGER, comment_count INTEGER, share_count INTEGER, favorite_count INTEGER, fetched_at DATETIME );

这里有个优化点值得说:不要把每次抓取都原地更新同一行,而是保留历史快照。也就是说,同一视频ID可以对应多行记录,每行都带一个抓取时间。这样你才能回溯"这个视频的点赞数是怎么涨上来的",而不是只能看到它当前的样子。历史快照是趋势分析的基础,所以我把表设计成了每次抓取都插入新记录的追加模式。

3.4 定时增量更新:让项目自己跑起来

数据要连续观察才有价值。我是用了操作系统的定时任务,每天固定时间执行增量更新脚本:先拉热榜快照,再拉几个固定话题的搜索列表,最后对当天新增的视频补充详情字段。整个过程在十五分钟内完成,对平台侧几乎无感。

增量更新的核心是一个"水位线"概念:每次运行记录下本次抓取的最大发布时间,下一次只处理比这个时间更新的内容。这样既能控制请求量,又能保证数据不遗漏。水位线存储在SQLite里一张专门的meta表,实现起来非常简单,但它是整个自动化的基石。

4. 热度评估模型:为什么"点赞百万"不等于"热度第一"

数据存下来了,接下来就是最有意思的部分:怎么给一条视频的热度打分。我的结论是:不能只看点赞数。

4.1 各类互动指标的价值差异

点赞是成本最低的互动,用户看视频觉得不错,点一下就行。评论的成本高一些,需要敲字或选择表情;转发的成本更高,会把自己的社交关系带进来;收藏则代表"我以后还想看",意愿很强。所以同样的数量级,转发、收藏、评论所代表的热度含金量,比点赞要高得多。

我做的权重设计是:点赞权重为1,评论权重为2,转发权重为3,收藏权重为1.5。这个权重不是拍脑袋,而是结合我对内容传播链的理解做的初始设定。你可以根据自己的观察目标调整,比如想重点考察"话题传播力",就把转发权重调得更高。

def base_score(row): return (row["like_count"] * 1.0 + row["comment_count"] * 2.0 + row["share_count"] * 3.0 + row["favorite_count"] * 1.5)

4.2 时间衰减与增速修正:热度是流动的

同样的互动量,发布一天达到的和发布三十天达到的,热度含义完全不同。所以打分必须引入时间因子。我采用的思路是:分数除以(发布后小时数加一)的某个幂次。加一是避免除零,幂次选0.3,表示热度随时间平缓衰减,而不是断崖式下跌。

import math def heat_score(row): hours = max((row["fetched_at"] - row["publish_time"]).total_seconds() / 3600, 0.1) base = base_score(row) return base / math.pow(hours + 1, 0.3)

但时间衰减只能反映"总量级的相对关系",无法反映"最近涨得快不快"。为了捕捉爆款的早期信号,我再引入一个增速变量:最近N小时内互动量的增量除以N。一条视频如果刚发布两小时就积累了很高的增速,即使总量还不大,也很可能是潜力股。

4.3 归一化与指数化:把多个维度揉成一个数字

基础分、时间衰减、增速,三个量纲不同,不能直接相加。我的做法是先各自做倒数归一化到0到1区间,再用加权和的方式合成最终热度指数。

def normalize(series): return (series - series.min()) / (series.max() - series.min() + 1e-9) df["base_norm"] = normalize(df["base_score"]) df["growth_norm"] = normalize(df["growth_5h"]) df["heat_index"] = (df["base_norm"] * 0.6 + df["growth_norm"] * 0.4)

0.6和0.4的比例,说明我偏向于"总量为主、增速为辅"。如果你想观察爆款的早期信号,可以把增速权重提高到0.5甚至0.6。这些参数没有标准答案,我的建议是先用历史数据回测几版参数,看哪一组更符合你的直觉判断,再固定下来。

4.4 冷启动样本的特殊处理:短时间高增速的幻觉

冷启动阶段有个现象值得单独说:刚发布的视频,可能只有几百个播放、几十个点赞,但因为基数小,算出来的增速高得吓人。如果直接参与排序,会污染整体排名。

我的处理方法是设置一个最小样本门槛:互动总量低于某个绝对值时,不参与热榜排序,只参与整体统计。这个门槛我设的是互动总量不低于200。这就是一个很土但很有效的过滤思路,也提醒大家,任何指标在做横向比较之前,都要先想清楚它的适用范围。

5. 数据清洗与异常样本处理

很多人觉得数据采集完就可以直接分析了,实际上接口数据里充满了各种意外。这一章我讲讲清洗环节的常见问题。

5.1 脏数据与空值处理

接口返回的数据偶尔会出现字段缺失,比如某条视频的转发数可能是null,有的视频没有话题标签列表。统一做法是:数值字段缺失时填0,字符串字段缺失时填空字符串,并单独记录一条日志。日志的作用不是让你马上处理,而是让你知道哪些字段经常缺失,方便后续调整解析逻辑。

还有一个经常被忽略的脏数据来源是重复采集。因为历史快照模式是追加写入,同一条视频会出现在多行里。分析前必须按"视频ID+抓取时间"去重,或者在全量统计时只取每个视频最新的一条快照。

5.2 时间字段与账号字段的标准化

平台返回的时间格式五花八门,有的是秒级时间戳,有的是毫秒级,还有的是带时区的ISO字符串。清洗的第一步是把它们统一转成北京时间并格式化为一致的字符串。

账号字段同样需要标准化。比如作者昵称可能带各种不可见字符,有的昵称本身是表情符号,直接存进SQLite没问题,但如果后面要做词频分析,就得先把特殊字符清理一遍。我的做法是保留原始昵称的同时,额外存一个清洗后的昵称字段用于分析。

def clean_text(text): import re text = re.sub(r"[\x00-\x1f\x7f]", "", text) text = re.sub(r"\s+", " ", text).strip() return text

5.3 异常值的识别思路

短视频数据里天然存在异常值,比如播放量上亿的头部视频和几千播放的视频混在一起。常规统计会被极端值带偏。我的处理方法是:先看四分位数和箱线图,把极度偏离的样本单独拎出来观察,但不直接删除。因为对于热度分析来说,那些极端值本身就是结论的一部分,它们代表了平台流量的天花板。

另一个值得关注的是"互动结构异常":比如一条视频点赞数很低,但转发数高得离谱,这种结构往往意味着内容具有强工具属性,大家想存下来发给别人,而不是单纯觉得好看。识别这类结构,用各维度互动的占比曲线就能看出来,比单一指标有信息量得多。

6. 热度趋势分析与可视化呈现

清洗完毕,数据总算可以进入分析阶段了。这一章是我整个项目里最有成就感的部分,因为规律真的会从图表里浮现出来。

6.1 日粒度趋势线:看平台整体热度波动

先把所有视频按抓取日期汇总,算出每日新增视频的平均热度指数,画一条时间序列折线。这条线能直观展示"我这个观察池里,内容热度随时间的整体涨落"。周末和工作日有没有差异,某个时间段会不会集中出现高热内容,一眼就能看出来。

6.2 内容类型与时长画像

把视频按话题标签归类,统计每一类的平均热度、中位数热度、高热度视频占比,就能看出哪些内容方向更容易出爆款。我自己的观察结论是:那些"实用性强、可收藏"的内容方向,平均热度不是最高的,但高热度尾部更厚;而纯粹的情绪向内容,爆发力强但衰减快。

时长画像也很有意思:把视频时长分成15秒以下、15到30秒、30到60秒、60秒以上几个区间,分别统计平均完播感指标和热度指数。我在实践中发现不同内容类型的最佳时长区间差异很大,不能一概而论。

6.3 标题关键词与发布时段交叉分析

把高热度视频的标题做分词,统计高频关键词,能帮你理解"什么样的表述更容易吸引点击"。注意,这里分析的只是标题文本的特征,比如数字、问句、悬念词、情绪词等,不去做任何用户画像。

发布时段分析则更直接:把视频按发布时间区间分桶,统计每个时段的平均热度。做这个分析前要先把发布时间统一到正确的时区,否则结果会整体偏移。我的实测是:不同内容类型的黄金发布时段不一样,早上的知识类和晚上的娱乐类有比较明显的差异。

6.4 简易周报的自动输出

整个分析流程最后落地为一个自动报告脚本:每周跑一次,生成一份HTML看板,包含本周热度趋势线、内容分类排行、新上榜视频列表和爆款特征摘要。因为前一步已经把数据清洗和热度计算封装成了函数,生成周报只是把这些函数串起来,再输出到模板里。这个周报我用了一两个月,对理解内容平台的流量规律很有帮助。

7. 踩坑记录与合规复盘

最后一个部分,聊聊实际跑这个项目时踩过的坑,以及我对"可以做和不该做"的复盘。

7.1 访问被限与503响应

项目跑到第三周左右,有一次某个接口突然开始返回503。第一个反应是看是不是请求频率太高了,果然,那几天我把间隔从随机1.5到3秒压到了固定1秒,想加速拉取,结果触发了服务端的访问限制。解决方式很简单:把间隔调回保守区间,暂停两小时再继续跑,之后就很稳定。

这件事给我的教训是:千万别贪快。数据采集的长期收益来自稳定,而不是某一小时的峰值速度。控制频率不只是礼貌,也是保证任务能持续跑下去的技术手段。

7.2 验证码出现时,我的处理方式是停

有一次我尝试解析一个嵌入式页面的额外字段,发现页面要求完成身份验证才能继续。这个信号很明确:接口由公开状态变成了受保护状态,继续尝试就是在突破边界。我的处理是立即停止该接口的采集,回到公开入口的范围内,并把相关代码从项目里移除。

在这个问题上我的立场很明确:遇到验证码、签名校验、行为验证这类机制,说明目标服务已经明确表态"这里不是公开数据",继续纠缠没有意义。数据观察项目的数据源一旦变得灰暗,整个项目的价值也会打折。

7.3 接口字段说变就变

平台在前端结构调整时,接口返回字段经常变化。我遇到过字段名从"favorite_count"改成了"collect_count",也遇到过数据外层多包了一层结构。这类问题没有一劳永逸的解法,只能靠抓取日志及时发现问题,并给所有解析函数写好字段缺失的兜底。运行监控比写更多代码更重要。

7.4 去重水位线与存储膨胀

增量更新跑久了,SQLite文件越来越大。因为每条视频每次抓取都追加一条记录,几个星期之后文件轻松超过几百兆。我的处理是定期清理:只保留每条视频最近四十个快照,更老的记录归档压缩。水位线也同步更新,避免下次任务重复处理旧数据。

7.5 项目复盘:边界清单

最后聊聊复盘。我给自己列了一份简单的边界清单,每次调整项目时都会对照一遍:只采集公开可见内容;不突破登录、验证码、签名校验等访问控制;控制访问频率;不采集与个人隐私直接相关的字段;原始数据不对外公开;分析结论仅用于个人学习与观察。

这几条写出来很简单,执行起来却需要时时提醒自己,尤其是当一个新入口看起来"唾手可得"的时候。回头来看,我这个项目之所以能稳定跑这么长时间,恰恰因为所有数据都来自公开入口,没有和任何访问控制机制产生过正面冲突。守住边界,既是为了合规,也是一种更聪明、更省力的做事方式。

我在实际使用中的体会是:这套数据观察流程的价值,不在于它能预测哪条视频一定会火,而在于它让我把一个原来凭感觉的话题,变成了可以反复检验的量化对象。如果你也对内容趋势感兴趣,完全可以从今天开始,用这个最小的闭环搭一套自己的观察工具。跑两周之后,你看到的内容平台,会和之前不太一样。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询