#!/usr/bin/env python3 """ 知乎元数据获取工具 支持以下内容类型: - zvideo: 知乎视频 (https://www.zhihu.com/zvideo/xxx) - zhuanlan: 知乎专栏文章 (https://zhuanlan.zhihu.com/p/xxx) - answer: 知乎回答 (https://www.zhihu.com/question/xxx/answer/yyy) - question: 知乎问题 (https://www.zhihu.com/question/xxx) """ import argparse import json import sys import os from typing import Dict, Optional, Any from .zhihu_extractor import ZhihuPlaywrightExtractor def extract_zvideo(video_id: str, headless: bool = True, cookie: str = None) -> Dict[str, Any]: """ 获取知乎视频元数据 Args: video_id: 视频 ID (数字字符串) headless: 是否使用无头浏览器 cookie: 知乎 Cookie(可选) Returns: 视频元数据字典 """ url = f"https://www.zhihu.com/zvideo/{video_id}" return _extract_metadata(url, "zvideo", headless, cookie) def extract_zhuanlan(article_id: str, headless: bool = True, cookie: str = None) -> Dict[str, Any]: """ 获取知乎专栏文章元数据 Args: article_id: 文章 ID (数字字符串) headless: 是否使用无头浏览器 cookie: 知乎 Cookie(可选) Returns: 文章元数据字典 """ url = f"https://zhuanlan.zhihu.com/p/{article_id}" return _extract_metadata(url, "zhuanlan", headless, cookie) def extract_answer(answer_id: str, headless: bool = True, cookie: str = None) -> Dict[str, Any]: """ 获取知乎回答元数据 Args: answer_id: 回答 ID (数字字符串) headless: 是否使用无头浏览器 cookie: 知乎 Cookie(可选) Returns: 回答元数据字典 """ # 回答需要构建包含问题ID的URL,但answer_id本身足以识别 # 使用一个占位符问题ID,因为提取器会从URL中提取answer_id url = f"https://www.zhihu.com/question/0/answer/{answer_id}" return _extract_metadata(url, "answer", headless, cookie) def extract_question(question_id: str, headless: bool = True, cookie: str = None) -> Dict[str, Any]: """ 获取知乎问题元数据 Args: question_id: 问题 ID (数字字符串) headless: 是否使用无头浏览器 cookie: 知乎 Cookie(可选) Returns: 问题元数据字典 """ url = f"https://www.zhihu.com/question/{question_id}" return _extract_metadata(url, "question", headless, cookie) def _extract_metadata(url: str, content_type: str, headless: bool, cookie: str = None) -> Dict[str, Any]: """ 内部函数:提取元数据 Args: url: 知乎 URL content_type: 内容类型 (zvideo/zhuanlan) headless: 是否使用无头浏览器 cookie: 知乎 Cookie(可选) Returns: 元数据字典 """ extractor = ZhihuPlaywrightExtractor(headless=headless, cookie=cookie) result = extractor.extract(url) if result: # 标准化输出格式 metadata = { "type": content_type, "id": result.get("content_id", ""), "url": result.get("url", url), "title": result.get("title", ""), "author": result.get("author", ""), "publish_time": result.get("publish_time", ""), "vote_up": result.get("vote_up", 0), "content_markdown": result.get("content_markdown", ""), "raw_html": result.get("content", ""), } return metadata else: return { "type": content_type, "error": "Failed to extract content", "url": url } def main(): """命令行入口""" parser = argparse.ArgumentParser( description="知乎元数据获取工具", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 支持的类型: zvideo - 知乎视频 (https://www.zhihu.com/zvideo/123456) zhuanlan - 知乎专栏文章 (https://zhuanlan.zhihu.com/p/123456) answer - 知乎回答 (https://www.zhihu.com/question/xxx/answer/yyy) question - 知乎问题 (https://www.zhihu.com/question/xxx) 示例: python zhihu_metadata.py --id "123456" --type zvideo python zhihu_metadata.py --id "123456" --type zhuanlan python zhihu_metadata.py --id "123456" --type answer python zhihu_metadata.py --id "123456" --type question """ ) parser.add_argument( "--id", "-i", required=True, help="知乎内容 ID (视频ID/文章ID/回答ID/问题ID)" ) parser.add_argument( "--type", "-t", required=True, choices=["zvideo", "zhuanlan", "answer", "question"], help="内容类型: zvideo=视频, zhuanlan=专栏文章, answer=回答, question=问题" ) parser.add_argument( "--output", "-o", default=None, help="输出文件路径 (默认: stdout)" ) parser.add_argument( "--headless", action="store_true", default=True, help="使用无头浏览器模式 (默认: True)" ) args = parser.parse_args() # 获取知乎 Cookie(优先使用环境变量,其次使用参数) cookie = os.environ.get('ZHIHU_COOKIE') # 提取元数据(传入 Cookie) if args.type == "zvideo": result = extract_zvideo(args.id, headless=args.headless, cookie=cookie) elif args.type == "zhuanlan": result = extract_zhuanlan(args.id, headless=args.headless, cookie=cookie) elif args.type == "answer": result = extract_answer(args.id, headless=args.headless, cookie=cookie) elif args.type == "question": result = extract_question(args.id, headless=args.headless, cookie=cookie) else: print(f"不支持的类型: {args.type}") sys.exit(1) # 输出结果 json_output = json.dumps(result, ensure_ascii=False, indent=2) if args.output: with open(args.output, 'w', encoding='utf-8') as f: f.write(json_output) print(f"结果已保存到: {args.output}") else: print(json_output) if __name__ == "__main__": main()