这是一个非常典型的爬虫实战需求,但也是风控极高的场景。知乎的反爬机制在国内属于第一梯队,直接硬爬API极易触发验证码甚至封号。
下面为你提供一套“合规优先 + 工程化兜底”的完整方案。我们不鼓励暴力抓取,而是教你如何用技术手段在尊重平台规则的前提下,高效获取并结构化保存内容。
⚠️风险与合规声明
本文代码仅供个人学习研究、知识管理备份使用。请严格遵守《知乎用户协议》及robots.txt,控制请求频率,禁止商用、批量采集或绕过付费墙。高频爬取可能导致账号受限,作者不承担连带责任。
一、 技术选型:为什么不建议直接调API?
| 方案 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
| 知乎Open API | 官方接口,稳定合规 | 权限极小,几乎不开放搜索/高赞筛选 | ⭐ |
| requests直连API | 速度快,轻量 | 签名算法频繁更新,反爬极强,易封号 | ⭐⭐ |
| Selenium/Playwright | 模拟真人,过验证容易 | 资源消耗大,速度慢 | ⭐⭐⭐ |
| Cookie注入 + 限流API | 平衡效率与安全,复用登录态 | 需手动登录一次,仍有频率限制 | ⭐⭐⭐⭐⭐ |
核心思路:用浏览器手动登录获取合法Cookie,通过Python携带Cookie访问知乎内部Web API(非公开但前端使用的接口),配合严格限流和内容解析,实现“半自动化”采集。
二、 完整实现代码
1. 环境准备
pipinstallrequests markdownify beautifulsoup4 fake-useragent tenacity2. 核心采集模块
importrequestsimporttimeimportjsonfromtenacityimportretry,stop_after_attempt,wait_exponentialfromfake_useragentimportUserAgentclassZhihuCrawler:BASE_URL="https://www.zhihu.com/api/v4"def__init__(self,cookie_str:str):self.session=requests.Session()self.session.headers.update({"User-Agent":UserAgent().chrome,"Referer":"https://www.zhihu.com/search?type=content&q=Python%E5%AD%A6%E4%B9%A0%E5%B9%B2%E8%B4%A7","Cookie":cookie_str})@retry(stop=stop_after_attempt(3),wait=wait_exponential(multiplier=2,min=10))defsearch_high_vote(self,keyword="Python学习干货",min_vote=100,limit=20):""" 搜索高赞回答,按点赞数排序 注意:知乎搜索API的sort_by参数可能随版本变化,需抓包确认 """url=f"{self.BASE_URL}/search_v3"params={"t":"general","q":keyword,"correction":1,"offset":0,"limit":limit,"filter_fields":"","lc_idx":0,"show_all_topics":0,"search_source":"Filter","time_interval":"","sort_by":"voteups"# 按点赞数排序的关键参数}resp=self.session.get(url,params=params)resp.raise_for_status()data=resp.json()results=[]foritemindata.get("data",[]):obj=item.get("object",{})voteup_count=obj.get("voteup_count",0)# 过滤低赞内容ifvoteup_count<min_vote:continueresults.append({"title":obj.get("question",{}).get("title","无标题"),"content":obj.get("excerpt",""),# 摘要"full_content":obj.get("content",""),# 富文本HTML"voteup_count":voteup_count,"author":obj.get("author",{}).get("name","匿名"),"url":obj.get("url","").replace("api.zhihu.com/answers","zhihu.com/question/xxx/answer"),"created_time":obj.get("created_time",0)})returnresultsdefget_answer_detail(self,answer_id:str)->str:"""获取回答完整HTML内容(搜索接口返回的可能是截断版)"""url=f"{self.BASE_URL}/answers/{answer_id}"resp=self.session.get(url)resp.raise_for_status()returnresp.json().get("content","")3. Markdown转换与保存模块
frommarkdownifyimportmarkdownifyasmdimportosimportredefhtml_to_markdown(html_content:str)->str:"""将知乎富文本HTML转为干净的Markdown"""ifnothtml_content:return""# 知乎特有的数据链接、公式等预处理html_content=re.sub(r'<a[^>]+data-draft-node[^>]*>','',html_content)text=md(html_content,heading_style="ATX",bullets="-",strip=['script','style','noscript'],convert=['img','a','strong','em','code','pre','blockquote'])# 清理多余空行text=re.sub(r'\n{3,}','\n\n',text)returntext.strip()defsave_as_markdown(articles:list,output_dir="./zhihu_python_dry_goods"):os.makedirs(output_dir,exist_ok=True)fori,articleinenumerate(articles):safe_title=re.sub(r'[\\/*?:"<>|]','_',article['title'])[:80]filename=f"{i+1:03d}_{safe_title}_{article['voteup_count']}赞.md"filepath=os.path.join(output_dir,filename)content_md=html_to_markdown(article.get('full_content')orarticle['content'])# 添加YAML Front Matter元数据front_matter=f"""--- title: "{article['title']}" author: "{article['author']}" votes:{article['voteup_count']}source: "{article['url']}" crawled_at: "{time.strftime('%Y-%m-%d %H:%M:%S')}" tags: [Python, 学习干货] --- """withopen(filepath,'w',encoding='utf-8')asf:f.write(front_matter+content_md)print(f"✅ 已保存:{filename}")time.sleep(3)# ⚠️ 写入间隔,降低磁盘IO压力,也作为二次限流4. 主流程串联
if__name__=="__main__":# 🔑 从浏览器开发者工具 → Network → 任意请求 → Headers → Cookie 复制COOKIE="你的知乎Cookie字符串"crawler=ZhihuCrawler(COOKIE)# 搜索并筛选 ≥500赞 的回答answers=crawler.search_high_vote(keyword="Python学习干货",min_vote=500,limit=20)print(f"🔍 找到{len(answers)}篇高赞回答")# 可选:逐篇获取完整内容(搜索接口可能截断长文)# for ans in answers:# ans['full_content'] = crawler.get_answer_detail(ans['id'])# time.sleep(5) # 详情页请求间隔更长save_as_markdown(answers)三、 关键避坑指南
- Cookie有效期:知乎Cookie通常7-30天有效,建议封装Cookie刷新检测逻辑,失效时自动提醒手动更新。
- 搜索接口变动:知乎API未公开文档,
sort_by=voteups等参数可能随时调整。务必先用浏览器F12抓包确认当前实际请求参数,再写入代码。 - 内容截断问题:搜索API返回的
content字段常被截断为摘要。若需全文,必须用answer_id单独请求详情接口,且该接口限流更严。 - 图片防盗链:知乎图片有Referer校验。保存到本地Markdown后,图片链接可能失效。建议增加图片下载+本地替换步骤,或使用图床转存。
- 法律红线:根据《数据安全法》和《个人信息保护法》,爬取内容不得包含用户隐私信息(如头像、个人简介中的联系方式),保存时务必脱敏处理。
四、 更优雅的替代方案
如果你的核心需求是个人知识管理而非“爬取”本身,强烈建议考虑:
- 知乎官方导出:部分创作者开放了内容授权,可通过私信获取原文。
- RSS订阅:使用RSSHub生成知乎话题/收藏夹的RSS源,配合Readwise/Obsidian自动同步,零风险、可持续。
- 浏览器插件:如「简悦 SimpRead」「MarkDownload」,单篇一键转Markdown,适合精读场景,完全规避反爬。
💡工程师思维提示
爬虫只是手段,不是目的。当平台提供了合规的数据获取路径时,优先选择合规路径。把精力花在内容结构化、知识图谱构建、检索增强上,远比和反爬系统斗智斗勇更有长期价值。
五、 总结
| 环节 | 最佳实践 |
|---|---|
| 身份认证 | Cookie注入 + 定期刷新检测 |
| 请求策略 | 严格限流(3-5s/次) + 指数退避重试 |
| 内容解析 | markdownify + 正则清洗知乎特有标签 |
| 持久化 | YAML Front Matter + 文件名含票数便于排序 |
| 合规底线 | 仅限个人学习、脱敏处理、尊重robots.txt |
这套方案在安全性、可维护性、内容质量三者间取得了较好平衡。记住:好的爬虫代码,首先是不会给目标站点造成负担的代码。