杰瑞科技汇

python requests 知乎

社区,它的网页和 App 都有大量的数据,比如问题、回答、文章、用户信息等,使用 requests 可以模拟浏览器发送 HTTP 请求,从而获取这些数据。

python requests 知乎-图1
(图片来源网络,侵删)

核心挑战:知乎的登录机制

直接访问知乎的公共页面(如问题列表)是很容易的,但一旦你想访问登录后才能看到的内容(如你的关注者的动态、你的收藏夹、私信等),就会立刻遇到一个巨大的障碍:知乎的反爬虫机制和登录验证

知乎现在主要使用两种登录方式:

  1. 扫码登录:这是最主流的方式,用户需要在手机 App 或网页上扫描二维码来确认登录。
  2. 账号密码登录:这种方式非常少见,且通常需要配合验证码,成功率极低。

对于 requests 模拟扫码登录是技术上最可行、最稳定的方式,直接破解密码或绕过验证码几乎是不可能的。

下面,我将分步讲解从简单到复杂的操作。

python requests 知乎-图2
(图片来源网络,侵删)

第一步:环境准备

确保你已经安装了 requests 库,如果没安装,打开终端或命令行工具运行:

pip install requests

第二步:获取公开数据(无需登录)

对于任何用户都能看到的数据,我们可以直接发送 GET 请求来获取。

示例:获取某个问题的标题和部分回答

我们可以使用 Chrome 浏览器的开发者工具(F12)来分析知乎的网络请求。

python requests 知乎-图3
(图片来源网络,侵删)
  1. 打开 Chrome 浏览器,访问一个知乎问题页面,如何用 Python 爬取知乎?
  2. 按 F12 打开“开发者工具”,切换到“网络”(Network)选项卡。
  3. 刷新页面,你会看到很多请求,我们关注那些返回 HTML 或 JSON 的请求,一个问题的 API 请求 URL 类似于:https://www.zhihu.com/api/v4/questions/20798354?include=...
  4. 复制这个 URL,我们就可以在 Python 中使用 requests 来获取它。

Python 代码示例:

import requests
import json
# 目标问题的 URL
url = "https://www.zhihu.com/api/v4/questions/20798354"
# 设置请求头,模拟浏览器访问
# User-Agent 可以从你的浏览器开发者工具的 Network 请求中复制
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
    "Host": "www.zhihu.com"
}
try:
    # 发送 GET 请求
    response = requests.get(url, headers=headers)
    # 检查请求是否成功 (状态码 200)
    response.raise_for_status() 
    # 将 JSON 字符串解析为 Python 字典
    data = response.json()
    # 打印问题标题和回答总数
    print(f"问题标题: {data['title']}")
    print(f"回答总数: {data['answer_count']}")
    # 获取前几个回答的片段
    # 注意:获取回答列表需要另一个 API 请求
    answers_url = f"https://www.zhihu.com/api/v4/questions/20798354/answers?include=content,voteup_count&limit=5"
    answers_response = requests.get(answers_url, headers=headers)
    answers_data = answers_response.json()
    print("\n--- 前 5 个回答的片段 ---")
    for answer in answers_data['data']:
        # 为了简洁,只截取回答内容的前 50 个字符
        content_snippet = answer['content'][:50] + "..."
        print(f"赞同数: {answer['voteup_count']}, 内容: {content_snippet}")
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")

代码解释:

  • headers:非常重要!它告诉服务器我们是一个什么样的客户端,如果不设置 User-Agent,知乎很容易识别出这是一个爬虫程序,可能会拒绝你的请求。
  • response.raise_for_status():如果请求失败(404 Not Found, 500 Server Error),这行代码会抛出异常。
  • response.json()requests 库非常贴心,它会自动将服务器返回的 JSON 格式数据解析成 Python 的字典和列表。

第三步:获取私有数据(需要登录)

这是最核心也是最困难的部分,我们需要先登录,然后才能访问那些需要身份验证的 API。

手动获取 cookies(简单但低效)

对于一些简单的需求,你可以先手动登录知乎,然后从浏览器中复制 cookies,在代码中使用。

操作步骤:

  1. 在 Chrome 浏览器中登录你的知乎账号。
  2. 打开开发者工具 (F12),切换到 "应用" (Application) 选项卡(旧版是 "资源" -> "Cookies")。
  3. 在左侧找到 https://www.zhihu.com -> Cookies -> __ac_noncez_c0 等关键字段。
  4. 复制所有 Cookies 的值,格式如 name1=value1; name2=value2; ...

Python 代码示例:

import requests
# 手动从浏览器复制的 cookies
# !!! 注意:这些 cookies 是会过期的,通常几个小时或一天后就会失效,需要重新获取 !!!
cookies_str = "_xsrf=...; z_c0=...; other_cookies=...;"
cookies_dict = {cookie.split('=')[0]: cookie.split('=')[1] for cookie in cookies_str.split('; ')}
# 访问一个需要登录才能看到的页面,例如你的个人主页
url = "https://www.zhihu.com/people/your-user-name" # 替换成你的用户名
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
    "Host": "www.zhihu.com",
    # 有些请求可能还需要 Referer
    "Referer": "https://www.zhihu.com/"
}
try:
    response = requests.get(url, headers=headers, cookies=cookies_dict)
    response.raise_for_status()
    # 检查页面内容是否包含你的用户名,以判断是否登录成功
    if "你的用户名" in response.text:
        print("登录成功!可以获取私有数据了。")
        # 在这里可以继续请求其他需要登录的 API
    else:
        print("登录失败,可能 cookies 已过期。")
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")

缺点:

  • Cookies 易失效:知乎会定期更新 cookies,这种方法需要频繁手动复制。
  • 无法自动化:完全无法实现无人值守的自动化脚本。

模拟扫码登录(推荐,技术实现)

这是最稳定、最接近真实用户行为的方式,原理是:

  1. requests 访问知乎的登录页面,获取一个用于扫码的 xsrf token。
  2. 生成一个二维码 URL,并用 qrcode 库将其展示出来。
  3. 模拟手机 App 扫描二维码的过程,向知乎的服务器轮询扫码状态。
  4. 用户在手机上确认登录后,服务器会返回登录成功的 cookies
  5. 将获取到的 cookies 保存下来,后续所有请求都带上它即可。

完整代码示例:

import requests
import time
import json
import qrcode
from io import BytesIO
# 1. 定义请求头和基础 URL
HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
    'Host': 'www.zhihu.com',
    'Referer': 'https://www.zhihu.com/signin'
}
BASE_URL = 'https://www.zhihu.com'
# 2. 获取 XSRF Token
def get_xsrf_token():
    """获取登录所需的 xsrf token"""
    url = f'{BASE_URL}/api/v3/oauth/sign_in'
    response = requests.get(url, headers=HEADERS)
    # xsrf token 通常在 cookie 中
    return response.cookies.get('_xsrf')
# 3. 生成二维码并展示
def generate_qrcode():
    """生成二维码并打印到控制台"""
    xsrf = get_xsrf_token()
    if not xsrf:
        print("无法获取 XSRF token!")
        return None
    # 请求二维码
    qr_url = f'{BASE_URL}/api/v3/oauth/sign_in/qrcode?source=com.zhihu.web&ref=www.zhihu.com&lang=zh'
    params = {'client_id': 'c3cef7c66a1843f8b3a9e6a1e3160e20', 'grant_type': 'password'}
    response = requests.get(qr_url, headers=HEADERS, params=params)
    data = response.json()
    # 二维码的 UUID
    qr_uuid = data['qr_code']
    print(f"二维码 UUID: {qr_uuid}")
    # 生成二维码图片
    qr_img = qrcode.make(qr_uuid)
    buf = BytesIO()
    qr_img.save(buf, format='PNG')
    qr_img_bytes = buf.getvalue()
    # 在控制台打印二维码(简单实现,实际项目中可以用 PIL 显示)
    print("\n请使用知乎 App 扫描以下二维码:")
    # 这是一个简化的打印方式,实际开发中建议使用 PIL 库的 Image.show() 来显示
    # 这里我们只打印 UUID,用户可以手动去网页扫码
    # 或者,你可以将二维码图片保存到本地
    with open("qrcode.png", "wb") as f:
        f.write(qr_img_bytes)
    print("二维码已保存为 qrcode.png,请用手机知乎 App 扫描。")
    return qr_uuid
# 4. 轮询扫码状态
def poll_qrcode_status(qr_uuid):
    """轮询二维码扫码状态,直到用户确认登录"""
    check_url = f'{BASE_URL}/api/v3/oauth/sign_in/qrcode/scan_status'
    params = {
        'uuid': qr_uuid,
        'source': 'com.zhihu.web',
        'ref': 'www.zhihu.com',
        'lang': 'zh'
    }
    print("等待扫码确认...")
    while True:
        time.sleep(2) # 每2秒检查一次
        response = requests.get(check_url, headers=HEADERS, params=params)
        data = response.json()
        status = data.get('status')
        print(f"当前扫码状态: {status}")
        if status == 'scanned':
            print("二维码已被扫描,请在手机上确认登录。")
        elif status == 'confirmed':
            print("登录成功!正在获取 cookies...")
            # 登录成功后,cookies 会自动保存在 response.cookies 中
            return response.cookies.get_dict()
        elif status == 'expired' or status == 'failed':
            print("二维码已过期或登录失败。")
            return None
# 主函数
if __name__ == '__main__':
    qr_uuid = generate_qrcode()
    if qr_uuid:
        # 将获取到的 cookies 保存到变量中
        login_cookies = poll_qrcode_status(qr_uuid)
        if login_cookies:
            print("\n成功获取到 Cookies:")
            print(login_cookies)
            # --- 测试登录后的访问 ---
            print("\n--- 测试访问个人主页 ---")
            profile_url = "https://www.zhihu.com/api/v4/members/your-username?include=..." # 替换成你的用户名
            profile_response = requests.get(profile_url, headers=HEADERS, cookies=login_cookies)
            if profile_response.status_code == 200:
                profile_data = profile_response.json()
                print(f"你好,{profile_data['name']}!")
            else:
                print("访问个人主页失败,可能 cookies 无效或 URL 错误。")

代码解释:

  • get_xsrf_token():知乎为了防止 CSRF 攻击,在登录时需要验证一个 _xsrf token,这个 token 在登录页面的 cookie 中。
  • generate_qrcode():向知乎请求一个二维码的 UUID,然后用 qrcode 库生成一个图片并保存,用户需要用手机 App 扫描这个二维码。
  • poll_qrcode_status():这是关键,它会循环(轮询)地向知乎服务器询问二维码的状态,当状态变为 confirmed 时,说明用户已在手机上确认,服务器会在响应的 cookies 中返回登录凭证。
  • login_cookies = poll_qrcode_status(qr_uuid):我们将成功获取到的 cookies 字典保存起来。
  • 后续使用:一旦你有了 login_cookies,之后所有需要登录的 requests 请求,都只需在 cookies 参数中传入这个字典即可,就像方法一那样。

第四步:进阶技巧与注意事项

  1. Session 对象: 为了避免在每个请求中都重复传入 headerscookies,可以使用 requests.Session() 对象,它会自动管理 cookies 和 headers。

    # 创建一个会话对象
    session = requests.Session()
    session.headers.update(HEADERS) # 设置默认的 headers
    # 登录成功后,将 cookies 存入 session
    # session.cookies.update(login_cookies) 
    # 之后的所有请求都直接使用 session 对象,它会自动带上 cookies
    response = session.get("https://www.zhihu.com/your-private-page")
  2. IP 封禁与代理: 如果你频繁地发送请求,知乎的 IP 封禁机制可能会将你的 IP 地址暂时屏蔽,解决方案是使用代理 IP,你可以在代码中为 requests 请求设置 proxies 参数。

    proxies = {
        "http": "http://your_proxy_ip:port",
        "https": "https://your_proxy_ip:port",
    }
    response = requests.get(url, proxies=proxies)
  3. 数据解析

    • JSON API:如上所示,知乎的很多 API 直接返回 JSON,这是最容易解析的。
    • HTML 页面:对于一些没有提供 API 的页面,你需要获取 HTML 内容,然后用 BeautifulSoup4lxml 库来解析 HTML,提取你需要的信息,找到特定的 divspan
    from bs4 import BeautifulSoup
    html_content = response.text
    soup = BeautifulSoup(html_content, 'html.parser')
    # 找到所有 class 为 "ContentItem" 的 div
    answers = soup.find_all('div', class_='ContentItem')
    for answer in answers:
        # 在 answer 内部继续查找...
        author = answer.find('span', class_='UserLink').text
        print(f"作者: {author}")
  4. 遵守 robots.txt: 一个有道德的爬虫应该遵守网站的 robots.txt 规则,你可以访问 https://www.zhihu.com/robots.txt 查看知乎对爬虫的约定。

目标 方法 难度 稳定性 备注
获取公开数据 直接 requests.get() + headers 最基础的操作。
获取私有数据 手动复制 cookies 极低 仅临时测试用,cookies 易失效。
获取私有数据 模拟扫码登录 推荐方案,技术实现最稳定,可自动化。
反反爬虫 使用 Session、代理、随机 User-Agent 必要的进阶技巧,防止被封禁。

对于知乎这种反爬虫做得很好的网站,直接破解是行不通的。模拟用户行为(如扫码登录) 是目前最有效、最主流的思路,希望这份详细的指南能帮助你顺利上手!

分享:
扫描分享到社交APP
上一篇
下一篇