Python爬虫基础:Requests库入门、HTTP协议、GET与POST请求、响应解析

说实话,很多人一听到「爬虫」就觉得是黑科技。其实说白了,爬虫就是模拟浏览器去服务器拿数据。你每天刷网页、看新闻、查天气,背后都是这套逻辑。我刚开始学的时候也犯过嘀咕——不就是发个请求吗?后来踩了几个坑才明白,这里面的门道还真不少。

这一章,咱们就把爬虫最核心的底子打牢。你不需要记住所有细节,但得理解整个流程是怎么转起来的。

本章核心目标:理解HTTP请求与响应的完整生命周期,掌握Requests库的基本用法,能独立完成GET/POST请求并解析返回数据。

Python爬虫基础 · 知识体系 Requests 库 HTTP 协议基础 URL 结构 · 状态码 请求头 · 响应头 GET 与 POST 参数传递 · 数据提交 适用场景对比 响应解析 text / json / content 编码处理 · 状态判断 实战流程:构造请求 → 发送 → 解析 → 处理数据

1. HTTP协议——爬虫的「交通规则」

爬虫和服务器之间怎么沟通?靠的就是HTTP协议。你可以把它想象成快递行业的统一标准——包裹怎么打包、面单怎么填、快递员怎么敲门,都有规矩。

HTTP协议的核心就两样东西:请求和响应。你发一个请求过去,服务器给你一个响应回来。就这么简单。

1.1 URL——你要去哪

URL就是网址,告诉爬虫「目标在哪」。我见过不少新手直接把浏览器地址栏的链接复制过来就用,结果发现拿不到数据。为什么?因为有些链接里带了跟踪参数或者会话ID,换个环境就失效了。

一个标准的URL长这样:

https://api.example.com:443/data?page=1&limit=20#section
|______||______________||_||____||______________||_______|
 协议       主机名       端口  路径      查询参数      片段

实际爬虫里,我们最常打交道的是查询参数。翻页、筛选、排序,全靠它。

1.2 状态码——服务器在说什么

每次请求,服务器都会返回一个三位数的状态码。我刚开始爬数据时,看到4xx就慌,后来才发现大部分问题都是自己粗心造成的。

状态码 含义 常见原因
200 OK 请求成功,数据正常返回
301/302 重定向 URL变了,Requests默认会自动跟随
403 Forbidden 服务器拒绝访问,大概率是没加请求头
404 Not Found 路径写错了,或者资源被删了
429 Too Many Requests 爬太快被限制了,加个延时就好
500 服务器内部错误 不是你的问题,是服务器炸了

我的小习惯:每次请求后先检查 response.status_code,等于200再往下处理。别一股脑解析,万一服务器返回的是错误页面,你解析半天也是白费功夫。

2. Requests库——Python里最好用的HTTP工具

Python里发HTTP请求的库不少,但Requests是王者。它的口号是「HTTP for Humans」,说白了就是让你用最少的代码干最多的活。

安装就一句话:

pip install requests

然后导入就能用:

import requests

3. GET请求——从服务器拿数据

GET是最常用的请求方式。你打开一个网页,浏览器发的就是GET请求。它的特点是:参数都挂在URL后面,肉眼可见。

3.1 最简单的GET

import requests

url = "https://httpbin.org/get"
response = requests.get(url)
print(response.text)

跑一下,你会看到服务器返回了一大段JSON。这就是最原始的响应内容。

3.2 带参数的GET

翻页、搜索、过滤,都需要传参数。我习惯用 params 参数来传,Requests会自动帮你拼接到URL上:

url = "https://httpbin.org/get"
params = {
    "page": 1,
    "limit": 20,
    "keyword": "爬虫"
}
response = requests.get(url, params=params)
print(response.url)  # 看看实际请求的URL长什么样

输出结果:

https://httpbin.org/get?page=1&limit=20&keyword=%E7%88%AC%E8%99%AB

注意那个 %E7%88%AC%E8%99%AB,这是中文被URL编码后的结果。Requests帮你自动处理了,不用自己操心。

我曾经踩过的坑:手动拼接参数时忘了URL编码,结果中文参数传过去服务器根本不认。后来一律用 params 字典,再也没出过问题。

4. POST请求——向服务器提交数据

POST和GET最大的区别在于:数据放在请求体里,不在URL上。登录、提交表单、上传文件,都用POST。

4.1 表单数据提交

大部分网站的表单提交,用 data 参数就行:

url = "https://httpbin.org/post"
form_data = {
    "username": "admin",
    "password": "123456"
}
response = requests.post(url, data=form_data)
print(response.text)

4.2 JSON数据提交

现在很多API接口都要求传JSON格式的数据。这时候用 json 参数,Requests会自动帮你做两件事:把字典转成JSON字符串,设置 Content-Type 头为 application/json。

url = "https://httpbin.org/post"
json_data = {
    "name": "张三",
    "age": 28,
    "skills": ["Python", "爬虫", "GIS"]
}
response = requests.post(url, json=json_data)
print(response.text)

关键区别:用 data 传的是表单格式(application/x-www-form-urlencoded),用 json 传的是JSON格式(application/json)。服务器怎么要求的,你就怎么传,搞错了会返回400错误。

5. 响应解析——拿到数据怎么处理

请求发出去了,响应也回来了。接下来就是解析。我见过有人拿到响应直接打印,然后手动复制粘贴——千万别这么干,太原始了。

5.1 响应对象的核心属性

属性/方法 说明 使用场景
response.status_code 状态码 判断请求是否成功
response.text 字符串形式的响应内容 HTML页面、普通文本
response.json() 解析为Python字典/列表 API接口返回的JSON数据
response.content 字节形式的响应内容 图片、文件、二进制数据
response.headers 响应头字典 查看服务器信息、编码方式
response.encoding 编码方式 乱码时手动指定编码

5.2 处理JSON响应

现在大部分API都返回JSON。用 .json() 方法直接转成Python对象,操作起来特别方便:

url = "https://api.github.com/users/octocat"
response = requests.get(url)

if response.status_code == 200:
    data = response.json()
    print(f"用户名: {data['login']}")
    print(f"仓库数: {data['public_repos']}")
    print(f"粉丝数: {data['followers']}")
else:
    print(f"请求失败,状态码: {response.status_code}")

5.3 处理乱码问题

你想想看,为什么有时候爬回来的中文全是乱码?因为服务器返回的编码和Python默认的编码对不上。Requests会尝试从响应头里猜编码,但有时候猜不准。

我的做法是:先看 response.encoding,如果不对就手动指定:

response = requests.get(url)
response.encoding = 'utf-8'  # 或者 'gbk'、'gb2312'
print(response.text)

一个小技巧:如果不知道编码是什么,可以用 response.apparent_encoding,Requests会基于内容自动推断。不过这个操作比较耗时,生产环境里我一般直接指定。

6. 请求头——别让服务器认出你是爬虫

很多网站会检查 User-Agent,发现不是浏览器就直接拒绝。我刚开始爬某个电商网站时,怎么都拿不到数据,后来加了个浏览器的User-Agent就好了。

url = "https://httpbin.org/headers"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Referer": "https://www.google.com/"
}
response = requests.get(url, headers=headers)
print(response.text)

常见的请求头字段:

  • User-Agent:标识客户端类型,必加
  • Referer:告诉服务器从哪个页面跳过来的
  • Cookie:维持登录状态
  • Authorization:携带认证令牌

7. 实战:完整的爬虫请求流程

把上面这些串起来,就是一个完整的爬虫请求流程。我写了个模板,每次爬新网站都拿它改:

import requests

def fetch_data(url, params=None, headers=None, method="GET"):
    """
    通用的请求函数
    """
    default_headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
    }
    if headers:
        default_headers.update(headers)
    
    try:
        if method.upper() == "GET":
            response = requests.get(url, params=params, headers=default_headers, timeout=10)
        elif method.upper() == "POST":
            response = requests.post(url, data=params, headers=default_headers, timeout=10)
        else:
            raise ValueError("不支持的请求方法")
        
        response.encoding = 'utf-8'
        
        if response.status_code == 200:
            return response.json() if 'application/json' in response.headers.get('Content-Type', '') else response.text
        else:
            print(f"请求失败: {response.status_code}")
            return None
            
    except requests.exceptions.Timeout:
        print("请求超时,请检查网络或目标服务器")
        return None
    except requests.exceptions.ConnectionError:
        print("连接失败,请检查URL是否正确")
        return None

# 使用示例
url = "https://api.example.com/data"
result = fetch_data(url, params={"page": 1})
if result:
    print(result)

注意:一定要加 timeout 参数。我遇到过爬虫跑了一整夜,结果卡在一个死链接上,所有线程都挂在那。加了超时,至少能保证程序不会无限等待。

8. 常见问题与避坑指南

  • SSL证书错误:有些网站证书过期或自签名,加 verify=False 可以跳过验证,但会有警告。用 requests.packages.urllib3.disable_warnings() 关掉警告。
  • 重定向:Requests默认自动跟随重定向,如果不想跟,设置 allow_redirects=False。
  • 会话保持:用 requests.Session() 可以保持Cookie,适合需要登录的网站。
  • 代理:用 proxies 参数设置代理,爬国外网站时经常用到。

嗯,到这里,Requests库的基础用法你应该心里有数了。说白了就是三步:构造请求、发送请求、解析响应。后面的章节我们会在这个基础上,一步步搭建真正能用的爬虫系统。