Python爬虫实战:知识挖掘机 - 知乎问答与专栏文章的深度分页采集系统(附CSV导出 + SQLite持久化存储)!

㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~

㊙️本期爬虫难度指数:⭐⭐

🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:

      • [🌟 开篇语](#🌟 开篇语)
      • [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
      • [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
      • [3️⃣ 合规与注意事项(必写)](#3️⃣ 合规与注意事项(必写))
      • [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
      • [5️⃣ 环境准备与依赖安装(可复现)](#5️⃣ 环境准备与依赖安装(可复现))
      • [6️⃣ 核心实现:请求层(Fetcher)](#6️⃣ 核心实现:请求层(Fetcher))
      • [7️⃣ 核心实现:解析层(Parser)](#7️⃣ 核心实现:解析层(Parser))
      • [8️⃣ 数据存储与导出(Storage)](#8️⃣ 数据存储与导出(Storage))
      • [9️⃣ 运行方式与结果展示(必写)](#9️⃣ 运行方式与结果展示(必写))
      • [🔟 常见问题与排错(强烈建议写)](#🔟 常见问题与排错(强烈建议写))
      • [1️⃣1️⃣ 进阶优化:赞数分布可视化](#1️⃣1️⃣ 进阶优化:赞数分布可视化)
      • [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
      • [🌟 文末](#🌟 文末)
        • [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
        • [✅ 互动征集](#✅ 互动征集)
        • [✅ 免责声明](#✅ 免责声明)

🌟 开篇语

哈喽,各位小伙伴们你们好呀~我是【喵手】。

运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO

欢迎大家常来逛逛,一起学习,一起进步~🌟

我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略反爬对抗 ,从数据清洗分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上

📌 专栏食用指南(建议收藏)

  • ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
  • ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
  • ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
  • ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用

📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。

💕订阅后更新会优先推送,按目录学习更高效💯~

1️⃣ 摘要(Abstract)

本文将构建一个生产级的知乎爬虫。区别于效率低下的 Selenium 模拟,我们将直接逆向分析知乎的 API v4 接口,实现对指定问题下所有回答指定专栏下所有文章的高效采集。

读完本文,你将获得

  • 掌握知乎 API 的分页游标(Cursor)机制,实现无限滑动抓取。
  • 学会使用 PyQueryBeautifulSoup 清洗富文本(HTML)内容,提取纯净摘要。
  • 一套完整的点赞数-评论数可视化分析方案。

2️⃣ 背景与需求(Why)

  • 为什么要爬

    • 舆情分析:分析"ChatGPT"相关话题下,高赞回答的情绪是焦虑还是乐观。
    • 内容聚合:将某个技术专栏(如"PyTorch源码解析")的文章导出为 PDF 或 Markdown 本地阅读。
    • 用户画像:分析大 V 的粉丝粘性(点赞/阅读比)。
  • 目标对象

    • Question (问答)https://www.zhihu.com/question/12345678
    • Column (专栏)https://zhuanlan.zhihu.com/c_12345678
  • 目标字段清单

    1. id: 回答/文章唯一 ID。
    2. author: 作者昵称。
    3. excerpt: 摘要(或清洗后的正文)。
    4. voteup_count: 赞同数(核心指标)。
    5. comment_count: 评论数。
    6. created_time: 创建时间。

3️⃣ 合规与注意事项(必写)

知乎的风控在业界也是数一数二的,请务必遵守:

  1. Cookie 隐私 :代码中需要填入你自己的 z_c0 Cookie。千万不要将包含 Cookie 的代码上传到 GitHub,否则你的账号可能会被他人盗用。
  2. 频率限制 :知乎 API 对并发非常敏感。建议设置 2-4 秒 的随机延时。
  3. 内容版权 :知乎协议明确禁止未经授权的商业转载。抓取内容仅限个人学习分析,严禁打包出售"知乎合集"。

4️⃣ 技术选型与整体流程(What/How)

  • 技术栈

    • 请求requests(配合 Headers 伪装)。
    • 清洗BeautifulSoup4(知乎 API 返回的 content 是 HTML 格式,必须清洗)。
    • 存储CSVJSONL
  • 流程图
    构造 API URL → 注入 Cookie → 获取 JSON → 解析 data 列表 → 提取 paging.next 链接 → 递归抓取下一页 → 清洗 HTML → 落库

5️⃣ 环境准备与依赖安装(可复现)

Python 版本 :3.9+
依赖安装

bash 复制代码
pip install requests beautifulsoup4 pandas loguru

项目结构

text 复制代码
ZhihuCrawler/
├── data/               # 数据存放
├── cookies.txt         # 存放 Cookie(这是个好习惯,不写在代码里)
├── zhihu_spider.py     # 主程序
└── cleaner.py          # HTML 清洗工具

6️⃣ 核心实现:请求层(Fetcher)

知乎的 API URL 非常长,包含了很多 include 参数。我们需要构建一个通用的 Fetcher。

关键点 :你需要先在浏览器登录知乎,按 F12 找到任意一个网络请求,复制 cookie 字段的值。

python 复制代码
import requests
import time
import random
from loguru import logger

class ZhihuFetcher:
    def __init__(self, cookie):
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
            "Cookie": cookie,
            "Referer": "https://www.zhihu.com/"
        }
        self.session = requests.Session()

    def fetch_url(self, url):
        """通用请求方法,处理分页 URL"""
        try:
            # 随机休眠,模拟人类阅读
            time.sleep(random.uniform(2, 4))
            
            response = self.session.get(url, headers=self.headers, timeout=15)
            
            if response.status_code == 200:
                return response.json()
            elif response.status_code == 401 or response.status_code == 403:
                logger.critical("⛔ 权限验证失败!请检查 Cookie 是否过期。")
                return None
            else:
                logger.error(f"❌ 请求失败: {response.status_code}")
                return None
        except Exception as e:
            logger.error(f"⚠️ 网络异常: {e}")
            return None

    def get_first_page_url(self, target_id, target_type="answer"):
        """构造第一页的 API URL"""
        limit = 20
        # 知乎 v4 接口的 include 参数决定了能拿到哪些字段
        include_params = "data[*].is_normal,admin_closed_comment,reward_info,is_collapsed,annotation_action,annotation_detail,collapse_reason,is_sticky,collapsed_by,suggest_edit,comment_count,can_comment,content,editable_content,voteup_count,reshipment_settings,comment_permission,created_time,updated_time,review_info,relevant_info,question,excerpt,relationship.is_authorized,is_author,voting,is_thanked,is_nothelp,is_labeled,is_recognized,paid_info,paid_info_content;data[*].mark_infos,author.follower_count,badge[?(type=best_answerer)].topics"
        
        if target_type == "answer":
            # 问题下的回答列表
            return f"https://www.zhihu.com/api/v4/questions/{target_id}/answers?include={include_params}&limit={limit}&offset=0&platform=desktop&sort_by=default"
        elif target_type == "article":
            # 专栏下的文章列表
            return f"https://www.zhihu.com/api/v4/columns/{target_id}/items?limit={limit}&offset=0"

7️⃣ 核心实现:解析层(Parser)

知乎返回的 content 是一大段 HTML,包含 <p>, <b>, <img> 等标签。为了存入 Excel 或做 NLP 分析,我们必须把它清洗成纯文本。

python 复制代码
from bs4 import BeautifulSoup
import datetime

class ZhihuParser:
    @staticmethod
    def clean_html(html_content):
        """将 HTML 清洗为纯文本"""
        if not html_content: return ""
        soup = BeautifulSoup(html_content, 'html.parser')
        # 提取纯文本,并用换行符连接
        return soup.get_text(separator="\n", strip=True)

    def parse_list(self, json_data):
        if not json_data or 'data' not in json_data:
            return [], None
            
        parsed_items = []
        for item in json_data['data']:
            try:
                # 区分是回答还是文章
                item_type = item.get('type') # 'answer' or 'article'
                
                # 提取核心字段
                content_html = item.get('content') or item.get('excerpt')
                pure_text = self.clean_html(content_html)
                
                # 时间戳转换
                created_ts = item.get('created_time')
                create_dt = datetime.datetime.fromtimestamp(created_ts).strftime('%Y-%m-%d %H:%M') if created_ts else "N/A"

                entry = {
                    "id": item.get('id'),
                    "type": item_type,
                    "author": item.get('author', {}).get('name', 'Anonymous'),
                    "title": item.get('question', {}).get('title') if item_type == 'answer' else item.get('title'),
                    "voteup_count": item.get('voteup_count', 0),
                    "comment_count": item.get('comment_count', 0),
                    "excerpt": pure_text[:200], # 只存前200字预览,节省空间
                    "created_time": create_dt,
                    "url": item.get('url') # API 链接,需自行转换为前端链接
                }
                parsed_items.append(entry)
            except Exception as e:
                logger.warning(f"⚠️ 解析单条数据出错: {e}")
                continue
        
        # 提取下一页的 URL (Pagination)
        paging = json_data.get('paging', {})
        next_url = paging.get('next')
        is_end = paging.get('is_end', False)
        
        # 如果 is_end 为 True,或者 next URL 为空,则停止
        real_next_url = next_url if not is_end else None
        
        return parsed_items, real_next_url

8️⃣ 数据存储与导出(Storage)

python 复制代码
import pandas as pd
import os

def save_to_csv(data_list, filename="zhihu_data.csv"):
    df = pd.DataFrame(data_list)
    # 按赞同数倒序排列
    if 'voteup_count' in df.columns:
        df = df.sort_values(by='voteup_count', ascending=False)
    
    file_path = f"data/{filename}"
    os.makedirs("data", exist_ok=True)
    
    # 增量写入模式 (mode='a') 或 覆盖模式 (mode='w'),这里演示覆盖
    df.to_csv(file_path, index=False, encoding='utf-8-sig')
    logger.success(f"💾 已保存 {len(df)} 条数据至 {file_path}")

9️⃣ 运行方式与结果展示(必写)

主逻辑控制:支持自动翻页。

python 复制代码
def main():
    # 配置区
    YOUR_COOKIE = '这里填入你的 z_c0 Cookie' 
    TARGET_ID = "55493026" # 示例:知乎关于"Python"的一个热门问题 ID
    TARGET_TYPE = "answer" # 或者 "article" (如果是专栏 ID)
    MAX_PAGES = 5          # 演示抓取 5 页
    
    fetcher = ZhihuFetcher(YOUR_COOKIE)
    parser = ZhihuParser()
    
    # 获取第一页
    next_url = fetcher.get_first_page_url(TARGET_ID, TARGET_TYPE)
    all_data = []
    
    page_count = 0
    while next_url and page_count < MAX_PAGES:
        page_count += 1
        logger.info(f"🔄 正在抓取第 {page_count} 页...")
        
        json_data = fetcher.fetch_url(next_url)
        if not json_data: break
        
        items, next_url = parser.parse_list(json_data)
        all_data.extend(items)
        logger.info(f"✅ 本页获取 {len(items)} 条,共计 {len(all_data)} 条。")
        
        if not next_url:
            logger.info("🏁 已到达最后一页。")
            break

    save_to_csv(all_data, f"zhihu_{TARGET_ID}.csv")
    visualize_votes(all_data)

if __name__ == "__main__":
    main()

示例输出(CSV Preview)

id author voteup_count comment_count excerpt created_time
19823... 阿里云云栖号 12500 320 Python 是一门... 2023-11-20 10:00
28371... 匿名用户 8900 120 谢邀。利益相关... 2023-10-15 14:30

🔟 常见问题与排错(强烈建议写)

  1. 关于 x-zse-96 签名错误

    • 现象 :如果你不使用 Cookie 或者 URL 参数变动太大,可能会返回 403 Forbidden,提示需要签名。
    • 对策 :最简单的办法就是完全模拟浏览器的 API 调用 。不要自己瞎改 include 参数里的字段,直接复制浏览器 Network 面板里的 URL 参数,并带上完整的 Cookie。
  2. 内容截断

    • 现象 :抓到的 content 只有一半。
    • 原因:非会员或者 Cookie 失效。知乎有时对长回答只返回预览。
    • 对策:确保账号登录且 Cookie 新鲜。
  3. 封号风险

    • 警告 :知乎对爬虫非常严格。如果只抓几十页通常没事,但如果你想抓取全站百万级数据,必死无疑。请控制在低速、小规模采集。

1️⃣1️⃣ 进阶优化:赞数分布可视化

抓取知乎的一大乐趣就是看"二八定律":绝大多数赞同集中在少数几个神回复上。

python 复制代码
import pandas as pd
import matplotlib.pyplot as plt

def visualize_votes(data_list):
    if not data_list: return
    df = pd.DataFrame(data_list)
    
    plt.figure(figsize=(10, 6))
    
    # 绘制 赞同数 vs 评论数 的散点图
    plt.scatter(df['voteup_count'], df['comment_count'], alpha=0.6, c='dodgerblue', edgecolors='k')
    
    # English Labels
    plt.title("Zhihu Answers: Votes vs Comments Distribution", fontsize=14)
    plt.xlabel("Vote Count (Log Scale)", fontsize=12)
    plt.ylabel("Comment Count", fontsize=12)
    
    # 使用对数坐标,因为知乎大V的赞数可能几十万,普通人只有几个
    plt.xscale('log')
    plt.grid(True, linestyle='--', alpha=0.5)
    
    plt.tight_layout()
    plt.savefig("data/zhihu_analysis.png")
    logger.success("📊 图表已生成: data/zhihu_analysis.png")

1️⃣2️⃣ 总结与延伸阅读

复盘

知乎爬虫的核心在于API 的利用HTML 的清洗

  • 我们绕过了 Selenium 的龟速渲染,直接拿到了结构化的 JSON。
  • 我们利用 BeautifulSoup 将复杂的回答内容变成了可供分析的纯文本。

下一步挑战

  • 想法(Idea)抓取:知乎"想法"的接口和回答不同,更像微博。
  • 评论区盖楼 :知乎的评论也是嵌套的(子评论),想抓取评论区需要递归调用 comments 接口。
  • PDF 生成 :利用 WeasyPrintpdfkit,将清洗后的 HTML 重新排版,生成一本精美的《知乎高赞回答集锦.pdf》。

🌟 文末

好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥

✅ 专栏持续更新中|建议收藏 + 订阅

墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:

✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)

📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?

评论区留言告诉我你的需求,我会优先安排实现(更新)哒~


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)

⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)

⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


✅ 免责声明

本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。

使用或者参考本项目即表示您已阅读并同意以下条款:

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
相关推荐
aiguangyuan3 小时前
基于BERT的中文命名实体识别实战解析
人工智能·python·nlp
铉铉这波能秀3 小时前
LeetCode Hot100数据结构背景知识之元组(Tuple)Python2026新版
数据结构·python·算法·leetcode·元组·tuple
kali-Myon3 小时前
2025春秋杯网络安全联赛冬季赛-day2
python·安全·web安全·ai·php·pwn·ctf
Olamyh3 小时前
【 超越 ReAct:手搓 Plan-and-Execute (Planner) Agent】
python·ai
deepxuan3 小时前
Day7--python
开发语言·python
曲幽4 小时前
FastAPI不止于API:手把手教你用Jinja2打造动态Web页面
python·fastapi·backend·jinja2·full stack·template engine·web development
禹凕4 小时前
Python编程——进阶知识(多线程)
开发语言·爬虫·python
Ulyanov4 小时前
基于Pymunk物理引擎的2D坦克对战游戏开发
python·游戏·pygame·pymunk
铉铉这波能秀4 小时前
LeetCode Hot100数据结构背景知识之字典(Dictionary)Python2026新版
数据结构·python·算法·leetcode·字典·dictionary