用视频数据采集 API 构建个人视频搜索引擎:从 C 罗频道到 Elasticsearch 全文检索

一、视频元数据好看,但不好稳定拿

做视频搜索、内容监测或者训练数据准备时,第一步通常不是模型,也不是搜索算法,而是先拿到一批质量稳定的视频元数据。

比如我们想做一个个人视频搜索引擎,输入关键词 Cristiano,系统可以返回相关视频的标题、描述、播放量、时长、上传者和视频链接。听起来很简单,但真正做起来会发现,视频平台页面结构经常变化,不同入口返回的信息也不一样:频道页、搜索页、标签页、播放页,每个页面的数据组织方式都不同。

如果自己做这件事,通常会有几种方案。

第一种是自己写数据采集工具,直接解析 YouTube 页面。优点是灵活,想采什么字段都可以控制;缺点是页面结构变化频繁,搜索页、频道页、播放页的数据组织方式并不一致,维护成本会很高。

第二种是使用平台官方 API。优点是稳定、合规、字段清晰;缺点是配额、权限和可获取字段会受到限制,有些场景还需要额外申请,前期验证不一定方便。

第三种是使用通用网页采集工具。优点是上手快,适合一次性导出数据;缺点是如果要持续跑批量任务、查看任务状态、下载结构化结果,还需要自己补很多工程逻辑。

所以在视频搜索引擎这个场景里,更适合的是任务化的视频数据采集 API:提交 URL 或关键词后,由系统完成采集任务,并返回结构化结果。本文就用 Dataify API 做一个实际演示:

dataify官网链接:https://dataify.com?utm_source=yhjsgzs&utm_term=01

整体流程如下:

plain 复制代码
Dataify API Token
  ↓
YouTube 视频采集工具
  ↓
URL / 关键词采集
  ↓
JSON / CSV / XLSX 结果
  ↓
字段清洗
  ↓
Elasticsearch
  ↓
个人视频搜索引擎

二、为什么适合做视频搜索引擎的数据入口

在个人视频搜索引擎这个场景里,我们最关心的不是单个视频页面能不能打开,而是能不能稳定拿到一批结构化字段。

搜索系统需要的字段通常很固定:

plain 复制代码
title
description
url
thumbnail
viewCount
date
likes
channelName
channelUrl
duration
subtitles
resolution
success
input
error_code

这些字段里,title 和 description 用来做全文检索,url 用来跳转原始视频,viewCount、likes、duration 可以作为排序或筛选条件,channelName 和 channelUrl 可以用于频道维度聚合。

对比自己写数据采集工具、直接调用官方 API 或使用通用采集工具,Dataify 更适合作为这个场景的数据入口,主要体现在三个点。

第一是任务化处理。 开发者提交一个 URL 或关键词后,系统会创建任务,任务完成后再下载结果。这样比一次性在终端里等待数据返回更适合批量场景,也方便在后台查看状态、成功率、文件大小和积分消耗。

第二是多入口支持。 同样是视频数据,有时候我们需要采集指定频道的视频列表,有时候需要按关键词获取一批候选视频,有时候还需要按标签、搜索过滤器、评论或字幕继续扩展。Dataify 把这些入口拆成不同工具,调用时只需要切换对应参数。

第三是结果结构化。 采集完成后可以直接下载 JSON、CSV 或 XLSX。对于开发者来说,JSON 适合进入后端流程,CSV 适合导入 Elasticsearch、PostgreSQL 或 ClickHouse,XLSX 则适合给业务同事直接查看。

所以这篇文章后面的实践会分两步走:

plain 复制代码
先用 URL 采集 C 罗频道视频,验证字段是否完整
  ↓
再用关键词 Cristiano 批量获取 100 条视频数据
  ↓
下载 CSV / XLSX 结果
  ↓
把标题和描述写入 Elasticsearch
  ↓
实现个人视频搜索

这种路线比较适合从零开始验证项目。先小批量跑通,再扩大关键词数量和数据规模。

三、上手实践:从 Token 到 C 罗视频数据采集

上面几种方案里,如果只是做一次性实验,自己写脚本或手工整理也能完成。但如果目标是后续持续采集不同频道、不同关键词,并且希望结果可以直接进入搜索或分析流程,任务化 API 会更省维护成本。

所以接下来用 Dataify 做一个完整链路演示:从获取 Token,到采集 C 罗频道视频,再到关键词批量采集。

  1. 开通账户并获取 API Token

进入 Dataify 后台,在「设置 → Token 管理」里可以查看和新增 API Token。

Token 用于接口鉴权,建议放在环境变量里:

python 复制代码
export DATAIFY_TOKEN="你的_API_TOKEN"

不要把真实 Token 写进代码仓库,也不要放在公开文章截图里。

通过 URL 采集频道视频

进入:

plain 复制代码
网页数据采集 → 网页采集商店 → YouTube视频帖子采集工具 → 通过URL采集

可以看到配置区域和右侧自动生成的 cURL 请求。

URL 采集适合处理某个频道的视频列表,例如:

python 复制代码
https://www.youtube.com/@stephcurry/videos

点击运行后,任务不会立即在页面上展开所有数据,而是会进入任务列表。

以 C 罗频道为例采集视频数据

打开 YouTube,搜索 C 罗,可以进入他的官方频道页面。

为了获取频道视频列表,建议使用频道视频页:

plain 复制代码
https://www.youtube.com/@cristiano/videos

在 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,也可以使用关键词采集。比如输入:

plain 复制代码
Cristiano

并设置采集数量:

plain 复制代码
100

对应的核心参数是:

plain 复制代码
[
  {
    "keyword": "Cristiano",
    "num_of_posts": "100"
  }
]

任务完成后,可以看到成功采集了有效结果:

下载后的结果如下:

从截图可以看到,关键词任务请求 100 条数据,实际返回了几十条有效结果,积分消耗不到 1 个。对于前期验证、搭建 demo、做小批量数据分析来说,这个成本比较容易接受。

四、代码接入:把下载结果写入 Elasticsearch

上一步已经拿到了 CSV 或 XLSX 结果。接下来要做的是把数据写入 Elasticsearch,让标题和描述可以被检索。

这里推荐先用 CSV,因为处理最简单。

安装依赖

python 复制代码
pip install pandas elasticsearch

启动 Elasticsearch

本地测试可以用 Docker:

python 复制代码
docker run -d \
  --name video-es \
  -p 9200:9200 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  elasticsearch:8.13.4

检查服务是否正常:

python 复制代码
curl http://localhost:9200

导入 CSV

假设从 Dataify 下载的文件名是:

python 复制代码
cristiano_videos.csv

新建 import_to_es.py

python 复制代码
import hashlib
import pandas as pd
from elasticsearch import Elasticsearch, helpers


ES_URL = "http://localhost:9200"
ES_INDEX = "youtube_videos"
CSV_PATH = "cristiano_videos.csv"


def make_doc_id(row: dict) -> str:
    raw = row.get("id") or row.get("url") or str(row)
    return hashlib.sha1(str(raw).encode("utf-8")).hexdigest()


def clean_value(value):
    if pd.isna(value):
        return None
    return value


def create_index_if_needed(es: Elasticsearch):
    if es.indices.exists(index=ES_INDEX):
        return

    es.indices.create(
        index=ES_INDEX,
        body={
            "mappings": {
                "properties": {
                    "title": {"type": "text"},
                    "description": {"type": "text"},
                    "url": {"type": "keyword"},
                    "thumbnail": {"type": "keyword"},
                    "viewCount": {"type": "keyword"},
                    "date": {"type": "keyword"},
                    "likes": {"type": "keyword"},
                    "channelName": {"type": "text"},
                    "channelUrl": {"type": "keyword"},
                    "duration": {"type": "keyword"},
                    "input": {"type": "keyword"},
                    "success": {"type": "keyword"},
                    "error_code": {"type": "keyword"},
                }
            }
        },
    )


def main():
    es = Elasticsearch(ES_URL)
    create_index_if_needed(es)

    df = pd.read_csv(CSV_PATH)
    actions = []

    for _, row in df.iterrows():
        doc = {key: clean_value(value) for key, value in row.to_dict().items()}

        # 低质量过滤:没有标题或 URL 的记录,不进入搜索库
        if not doc.get("title") or not doc.get("url"):
            continue

        actions.append(
            {
                "_op_type": "index",
                "_index": ES_INDEX,
                "_id": make_doc_id(doc),
                "_source": doc,
            }
        )

    if not actions:
        print("No valid records to import.")
        return

    success, errors = helpers.bulk(es, actions, raise_on_error=False)

    print(f"Imported records: {success}")
    if errors:
        print(f"Failed records: {len(errors)}")


if __name__ == "__main__":
    main()

运行:

python 复制代码
python import_to_es.py

实现全文检索

新建 search_videos.py

python 复制代码
from elasticsearch import Elasticsearch


ES_URL = "http://localhost:9200"
ES_INDEX = "youtube_videos"


def search_videos(keyword: str, size: int = 10):
    es = Elasticsearch(ES_URL)

    response = es.search(
        index=ES_INDEX,
        body={
            "query": {
                "multi_match": {
                    "query": keyword,
                    "fields": [
                        "title^3",
                        "description",
                        "channelName",
                    ],
                }
            },
            "size": size,
        },
    )

    return response["hits"]["hits"]


if __name__ == "__main__":
    while True:
        keyword = input("search> ").strip()

        if keyword in {"exit", "quit"}:
            break

        if not keyword:
            continue

        results = search_videos(keyword)

        for index, hit in enumerate(results, start=1):
            item = hit["_source"]

            print("-" * 80)
            print(f"{index}. {item.get('title')}")
            print(f"channel: {item.get('channelName')}")
            print(f"views: {item.get('viewCount')}")
            print(f"duration: {item.get('duration')}")
            print(f"url: {item.get('url')}")

运行:

plain 复制代码
python search_videos.py

输入:

plain 复制代码
Cristiano

就可以检索刚才导入的视频数据。

五、效果对比:从手工整理到 API 任务化

这次实践里,URL 采集和关键词采集分别解决了两个问题。

URL 采集适合拿指定频道的数据。比如 C 罗频道,我们只需要把频道视频页填进去,再设置采集数量,就能拿到一批视频元数据。

关键词采集适合扩大数据量。比如关键词 Cristiano,一次请求 100 条,任务完成后可以下载结构化结果。从截图看,这类任务实际返回了几十条有效数据,积分消耗不到 1 个,用来做前期验证是比较划算的。

对比项 手工整理 Dataify API
数据入口 人工打开页面逐条整理 URL 或关键词提交任务
批量能力 不适合处理几十到上百条 可通过 num_of_posts 控制数量
结果格式 需要自己整理表格 支持 JSON、CSV、XLSX
状态追踪 依赖人工记录 任务列表展示状态和成功率
错误信息 不容易统一记录 结果中包含 success、error_code
后续入库 需要额外清洗 CSV / JSON 可以直接进入处理脚本
成本判断 时间成本不直观 后台可看到积分消耗

这里不建议只看"请求了多少条",更应该看"有效结果数"和"字段完整度"。

比如标题、URL、频道名、描述这些字段越完整,后面做搜索和分析就越顺。

六、最佳实践与扩展

这个项目第一版不用做得太复杂。能完成下面这条链路,就已经可以验证价值:

plain 复制代码
关键词 / URL
  ↓
视频元数据
  ↓
CSV / JSON 下载
  ↓
Elasticsearch 入库
  ↓
标题和描述检索

几个建议可以提前加上。

第一,先小批量测试。

URL 采集先设置 5 或 10 条,确认字段没问题后,再调到 100 或更多。

第二,保留 input 字段。 这个字段能告诉你数据来自哪个 URL 或关键词。后面如果同时采集多个关键词,比如 CristianoMessiMbappe,就可以靠 input 做来源区分。

第三,入库前做基础过滤。 标题为空、URL 为空、success 不是成功状态的数据,不建议直接进入搜索库。

第四,先采元数据,再处理更重的内容。

如果后续要继续做字幕、评论或训练数据,建议先用元数据筛选出高价值视频,再进入下一步。这样成本更好控制。

比较推荐的端到端流程是:

plain 复制代码
Dataify 视频数据采集 API
  ↓
视频元数据
  ↓
质量过滤
  ↓
Elasticsearch 检索
  ↓
筛选高价值视频
  ↓
字幕 / 评论数据
  ↓
训练数据或内容分析

对于个人视频搜索引擎来说,核心难点不是"能不能打开某个视频页面",而是能不能持续、稳定、批量地拿到结构化元数据。

自己写数据采集工具适合高度定制,官方 API 适合规范场景,通用采集工具适合一次性导出;而 Dataify 更适合把采集过程做成任务化流程,并直接输出 JSON、CSV、XLSX 等结构化结果。

这样不是单纯演示怎么点后台,而是通过 Dataify API 把视频元数据变成可检索、可分析、可继续加工的数据资产。