Python urllib 模块使用指南
1. 概述
urllib 是 Python 标准库中唯一内置的 HTTP 全链路请求工具集,从请求发送、异常捕获、URL 处理到 robots.txt 规则解析,一站式覆盖基础爬虫的核心需求,对入门学习者非常友好。
它主要包含 4 个模块:
urllib.request:发送 GET/POST 等 HTTP 请求的核心
urllib.error:统一捕获请求过程中的 URL/HTTP 异常
urllib.parse:处理 URL 拼接、编码、分割等操作的工具箱
urllib.robotparser:robots.txt 协议解析工具(入门后偶尔用到)
⚠️ 别再查 Python 2 的 urllib/urllib2 了!Python 3 已经彻底统一为 urllib 包,拆分出了更清晰的子模块。
2. 发送请求
2.1 基础请求:urlopen
urlopen 是最简单的请求入口,一行代码就能发起 GET 请求:
from urllib.request import urlopen
# 发起无参数的 GET 请求
response = urlopen('https://www.python.org')
# 读取响应字节流,转为 UTF-8 文本
print(response.read().decode('utf-8'))
除了 URL 外,urlopen 还支持几个关键参数:
参数1:data → 用于 POST 请求
POST 请求需要先把字典参数编码成字节流:
from urllib.parse import urlencode
from urllib.request import urlopen
# 构造字典参数
data_dict = {'word': 'hello urllib', 'author': 'germey'}
# 1. 用 urlencode 转为 URL 编码字符串
# 2. 再用 bytes() 转为 UTF-8 字节流
data_bytes = bytes(urlencode(data_dict), encoding='utf-8')
# 带 data 参数会自动转为 POST 请求
response = urlopen('https://httpbin.org/post', data=data_bytes)
print(response.read().decode('utf-8'))
参数2:timeout → 超时控制
防止网络波动导致程序卡死:
import socket
import urllib.error
from urllib.request import urlopen
try:
# 设置 0.1 秒超时(故意设短测试异常)
response = urlopen('https://httpbin.org/get', timeout=0.1)
except urllib.error.URLError as e:
# 判断异常原因是否为超时
if isinstance(e.reason, socket.timeout):
print('⚠️ 请求超时,请检查网络或延长 timeout')
参数3:其他安全参数
context:自定义 SSL 验证规则(入门时可以默认跳过验证,但生产环境禁用)
cafile/capath:指定本地 CA 证书路径(适用于内网 HTTPS 站点)
2.2 进阶请求:Request 类
当需要自定义请求头(User-Agent、Referer 等)、指定请求方法时,直接用 urlopen 就不够灵活了,需要先构建 Request 对象:
from urllib.request import Request, urlopen
# 1. 基础构造
req = Request('https://python.org')
# 2. 动态添加请求头(伪装成浏览器很重要!)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
# 3. 发起请求
response = urlopen(req)
print(response.status) # 输出响应码 200
Request 完整构造方法(用的时候按需传参):
Request(
url,
data=None, # POST 字节流
headers={}, # 请求头字典,也可以用 add_header 动态加
origin_req_host=None, # 请求发起的主机(一般自动生成)
unverifiable=False, # 很少用到的参数,默认 False
method=None # 显式指定请求方法:GET/POST/PUT/DELETE 等
)
2.3 高级场景:OpenerDirector
urlopen 其实是 Python 内置的默认 OpenerDirector,如果需要处理身份验证、代理、Cookies 等复杂场景,就需要自定义 Opener:
场景1:网站身份验证(Basic Auth)
比如测试站 https://ssr3.scrape.center/ 需要账号密码:
from urllib.request import (
HTTPPasswordMgrWithDefaultRealm,
HTTPBasicAuthHandler,
build_opener
)
# 1. 创建密码管理器
password_mgr = HTTPPasswordMgrWithDefaultRealm()
# 2. 添加验证信息
password_mgr.add_password(
realm=None, # 一般网站没有指定,留空即可
uri='https://ssr3.scrape.center/',
user='admin',
passwd='admin'
)
# 3. 创建验证 Handler
auth_handler = HTTPBasicAuthHandler(password_mgr)
# 4. 用 Handler 构建自定义 Opener
opener = build_opener(auth_handler)
# 5. 发起验证请求
response = opener.open('https://ssr3.scrape.center/')
print(response.read().decode('utf-8'))
场景2:代理 IP 设置
防止同一 IP 请求过多被封:
from urllib.request import ProxyHandler, build_opener
# 1. 配置代理字典(HTTP/HTTPS 分开写)
proxy_dict = {
'http': 'http://127.0.0.1:8080',
'https': 'https://127.0.0.1:8080'
}
# 2. 创建代理 Handler
proxy_handler = ProxyHandler(proxy_dict)
# 3. 构建 Opener
opener = build_opener(proxy_handler)
# 4. 发起代理请求
try:
response = opener.open('https://www.baidu.com', timeout=3)
print('✅ 代理连接成功')
except Exception as e:
print(f'❌ 代理连接失败:{e}')
场景3:Cookies 处理
模拟登录后保持会话状态:
from http.cookiejar import CookieJar, MozillaCookieJar
from urllib.request import HTTPCookieProcessor, build_opener
# --- 情况1:内存中保存 Cookies(临时会话)
cookie_jar = CookieJar()
cookie_handler = HTTPCookieProcessor(cookie_jar)
opener = build_opener(cookie_handler)
# 第一次请求(比如登录后),Cookies 会自动存入 cookie_jar
opener.open('https://www.baidu.com')
# 打印内存中的 Cookies
print('内存中的 Cookies:')
for cookie in cookie_jar:
print(f' {cookie.name}: {cookie.value}')
# --- 情况2:保存 Cookies 到本地文件(持久化会话)
# 用 MozillaCookieJar 兼容浏览器的 cookies.txt 格式
local_cookie = MozillaCookieJar('baidu_cookies.txt')
local_handler = HTTPCookieProcessor(local_cookie)
local_opener = build_opener(local_handler)
# 发起请求并保存
local_opener.open('https://www.baidu.com')
local_cookie.save(ignore_discard=True, ignore_expires=True) # 忽略过期和临时 Cookie
print('\n✅ 已保存到 baidu_cookies.txt')
3. 异常处理
爬虫运行过程中难免遇到网络错误、404/500 等问题,必须用异常捕获增强程序健壮性:
urllib 主要提供两个异常类,且 HTTPError 是 URLError 的子类,捕获时先抓子类再抓父类:
from urllib.request import urlopen
from urllib.error import HTTPError, URLError
try:
response = urlopen('https://httpbin.org/status/404', timeout=3)
except HTTPError as e:
# HTTP 错误:比如 404 未找到、500 服务器错误
print(f'❌ HTTP错误:状态码 {e.code},原因 {e.reason}')
# 可以获取响应头
# print(e.headers)
except URLError as e:
# URL 错误:比如域名不存在、网络不通、超时
print(f'❌ URL错误:{e.reason}')
else:
# 无异常时执行
print('✅ 请求成功')
4. URL 解析
爬虫经常需要拼接、分割或编码 URL,urllib.parse 模块完全能搞定:
4.1 分割 URL:urlparse/urlsplit
把完整 URL 拆成 6/5 个部分(区别是 urlparse 会拆出 params,现代网站很少用):
from urllib.parse import urlparse
url = 'https://www.baidu.com/index.html;user?id=5&name=test#comment'
result = urlparse(url)
print(result)
# 输出:ParseResult(scheme='https', netloc='www.baidu.com', path='/index.html', params='user', query='id=5&name=test', fragment='comment')
# 直接用属性访问对应部分
print(f'协议:{result.scheme}')
print(f'域名:{result.netloc}')
print(f'路径:{result.path}')
4.2 拼接 URL:urlunparse/urlunsplit/urljoin
urlunparse/urlunsplit:对应反向操作,把列表/元组拼成完整 URL
urljoin:更常用的「相对路径转绝对路径」工具
from urllib.parse import urlunparse, urljoin
# --- urlunparse
data = ['https', 'www.baidu.com', '/index.html', '', 'id=6', 'new_comment']
print(urlunparse(data)) # 输出:https://www.baidu.com/index.html?id=6#new_comment
# --- urljoin(处理 HTML 中的相对链接非常有用)
base_url = 'https://www.example.com/blog/'
rel_url1 = 'article1.html'
rel_url2 = '../about.html'
print(urljoin(base_url, rel_url1)) # https://www.example.com/blog/article1.html
print(urljoin(base_url, rel_url2)) # https://www.example.com/about.html
4.3 编码 URL 参数/中文字符:urlencode/quote
urlencode:把字典参数转为 URL 编码的查询字符串
quote/unquote:单独编码/解码中文字符或特殊符号
from urllib.parse import urlencode, quote, unquote
# --- urlencode
params = {'keyword': 'Python爬虫', 'page': 1, 'size': 10}
base_url = 'https://www.example.com/search?'
full_url = base_url + urlencode(params)
print(full_url) # keyword 会自动编码
# --- quote/unquote
chinese = 'Python入门到放弃'
encoded = quote(chinese)
decoded = unquote(encoded)
print(f'编码后:{encoded}')
print(f'解码后:{decoded}')
5. 最佳实践(避坑指南)
- 必须设置 User-Agent:默认的 urllib User-Agent 会被大多数网站直接识别为爬虫,大概率返回 403 Forbidden
- 合理设置 timeout:一般设置 3-10 秒,配合异常捕获
- 显式处理编码:响应字节流转文本时,最好先判断响应头的
Content-Type 编码(虽然入门可以直接用 utf-8 试)
- 遵守 robots.txt:入门时可以暂时不管,但写公开爬虫或批量爬取时,一定要先检查
can_fetch
- 复杂需求换库:urllib 虽然内置,但语法较繁琐,有 Cookie 池、代理池、异步需求的话,建议用 requests(同步)或 aiohttp(异步)
总结
urllib 是 Python 爬虫入门的「敲门砖」,通过它能理解 HTTP 请求的基本流程(构造请求→发送→接收响应→异常处理)。熟练掌握后,再转用第三方库会非常轻松!