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 3 已经把 Python 2 里混乱的 urllib / urllib2 彻底统一成了现在的 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 请求,需要先把参数字典编码成字节串再传入 data:
from urllib.parse import urlencode
from urllib.request import urlopen
# 准备要提交的参数
data_dict = {'word': 'hello urllib', 'author': 'germey'}
# 用 urlencode 转换成 URL 编码的字符串,再编码成 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:
# 故意设置一个极短的超时时间来测试异常
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. 用 Request 构造请求
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, # 很少用到,默认即可
method=None # 显式指定请求方法:GET / POST / PUT / DELETE 等
)
2.3 高级玩法:OpenerDirector
urlopen 背后其实是 Python 帮我们准备好的一个默认 Opener。如果要处理身份认证、代理、Cookies 等复杂场景,就需要自己动手构建 Opener 了。
场景1:网站需要 Basic Auth 认证
比如某些测试站点需要输入用户名和密码才能访问:
from urllib.request import (
HTTPPasswordMgrWithDefaultRealm,
HTTPBasicAuthHandler,
build_opener
)
# 1. 创建密码管理器
password_mgr = HTTPPasswordMgrWithDefaultRealm()
# 2. 添加认证信息
password_mgr.add_password(
realm=None, # 一般网站没有指定 realm,留空即可
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)
opener.open('https://www.baidu.com')
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)
print('\n✅ Cookies 已保存到 baidu_cookies.txt')
3. exception-handling
爬虫在运行过程中难免遇到网络故障、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 Not Found、500 Internal Server Error
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 拆成多个组成部分:
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}')
urlsplit 和 urlparse 很相似,只是不会单独拆出 params(现代网站已经很少使用这一部分)。
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 处理相对链接
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 / unquote
中文和特殊符号放在 URL 里必须经过编码,才能被服务器正确识别:
from urllib.parse import urlencode, quote, unquote
# urlencode:把字典转换成 URL 查询字符串
params = {'keyword': 'Python爬虫', 'page': 1, 'size': 10}
base_url = 'https://www.example.com/search?'
full_url = base_url + urlencode(params)
print(full_url) # 中文会被自动编码
# quote / unquote:单独处理字符串的编码和解码
chinese = 'Python入门到放弃'
encoded = quote(chinese)
decoded = unquote(encoded)
print(f'编码后:{encoded}')
print(f'解码后:{decoded}')
5. 最佳实践(避坑指南)
-
一定要设置 User-Agent
默认的 urllib User-Agent 会直接暴露你的爬虫身份,大概率遇到 403,务必伪装成浏览器。
-
合理设置 timeout
一般建议 3~10 秒,并且配合异常捕获,避免程序长时间无响应。
-
显式处理编码
将响应字节流转成字符串时,最好先根据响应头的 Content-Type 来确定编码,直接写死 utf-8 有时会碰到乱码。
-
遵守 robots.txt
入门时可以暂时不处理,但如果要写公开爬虫或大规模采集,一定要先通过 robotparser 检查是否允许访问。
-
复杂需求及时换库
urllib 虽然内置,但语法相对繁琐。当遇到 Cookie 池、代理池、异步并发等需求时,更推荐使用 requests(同步)或 aiohttp(异步)。
总结
urllib 是 Python 爬虫入门的“敲门砖”。掌握它,你就能理解 HTTP 请求的基本流程:构造请求 → 发送 → 接收响应 → 处理异常。一旦把这些基础打牢,以后再学习其他第三方库就会特别顺畅。