feat: kb-zhihu-metadata 支持 ZHIHU_COOKIE
- 安装 markdownify 依赖 - PlaywrightBrowser 支持传入 Cookie - ZhihuPlaywrightExtractor 支持传入 Cookie - zhihu_metadata.py 读取 ZHIHU_COOKIE 环境变量 - 更新 SKILL.md 添加 Cookie 配置说明
This commit is contained in:
@@ -9,6 +9,24 @@
|
||||
- User information extraction
|
||||
- JSON output format
|
||||
- Markdown conversion support
|
||||
- **Cookie 支持**:可配置 ZHIHU_COOKIE 访问需要登录的内容
|
||||
|
||||
## 环境变量
|
||||
|
||||
| 变量名 | 用途 | 必需 |
|
||||
|--------|------|------|
|
||||
| `ZHIHU_COOKIE` | 知乎登录 Cookie | 推荐(访问专栏等需要) |
|
||||
|
||||
**获取 ZHIHU_COOKIE**:
|
||||
1. 登录知乎网页版
|
||||
2. 打开开发者工具 (F12) → Application → Cookies → zhihu.com
|
||||
3. 复制 Cookie 字符串
|
||||
|
||||
```bash
|
||||
# 添加到 ~/.kb_env
|
||||
echo 'export ZHIHU_COOKIE="你的知乎Cookie"' >> ~/.kb_env
|
||||
source ~/.kb_env
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
|
||||
@@ -30,16 +30,42 @@ class PlaywrightBrowser:
|
||||
self.context = None
|
||||
self.page = None
|
||||
|
||||
def start(self):
|
||||
def start(self, cookie: str = None):
|
||||
"""启动浏览器"""
|
||||
from playwright.sync_api import sync_playwright
|
||||
self.playwright = sync_playwright().start()
|
||||
self.browser = self.playwright.chromium.launch(headless=self.headless)
|
||||
self.context = self.browser.new_context(
|
||||
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
|
||||
)
|
||||
|
||||
# 创建浏览器上下文
|
||||
context_options = {
|
||||
'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
|
||||
}
|
||||
|
||||
# 如果提供了 Cookie,设置 Cookie
|
||||
if cookie:
|
||||
context_options['cookies'] = self._parse_cookie(cookie)
|
||||
|
||||
self.context = self.browser.new_context(**context_options)
|
||||
self.page = self.context.new_page()
|
||||
self.page.set_default_timeout(self.timeout)
|
||||
|
||||
def _parse_cookie(self, cookie_str: str) -> list:
|
||||
"""解析 Cookie 字符串为 Playwright 格式"""
|
||||
cookies = []
|
||||
for item in cookie_str.split(';'):
|
||||
item = item.strip()
|
||||
if '=' in item:
|
||||
name, value = item.split('=', 1)
|
||||
cookies.append({
|
||||
'name': name.strip(),
|
||||
'value': value.strip(),
|
||||
'domain': '.zhihu.com',
|
||||
'path': '/',
|
||||
'secure': True,
|
||||
'httpOnly': False,
|
||||
'sameSite': 'Lax'
|
||||
})
|
||||
return cookies
|
||||
|
||||
def close(self):
|
||||
"""关闭浏览器"""
|
||||
@@ -84,14 +110,16 @@ class ZhihuPlaywrightExtractor:
|
||||
'question': r'zhihu\.com/question/(\d+)',
|
||||
}
|
||||
|
||||
def __init__(self, headless: bool = True, timeout: int = 30):
|
||||
def __init__(self, headless: bool = True, timeout: int = 30, cookie: str = None):
|
||||
"""
|
||||
初始化提取器
|
||||
|
||||
Args:
|
||||
headless: 是否使用无头浏览器
|
||||
timeout: 超时时间(秒)
|
||||
cookie: 知乎 Cookie 字符串(可选)
|
||||
"""
|
||||
self.cookie = cookie
|
||||
self.browser = PlaywrightBrowser(headless=headless, timeout=timeout)
|
||||
self.markdown_converter = MarkdownConverter()
|
||||
|
||||
@@ -383,8 +411,8 @@ class ZhihuPlaywrightExtractor:
|
||||
print(f"URL 类型: {url_type}")
|
||||
print(f"启动浏览器...")
|
||||
|
||||
# 启动浏览器
|
||||
self.browser.start()
|
||||
# 启动浏览器(传入 Cookie)
|
||||
self.browser.start(cookie=self.cookie)
|
||||
|
||||
print(f"访问页面: {url}")
|
||||
# 访问页面
|
||||
|
||||
@@ -23,43 +23,46 @@ if current_dir not in sys.path:
|
||||
from zhihu_extractor import ZhihuPlaywrightExtractor
|
||||
|
||||
|
||||
def extract_zvideo(video_id: str, headless: bool = True) -> Dict[str, Any]:
|
||||
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)
|
||||
return _extract_metadata(url, "zvideo", headless, cookie)
|
||||
|
||||
|
||||
def extract_zhuanlan(article_id: str, headless: bool = True) -> Dict[str, Any]:
|
||||
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)
|
||||
return _extract_metadata(url, "zhuanlan", headless, cookie)
|
||||
|
||||
|
||||
def extract_answer(answer_id: str, headless: bool = True) -> Dict[str, Any]:
|
||||
def extract_answer(answer_id: str, headless: bool = True, cookie: str = None) -> Dict[str, Any]:
|
||||
"""
|
||||
获取知乎回答元数据
|
||||
|
||||
Args:
|
||||
answer_id: 回答 ID (数字字符串)
|
||||
headless: 是否使用无头浏览器
|
||||
cookie: 知乎 Cookie(可选)
|
||||
|
||||
Returns:
|
||||
回答元数据字典
|
||||
@@ -67,25 +70,26 @@ def extract_answer(answer_id: str, headless: bool = True) -> Dict[str, Any]:
|
||||
# 回答需要构建包含问题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)
|
||||
return _extract_metadata(url, "answer", headless, cookie)
|
||||
|
||||
|
||||
def extract_question(question_id: str, headless: bool = True) -> Dict[str, Any]:
|
||||
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)
|
||||
return _extract_metadata(url, "question", headless, cookie)
|
||||
|
||||
|
||||
def _extract_metadata(url: str, content_type: str, headless: bool) -> Dict[str, Any]:
|
||||
def _extract_metadata(url: str, content_type: str, headless: bool, cookie: str = None) -> Dict[str, Any]:
|
||||
"""
|
||||
内部函数:提取元数据
|
||||
|
||||
@@ -93,11 +97,12 @@ def _extract_metadata(url: str, content_type: str, headless: bool) -> Dict[str,
|
||||
url: 知乎 URL
|
||||
content_type: 内容类型 (zvideo/zhuanlan)
|
||||
headless: 是否使用无头浏览器
|
||||
cookie: 知乎 Cookie(可选)
|
||||
|
||||
Returns:
|
||||
元数据字典
|
||||
"""
|
||||
extractor = ZhihuPlaywrightExtractor(headless=headless)
|
||||
extractor = ZhihuPlaywrightExtractor(headless=headless, cookie=cookie)
|
||||
result = extractor.extract(url)
|
||||
|
||||
if result:
|
||||
@@ -170,15 +175,18 @@ def main():
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 提取元数据
|
||||
# 获取知乎 Cookie(优先使用环境变量,其次使用参数)
|
||||
cookie = os.environ.get('ZHIHU_COOKIE')
|
||||
|
||||
# 提取元数据(传入 Cookie)
|
||||
if args.type == "zvideo":
|
||||
result = extract_zvideo(args.id, headless=args.headless)
|
||||
result = extract_zvideo(args.id, headless=args.headless, cookie=cookie)
|
||||
elif args.type == "zhuanlan":
|
||||
result = extract_zhuanlan(args.id, headless=args.headless)
|
||||
result = extract_zhuanlan(args.id, headless=args.headless, cookie=cookie)
|
||||
elif args.type == "answer":
|
||||
result = extract_answer(args.id, headless=args.headless)
|
||||
result = extract_answer(args.id, headless=args.headless, cookie=cookie)
|
||||
elif args.type == "question":
|
||||
result = extract_question(args.id, headless=args.headless)
|
||||
result = extract_question(args.id, headless=args.headless, cookie=cookie)
|
||||
else:
|
||||
print(f"不支持的类型: {args.type}")
|
||||
sys.exit(1)
|
||||
|
||||
Reference in New Issue
Block a user