从零到接单 08:HTTP 协议与 requests 库——让程序上网
系列目录:本文是「从零到接单:Python 自动化与爬虫实战」系列的第 8 篇。基础语法已经学完,从这篇开始,我们正式进入爬虫世界。你将学会用 Python 发网络请求、获取网页内容——这是所有爬虫的第一步。
前面的程序都在本地"自娱自乐"。现在让 Python 上网——从互联网抓取数据。这才是接单赚钱的核心能力。
一、理解 HTTP:浏览器和服务器怎么"说话"
打开一个网页,浏览器和服务器之间经历了什么?
你(浏览器) 服务器
| |
|——— GET /index.html ————→ |
| |(查找文件)
|←—— 200 OK + HTML 内容 ——— |
| |
|——— GET /style.css ——————→ |
|←—— 200 OK + CSS 内容 ——— |
| |
一个 HTTP 请求包含:
- URL:要访问的地址
- 方法:GET(拿数据)、POST(提交数据)、PUT(更新)、DELETE(删除)
- 请求头(Headers):User-Agent(我是谁)、Cookie、Content-Type 等
- 请求体(Body):POST 时携带的数据
一个 HTTP 响应包含:
- 状态码:200(成功)、301(重定向)、404(找不到)、500(服务器错误)
- 响应头:Content-Type、Set-Cookie 等
- 响应体:HTML、JSON、图片等实际内容
二、安装 requests
pip install requests
requests 是 Python 最流行的 HTTP 库,被称为"HTTP for Humans"——把复杂的 HTTP 操作变得简单优雅。
三、第一个爬虫:GET 请求
import requests
# 发送 GET 请求
response = requests.get("https://httpbin.org/get")
# 状态码
print(f"状态码:{response.status_code}") # 200
# 响应头
print(f"Content-Type:{response.headers['Content-Type']}")
# 响应内容(文本)
print(f"内容:{response.text[:200]}...") # 前 200 个字符
# 如果是 JSON 响应
data = response.json()
print(f"JSON 数据:{data}")
常用响应属性
resp = requests.get("https://example.com")
resp.status_code # 状态码:200, 404, 500...
resp.text # 响应文本(字符串)
resp.content # 响应内容(bytes,处理图片/文件时用)
resp.json() # 解析 JSON 响应
resp.headers # 响应头字典
resp.url # 最终 URL(可能被重定向过)
resp.encoding # 编码
resp.cookies # Cookie
resp.elapsed # 请求耗时
四、带参数的请求
URL 参数(查询字符串)
# 方式一:手动拼在 URL 里
resp = requests.get("https://httpbin.org/get?name=张三&age=25")
# 方式二:用 params 参数(推荐,自动处理特殊字符)
params = {
"name": "张三",
"age": 25,
"city": "北京"
}
resp = requests.get("https://httpbin.org/get", params=params)
print(resp.url) # https://httpbin.org/get?name=%E5%BC%A0%E4%B8%89&age=25&city=%E5%8C%97%E4%BA%AC
POST 请求:提交数据
# 提交表单数据
data = {
"username": "admin",
"password": "123456"
}
resp = requests.post("https://httpbin.org/post", data=data)
print(resp.json()["form"])
# 提交 JSON 数据
import json
resp = requests.post(
"https://httpbin.org/post",
json={"name": "小明", "score": 95}
)
print(resp.json()["json"])
五、请求头:伪装成浏览器
服务器会检查请求头,如果发现是 Python 脚本,可能拒绝访问。
# 默认的 requests User-Agent 长这样:
# python-requests/2.28.0
# ❌ 很多网站会封掉这种请求!
设置请求头
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Referer": "https://www.google.com/",
}
resp = requests.get("https://httpbin.org/headers", headers=headers)
print(resp.json()["headers"]["User-Agent"])
# Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...
💡 一个好习惯:把常用 headers 封装到函数或配置文件里。
六、Cookie 与 Session:保持登录状态
# 方式一:直接传 Cookie
cookies = {"session_id": "abc123", "token": "xyz789"}
resp = requests.get("https://httpbin.org/cookies", cookies=cookies)
print(resp.json())
# 方式二:用 Session 自动管理 Cookie(推荐!)
session = requests.Session()
# 先登录
login_data = {"username": "test", "password": "123456"}
session.post("https://httpbin.org/post", data=login_data)
# 之后所有请求都自动携带 Cookie
resp = session.get("https://httpbin.org/cookies")
print(resp.json())
七、超时与重试:健壮的爬虫
# 设置超时(秒)
try:
resp = requests.get("https://httpbin.org/delay/5", timeout=3)
except requests.Timeout:
print("请求超时!")
# timeout 可以设为元组:(连接超时, 读取超时)
resp = requests.get("https://httpbin.org/get", timeout=(3, 10))
封装一个带重试的请求函数
import time
def safe_request(url, max_retries=3, timeout=10, **kwargs):
"""带重试的请求函数"""
for attempt in range(max_retries):
try:
resp = requests.get(url, timeout=timeout, **kwargs)
resp.raise_for_status() # 非 200 状态码抛出异常
return resp
except requests.RequestException as e:
if attempt == max_retries - 1:
raise e
wait = 2 ** attempt # 指数退避:1s, 2s, 4s
print(f"第 {attempt + 1} 次失败,{wait} 秒后重试...")
time.sleep(wait)
# 使用
resp = safe_request("https://httpbin.org/get")
print(resp.status_code)
八、代理设置
爬虫必备技能——用代理避免 IP 被封。
# HTTP 代理
proxies = {
"http": "http://127.0.0.1:7890",
"https": "http://127.0.0.1:7890",
}
resp = requests.get("https://httpbin.org/ip", proxies=proxies)
print(resp.json())
# 带认证的代理
proxies = {
"http": "http://user:password@proxy.example.com:8080",
}
九、实战:写一个简单的网页抓取器
import requests
import re
import time
from pathlib import Path
class SimpleCrawler:
"""简易网页抓取器"""
def __init__(self, delay=1):
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36",
})
self.delay = delay
self.output_dir = Path("crawled_pages")
self.output_dir.mkdir(exist_ok=True)
def fetch(self, url, max_retries=3):
"""抓取单个页面"""
for attempt in range(max_retries):
try:
print(f"正在抓取:{url}")
resp = self.session.get(url, timeout=10)
resp.raise_for_status()
# 自动检测编码
resp.encoding = resp.apparent_encoding
print(f"✅ 成功!状态码:{resp.status_code},大小:{len(resp.text)} 字符")
time.sleep(self.delay)
return resp.text
except requests.RequestException as e:
print(f"❌ 第 {attempt + 1} 次失败:{e}")
if attempt == max_retries - 1:
return None
time.sleep(2 ** attempt)
def save_page(self, url, html):
"""保存页面到本地"""
safe_name = re.sub(r"[^\w\-_.]", "_", url)[:100]
filepath = self.output_dir / f"{safe_name}.html"
with open(filepath, "w", encoding="utf-8") as f:
f.write(html)
print(f"已保存到:{filepath}")
return filepath
def extract_links(self, html):
"""从 HTML 中提取所有链接"""
return re.findall(r'href="(https?://[^"]+)"', html)
# 使用
if __name__ == "__main__":
crawler = SimpleCrawler(delay=1)
# 抓取单个页面
html = crawler.fetch("https://httpbin.org/html")
if html:
crawler.save_page("httpbin-test", html)
links = crawler.extract_links(html)
print(f"提取到 {len(links)} 个链接")
总结
| 操作 | 代码 |
|------|------|
| GET 请求 | requests.get(url) |
| POST 请求 | requests.post(url, data={}) |
| 带参数 | requests.get(url, params={...}) |
| 请求头 | requests.get(url, headers={...}) |
| 保持登录 | session = requests.Session() |
| 超时 | requests.get(url, timeout=10) |
| 代理 | requests.get(url, proxies={...}) |
| 检查状态 | resp.raise_for_status() |
| 解析 JSON | resp.json() |
| 获取文本 | resp.text |
你已经能让 Python 上网了!下一篇,我们将学习 BeautifulSoup——从抓回来的 HTML 中精确提取想要的信息。
练习:写一个程序,获取 httpbin.org/headers 的内容,打印出服务器看到的你的 User-Agent。然后修改请求头,伪装成 iPhone 浏览器再试一次。
iPhone Safari UA:
Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1
Comments
Sign in to leave a comment.