一、Python 爬虫 403 错误的核心成因
403 Forbidden 本质是服务器对请求的「身份校验失败」,常见触发原因可分为以下几类:
- 请求头缺失或异常:服务器会校验 User-Agent、Referer、Cookie 等关键请求头,若缺失或为默认值(如 Python-urllib/3.8),会被判定为爬虫;
- IP 封禁:同一 IP 短时间内高频请求,被服务器加入黑名单;
- 反爬机制检测:网站通过 JS 加密、验证码、浏览器指纹(如 navigator 对象)等识别非人工访问;
- 会话验证失败:部分网站需要先登录生成有效会话,无会话请求会直接返回 403。
普通 HTTP 请求(requests 库)和 Selenium 的核心差异,本质是「模拟请求」与「模拟真实浏览器行为」的区别,这也决定了二者处理 403 的能力边界。
二、普通请求(requests)vs Selenium:核心差异对比
表格
| 维度 | 普通请求(requests) | Selenium |
|---|---|---|
| 请求本质 | 构造 HTTP/HTTPS 请求包,无浏览器环境 | 驱动真实浏览器(Chrome/Firefox),模拟人工操作 |
| 请求头特征 | 需手动构造,易被识别为非浏览器请求 | 自动携带浏览器原生请求头,更接近真实用户 |
| JS 渲染能力 | 无,无法处理动态加载内容 | 支持完整 JS 渲染,可绕过 JS 反爬 |
| 浏览器指纹检测 | 无指纹,易被识别 | 有完整浏览器指纹,不易被检测 |
| 性能 | 轻量,请求速度快 | 重量级,启动浏览器耗时,请求效率低 |
| 反爬绕过难度 | 高,需手动破解各类反爬规则 | 低,天然模拟人工行为,绕过基础反爬更简单 |
简言之:requests 适合爬取反爬较弱的静态页面,处理 403 需手动「伪装」请求;Selenium 适合爬取反爬严格的动态页面,通过模拟真实浏览器天然降低 403 概率,但牺牲了性能。
三、实战:处理 403 错误的代码实现
场景 1:requests 处理 403 错误(手动伪装请求)
核心思路
通过完善请求头、添加延时、使用代理 IP 等方式,模拟真实浏览器请求,绕过服务器的基础校验。
python
运行
import requests
import time
from fake_useragent import UserAgent # 需额外安装:pip install fake-useragent
def requests_crawl(url):
# 1. 构造模拟浏览器的请求头
ua = UserAgent()
headers = {
"User-Agent": ua.chrome, # 随机生成 Chrome 浏览器 UA
"Referer": url.split("/")[0] + "//" + url.split("/")[2], # 构造 Referer,模拟从本站跳转
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1"
}
# 2. 配置代理信息(使用指定的带认证代理)
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"
# 构造带用户名密码认证的代理链接
proxy_auth = f"{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"
proxies = {
"http": f"http://{proxy_auth}",
"https": f"http://{proxy_auth}" # https请求也走http代理通道
}
try:
# 3. 发送请求(添加延时,避免高频)
time.sleep(1) # 每次请求间隔 1 秒
response = requests.get(
url=url,
headers=headers,
proxies=proxies,
timeout=10,
allow_redirects=True # 允许重定向,部分 403 是临时重定向导致
)
# 4. 校验响应状态
if response.status_code == 200:
print("Requests 爬取成功,响应长度:", len(response.text))
return response.text
elif response.status_code == 403:
print("Requests 仍返回 403,需升级方案(如 Selenium)")
return None
else:
print(f"Requests 请求失败,状态码:{response.status_code}")
return None
except Exception as e:
print(f"Requests 请求异常:{str(e)}")
return None
# 测试调用
if __name__ == "__main__":
target_url = "https://example.com/test" # 替换为实际目标 URL
requests_crawl(target_url)
关键说明
<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">fake-useragent</font>库用于生成随机真实的 User-Agent,避免固定 UA 被识别;- Referer 需与目标域名一致,模拟从本站内跳转的请求;
- 代理 IP 和延时是避免 IP 封禁的核心手段,高频请求下必须添加;
- 若仍返回 403,说明网站启用了更严格的反爬(如 JS 验证、浏览器指纹检测),需切换 Selenium。
场景 2:Selenium 处理 403 错误(模拟真实浏览器)
核心思路
通过驱动真实 Chrome 浏览器,自动携带浏览器原生请求头,绕过 JS 验证和浏览器指纹检测,从根本上降低 403 概率。
python
运行
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
import time
def selenium_crawl(url):
# 1. 配置 Chrome 选项,模拟真实浏览器
chrome_options = Options()
# 禁用自动化提示(关键:避免被网站检测到 Selenium)
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option('useAutomationExtension', False)
# 禁用图片/视频加载,提升速度
chrome_options.add_experimental_option("prefs", {
"profile.managed_default_content_settings.images": 2,
"profile.managed_default_content_settings.video": 2
})
# 添加启动参数,进一步伪装
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
chrome_options.add_argument("--disable-dev-shm-usage") # 解决容器环境内存不足问题
chrome_options.add_argument("--no-sandbox") # 禁用沙箱模式
chrome_options.add_argument("--headless=new") # 无头模式(无浏览器窗口),注释可显示窗口
# 随机 UA(也可省略,浏览器会自动携带原生 UA)
chrome_options.add_argument(f"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")
# 2. 初始化浏览器驱动
try:
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)
# 3. 执行 JS 移除 webdriver 标识(关键:绕过浏览器指纹检测)
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
# 4. 访问目标 URL
driver.get(url)
time.sleep(2) # 等待页面加载完成
# 5. 校验响应状态(Selenium 无法直接获取状态码,通过页面内容判断)
page_source = driver.page_source
if "403 Forbidden" in page_source:
print("Selenium 仍返回 403,需检查代理/登录状态")
else:
print("Selenium 爬取成功,页面长度:", len(page_source))
# 6. 关闭浏览器
driver.quit()
return page_source
except Exception as e:
print(f"Selenium 请求异常:{str(e)}")
return None
# 测试调用
if __name__ == "__main__":
target_url = "https://example.com/test" # 替换为实际目标 URL
selenium_crawl(target_url)
关键说明
<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">excludeSwitches</font>和<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">disable-blink-features</font>是核心:移除 Selenium 的自动化标识,避免被网站检测;- 无头模式(
<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">--headless=new</font>)适合服务器运行,注释后可显示浏览器窗口调试; - Selenium 无法直接获取 HTTP 状态码,需通过页面内容(如是否包含「403 Forbidden」)判断是否爬取成功;
- 若仍返回 403,需进一步添加代理 IP、登录会话(通过 Cookie 或手动登录)。
四、方案选择与优化建议
- 优先使用 requests:若目标网站反爬较弱(仅校验请求头),requests 性能更高,通过完善请求头、添加延时即可解决 403;
- 切换 Selenium:若 requests 多次尝试仍返回 403,且网站包含动态 JS 渲染,Selenium 是更优解;
- 混合方案:用 Selenium 获取登录 Cookie / 会话,再用 requests 携带 Cookie 请求,兼顾性能与反爬能力;
- 进阶优化:
- 避免单一 IP 高频请求,使用代理池轮换 IP;
- 针对严格反爬网站,可搭配 undetected-chromedriver(替代原生 ChromeDriver),进一步隐藏 Selenium 特征;
- 添加随机延时(如 1-3 秒),模拟人工操作节奏。
五、注意事项
- 遵守网站
<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">robots.txt</font>协议,避免过度爬取给服务器造成压力; - 部分网站的反爬机制受法律保护,爬取前需确认合规性;
- Selenium 版本需与浏览器版本匹配,推荐使用
<font style="color:rgb(0, 0, 0);background-color:rgba(0, 0, 0, 0);">webdriver-manager</font>自动管理驱动版本。
总结
- Python 爬虫 403 错误的核心是服务器的身份校验失败,普通请求(requests)需手动伪装请求头、添加代理,而 Selenium 通过模拟真实浏览器天然降低 403 概率;
- requests 适合反爬较弱的静态页面,性能更高;Selenium 适合反爬严格的动态页面,上手更简单但性能较低;
- 处理 403 的关键是「模拟真实用户行为」:requests 靠手动构造请求头,Selenium 靠隐藏自动化标识、模拟浏览器操作。