Python 爬虫项目:GET 与 POST 请求详解
前言
HTTP 协议定义了多种请求方法,其中GET与POST是互联网应用中使用频次最高的两种方式,也是爬虫开发必须熟练掌握的核心内容。上一章节完成了 requests 库基础 GET 请求的实战演练,本章将从协议规范、应用场景、底层差异、代码实现、参数传递格式等维度,对 GET 与 POST 请求进行系统性拆解,同时结合真实业务场景完成多类型 POST 请求开发,并对比两类请求的选型原则与避坑要点。
在日常网络交互中,网页浏览、内容查询、资源加载多采用 GET 请求;账号登录、表单提交、数据上传、接口交互、评论发布等主动向服务器提交数据的场景,几乎全部依赖 POST 请求。二者在参数传输位置、数据大小限制、缓存机制、安全性、编码规则上存在本质区别,若混用请求方式,会直接导致爬虫请求失效、数据提交失败、登录状态无法维持等问题。
本章配套大量可直接运行的代码案例,结合 HTTP 报文原理、参数解析逻辑、异常场景处理进行讲解,同时区分表单数据、JSON 数据、文件上传等不同 POST 提交形式,覆盖爬虫开发中 99% 的请求场景。文中相关参考链接如下:
- HTTP 协议官方规范参考:https://www.rfc-editor.org/rfc/rfc9110.html
- HTTP 在线测试接口:https://httpbin.org/
- JSON 数据格式标准文档:https://www.json.org/
一、GET 与 POST 请求核心理论差异
在编写代码之前,首先明确两类请求的协议定义、核心区别与适用边界,从底层理解请求逻辑,避免开发中出现方向性错误。本节整理二者在传输机制、使用规范、特性表现上的核心差异,并结合爬虫场景说明影响。
1.1 核心特性对比表
表格
| 对比维度 | GET 请求 | POST 请求 | 爬虫开发影响 |
|---|---|---|---|
| 参数传输位置 | 参数拼接在URL 地址栏,跟随请求行发送 | 参数放置在请求体(Request Body)中,独立传输 | GET 参数直接可见,POST 参数无法从 URL 直观查看 |
| 数据长度限制 | 受浏览器、服务器对 URL 长度限制,一般不超过 2KB | 理论无长度限制,仅受服务器配置影响 | 长文本、大体积数据、文件上传必须使用 POST |
| 缓存机制 | 默认被浏览器、代理服务器缓存,重复访问读取缓存 | 默认不启用缓存,每次请求都会与服务器交互 | 实时数据采集建议优先 POST,避免缓存造成数据陈旧 |
| 书签与历史记录 | URL 完整保留,可保存为书签、记录在浏览历史 | 参数不在 URL 中,无法通过书签记录提交内容 | 登录类 POST 请求不会泄露提交的账号密码 |
| 编码方式 | 仅支持 ASCII 字符,中文、特殊符号需 URL 编码 | 支持多种编码格式,适配表单、JSON、二进制等数据 | 复杂数据提交、中文大批量传参优先使用 POST |
| 幂等性 | 具备幂等性,多次重复请求结果一致 | 不保证幂等性,多次提交可能产生多条数据 | 数据查询用 GET,表单提交、发布内容必须控制 POST 重复请求 |
| 安全性 | 较低,参数明文暴露在地址栏 | 相对更高,数据在请求体中传输,不易被直接抓取 | 账号、密码、隐私数据禁止使用 GET 传参 |
1.2 幂等性概念与爬虫应用
幂等性是 HTTP 协议中的重要概念:同一个请求执行一次与执行多次,服务器产生的效果完全相同。 GET 请求仅用于查询数据,不会修改服务器资源,因此天然具备幂等性,重复请求不会产生副作用。例如多次刷新新闻列表页,页面内容不会发生变化。
POST 请求多用于新增、修改服务器数据,不具备幂等性。例如提交评论、注册账号、发布动态,重复发送 POST 请求会生成多条重复数据,甚至触发平台风控。在爬虫开发中,针对 POST 提交场景,必须增加请求去重、频率限制,规避重复提交问题。
1.3 场景选型原则
结合爬虫业务,制定标准化选型规则:
- 仅查询、获取、浏览数据,不向服务器提交任何新增内容,选择 GET 请求;
- 登录、注册、表单提交、发布内容、上传文件、提交接口数据,选择 POST 请求;
- 参数数量多、数据长度大、包含中文 / 特殊字符,优先选择 POST;
- 传输账号、密码、手机号等隐私信息,严禁使用 GET 请求。
二、GET 请求补充拓展(进阶用法)
上一章节讲解了 GET 基础用法,本节针对实际开发中的高频进阶场景进行补充,包括 URL 编码处理、重定向控制、请求层级参数等内容,完善 GET 请求知识体系。
2.1 手动控制重定向行为
requests 库中 GET 请求默认自动跟随 3xx 重定向,部分场景下需要关闭自动跳转,直接获取重定向状态码与原始响应,例如判断页面是否失效、检测域名跳转关系。通过allow_redirects参数控制重定向开关。
代码示例
python
运行
import requests # 目标地址会触发临时重定向 url = "https://httpbin.org/redirect/2" # 关闭自动重定向 resp = requests.get(url, allow_redirects=False, timeout=5) print("状态码:", resp.status_code) print("响应头 Location(跳转地址):", resp.headers.get("Location"))原理说明
allow_redirects=False表示禁止库自动跳转,程序接收到 301/302 状态码后直接返回响应,不会继续访问新地址;- 重定向目标地址会存放在响应头
Location字段中,开发者可手动解析该地址完成二次请求; - 该用法常用于站点状态检测、恶意链接识别、跳转链路分析等爬虫辅助场景。
2.2 长参数与特殊字符自动编码
当 GET 参数包含中文、空格、#、&、= 等特殊字符时,手动拼接 URL 极易出现解析错误。而 requests 的params参数会自动完成 URL 编码,这也是行业内统一推荐的传参方式。
代码示例
python
运行
import requests url = "https://httpbin.org/get" # 包含中文、空格、特殊符号的参数 params = { "title": "Python 爬虫&实战教程", "content": "字符测试 # @ !" } resp = requests.get(url, params=params, timeout=5) print("编码后的完整URL:", resp.url) print("服务器接收的参数:", resp.text)原理说明
- URL 标准规定地址栏内不允许直接出现中文、特殊符号,必须转换为百分号编码格式;
- requests 内部集成 URL 编码算法,传入字典参数后自动完成转义,无需开发者手动调用编码函数;
- 禁止使用字符串拼接方式组装带特殊字符的 GET 参数,会大幅提升请求失败概率。
三、POST 请求基础语法与分类
POST 请求的核心方法为requests.post(),语法结构与get()类似,但新增了多种数据提交参数。根据服务器接收数据的格式,主流分为三大类:表单格式 POST、JSON 格式 POST、文件上传 POST,这也是爬虫开发中最核心的三种 POST 场景。
3.1 POST 通用语法结构
python
运行
requests.post(url, data=None, json=None, files=None, headers=None, timeout=None, allow_redirects=True)核心参数释义:
url:目标请求地址,必填项;data:用于提交表单格式数据,接收字典、元组、字符串类型;json:用于提交JSON 格式数据,接收字典类型,自动设置对应请求头;files:用于上传文件,接收文件对象组成的字典;- 其余
headers、timeout、allow_redirects等参数用法与 GET 请求完全一致。
3.2 表单格式 POST(application/x-www-form-urlencoded)
表单提交是传统网页最常用的 POST 数据格式,也是早期网站登录、表单提交的主流方式。请求头中Content-Type默认值为application/x-www-form-urlencoded,数据以键=值&键=值的格式存放在请求体中。
在 requests 中,使用data 参数传递表单数据,传入 Python 字典即可。
3.2.1 基础表单提交实战
python
运行
import requests url = "https://httpbin.org/post" # 表单数据,字典格式 form_data = { "username": "spider_user", "password": "123456abc", "submit": "登录" } # 使用 data 参数提交表单 resp = requests.post(url, data=form_data, timeout=5) print("响应状态码:", resp.status_code) print("服务器接收的表单数据:", resp.text)原理说明
- 代码中字典
form_data会被 requests 自动转换为username=spider_user&password=123456abc&submit=登录字符串,放入请求体; - 库自动添加请求头
Content-Type: application/x-www-form-urlencoded,适配传统表单接口; - 该方式适用于绝大多数 PC 网页登录、留言提交、普通表单交互场景。
3.2.2 一维多值表单提交
部分网页表单支持一个字段对应多个值(如多选框、标签选择),此时可将字典的值设置为列表,requests 会自动解析为多组同名参数。
python
运行
import requests url = "https://httpbin.org/post" # 单个字段对应多个值 form_data = { "hobby": ["阅读", "编程", "运动"] } resp = requests.post(url, data=form_data) print(resp.text)3.3 JSON 格式 POST(application/json)
随着前后端分离架构、移动端接口普及,JSON 格式已经成为现代接口的主流数据交互格式。移动端 APP、小程序、新版网站接口几乎全部使用 JSON 传参,这类接口必须使用json参数提交数据。
使用json参数时,requests 会自动完成两项工作:一是将字典转为标准 JSON 字符串放入请求体;二是自动设置请求头Content-Type: application/json,无需手动配置。
3.3.1 基础 JSON 数据提交
python
运行
import requests url = "https://httpbin.org/post" # JSON 格式数据,使用字典定义 json_data = { "uid": 10086, "nickname": "爬虫开发者", "info": { "level": "高级", "skill": "Python" } } # 使用 json 参数提交数据 resp = requests.post(url, json=json_data, timeout=5) print("JSON 接口响应:", resp.text)原理说明
- 字典类型的
json_data会被内部序列化为标准 JSON 字符串,支持多层嵌套结构,适配复杂接口参数; - 自动补充 JSON 专属请求头,这是区分表单 POST 与 JSON POST 的核心标识;
- 移动端爬虫、小程序接口、前后端分离项目爬虫,优先使用该方式。
3.3.4 data 与 json 参数混用误区
严禁同时使用 data 和 json 参数,二者作用互斥。若同时传入,仅json参数生效,data 数据会被丢弃,导致请求参数缺失,这是新手高频错误。
3.4 文件上传 POST(multipart/form-data)
网页文件上传、图片提交、附件上传场景,会使用multipart/form-data格式的 POST 请求,该格式专门用于传输二进制文件 + 表单混合数据,在 requests 中通过files参数实现。
3.4.1 单文件上传实战
python
运行
import requests url = "https://httpbin.org/post" # 以二进制读取本地文件 file_path = "test.jpg" # files 字典:键为表单字段名,值为(文件名, 文件对象, 文件类型) files = { "upload_file": (file_path, open(file_path, "rb"), "image/jpeg") } resp = requests.post(url, files=files, timeout=10) print("文件上传响应:", resp.text)原理说明
- 文件必须以二进制只读模式 rb打开,文本模式会造成文件损坏;
files参数的字典结构为固定格式,依次定义表单字段、文件名、文件 MIME 类型;- 库自动设置
Content-Type: multipart/form-data,按照文件传输协议拆分数据包完成上传。
3.4.2 文件 + 普通表单混合提交
实际场景中,上传文件的同时往往需要附带账号、描述等表单数据,可同时搭配data参数使用:
python
运行
import requests url = "https://httpbin.org/post" # 普通表单数据 form_data = {"desc": "爬虫上传的测试图片"} # 文件数据 files = {"img": ("test.png", open("test.png", "rb"))} # 同时提交表单与文件 resp = requests.post(url, data=form_data, files=files) print(resp.text)四、POST 请求进阶配置
本节讲解 POST 请求的通用进阶配置,包括手动设置请求头、HTTPS 证书忽略、超时与异常处理、会话保持等内容,适配复杂线上站点。
4.1 手动指定请求头
部分接口会校验请求头信息,默认请求头会被服务器拦截,此时需要通过headers参数自定义请求头,该参数 GET/POST 请求通用。
python
运行
import requests url = "https://httpbin.org/post" # 自定义请求头 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Referer": "https://httpbin.org/" } # JSON 格式 POST + 自定义请求头 json_data = {"name": "test"} resp = requests.post(url, json=json_data, headers=headers, timeout=5) print(resp.text)4.2 忽略 HTTPS 证书验证
部分网站使用自签名证书、过期证书,发送 POST 请求时会触发SSLError证书异常。通过verify=False关闭证书校验,该配置适用于内网站点、测试站点爬虫。
python
运行
import requests url = "https://httpbin.org/post" data = {"key": "value"} # verify=False 关闭 HTTPS 证书验证 resp = requests.post(url, data=data, verify=False, timeout=5) print(resp.status_code)补充说明:关闭证书校验后控制台会出现警告信息,可导入 urllib3 模块屏蔽警告,适配正式爬虫项目。
4.3 POST 请求异常捕获模板
POST 请求网络异常类型与 GET 一致,结合请求状态码校验、异常捕获编写通用模板,适用于所有 POST 场景:
python
运行
import requests from requests.exceptions import RequestException url = "https://httpbin.org/post" json_data = {"username": "demo"} try: resp = requests.post(url, json=json_data, timeout=6) # 主动抛出 4xx/5xx 异常 resp.raise_for_status() print("请求成功:", resp.text) except RequestException as e: print("POST 请求异常:", str(e))五、Session 会话下的 POST 请求(登录态保持)
登录是爬虫最典型的 POST 应用场景:首先发送 POST 登录请求提交账号密码,服务器返回 Cookie,后续页面访问携带该 Cookie 即可维持登录状态。结合上一章节的 Session 会话对象,可自动保存并传递 Cookie,实现一站式登录爬虫。
5.1 模拟账号登录完整案例
python
运行
import requests # 创建会话对象,自动管理 Cookie session = requests.Session() # 1. 第一步:POST 登录请求 login_url = "https://httpbin.org/post" login_data = { "username": "spider001", "password": "666888" } # 提交登录表单 login_resp = session.post(login_url, data=login_data, timeout=5) print("登录请求响应:", login_resp.text) # 2. 第二步:使用同一会话访问需要登录的页面,自动携带 Cookie page_url = "https://httpbin.org/cookies" page_resp = session.get(page_url) print("登录后页面 Cookie:", page_resp.text) # 关闭会话 session.close()原理说明
- 登录操作本质是向服务器提交身份信息的 POST 请求,验证通过后服务器下发身份 Cookie;
- Session 对象自动保存登录返回的 Cookie,后续同一会话内的所有请求都会自动携带;
- 该案例完全还原了网页登录、浏览个人中心的完整流程,是登录类爬虫的标准写法。
六、GET 与 POST 混合实战综合案例
本案例模拟真实网站查询(GET)+ 提交数据(POST)的混合业务流程,整合本章及上一章节全部知识点,实现完整业务链路爬虫。
6.1 业务逻辑
- 使用 GET 请求携带参数查询列表数据;
- 使用 Session 保持会话;
- 使用 POST 请求提交评论数据;
- 增加超时、异常捕获、请求头伪装,保证程序健壮性。
6.2 完整代码实现
python
运行
import requests from requests.exceptions import RequestException # 全局配置 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" } base_session = requests.Session() base_session.headers = headers base_session.timeout = 8 # 第一步:GET 请求查询内容列表 query_url = "https://httpbin.org/get" query_params = { "page": 1, "type": "article" } try: print("===== 发起GET查询请求 =====") get_resp = base_session.get(query_url, params=query_params) get_resp.raise_for_status() print("查询结果:", get_resp.text) except RequestException as e: print("查询失败:", e) # 第二步:POST 请求提交评论 post_url = "https://httpbin.org/post" comment_data = { "article_id": 1001, "content": "通过爬虫模拟提交评论" } try: print("\n===== 发起POST提交请求 =====") post_resp = base_session.post(post_url, data=comment_data) post_resp.raise_for_status() print("提交结果:", post_resp.text) except RequestException as e: print("提交失败:", e) # 释放资源 base_session.close() print("\n全流程执行完毕")案例总结
- 会话对象统一管理请求头、超时时间,减少重复代码;
- 查询操作使用 GET,数据提交使用 POST,严格遵循协议规范;
- 全流程增加异常捕获,保证爬虫连续运行;
- 该案例可直接改造应用于资讯网站、论坛、社区类爬虫项目。
七、常见问题与排错方案
7.1 POST 参数提交后服务器接收为空
故障现象:代码正常运行,状态码 200,但服务器未获取到提交的数据。原因与解决方案:
- 参数格式不匹配:接口要求 JSON 格式却使用 data 传参,改为
json参数;接口要求表单格式却使用 json 传参,改为data参数; - 请求头缺失:部分接口强制校验
Content-Type,手动补充对应请求头; - 参数字段名错误:核对网页抓包结果,保证字典键名与网站原始参数完全一致。
7.2 登录 POST 请求返回 403 拒绝访问
故障现象:账号密码无误,但登录请求被拦截。原因与解决方案:缺少请求头伪装、缺少 Referer 字段、未携带前置 Cookie。解决方案:抓包浏览器完整请求头,复刻至代码中,使用 Session 完成前置请求。
7.3 GET 参数中文乱码
故障现象:URL 中中文参数解析异常,服务器识别为乱码。原因与解决方案:禁止手动拼接 URL 字符串,统一使用params字典传参,依靠库自动编码。
