← 返回
未分类

bilibili-video-search-and-download

Search and download Bilibili videos using yt-dlp and httpx. Use this skill whenever the user wants to search Bilibili for videos by keyword, retrieve video m...
使用yt-dlp和httpx搜索并下载B站(哔哩哔哩)视频,适用于按关键词搜索、获取视频信息、下载视频等需求。
ecojust
未分类 clawhub v1.0.0 1 版本 99447.5 Key: 无需
★ 0
Stars
📥 180
下载
💾 0
安装
1
版本
#latest

概述

Bilibili Video Search

Use the bilibili-api-python library to search Bilibili videos by keyword and save results to search.json.

Environment Checks (Prerequisites)

Before running any search script, perform these checks:

1. Python availability

python3 --version

2. Virtual environment & dependencies

macOS may block system-wide pip install due to PEP 668. Always use a venv:

# Create venv if it doesn't exist
python3 -m venv /tmp/bilibili_venv

# Install dependencies (search)
/tmp/bilibili_venv/bin/pip install bilibili-api-python httpx

# Install dependencies (download)
/tmp/bilibili_venv/bin/pip install yt-dlp

# Fallback: if lxml download fails (common on slow networks), use douban mirror
/tmp/bilibili_venv/bin/pip install yt-dlp -i https://pypi.doubanio.com/simple/

Also ensure ffmpeg is installed (required for merging audio/video streams):

ffmpeg -version
# If not installed: brew install ffmpeg

3. Import check

Verify the library works:

/tmp/bilibili_venv/bin/python3 -c "import httpx; print('httpx OK')"

If bilibili_api is also available:

/tmp/bilibili_venv/bin/python3 -c "from bilibili_api import search; print('bilibili_api OK')"

4. Network check

Ensure Bilibili API is reachable:

curl -s -o /dev/null -w "%{http_code}" "https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword=test"

Expected: 200

> Note: If bilibili-api-python is unavailable (e.g. lxml fails to download), fall back to calling the Bilibili REST API directly with httpx — see the example script below.

Core API

from bilibili_api import search

results = await search.search_by_type(
    keyword="关键词",
    search_type=search.SearchObjectType.VIDEO,
    page=1,
    page_size=20,
)

The response is a dict with a "result" key containing a list of video dicts.

Key Fields in Each Result

FieldDescriptionExample
----------------------------------------------------------------------
bvidBV号"BV1uv411q7Mv"
title标题(含 高亮标签)"【黑神话】..."
authorUP主名"萌宠教主"
play播放量8838288
duration时长(mm:ss 或秒)"2:51"
pic封面图 URL"http://i2.hdslb.com/..."
pubdate发布时间戳1595203214

Sorting & Filtering

from bilibili_api.search import OrderVideo

await search.search_by_type(
    keyword="黑神话",
    search_type=search.SearchObjectType.VIDEO,
    order_type=OrderVideo.CLICK,       # 按播放量排序
    order_sort=0,                       # 0=降序, 1=升序
    time_range=7,                       # 7天内, -1=全部
    page=1,
    page_size=42,
)

OrderVideo options:

  • CLICK — 播放量
  • PUBDATE — 发布日期
  • DANMAKU — 弹幕数
  • STOW — 收藏数
  • SCORE — 评分

HTML Tags Cleanup

Bilibili titles contain highlight tags. Strip them:

title = v.get("title", "").replace("<em class=\"keyword\">", "").replace("</em>", "")

Save Results to JSON

Always save results to search.json with this structure:

{
  "keyword": "黑神话",
  "timestamp": "2026-05-27T23:31:55",
  "total": 20,
  "videos": [
    {
      "bvid": "BV1yup4ewEKz",
      "title": "【黑神话】...",
      "author": "萌宠教主",
      "play": 8838288,
      "duration": "2:51",
      "url": "https://www.bilibili.com/video/BV1yup4ewEKz"
    }
  ]
}

Video Download

Use yt-dlp to download Bilibili videos. The download_video.py script wraps yt-dlp with sensible defaults.

Usage

# By BV号
/tmp/bilibili_venv/bin/python3 scripts/download_video.py BV1qD4y1U7fs

# By full URL
/tmp/bilibili_venv/bin/python3 scripts/download_video.py https://www.bilibili.com/video/BV1qD4y1U7fs

Features

  • Downloads best available quality (auto-selects format)
  • Merges separate audio/video streams into single MP4 via ffmpeg
  • Embeds thumbnail and metadata into the output file
  • Files saved to downloads/ directory (relative to current working directory, i.e. the project root)
  • Post-download verification: checks file exists and is not empty

> Important: The script must be run from the project root directory (where opencode.json is), not from the scripts/ folder itself. The downloads/ folder is created relative to the current working directory.

Troubleshooting & Known Issues

1. --print Implies --simulate (Download Does Not Actually Happen)

yt-dlp --print implies --simulate by default — it only prints the template result without downloading.

# ❌ 只计算文件名,不会下载
yt-dlp --print filename "URL"

# ✅ 下载完成后打印最终文件名
yt-dlp --print after_move:filename "URL"

Per yt-dlp docs: --print "Implies --quiet. Implies --simulate unless --no-simulate or later stages of WHEN are used." The download_video.py script uses --print after_move:filename which is a "later stage" and correctly triggers the download.

2. Always Verify Files After Download

The script output may claim a download succeeded when in fact the file was not created (e.g., if --print was used without after_move, or if yt-dlp encountered a silent error). Always verify:

  • The output file path exists on disk
  • The file size is greater than 0 bytes
  • (If using the script directly) Check ls -lh downloads/ after download

The download_video.py script now performs these checks automatically and exits with an error if the file is missing or empty.

3. Anthology Videos (合集)

Some Bilibili BV号 are anthologies (合集) containing multiple videos under one BV号. yt-dlp downloads the first video in the anthology by default:

[BiliBili] BV1mD4y1U7z9: Extracting videos in anthology
[BiliBili] BV1mD4y1U7z9: Downloading video formats for cid 348538681

If you need a different episode, use the specific URL with ?p=N parameter or the episode page URL directly.

4. Working Directory

The script creates downloads/ relative to the current working directory, not relative to the script location. Always run from the project root:

# ✅ 正确:在项目根目录运行
cd /path/to/project
/tmp/bilibili_venv/bin/python3 .opencode/skill/.../scripts/download_video.py BVxxxx

# ❌ 错误:会在 scripts/ 旁边创建 downloads/
cd .opencode/skill/.../scripts/
python3 download_video.py BVxxxx

5. Authentication for HD Quality

Without login, Bilibili limits downloads to 480P. For 1080P+/4K, pass browser cookies:

/tmp/bilibili_venv/bin/yt-dlp --cookies-from-browser chrome \
  -o "downloads/%(title)s.%(ext)s" \
  --merge-output-format mp4 \
  "https://www.bilibili.com/video/BV1qD4y1U7fs"

6. Filename Handling

yt-dlp keeps Chinese characters and punctuation (【】《》~, etc.) in filenames as-is. This can cause shell globbing issues when using rm or other commands. Always quote file paths with double quotes.

Download Script

# scripts/download_video.py
import subprocess
import sys
from pathlib import Path

DOWNLOAD_DIR = Path("downloads")

def download_video(url: str, output_dir: Path):
    output_dir.mkdir(parents=True, exist_ok=True)
    print(f"正在下载: {url}")
    result = subprocess.run(
        [
            sys.executable, "-m", "yt_dlp",
            "-o", str(output_dir / "%(title)s.%(ext)s"),
            "--merge-output-format", "mp4",
            "--write-thumbnail",
            "--embed-thumbnail",
            "--embed-metadata",
            "--print", "after_move:filename",
            url,
        ],
        capture_output=True, text=True,
    )

    if result.returncode != 0:
        print(f"下载失败: {result.stderr.strip()}")
        sys.exit(1)

    out_path = result.stdout.strip()
    if not out_path:
        print("下载失败: 无法获取输出文件路径")
        sys.exit(1)

    out_file = Path(out_path)
    if not out_file.exists():
        print(f"下载失败: 文件不存在 — {out_path}")
        sys.exit(1)
    if out_file.stat().st_size == 0:
        print(f"下载失败: 文件为空 — {out_path}")
        sys.exit(1)

    print(f"下载完成: {out_path} ({out_file.stat().st_size / 1024 / 1024:.1f}MB)")

def main():
    if len(sys.argv) < 2:
        print("用法: python download_video.py <B站视频URL 或 BV号>")
        print("示例: python download_video.py https://www.bilibili.com/video/BV1qD4y1U7fs")
        print("示例: python download_video.py BV1qD4y1U7fs")
        sys.exit(1)

    arg = sys.argv[1]
    if arg.startswith("BV"):
        url = f"https://www.bilibili.com/video/{arg}"
    else:
        url = arg

    download_video(url, DOWNLOAD_DIR)

if __name__ == "__main__":
    main()

Complete Example Script

import asyncio
import json
from datetime import datetime
from bilibili_api import search


def clean_title(title: str) -> str:
    return title.replace("<em class=\"keyword\">", "").replace("</em>", "")


async def search_videos(keyword: str, page: int = 1, page_size: int = 20):
    res = await search.search_by_type(
        keyword=keyword,
        search_type=search.SearchObjectType.VIDEO,
        page=page,
        page_size=page_size,
    )
    return res.get("result", [])


def save_results(keyword: str, results: list):
    data = {
        "keyword": keyword,
        "timestamp": datetime.now().isoformat(),
        "total": len(results),
        "videos": [
            {
                "bvid": v.get("bvid", ""),
                "title": clean_title(v.get("title", "")),
                "author": v.get("author", ""),
                "play": v.get("play", 0),
                "duration": v.get("duration", ""),
                "url": f"https://www.bilibili.com/video/{v.get('bvid', '')}",
            }
            for v in results
        ],
    }
    with open("search.json", "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    print(f"结果已保存到 search.json ({len(results)} 条)")


async def main():
    keyword = input("请输入搜索关键词: ").strip()
    if not keyword:
        print("关键词不能为空")
        return
    results = await search_videos(keyword)
    if not results:
        print("未找到相关视频")
        return
    save_results(keyword, results)
    for i, v in enumerate(results, 1):
        title = clean_title(v.get("title", ""))
        print(f"{i:2d}. {title} — {v.get('author')} | 播放: {v.get('play')}")


if __name__ == "__main__":
    asyncio.run(main())

Dependencies

For search

python3 -m venv /tmp/bilibili_venv
/tmp/bilibili_venv/bin/pip install httpx

If bilibili-api-python is needed (optional):

/tmp/bilibili_venv/bin/pip install bilibili-api-python

For download

/tmp/bilibili_venv/bin/pip install yt-dlp
brew install ffmpeg          # for merging audio/video streams

If PyPI is slow, use a mirror:

/tmp/bilibili_venv/bin/pip install yt-dlp -i https://pypi.doubanio.com/simple/

Always run scripts with the venv Python:

/tmp/bilibili_venv/bin/python3 script.py

The library is fully async — always use asyncio.run() to execute.

版本历史

共 1 个版本

  • v1.0.0 当前
    2026-05-29 21:30 安全 安全

安全检测

腾讯云安全 (Keen)

安全,无风险
查看报告

腾讯云安全 (Sanbu)

安全,无风险
查看报告

🔗 相关推荐

ai-intelligence

self-improving agent

pskoett
捕获经验教训、错误及修正内容,以实现持续改进。适用于以下场景:(1)命令或操作意外失败;(2)用户纠正Claude(如“不,那不对……”“实际上……”);(3)用户请求的功能不存在;(4)外部API或工具出现故障;(5)Claude发现自身
★ 4,065 📥 802,098
ai-intelligence

Self-Improving + Proactive Agent

ivangdavila
自我反思+自我批评+自我学习+自组织记忆。智能体评估自身工作、发现错误并持续改进。
★ 1,364 📥 319,270
developer-tools

Github

steipete
使用 `gh` CLI 与 GitHub 交互,通过 `gh issue`、`gh pr`、`gh run` 和 `gh api` 管理议题、PR、CI 运行及高级查询。
★ 672 📥 324,671