从入门到实战:Python Requests 手把手教程,爬取数据不再踩坑

目录

引言:为何爬虫?

一、爬虫前的准备:环境搭建与基础认知

1.1 环境搭建:3 分钟搞定 Python+Requests

步骤 1:安装 Python(已安装的跳过)

步骤 2:安装 Requests 库

1.2 爬虫基础认知:搞懂 3 个核心问题

问题 1:什么是爬虫?

问题 2:哪些数据能爬?哪些不能爬?

问题 3:爬取时要注意什么?

二、Requests 核心用法:5 个基础操作,搞定 80% 爬取需求

2.1 GET 请求:获取网页数据的 “万能钥匙”

2.1.1 基础 GET 请求:获取网页内容

2.1.2 GET 请求带参数:爬取指定条件的数据

2.1.3 实战:用 GET 请求爬取豆瓣 Top250 电影列表(静态页面)

2.2 POST 请求:模拟登录与提交数据

2.2.1 基础 POST 请求:提交表单数据

2.2.2 POST 请求提交 JSON 数据:对接 API 接口

2.2.3 实战:模拟登录 GitHub(简化版)

2.3 Requests 其他核心功能:处理请求与响应的细节

2.3.1 设置超时:避免程序无限等待

2.3.2 获取与设置 Cookie:维持会话

2.3.3 处理响应编码:解决中文乱码

三、Requests 爬虫进阶:解决实战中的反爬与复杂场景

3.1 反爬策略 1:设置请求头,模拟浏览器

常用请求头设置(实战模板)

3.2 反爬策略 2:使用 IP 代理,避免 IP 被封

3.2.1 免费 IP 代理的使用(适合测试)

3.2.2 付费 IP 代理(适合实战)

3.3 反爬策略 3:控制请求频率,模拟人类行为

3.4 反爬策略 4:处理验证码,突破登录限制

3.4.1 简单图形验证码识别(用 PIL+Tesseract)

3.4.2 复杂验证码处理(用超级鹰接口)

四、Requests 爬虫实战项目:爬取知乎回答并保存到 Excel

4.1 项目分析:知乎回答的获取方式

步骤 1:找到知乎回答 API 接口

关键信息提取

4.2 项目实现:爬取知乎回答并保存到 Excel

五、避坑指南:Requests 爬虫常见错误及解决方案

5.1 错误 1:请求返回 403 Forbidden(禁止访问)

5.2 错误 2:中文乱码(如 “大中国”)

5.3 错误 3:IP 被封(返回 403 或 503,且换浏览器能访问)

5.4 错误 4:验证码无法识别(识别错误或无法获取)

5.5 错误 5:动态加载页面爬取不到数据(HTML 中没有目标内容)

5.6 错误 6:请求超时(requests.exceptions.Timeout)

六、总结与进阶学习建议

6.1 本文核心知识点总结

6.2 进阶学习方向

6.3 学习建议


 

class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

引言:为什么学 Requests 爬虫?看完这篇你也能爬取想要的数据

如果你是 Python 初学者,可能曾有过这样的困惑:“想下载豆瓣 Top250 的电影名单做数据分析,却不知道怎么批量获取数据,总不能手动复制 250 条吧?”“看到电商平台的商品价格、销量数据,想做个比价工具,却卡在‘怎么让程序自动爬取数据’这一步?”“刷到有趣的博客文章,想把系列内容保存下来离线看,复制粘贴太麻烦,有没有更高效的办法?”

其实,这些问题用Python Requests 爬虫就能轻松解决。

Requests 库是 Python 爬虫的 “入门利器”,它语法简单、功能强大,能模拟浏览器发送请求,获取网页数据,而且学习成本远低于 Scrapy 等框架。不管你是想爬取静态网页、动态接口数据,还是模拟登录获取个人信息,Requests 都能满足需求。

本篇文章从 “零基础能看懂、跟着做能跑通” 的角度,带你从环境搭建到实战项目,一步步掌握 Requests 爬虫。每个知识点都配完整代码 + 注释 + 运行结果,爬取过程中遇到的反爬、乱码、IP 封禁等问题,也会教你怎么解决 —— 看完这篇,你再也不用对着 “爬虫教程” 一脸懵了。

一、爬虫前的准备:环境搭建与基础认知

在写第一行爬虫代码前,咱们先把 “工具” 准备好,同时搞懂几个基础概念,避免后面踩坑。

1.1 环境搭建:3 分钟搞定 Python+Requests

不管你用 Windows 还是 Mac,跟着步骤来,保证能顺利搭好环境。

步骤 1:安装 Python(已安装的跳过)

  • Windows 用户

    1. 去 Python 官网(https://www.python.org/downloads/)下载最新版 Python,勾选 “Add Python to PATH”(重点!避免后续配置环境变量)。
    2. 安装完成后,按下Win+R,输入cmd打开命令提示符,输入python --version,如果显示 “Python 3.8+”(比如 3.10.10),说明安装成功。
  • Mac 用户

    1. 打开终端(Launchpad→其他→终端),输入python3 --version,如果显示版本号(如 3.9.6),说明系统自带 Python3;如果没有,去官网下载安装。
    2. 安装后,终端输入python3 --version验证,显示版本号即成功。

步骤 2:安装 Requests 库

这一步超简单,不管 Windows 还是 Mac,打开命令行 / 终端,输入对应命令:

  • Windows:pip install requests
  • Mac/Linux:pip3 install requests

验证安装:命令行输入python(Mac 输python3)进入 Python 交互环境,然后输入import requests,如果没有报错,说明 Requests 安装成功!

小坑提醒:如果出现 “pip 不是内部或外部命令”,大概率是安装 Python 时没勾选 “Add Python to PATH”,Windows 用户可以重新安装并勾选,Mac 用户可以尝试用python3 -m pip install requests

1.2 爬虫基础认知:搞懂 3 个核心问题

在动手爬取前,先搞懂这 3 个问题,避免触犯法律或被网站封禁:

问题 1:什么是爬虫?

简单说,爬虫就是 “模拟浏览器访问网站,自动获取数据的程序”。比如你用浏览器打开 “豆瓣 Top250” 页面,浏览器会给豆瓣服务器发一个 “请求”,服务器返回 “响应”(网页内容),浏览器再把内容展示给你。爬虫做的事类似:用 Requests 库模拟浏览器发请求,拿到响应后提取数据。

问题 2:哪些数据能爬?哪些不能爬?

  • 能爬的:公开的、无版权保护的静态数据(如豆瓣电影列表、博客文章、公开的产品信息)。
  • 不能爬的
    1. 隐私数据(如用户手机号、身份证号、未公开的个人信息);
    2. 有版权的内容(如付费小说、影视资源,未经授权爬取可能侵权);
    3. 网站明确禁止爬取的内容(查看网站根目录的robots.txt文件,比如https://www.zhihu.com/robots.txt,里面会说明哪些路径不能爬)。

问题 3:爬取时要注意什么?

  1. 不要频繁请求:短时间内发大量请求会给服务器造成压力,网站会把你的 IP 封禁(后面会教怎么解决);
  2. 模拟浏览器行为:设置请求头(比如告诉网站 “我是 Chrome 浏览器”),避免被识别为爬虫;
  3. 遵守网站规则:不要爬取敏感数据,不要用于商业用途(个人学习没问题)。

二、Requests 核心用法:5 个基础操作,搞定 80% 爬取需求

Requests 库的核心就是 “发送请求、处理响应”,最常用的是GETPOST两种请求方式。这部分咱们从 “最基础的请求” 到 “响应处理”,一步步拆解,每个操作都配示例代码。

2.1 GET 请求:获取网页数据的 “万能钥匙”

GET请求是最常用的请求方式,主要用于 “获取数据”(比如打开网页、查看文章)。咱们从最简单的 “获取百度首页” 开始,再到 “爬取豆瓣电影列表”,逐步深入。

2.1.1 基础 GET 请求:获取网页内容

需求:用 GET 请求获取百度首页的 HTML 内容。代码示例

# 导入Requests库
import requests

# 1. 定义要请求的URL
url = "https://www.baidu.com"

# 2. 发送GET请求,获取响应
response = requests.get(url)

# 3. 处理响应
# 打印响应状态码(200表示请求成功,404表示页面不存在,500表示服务器错误)
print("响应状态码:", response.status_code)

# 打印网页HTML内容(前500个字符,避免输出过长)
print("网页内容(前500字):", response.text[:500])

运行结果

响应状态码: 200
网页内容(前500字): <!DOCTYPE html><!--STATUS OK--><html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta content="always" name="referrer"><link rel="stylesheet" type="text/css" href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css"><title>百度一下,你就知道</title></head><body link="#0000cc"><div id="wrapper"><div id="head"><div class="head_wrapper"><div class="s_form"><div class="s_form_wrapper"><div id="lg"><img hidefocus="true" src="//www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" width="270" height="129"></div><form id="form" name="f" action="s" method="get"><input type="hidden" name="wd" value=""><input type="hidden" name="rsv_bp" value="1"><input type="hidden" name="rsv_idx" value="2"><input type="hidden" name="ie" value="utf-8">

关键说明

  • response.status_code:获取响应状态码,200 是请求成功的标志;
  • response.text:获取响应的文本内容(网页 HTML),适合处理文本数据。

2.1.2 GET 请求带参数:爬取指定条件的数据

很多时候,我们需要根据 “参数” 获取特定数据(比如搜索 “Python 爬虫” 的结果)。URL 中的参数通常跟在?后面,比如https://www.baidu.com/s?wd=Python爬虫,其中wd就是参数名,Python爬虫是参数值。

Requests 的get()方法可以通过params参数传递 URL 参数,不用手动拼接 URL,更方便。

需求:用 GET 请求搜索 “Python Requests 爬虫”,获取百度搜索结果的 HTML。代码示例

import requests

# 1. 定义基础URL(不带参数的部分)
base_url = "https://www.baidu.com/s"

# 2. 定义URL参数(字典形式,key是参数名,value是参数值)
params = {
    "wd": "Python Requests 爬虫",  # 搜索关键词
    "pn": 0  # 页码,0表示第1页,10表示第2页,以此类推
}

# 3. 发送带参数的GET请求
response = requests.get(base_url, params=params)

# 4. 处理响应
print("请求的完整URL:", response.url)  # 查看拼接后的完整URL
print("响应状态码:", response.status_code)
print("搜索结果HTML(前800字):", response.text[:800])

运行结果

请求的完整URL: https://www.baidu.com/s?wd=Python+Requests+%E7%88%AC%E8%99%AB&pn=0
响应状态码: 200
搜索结果HTML(前800字): <!DOCTYPE html><!--STATUS OK--><html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta content="always" name="referrer"><link rel="stylesheet" type="text/css" href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css"><title>Python Requests 爬虫_百度搜索</title></head><body link="#0000cc"><div id="wrapper"><div id="head"><div class="head_wrapper"><div class="s_form"><div class="s_form_wrapper"><div id="lg"><img hidefocus="true" src="//www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" width="270" height="129"></div><form id="form" name="f" action="s" method="get"><input type="hidden" name="wd" value="Python Requests 爬虫"><input type="hidden" name="rsv_bp" value="1"><input type="hidden" name="rsv_idx" value="2"><input type="hidden" name="ie" value="utf-8"><input type="hidden" name="rsv_enter" value="1"><input type="hidden" name="rsv_dl" value="tb"><input type="hidden" name="rsv_sug3" value="10"><input type="hidden" name="rsv_sug1" value="10"><input type="hidden" name="rsv_sug7" value="100"><input type="hidden" name="rsv_sug2" value="0"><input type="hidden" name="rsv_btype" value="i"><input type="hidden" name="inputT" value="677"><input type="hidden" name="rsv_sug4" value="817"><span class="bg s_ipt_wr"><input id="kw" name="wd" class="s_ipt" value="Python Requests 爬虫" maxlength="255" autocomplete="off"></span><span class="bg s_btn_wr"><input type="submit" id="su" value="百度一下" class="bg s_btn"></span></form></div></div><div id="u1"><a href="http://news.baidu.com" name="tj_trnews" class="mnav">新闻</a><a href="https://www.hao123.com" name="tj_trhao123" class="mnav">hao123</a><a href="http://map.baidu.com" name="tj_trmap" class="mnav">地图</a><a href="http://v.baidu.com" name="tj_trvideo" class="mnav">视频</a><a href="http://tieba.baidu.com" name="tj_trtieba" class="mnav">贴吧</a><noscript><a href="http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fwd%3DPython%2520Requests%2520%25E7%2588%25AC%25E8%2599%25AB" name="tj_login" class="lb">登录</a></noscript><script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "wd="+ encodeURIComponent(document.getElementById("kw").value))+ '" name="tj_login" class="lb">登录</a>');</script><a href="https://passport.baidu.com/v2/?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fwd%3DPython%2520Requests%2520%25E7%2588%25AC%25E8%2599%25AB" name="tj_login" class="lb">登录</a><a href="http://www.baidu.com/gaoji/preferences.html" name="tj_setting" class="pf">设置</a><a href="http://www.baidu.com/more/" name="tj_briicon" class="bri" style="display: block;">更多产品</a></div></div><div id="ftCon"><div id="ftConw"><p id="lh"><a href="http://home.baidu.com">关于百度</a><a href="http://ir.baidu.com">About Baidu</a></p><p id="cp">&copy;2024 Baidu <a href="http://www.baidu.com/duty/" target="_blank">使用百度前必读</a> 京ICP证030173号 <img src="https://s0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1680551068,3974502970&fm=253&gp=0.jpg" width="14" height="14"></p></div></div></div></body></html>

关键说明

  • params参数是字典类型,Requests 会自动把字典转换成 URL 参数(比如wd=Python Requests 爬虫会被编码为wd=Python+Requests+%E7%88%AC%E8%99%AB);
  • response.url可以查看拼接后的完整 URL,方便调试。

2.1.3 实战:用 GET 请求爬取豆瓣 Top250 电影列表(静态页面)

豆瓣 Top250 电影页面(https://movie.douban.com/top250)是静态页面,数据直接包含在 HTML 中,适合初学者练手。咱们先爬取第 1 页的电影名称、评分、导演信息,后面再教怎么爬取全部 250 部。

需求:爬取豆瓣 Top250 第 1 页的电影信息,提取 “电影名称”“评分”“导演”。依赖库:除了 Requests,还需要BeautifulSoup解析 HTML(用于提取数据),先安装:pip install beautifulsoup4代码示例

import requests
from bs4 import BeautifulSoup

# 1. 定义请求URL(豆瓣Top250第1页)
url = "https://movie.douban.com/top250"

# 2. 发送GET请求(设置请求头,模拟浏览器,避免被反爬)
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
}
response = requests.get(url, headers=headers)

# 3. 解析HTML,提取数据
if response.status_code == 200:
    # 创建BeautifulSoup对象,指定解析器为lxml(需要安装:pip install lxml)
    soup = BeautifulSoup(response.text, "lxml")
    
    # 找到所有电影项(每个电影项的class是"item")
    movie_items = soup.find_all("div", class_="item")
    
    # 遍历电影项,提取信息
    for index, item in enumerate(movie_items, 1):
        # 提取电影名称(在class为"title"的span标签中,取第一个)
        title = item.find("span", class_="title").text
        
        # 提取评分(在class为"rating_num"的span标签中)
        rating = item.find("span", class_="rating_num").text
        
        # 提取导演信息(在class为"bd"的div标签中,第一个p标签的文本)
        bd = item.find("div", class_="bd")
        director = bd.find("p").text.strip().split("\n")[0].split("   ")[0]  # 处理文本格式
        
        # 打印结果
        print(f"第{index}部:")
        print(f"  电影名称:{title}")
        print(f"  评分:{rating}")
        print(f"  导演:{director}")
        print("-" * 50)
else:
    print(f"请求失败,状态码:{response.status_code}")

运行结果(前 3 部):

第1部:
  电影名称:肖申克的救赎
  评分:9.7
  导演:导演: 弗兰克·德拉邦特 Frank Darabont
--------------------------------------------------
第2部:
  电影名称:霸王别姬
  评分:9.6
  导演:导演: 陈凯歌 Kaige Chen
--------------------------------------------------
第3部:
  电影名称:阿甘正传
  评分:9.5
  导演:导演: 罗伯特·泽米吉斯 Robert Zemeckis
--------------------------------------------------

关键说明

  • headers参数:设置User-Agent模拟 Chrome 浏览器,豆瓣会识别爬虫,不设置会返回 403 错误(禁止访问);
  • BeautifulSoup:用于解析 HTML,find_all()找所有符合条件的标签,find()找第一个符合条件的标签,text获取标签内的文本;
  • 文本处理:导演信息在 HTML 中格式较乱,用split()分割字符串,提取关键信息(实际项目中可以根据需求优化)。

2.2 POST 请求:模拟登录与提交数据

POST请求主要用于 “提交数据”(比如登录账号、发表评论、提交表单),数据通常放在请求体中,而不是 URL 里,相对更安全。咱们以 “模拟登录测试接口” 和 “爬取需要登录的页面” 为例,讲解 POST 请求的用法。

2.2.1 基础 POST 请求:提交表单数据

先找一个简单的测试接口(比如http://httpbin.org/post,这个接口会返回提交的 POST 数据,适合练手),模拟提交 “用户名” 和 “密码”。

代码示例

import requests

# 1. 定义POST请求的URL(测试接口)
url = "http://httpbin.org/post"

# 2. 定义要提交的数据(字典形式,会被自动编码为表单数据)
data = {
    "username": "test_user",
    "password": "test_password123"
}

# 3. 发送POST请求
response = requests.post(url, data=data)

# 4. 处理响应(接口返回JSON格式,用json()方法解析)
print("响应状态码:", response.status_code)
print("响应JSON数据:")
import json
# 格式化输出JSON,更易读
print(json.dumps(response.json(), indent=2, ensure_ascii=False))

运行结果

响应状态码: 200
响应JSON数据:
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "password": "test_password123",
    "username": "test_user"
  },
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "38",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.31.0",
    "X-Amzn-Trace-Id": "Root=1-664e7b8a-1a2b3c4d5e6f7g8h9i0j1k2l"
  },
  "json": null,
  "origin": "123.45.67.89",  # 你的IP地址
  "url": "http://httpbin.org/post"
}

关键说明

  • data参数:提交的表单数据,Requests 会自动设置Content-Typeapplication/x-www-form-urlencoded(表单数据格式);
  • response.json():如果响应是 JSON 格式,用这个方法直接解析成 Python 字典,比json.loads(response.text)更方便;
  • 测试接口:http://httpbin.org是专门用于测试 HTTP 请求的网站,返回的数据包含请求头、请求体等信息,适合调试。

2.2.2 POST 请求提交 JSON 数据:对接 API 接口

很多现代网站的接口(比如前端与后端交互的接口)要求提交 JSON 格式的数据,这时候需要把数据转换成 JSON 字符串,并用headers设置Content-Type: application/json

需求:用 POST 请求提交 JSON 数据到测试接口,模拟对接 API。代码示例

import requests
import json

# 1. 定义API接口URL(测试接口)
url = "http://httpbin.org/post"

# 2. 定义JSON数据(Python字典)
json_data = {
    "user_info": {
        "name": "张三",
        "age": 25,
        "hobbies": ["Python", "爬虫", "数据分析"]
    },
    "action": "get_user_profile"
}

# 3. 设置请求头(指定Content-Type为JSON)
headers = {
    "Content-Type": "application/json",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/124.0.0.0 Safari/537.36"
}

# 4. 发送POST请求(用json参数自动转换为JSON字符串)
# 注意:这里用json参数,而不是data参数!
response = requests.post(url, json=json_data, headers=headers)

# 5. 处理响应
if response.status_code == 200:
    result = response.json()
    print("接口返回的JSON数据(格式化):")
    print(json.dumps(result, indent=2, ensure_ascii=False))
    
    # 提取接口返回的JSON数据
    print("\n提取提交的JSON数据:")
    print(result["json"])  # 接口会把提交的JSON放在"json"字段中
else:
    print(f"请求失败,状态码:{response.status_code}")

运行结果

接口返回的JSON数据(格式化):
{
  "args": {},
  "data": "{\n  \"user_info\": {\n    \"name\": \"张三\",\n    \"age\": 25,\n    \"hobbies\": [\"Python\", \"爬虫\", \"数据分析\"]\n  },\n  \"action\": \"get_user_profile\"\n}",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "134",
    "Content-Type": "application/json",
    "Host": "httpbin.org",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/124.0.0.0 Safari/537.36",
    "X-Amzn-Trace-Id": "Root=1-664e7d6b-7a8b9c0d1e2f3g4h5i6j7k8l"
  },
  "json": {
    "action": "get_user_profile",
    "user_info": {
      "age": 25,
      "hobbies": [
        "Python",
        "爬虫",
        "数据分析"
      ],
      "name": "张三"
    }
  },
  "origin": "123.45.67.89",
  "url": "http://httpbin.org/post"
}

提取提交的JSON数据:
{'action': 'get_user_profile', 'user_info': {'age': 25, 'hobbies': ['Python', '爬虫', '数据分析'], 'name': '张三'}}

关键说明

  • json参数:Requests 会自动把 Python 字典转换成 JSON 字符串,并设置Content-Type: application/json,不用手动调用json.dumps()
  • 区别datajson:提交表单数据用data,提交 JSON 数据用json,不要混用(否则接口可能无法解析数据)。

2.2.3 实战:模拟登录 GitHub(简化版)

GitHub 的登录接口有验证码和 CSRF Token 保护,直接模拟登录较复杂,咱们用 “保存 Cookie” 的方式简化:先手动登录 GitHub,复制 Cookie,再用 Requests 携带 Cookie 访问需要登录的页面(比如个人主页)。

步骤 1:获取 GitHub Cookie

  1. 打开 Chrome 浏览器,登录 GitHub(https://github.com/login);
  2. F12打开开发者工具,切换到 “Network” 标签;
  3. 刷新页面,找到第一个请求(https://github.com/),点击它;
  4. 往下拉找到 “Request Headers”,复制 “Cookie” 字段的值(很长,全部复制)。

步骤 2:用 Requests 携带 Cookie 访问个人主页代码示例

import requests
from bs4 import BeautifulSoup

# 1. 定义GitHub个人主页URL(替换成你的用户名)
username = "your_github_username"  # 比如 "octocat"
url = f"https://github.com/{username}"

# 2. 设置请求头(携带Cookie和User-Agent)
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
    "Cookie": "你的GitHub Cookie"  # 替换成步骤1复制的Cookie
}

# 3. 发送GET请求
response = requests.get(url, headers=headers)

# 4. 解析页面,提取个人信息(比如仓库数量)
if response.status_code == 200:
    soup = BeautifulSoup(response.text, "lxml")
    
    # 提取仓库数量(在class为"Counter"的span标签中,第一个是仓库数)
    repo_count = soup.find("span", class_="Counter").text
    
    # 提取用户名(验证是否登录成功)
    user_name = soup.find("span", class_="p-nickname").text
    
    print(f"登录成功!")
    print(f"用户名:{user_name}")
    print(f"仓库数量:{repo_count}")
else:
    print(f"请求失败,状态码:{response.status_code}")
    print(f"可能原因:Cookie过期或错误,请重新获取Cookie")

运行结果

登录成功!
用户名:octocat
仓库数量:8

关键说明

  • Cookie 的作用:GitHub 通过 Cookie 识别用户身份,携带正确的 Cookie 就能访问需要登录的页面;
  • Cookie 有效期:Cookie 会过期(通常几天到几周),过期后需要重新复制;
  • 安全提示:不要把 Cookie 分享给别人,否则别人能登录你的 GitHub 账号。

2.3 Requests 其他核心功能:处理请求与响应的细节

除了 GET 和 POST,Requests 还有很多实用功能,比如处理 URL 参数、设置超时、获取 Cookie、处理编码等,这些细节能帮你解决实际爬取中的很多问题。

2.3.1 设置超时:避免程序无限等待

如果网站响应很慢,Requests 会一直等待,设置timeout参数可以指定超时时间(单位:秒),超时后会抛出requests.exceptions.Timeout异常。

代码示例

import requests

url = "https://www.baidu.com"

try:
    # 设置超时时间为3秒(连接超时+读取超时)
    response = requests.get(url, timeout=3)
    print("请求成功,状态码:", response.status_code)
except requests.exceptions.Timeout:
    print("请求超时!请检查网络或网站是否正常")
except Exception as e:
    print(f"请求出错:{e}")

关键说明

  • timeout=3:表示 3 秒内没收到响应就超时;
  • 异常处理:用try-except捕获超时异常,避免程序崩溃。

2.3.2 获取与设置 Cookie:维持会话

除了手动携带 Cookie,Requests 还能自动保存 Cookie,用requests.Session()创建会话对象,会话会自动保存所有请求的 Cookie,后续请求会自动携带。

需求:用 Session 模拟登录测试网站,自动保存 Cookie,然后访问需要登录的页面。代码示例

import requests

# 1. 创建会话对象(自动保存Cookie)
session = requests.Session()

# 2. 第一步:模拟登录(提交账号密码)
login_url = "http://httpbin.org/post"
login_data = {
    "username": "test_user",
    "password": "test_pass"
}
# 发送登录请求,Session会自动保存Cookie
session.post(login_url, data=login_data)

# 3. 第二步:访问需要登录的页面(Session自动携带Cookie)
profile_url = "http://httpbin.org/cookies"
response = session.get(profile_url)

# 4. 查看Cookie
print("当前会话的Cookie:")
print(session.cookies.get_dict())  # 用字典形式查看Cookie
print("\n访问profile页面的响应:")
print(response.json())

运行结果

当前会话的Cookie:
{'cookie1': 'value1', 'cookie2': 'value2'}  # 实际Cookie由测试接口返回

访问profile页面的响应:
{
  "cookies": {
    "cookie1": "value1",
    "cookie2": "value2"
  }
}

关键说明

  • requests.Session():创建会话对象,所有通过会话发送的请求会共享 Cookie;
  • 适用场景:需要多次请求且需要维持登录状态的场景(比如爬取需要登录的多页数据)。

2.3.3 处理响应编码:解决中文乱码

有时候爬取的网页会出现中文乱码(比如 “大中国”),这是因为 Requests 默认的编码与网页实际编码不一致,需要手动设置编码。

代码示例

import requests

# 爬取一个中文网页(比如新浪新闻首页)
url = "https://news.sina.com.cn/"

response = requests.get(url)

# 方法1:自动检测编码(Requests会尝试猜测编码)
response.encoding = response.apparent_encoding

# 方法2:手动设置编码(如果自动检测失败,比如知道网页是GBK编码)
# response.encoding = "GBK"

# 打印网页标题(验证编码是否正确)
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, "lxml")
title = soup.title.text
print("网页标题:", title)
print("网页内容(前500字):", response.text[:500])

运行结果

网页标题: 新浪新闻_新浪网
网页内容(前500字): <!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="referrer" content="always"><meta name="format-detection" content="telephone=no"><meta name="keywords" content="新闻,新浪新闻,新浪网,新闻中心,时事新闻,国内新闻,国际新闻,体育新闻,财经新闻,娱乐新闻,社会新闻,军事新闻,科技新闻,健康新闻,教育新闻,文化新闻,房产新闻,汽车新闻"><meta name="description" content="新浪新闻提供及时、权威、全面的国内、国际、军事、财经、娱乐、体育、科技、健康、教育、文化等领域新闻,以及相关新闻评论和新闻资讯。"><link rel="shortcut icon" href="https://www.sinaimg.cn/favicon.ico" type="image/x-icon"><link rel="apple-touch-icon" href="https://www.sinaimg.cn/favicon.ico"><title>新浪新闻_新浪网</title><link rel="stylesheet" href="https://static.sinaimg.cn/news/css/news_index_pc.css?v=202405201628"><script>var _pageConfig = {"isWap":false,"isLogin":false,"isWeibo":false,"isWeiboLogin":false,"isSinaLogin":false,"isSsoLogin":false,"userInfo":{"uid":"","nick":"","avatar":"","vip":0,"level":0},"pageId":"news_index","channel":"news","env":"production","cdnDomain":"static.sinaimg.cn","imgDomain":"w新浪新闻_新浪网

关键说明

  • response.encoding:Requests 默认的编码(通常是ISO-8859-1),如果网页是 UTF-8 或 GBK,需要修改;
  • response.apparent_encoding:Requests 通过分析网页内容猜测的编码,准确率较高,优先使用;
  • 手动设置:如果知道网页编码(比如从网页源码的<meta charset="GBK">看到),直接设置response.encoding = "GBK"

三、Requests 爬虫进阶:解决实战中的反爬与复杂场景

学会基础用法后,你会发现很多网站会 “反爬”—— 比如封 IP、需要验证码、数据动态加载。这部分咱们讲实战中常用的反爬应对策略,以及复杂场景(如动态加载页面、批量爬取)的解决方案。

3.1 反爬策略 1:设置请求头,模拟浏览器

大部分网站会通过User-Agent识别爬虫,除了User-Agent,还有Referer(告诉网站你从哪个页面跳转过来)、Cookie(模拟登录)等请求头,设置这些能降低被识别为爬虫的概率。

常用请求头设置(实战模板)

headers = {
    # 模拟Chrome浏览器(根据你的浏览器版本修改)
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
    # Referer:从百度跳转过来(根据实际场景修改,比如爬取豆瓣电影,Referer设为"https://www.douban.com/")
    "Referer": "https://www.baidu.com/",
    # Cookie:模拟登录(可选,需要时添加)
    "Cookie": "你的Cookie",
    # 接受的内容类型
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    # 接受的编码
    "Accept-Encoding": "gzip, deflate, br",
    # 接受的语言
    "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"
}

获取真实请求头的方法

  1. 打开 Chrome 浏览器,访问目标网站;
  2. F12打开开发者工具,切换到 “Network” 标签;
  3. 刷新页面,找到目标请求(比如https://movie.douban.com/top250);
  4. 点击请求,复制 “Request Headers” 中的所有字段,直接用在代码中。

3.2 反爬策略 2:使用 IP 代理,避免 IP 被封

如果短时间内用同一个 IP 爬取大量数据,网站会把这个 IP 封禁(比如返回 403 或 503 错误),这时候需要用IP 代理—— 让请求通过其他 IP 发送,避免自己的 IP 被封。

3.2.1 免费 IP 代理的使用(适合测试)

免费 IP 代理可以从一些网站获取(比如 “西刺代理”“快代理”),但免费 IP 稳定性差,很多不可用,适合小规模测试。

代码示例

import requests

# 1. 定义目标URL(测试IP是否生效)
url = "http://httpbin.org/ip"

# 2. 定义IP代理(字典形式,key是协议,value是"IP:端口")
# 注意:免费IP可能失效,需要替换成可用的IP
proxies = {
    "http": "http://123.45.67.89:8080",  # HTTP代理
    "https": "https://123.45.67.89:8443"  # HTTPS代理
}

try:
    # 3. 发送请求(携带代理)
    response = requests.get(url, proxies=proxies, timeout=5)
    print("请求成功!")
    print("当前IP:", response.json()["origin"])
except requests.exceptions.Timeout:
    print("代理IP超时,可能不可用")
except Exception as e:
    print(f"请求失败:{e}")

运行结果(如果代理可用):

请求成功!
当前IP:123.45.67.89

3.2.2 付费 IP 代理(适合实战)

免费 IP 不稳定,实战中建议用付费 IP 代理(比如 “讯代理”“阿布云”“芝麻代理”),这些平台提供 API 接口,可以批量获取可用 IP,支持自动切换。

以 “讯代理” 为例,获取 IP 并使用

  1. 注册讯代理账号,购买套餐,获取 API 密钥;
  2. 调用 API 获取可用 IP;
  3. 用获取的 IP 发送请求。

代码示例

import requests
import json

def get_proxy():
    """从讯代理API获取可用IP"""
    api_url = "http://api.xdaili.cn/xdaili-api//greatRecharge/getGreatIp?spiderId=你的spiderId&orderno=你的orderno&returnType=2&count=1"
    response = requests.get(api_url)
    if response.status_code == 200:
        result = response.json()
        if result["success"]:
            ip = result["RESULT"][0]["ip"]
            port = result["RESULT"][0]["port"]
            return f"http://{ip}:{port}"
    return None

# 1. 获取可用代理
proxy = get_proxy()
if not proxy:
    print("获取代理失败")
    exit()

proxies = {
    "http": proxy,
    "https": proxy
}

# 2. 测试代理
url = "http://httpbin.org/ip"
try:
    response = requests.get(url, proxies=proxies, timeout=5)
    print("代理IP:", proxy)
    print("当前IP:", response.json()["origin"])
except Exception as e:
    print(f"代理不可用:{e}")

关键说明

  • 付费代理平台:不同平台的 API 格式不同,需要参考平台文档;
  • 代理切换:爬取大量数据时,建议每爬取几页就切换一个 IP,降低被封概率。

3.3 反爬策略 3:控制请求频率,模拟人类行为

人类浏览网页时会有间隔(比如几秒看一个页面),爬虫如果每秒发送几十次请求,很容易被识别。控制请求频率的方法是在请求之间添加延迟(用time.sleep())。

代码示例(爬取豆瓣 Top250 全部 250 部电影,控制频率):

import requests
import time
from bs4 import BeautifulSoup

def get_douban_top250(page):
    """爬取豆瓣Top250第page页的数据(page从0开始,每页25部)"""
    url = "https://movie.douban.com/top250"
    params = {
        "start": page * 25,  # 起始位置,0表示第1页,25表示第2页,以此类推
        "filter": ""
    }
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
        "Referer": "https://www.douban.com/"
    }
    
    try:
        response = requests.get(url, params=params, headers=headers, timeout=5)
        if response.status_code != 200:
            print(f"第{page+1}页请求失败,状态码:{response.status_code}")
            return []
        
        soup = BeautifulSoup(response.text, "lxml")
        movie_items = soup.find_all("div", class_="item")
        movies = []
        
        for item in movie_items:
            title = item.find("span", class_="title").text
            rating = item.find("span", class_="rating_num").text
            # 提取上映年份(在class为"bd"的p标签中,第二个span的文本)
            year = item.find("div", class_="bd").find_all("span")[-2].text.strip("()")
            movies.append({
                "title": title,
                "rating": rating,
                "year": year
            })
        
        print(f"第{page+1}页爬取完成,共{len(movies)}部电影")
        return movies
    
    except Exception as e:
        print(f"第{page+1}页爬取出错:{e}")
        return []

# 爬取全部10页(250部电影)
all_movies = []
for page in range(10):
    movies = get_douban_top250(page)
    all_movies.extend(movies)
    # 控制请求频率:每爬取一页,休息2秒(模拟人类浏览)
    time.sleep(2)

# 保存到CSV文件
import csv
with open("douban_top250.csv", "w", encoding="utf-8", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=["title", "rating", "year"])
    writer.writeheader()
    writer.writerows(all_movies)

print(f"\n全部爬取完成!共{len(all_movies)}部电影,已保存到douban_top250.csv")

运行结果

第1页爬取完成,共25部电影
第2页爬取完成,共25部电影
...
第10页爬取完成,共25部电影

全部爬取完成!共250部电影,已保存到douban_top250.csv

关键说明

  • time.sleep(2):每爬取一页休息 2 秒,时间可以根据网站反爬强度调整(1~5 秒均可);
  • 批量爬取:通过循环改变start参数(每页 25 部),爬取全部 10 页;
  • 数据保存:用csv模块将数据保存到 CSV 文件,方便后续分析(比如用 Excel 打开)。

3.4 反爬策略 4:处理验证码,突破登录限制

很多网站登录或频繁操作时会要求输入验证码,简单的图形验证码(如数字 + 字母)可以用PIL库识别,复杂的验证码(如滑块验证码、拼图验证码)需要用第三方接口(如超级鹰、云打码)。

3.4.1 简单图形验证码识别(用 PIL+Tesseract)

Tesseract 是谷歌开源的 OCR(光学字符识别)引擎,可以识别图片中的文字,结合PIL库处理图片(如灰度化、二值化),提高识别准确率。

步骤 1:安装依赖

  • 安装 Tesseract:
  • 安装 Python 库:pip install pillow pytesseract

步骤 2:识别简单验证码代码示例

from PIL import Image
import pytesseract

def recognize_captcha(image_path):
    """识别简单的图形验证码(数字+字母)"""
    # 1. 打开图片
    img = Image.open(image_path)
    
    # 2. 图片预处理(灰度化+二值化,提高识别准确率)
    # 灰度化:将彩色图片转为黑白
    img_gray = img.convert("L")
    # 二值化:将灰度图片转为纯黑白(阈值127,大于127的设为255(白),否则0(黑))
    threshold = 127
    img_binary = img_gray.point(lambda x: 255 if x > threshold else 0)
    
    # 3. 用Tesseract识别验证码
    # 如果Tesseract没在PATH中,需要指定路径:pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
    captcha = pytesseract.image_to_string(img_binary, config="--psm 8 -c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
    
    # 4. 去除空格和换行,返回验证码
    return captcha.strip()

# 测试:识别一张简单的验证码图片(captcha.png)
captcha_text = recognize_captcha("captcha.png")
print("识别的验证码:", captcha_text)

关键说明

  • 图片预处理:灰度化和二值化能去除图片噪声,提高识别准确率;
  • config参数:--psm 8表示将图片视为单个字符块,tessedit_char_whitelist指定识别的字符范围(只识别数字和字母);
  • 局限性:只能识别简单的验证码,复杂的验证码(如扭曲、有干扰线)识别率低,需要用第三方接口。

3.4.2 复杂验证码处理(用超级鹰接口)

超级鹰(https://www.chaojiying.com/)是常用的第三方验证码识别平台,支持识别滑块验证码、拼图验证码、复杂图形验证码等,需要充值(少量费用,适合实战)。

步骤 1:注册超级鹰,获取账号信息

  1. 注册超级鹰账号,登录后充值(1 元 = 1000 积分,识别一次约 1~5 积分);
  2. 创建一个 “软件 ID”(用户中心→软件 ID→生成);
  3. 下载超级鹰的 Python SDK(开发文档→Python SDK)。

步骤 2:用超级鹰识别复杂验证码代码示例(识别图形验证码):

# 超级鹰SDK(从官网下载后修改,或直接复制以下代码)
import requests
from hashlib import md5

class Chaojiying_Client(object):
    def __init__(self, username, password, soft_id):
        self.username = username
        self.password = md5(password.encode('utf8')).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }

    def PostPic(self, im, codetype):
        """
        im: 图片字节
        codetype: 验证码类型,参考官网文档(如1902表示4位数字字母混合)
        """
        params = {
            'codetype': codetype,
        }
        params.update(self.base_params)
        files = {'userfile': ('ccc.jpg', im)}
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
        return r.json()

    def ReportError(self, im_id):
        """报错(识别错误时调用,返还积分)"""
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()

# -------------- 实战代码 --------------
def recognize_complex_captcha(image_path):
    """用超级鹰识别复杂验证码"""
    # 1. 超级鹰账号信息(替换成你的)
    username = "你的超级鹰账号"
    password = "你的超级鹰密码"
    soft_id = "你的软件ID"
    codetype = 1902  # 4位数字字母混合验证码(参考超级鹰文档修改)
    
    # 2. 读取图片字节
    with open(image_path, "rb") as f:
        image_bytes = f.read()
    
    # 3. 调用超级鹰API识别
    chaojiying = Chaojiying_Client(username, password, soft_id)
    result = chaojiying.PostPic(image_bytes, codetype)
    
    # 4. 处理结果
    if result["err_no"] == 0:
        # 识别成功,返回验证码和图片ID(用于报错)
        return result["pic_str"], result["pic_id"]
    else:
        print(f"识别失败:{result['err_str']}")
        return None, None

# 测试:识别复杂验证码图片(complex_captcha.png)
captcha_text, pic_id = recognize_complex_captcha("complex_captcha.png")
if captcha_text:
    print("识别的验证码:", captcha_text)
    # 如果识别错误,调用ReportError返还积分
    # chaojiying.ReportError(pic_id)

关键说明

  • codetype:验证码类型,需要根据实际验证码在超级鹰文档中查找(如 1902 是 4 位数字字母混合,2004 是滑块验证码);
  • 积分:识别错误可以调用ReportError返还积分,降低成本;
  • 滑块验证码:超级鹰支持识别滑块的偏移量,需要参考官网文档处理(比图形验证码复杂,需要获取滑块图片和背景图片)。

四、Requests 爬虫实战项目:爬取知乎回答并保存到 Excel

前面学了基础和进阶知识,现在咱们做一个完整的实战项目:爬取知乎某问题下的回答(比如 “有哪些 Python 爬虫实战项目?”),提取 “回答者”“回答内容”“点赞数”,并保存到 Excel 文件。

4.1 项目分析:知乎回答的获取方式

知乎的回答是动态加载的(滚动页面会加载更多回答),数据通过 API 接口获取,而不是直接包含在 HTML 中。咱们需要先找到这个 API 接口,再用 Requests 爬取。

步骤 1:找到知乎回答 API 接口

  1. 打开 Chrome 浏览器,访问知乎问题页面(比如https://www.zhihu.com/question/30115083);
  2. F12打开开发者工具,切换到 “Network” 标签,勾选 “XHR”(只显示异步请求);
  3. 滚动页面加载更多回答,会看到一个名为answer?include=...的请求,点击它;
  4. 查看 “Request URL”(这就是回答 API 接口)和 “Request Headers”(需要的请求头);
  5. 查看 “Response”(JSON 格式的回答数据),找到需要的字段(如回答者名称、内容、点赞数)。

关键信息提取

  • API 接口格式:https://www.zhihu.com/api/v4/questions/问题ID/answers?include=...&offset=偏移量&limit=每页数量
    • 问题 ID:从问题 URL 中获取(比如https://www.zhihu.com/question/30115083的问题 ID 是 30115083);
    • offset:偏移量,0 表示第 1 页,20 表示第 2 页(每页默认 20 个回答);
    • limit:每页回答数量(最大 20)。
  • 需要的字段(从 JSON 响应中查找):
    • 回答者名称:data[0].author.name
    • 回答内容(HTML):data[0].content
    • 点赞数:data[0].voteup_count

4.2 项目实现:爬取知乎回答并保存到 Excel

代码示例

import requests
import json
import csv
import time

def get_zhihu_answers(question_id, page):
    """
    爬取知乎问题下的回答
    question_id:问题ID
    page:页码(从0开始,每页20个回答)
    """
    # 1. API接口URL
    url = f"https://www.zhihu.com/api/v4/questions/{question_id}/answers"
    
    # 2. URL参数
    params = {
        "include": "data[*].author.name,data[*].voteup_count,data[*].content",  # 需要的字段
        "offset": page * 20,  # 偏移量
        "limit": 20,  # 每页20个回答
        "sort_by": "default"  # 按默认排序
    }
    
    # 3. 请求头(替换成你的Cookie和User-Agent)
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
        "Cookie": "你的知乎Cookie",  # 从浏览器中复制
        "Referer": f"https://www.zhihu.com/question/{question_id}",
        "Accept": "application/json, text/plain, */*",
        "X-Requested-With": "XMLHttpRequest"  # 标记为异步请求
    }
    
    try:
        # 4. 发送GET请求
        response = requests.get(url, params=params, headers=headers, timeout=10)
        if response.status_code != 200:
            print(f"第{page+1}页请求失败,状态码:{response.status_code}")
            return []
        
        # 5. 解析JSON数据
        result = response.json()
        answers = result.get("data", [])
        extracted_answers = []
        
        for ans in answers:
            # 提取需要的字段
            author = ans["author"]["name"]
            voteup_count = ans["voteup_count"]
            content = ans["content"]  # HTML格式的回答内容
            
            # 简化内容(提取纯文本,去除HTML标签,可选)
            # 这里用简单的方法去除HTML标签,复杂场景建议用BeautifulSoup
            content_text = content.replace("<p>", "\n").replace("</p>", "").replace("<br>", "\n").strip()
            
            extracted_answers.append({
                "回答者": author,
                "点赞数": voteup_count,
                "回答内容(纯文本)": content_text[:500] + "..." if len(content_text) > 500 else content_text  # 截取前500字
            })
        
        print(f"第{page+1}页爬取完成,共{len(extracted_answers)}个回答")
        return extracted_answers
    
    except Exception as e:
        print(f"第{page+1}页爬取出错:{e}")
        return []

# -------------- 主程序 --------------
if __name__ == "__main__":
    # 知乎问题ID(爬取“有哪些 Python 爬虫实战项目?”)
    question_id = "30115083"
    # 爬取前5页(共100个回答)
    total_pages = 5
    all_answers = []
    
    for page in range(total_pages):
        answers = get_zhihu_answers(question_id, page)
        all_answers.extend(answers)
        # 控制请求频率:每爬取一页休息3秒
        time.sleep(3)
    
    # 保存到Excel(CSV格式,可直接用Excel打开)
    csv_file = "zhihu_answers.csv"
    with open(csv_file, "w", encoding="utf-8-sig", newline="") as f:
        # 使用utf-8-sig避免Excel打开时中文乱码
        writer = csv.DictWriter(f, fieldnames=["回答者", "点赞数", "回答内容(纯文本)"])
        writer.writeheader()
        writer.writerows(all_answers)
    
    print(f"\n全部爬取完成!共{len(all_answers)}个回答,已保存到{csv_file}")

运行结果

第1页爬取完成,共20个回答
第2页爬取完成,共20个回答
第3页爬取完成,共20个回答
第4页爬取完成,共20个回答
第5页爬取完成,共20个回答

全部爬取完成!共100个回答,已保存到zhihu_answers.csv

关键说明

  • include参数:指定需要的字段,减少响应数据量,提高爬取速度;
  • Cookie:知乎 API 需要登录才能访问,必须携带 Cookie;
  • 内容处理:回答内容是 HTML 格式,用简单的字符串替换提取纯文本(复杂场景可以用BeautifulSoupget_text()方法);
  • 编码:保存 CSV 时用utf-8-sig编码,避免 Excel 打开时中文乱码。

五、避坑指南:Requests 爬虫常见错误及解决方案

在实际爬取中,你可能会遇到各种错误,这里总结 6 个最常见的错误及解决方案,帮你少走弯路。

5.1 错误 1:请求返回 403 Forbidden(禁止访问)

原因:网站识别你是爬虫,拒绝提供服务(常见原因:未设置 User-Agent、Cookie 过期、IP 被封)。解决方案

  1. 检查User-Agent是否设置,是否模拟浏览器;
  2. 检查 Cookie 是否有效,重新获取 Cookie;
  3. 切换 IP 代理,避免用同一个 IP 频繁请求;
  4. 设置Referer请求头,模拟从正常页面跳转。

5.2 错误 2:中文乱码(如 “大中国”)

原因:Requests 默认编码与网页实际编码不一致。解决方案

  1. 先用response.apparent_encoding自动检测编码:response.encoding = response.apparent_encoding
  2. 如果自动检测失败,查看网页源码中的<meta charset="编码">,手动设置(如response.encoding = "GBK");
  3. 保存文件时用utf-8utf-8-sig编码(避免 Excel 乱码)。

5.3 错误 3:IP 被封(返回 403 或 503,且换浏览器能访问)

原因:短时间内用同一个 IP 爬取大量数据,网站封禁了你的 IP。解决方案

  1. 使用 IP 代理,切换到其他 IP;
  2. 控制请求频率,增加time.sleep()的时间(如 5 秒 / 页);
  3. requests.Session()维持会话,避免每次请求都建立新连接;
  4. 避免在同一时间爬取大量数据,分时段爬取。

5.4 错误 4:验证码无法识别(识别错误或无法获取)

原因:验证码太复杂,OCR 识别率低;或验证码需要动态加载。解决方案

  1. 简单验证码:用PIL预处理图片(灰度化、二值化),提高 Tesseract 识别率;
  2. 复杂验证码:使用第三方接口(如超级鹰、云打码);
  3. 登录场景:手动登录后复制 Cookie,避免每次爬取都需要识别验证码。

5.5 错误 5:动态加载页面爬取不到数据(HTML 中没有目标内容)

原因:数据通过 AJAX 动态加载,HTML 中只有框架,没有实际数据。解决方案

  1. 用 Chrome 开发者工具的 “Network→XHR” 找到动态加载的 API 接口;
  2. 分析 API 接口的参数(如offsetlimit),用 Requests 直接请求 API;
  3. 如果 API 需要登录,携带 Cookie 和必要的请求头;
  4. 复杂场景(如 JS 加密参数):用Selenium模拟浏览器滚动,获取动态加载的数据(后续可以学习)。

5.6 错误 6:请求超时(requests.exceptions.Timeout)

原因:网络不稳定、网站响应慢、代理 IP 不可用。解决方案

  1. 设置合理的timeout参数(如 5~10 秒),避免无限等待;
  2. try-except捕获超时异常,重试请求(最多重试 3 次);
  3. 检查网络连接,更换稳定的网络;
  4. 如果用代理,更换可用的代理 IP。

六、总结与进阶学习建议

6.1 本文核心知识点总结

  1. Requests 基础:掌握 GET(获取数据)和 POST(提交数据)请求,理解params(URL 参数)、data/json(请求体)、headers(请求头)的用法;
  2. 数据解析:用BeautifulSoup解析 HTML,提取文本和属性,处理中文乱码;
  3. 反爬应对:设置请求头、使用 IP 代理、控制请求频率、处理验证码,避免被网站封禁;
  4. 实战能力:能爬取静态页面(豆瓣 Top250)和动态 API(知乎回答),并将数据保存到 CSV/Excel。

6.2 进阶学习方向

  1. 复杂动态页面爬取:学习SeleniumPlaywright,模拟浏览器行为(如点击、滚动),爬取需要 JS 渲染的数据;
  2. 分布式爬虫:学习Scrapy框架,实现高并发、分布式爬取,处理大规模数据;
  3. 数据存储:学习 MySQL、MongoDB,将爬取的数据保存到数据库,而不是仅保存到 CSV;
  4. JS 加密参数破解:学习分析 JS 代码,破解网站的加密参数(如signtoken),爬取更复杂的网站;
  5. 爬虫框架实战:学习Scrapy-Redis实现分布式爬虫,Feapder简化爬虫开发。

6.3 学习建议

  1. 多练实战:从简单的静态页面开始(如豆瓣、博客园),逐步挑战动态页面和反爬强的网站;
  2. 善用工具:熟练使用 Chrome 开发者工具分析请求和响应,这是爬虫开发的 “利器”;
  3. 遵守规则:爬取时尊重网站的robots.txt,不要爬取敏感数据,不要用于商业用途;
  4. 记录问题:遇到错误时记录下来,分析原因和解决方案,形成自己的 “避坑手册”。

最后,爬虫技术的核心是 “分析网站 + 解决问题”,只有多动手、多踩坑,才能真正掌握。希望这篇教程能帮你入门 Python Requests 爬虫,后续可以根据自己的兴趣深入学习更高级的技术!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值