81d67b7672
- Remove unnecessary sys.path.insert for current directory - Use relative import for zhihu_extractor - Package imports work correctly without path manipulation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
202 lines
6.0 KiB
Python
202 lines
6.0 KiB
Python
#!/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()
|