一、视频元数据好看,但不好稳定拿
做视频搜索、内容监测或者训练数据准备时,第一步通常不是模型,也不是搜索算法,而是先拿到一批质量稳定的视频元数据。
比如我们想做一个个人视频搜索引擎,输入关键词 Cristiano,系统可以返回相关视频的标题、描述、播放量、时长、上传者和视频链接。听起来很简单,但真正做起来会发现,视频平台页面结构经常变化,不同入口返回的信息也不一样:频道页、搜索页、标签页、播放页,每个页面的数据组织方式都不同。
如果自己做这件事,通常会有几种方案。
第一种是自己写数据采集工具,直接解析 YouTube 页面。优点是灵活,想采什么字段都可以控制;缺点是页面结构变化频繁,搜索页、频道页、播放页的数据组织方式并不一致,维护成本会很高。
第二种是使用平台官方 API。优点是稳定、合规、字段清晰;缺点是配额、权限和可获取字段会受到限制,有些场景还需要额外申请,前期验证不一定方便。
第三种是使用通用网页采集工具。优点是上手快,适合一次性导出数据;缺点是如果要持续跑批量任务、查看任务状态、下载结构化结果,还需要自己补很多工程逻辑。
所以在视频搜索引擎这个场景里,更适合的是任务化的视频数据采集 API:提交 URL 或关键词后,由系统完成采集任务,并返回结构化结果。本文就用 Dataify API 做一个实际演示:
dataify官网链接:https://dataify.com?utm%5Fsource=yhjsgzs&utm%5Fterm=01
整体流程如下:
1Dataify API Token 2 ↓ 3YouTube 视频采集工具 4 ↓ 5URL / 关键词采集 6 ↓ 7JSON / CSV / XLSX 结果 8 ↓ 9字段清洗 10 ↓ 11Elasticsearch 12 ↓ 13个人视频搜索引擎 14
二、为什么适合做视频搜索引擎的数据入口
在个人视频搜索引擎这个场景里,我们最关心的不是单个视频页面能不能打开,而是能不能稳定拿到一批结构化字段。
搜索系统需要的字段通常很固定:
1title 2description 3url 4thumbnail 5viewCount 6date 7likes 8channelName 9channelUrl 10duration 11subtitles 12resolution 13success 14input 15error_code 16
这些字段里,title 和 description 用来做全文检索,url 用来跳转原始视频,viewCount、likes、duration 可以作为排序或筛选条件,channelName 和 channelUrl 可以用于频道维度聚合。
对比自己写数据采集工具、直接调用官方 API 或使用通用采集工具,Dataify 更适合作为这个场景的数据入口,主要体现在三个点。
第一是任务化处理。 开发者提交一个 URL 或关键词后,系统会创建任务,任务完成后再下载结果。这样比一次性在终端里等待数据返回更适合批量场景,也方便在后台查看状态、成功率、文件大小和积分消耗。
第二是多入口支持。 同样是视频数据,有时候我们需要采集指定频道的视频列表,有时候需要按关键词获取一批候选视频,有时候还需要按标签、搜索过滤器、评论或字幕继续扩展。Dataify 把这些入口拆成不同工具,调用时只需要切换对应参数。
第三是结果结构化。 采集完成后可以直接下载 JSON、CSV 或 XLSX。对于开发者来说,JSON 适合进入后端流程,CSV 适合导入 Elasticsearch、PostgreSQL 或 ClickHouse,XLSX 则适合给业务同事直接查看。
所以这篇文章后面的实践会分两步走:
1先用 URL 采集 C 罗频道视频,验证字段是否完整 2 ↓ 3再用关键词 Cristiano 批量获取 100 条视频数据 4 ↓ 5下载 CSV / XLSX 结果 6 ↓ 7把标题和描述写入 Elasticsearch 8 ↓ 9实现个人视频搜索 10
这种路线比较适合从零开始验证项目。先小批量跑通,再扩大关键词数量和数据规模。
三、上手实践:从 Token 到 C 罗视频数据采集
上面几种方案里,如果只是做一次性实验,自己写脚本或手工整理也能完成。但如果目标是后续持续采集不同频道、不同关键词,并且希望结果可以直接进入搜索或分析流程,任务化 API 会更省维护成本。
所以接下来用 Dataify 做一个完整链路演示:从获取 Token,到采集 C 罗频道视频,再到关键词批量采集。
1. 开通账户并获取 API Token
进入 Dataify 后台,在「设置 → Token 管理」里可以查看和新增 API Token。

Token 用于接口鉴权,建议放在环境变量里:
1export DATAIFY_TOKEN="你的_API_TOKEN" 2
不要把真实 Token 写进代码仓库,也不要放在公开文章截图里。
通过 URL 采集频道视频
进入:
1网页数据采集 → 网页采集商店 → YouTube视频帖子采集工具 → 通过URL采集 2
可以看到配置区域和右侧自动生成的 cURL 请求。

URL 采集适合处理某个频道的视频列表,例如:
1https://www.youtube.com/@stephcurry/videos 2
点击运行后,任务不会立即在页面上展开所有数据,而是会进入任务列表。

以 C 罗频道为例采集视频数据
打开 YouTube,搜索 C 罗,可以进入他的官方频道页面。

为了获取频道视频列表,建议使用频道视频页:
1https://www.youtube.com/@cristiano/videos 2
在 Dataify 中把 URL 换成 C 罗频道地址,并设置采集数量为 10。

运行完成后,可以在任务列表看到任务状态、成功率、结果数量和消耗积分。结果支持三种下载方式:
- JSON 下载
- CSV 下载
- XLSX 下载

下载后的表格结果如下:

可以看到,结果已经整理成结构化字段,包括:
| 字段 | 说明 |
|---|---|
| title | 视频标题 |
| id | 视频 ID |
| url | 视频地址 |
| thumbnail | 缩略图 |
| viewCount | 播放量 |
| date | 发布时间 |
| likes | 点赞数 |
| channelName | 上传者 / 频道名 |
| channelUrl | 频道地址 |
| duration | 视频时长 |
| description | 视频描述 |
| subtitles | 是否包含字幕 |
| resolution | 分辨率 |
| success | 当前记录是否成功 |
| input | 本次输入参数 |
| error_code | 错误码 |
这些字段后续可以直接进入搜索、分析或训练数据处理流程。
通过关键词批量获取视频元数据
除了频道 URL,也可以使用关键词采集。比如输入:
1Cristiano 2
并设置采集数量:
1100 2

对应的核心参数是:
1[ 2 { 3 "keyword": "Cristiano", 4 "num_of_posts": "100" 5 } 6] 7
任务完成后,可以看到成功采集了有效结果:

下载后的结果如下:

从截图可以看到,关键词任务请求 100 条数据,实际返回了几十条有效结果,积分消耗不到 1 个。对于前期验证、搭建 demo、做小批量数据分析来说,这个成本比较容易接受。
四、代码接入:把下载结果写入 Elasticsearch
上一步已经拿到了 CSV 或 XLSX 结果。接下来要做的是把数据写入 Elasticsearch,让标题和描述可以被检索。
这里推荐先用 CSV,因为处理最简单。
安装依赖
1pip install pandas elasticsearch 2
启动 Elasticsearch
本地测试可以用 Docker:
1docker run -d \ 2 --name video-es \ 3 -p 9200:9200 \ 4 -e "discovery.type=single-node" \ 5 -e "xpack.security.enabled=false" \ 6 elasticsearch:8.13.4 7
检查服务是否正常:
1curl http://localhost:9200 2
导入 CSV
假设从 Dataify 下载的文件名是:
1cristiano_videos.csv 2
新建 import_to_es.py:
1import hashlib 2import pandas as pd 3from elasticsearch import Elasticsearch, helpers 4 5 6ES_URL = "http://localhost:9200" 7ES_INDEX = "youtube_videos" 8CSV_PATH = "cristiano_videos.csv" 9 10 11def make_doc_id(row: dict) -> str: 12 raw = row.get("id") or row.get("url") or str(row) 13 return hashlib.sha1(str(raw).encode("utf-8")).hexdigest() 14 15 16def clean_value(value): 17 if pd.isna(value): 18 return None 19 return value 20 21 22def create_index_if_needed(es: Elasticsearch): 23 if es.indices.exists(index=ES_INDEX): 24 return 25 26 es.indices.create( 27 index=ES_INDEX, 28 body={ 29 "mappings": { 30 "properties": { 31 "title": {"type": "text"}, 32 "description": {"type": "text"}, 33 "url": {"type": "keyword"}, 34 "thumbnail": {"type": "keyword"}, 35 "viewCount": {"type": "keyword"}, 36 "date": {"type": "keyword"}, 37 "likes": {"type": "keyword"}, 38 "channelName": {"type": "text"}, 39 "channelUrl": {"type": "keyword"}, 40 "duration": {"type": "keyword"}, 41 "input": {"type": "keyword"}, 42 "success": {"type": "keyword"}, 43 "error_code": {"type": "keyword"}, 44 } 45 } 46 }, 47 ) 48 49 50def main(): 51 es = Elasticsearch(ES_URL) 52 create_index_if_needed(es) 53 54 df = pd.read_csv(CSV_PATH) 55 actions = [] 56 57 for _, row in df.iterrows(): 58 doc = {key: clean_value(value) for key, value in row.to_dict().items()} 59 60 # 低质量过滤:没有标题或 URL 的记录,不进入搜索库 61 if not doc.get("title") or not doc.get("url"): 62 continue 63 64 actions.append( 65 { 66 "_op_type": "index", 67 "_index": ES_INDEX, 68 "_id": make_doc_id(doc), 69 "_source": doc, 70 } 71 ) 72 73 if not actions: 74 print("No valid records to import.") 75 return 76 77 success, errors = helpers.bulk(es, actions, raise_on_error=False) 78 79 print(f"Imported records: {success}") 80 if errors: 81 print(f"Failed records: {len(errors)}") 82 83 84if __name__ == "__main__": 85 main() 86
运行:
1python import_to_es.py 2
实现全文检索
新建 search_videos.py:
1from elasticsearch import Elasticsearch 2 3 4ES_URL = "http://localhost:9200" 5ES_INDEX = "youtube_videos" 6 7 8def search_videos(keyword: str, size: int = 10): 9 es = Elasticsearch(ES_URL) 10 11 response = es.search( 12 index=ES_INDEX, 13 body={ 14 "query": { 15 "multi_match": { 16 "query": keyword, 17 "fields": [ 18 "title^3", 19 "description", 20 "channelName", 21 ], 22 } 23 }, 24 "size": size, 25 }, 26 ) 27 28 return response["hits"]["hits"] 29 30 31if __name__ == "__main__": 32 while True: 33 keyword = input("search> ").strip() 34 35 if keyword in {"exit", "quit"}: 36 break 37 38 if not keyword: 39 continue 40 41 results = search_videos(keyword) 42 43 for index, hit in enumerate(results, start=1): 44 item = hit["_source"] 45 46 print("-" * 80) 47 print(f"{index}. {item.get('title')}") 48 print(f"channel: {item.get('channelName')}") 49 print(f"views: {item.get('viewCount')}") 50 print(f"duration: {item.get('duration')}") 51 print(f"url: {item.get('url')}") 52
运行:
1python search_videos.py 2
输入:
1Cristiano 2
就可以检索刚才导入的视频数据。
五、效果对比:从手工整理到 API 任务化
这次实践里,URL 采集和关键词采集分别解决了两个问题。
URL 采集适合拿指定频道的数据。比如 C 罗频道,我们只需要把频道视频页填进去,再设置采集数量,就能拿到一批视频元数据。
关键词采集适合扩大数据量。比如关键词 Cristiano,一次请求 100 条,任务完成后可以下载结构化结果。从截图看,这类任务实际返回了几十条有效数据,积分消耗不到 1 个,用来做前期验证是比较划算的。
| 对比项 | 手工整理 | Dataify API |
|---|---|---|
| 数据入口 | 人工打开页面逐条整理 | URL 或关键词提交任务 |
| 批量能力 | 不适合处理几十到上百条 | 可通过 num_of_posts 控制数量 |
| 结果格式 | 需要自己整理表格 | 支持 JSON、CSV、XLSX |
| 状态追踪 | 依赖人工记录 | 任务列表展示状态和成功率 |
| 错误信息 | 不容易统一记录 | 结果中包含 success、error_code |
| 后续入库 | 需要额外清洗 | CSV / JSON 可以直接进入处理脚本 |
| 成本判断 | 时间成本不直观 | 后台可看到积分消耗 |
这里不建议只看“请求了多少条”,更应该看“有效结果数”和“字段完整度”。
比如标题、URL、频道名、描述这些字段越完整,后面做搜索和分析就越顺。
六、最佳实践与扩展
这个项目第一版不用做得太复杂。能完成下面这条链路,就已经可以验证价值:
1关键词 / URL 2 ↓ 3视频元数据 4 ↓ 5CSV / JSON 下载 6 ↓ 7Elasticsearch 入库 8 ↓ 9标题和描述检索 10
几个建议可以提前加上。
第一,先小批量测试。
URL 采集先设置 5 或 10 条,确认字段没问题后,再调到 100 或更多。
第二,保留 input 字段。 这个字段能告诉你数据来自哪个 URL 或关键词。后面如果同时采集多个关键词,比如 Cristiano、Messi、Mbappe,就可以靠 input 做来源区分。
第三,入库前做基础过滤。 标题为空、URL 为空、success 不是成功状态的数据,不建议直接进入搜索库。
第四,先采元数据,再处理更重的内容。
如果后续要继续做字幕、评论或训练数据,建议先用元数据筛选出高价值视频,再进入下一步。这样成本更好控制。
比较推荐的端到端流程是:
1Dataify 视频数据采集 API 2 ↓ 3视频元数据 4 ↓ 5质量过滤 6 ↓ 7Elasticsearch 检索 8 ↓ 9筛选高价值视频 10 ↓ 11字幕 / 评论数据 12 ↓ 13训练数据或内容分析 14
对于个人视频搜索引擎来说,核心难点不是“能不能打开某个视频页面”,而是能不能持续、稳定、批量地拿到结构化元数据。
自己写数据采集工具适合高度定制,官方 API 适合规范场景,通用采集工具适合一次性导出;而 Dataify 更适合把采集过程做成任务化流程,并直接输出 JSON、CSV、XLSX 等结构化结果。
这样不是单纯演示怎么点后台,而是通过 Dataify API 把视频元数据变成可检索、可分析、可继续加工的数据资产。
《用视频数据采集 API 构建个人视频搜索引擎:从 C 罗频道到 Elasticsearch 全文检索》 是转载文章,点击查看原文。
