从403错误到合规获取:Python爬虫反爬策略实战解析 📅 发布时间:2026/8/29 19:38:50 👁 浏览次数: 1. 一次意料之外的“撞墙”经历那天下午我像往常一样准备抓取一个主流天气网站的七日预报数据用来做一个小型的本地天气聚合器。脚本已经稳定运行了小半个月数据一直很准我也就没太在意。然而就在我按下回车键准备迎接熟悉的JSON数据流时控制台却弹出了一行刺眼的红色错误信息。不是网络超时也不是页面结构变化而是一个清晰的“403 Forbidden”。我的心咯噔一下脑子里闪过一个念头“坏了被反爬了。”这其实不是第一次遇到反爬虫机制但这次有点不一样。这个网站我之前分析过它没有使用那些复杂的前端渲染框架数据接口看起来也是直来直去的JSON理论上应该很好处理。但这次它用一个简单的HTTP状态码就把我的请求拒之门外。这让我意识到反爬虫的战场早已不局限于复杂的JavaScript混淆和验证码一些基础但有效的策略同样能让爬虫开发者“撞墙”。这次经历让我决定把整个排查、分析和最终绕过或者说合规获取数据的过程记录下来这不仅仅是一次技术复盘更是一次对当前网络数据获取生态的重新审视。无论你是刚入门的数据爱好者还是有一定经验的开发者相信都能从这次“撞墙”之旅中获得一些启发。2. 初步诊断为什么是403看到403错误第一反应是检查请求头Headers。这是反爬虫最基础也最常用的一道防线。一个来自标准requests库或curl命令的“裸请求”在服务器看来和一个普通浏览器发出的请求有着天壤之别。2.1 关键请求头字段的缺失我立刻检查了我脚本中的请求头。果然问题就出在这里。我之前只简单设置了User-Agent模仿了一个浏览器的标识但这远远不够。现代浏览器在发起请求时会携带一整套完整的头信息而服务器端的风控系统会检查这些头是否存在、是否合理。以下是我最初简陋的请求头与一个典型浏览器请求头的对比请求头字段我的爬虫问题版本典型浏览器请求参考作用与风险分析User-AgentPython-urllib/3.9Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...核心标识。直接暴露了爬虫身份。服务器看到Python、urllib、curl等字眼几乎可以立刻判定为非浏览器流量。Accept未设置或默认值text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8内容协商。告诉服务器客户端能接受哪些类型的响应。缺失或不规范会显得异常。Accept-Language未设置zh-CN,zh;q0.9,en;q0.8语言偏好。普通用户浏览器都会携带。缺少此头不符合人类用户行为。Accept-Encoding未设置gzip, deflate, br编码偏好。表明支持压缩节省带宽。缺少此头请求看起来“不经济”有点可疑。Connection未设置keep-alive连接管理。现代浏览器默认使用持久连接。Upgrade-Insecure-Requests未设置1安全升级。常见于浏览器要求将HTTP升级为HTTPS。Sec-Fetch-系列头*未设置Sec-Fetch-Site: same-site,Sec-Fetch-Mode: navigate等Fetch元数据。这是关键防线。这些头由浏览器自动添加用于指示请求的来源、目的和模式。伪造极其困难缺失则强烈暗示为非浏览器环境。注意Sec-Fetch-*系列头如Sec-Fetch-Dest,Sec-Fetch-Mode,Sec-Fetch-Site,Sec-Fetch-User是近年来反爬虫的重要依据。它们由浏览器控制普通脚本无法自然生成。如果服务器严格校验这些头那么仅靠修改User-Agent将完全无效。幸运的是许多网站包括我这次遇到的并未启用如此严格的校验它们更依赖于对传统头部的完备性检查。2.2 请求频率与行为模式除了请求头另一个触发403的常见原因是请求频率过高。即使头信息伪装得再好如果你在短时间内从一个IP地址发起大量、高频的请求服务器也很容易将其判定为爬虫攻击或恶意扫描从而触发IP级别的限流或封禁。我回顾了我的脚本为了获取全国多个城市的天气我是在一个循环里依次请求不同城市的接口。虽然每个请求之间我加了1-2秒的随机延迟但对于一些风控严格的站点来说这种有规律的、来自同一IP的序列请求仍然可能被识别。真正的用户行为是分散的、无规律的并且会伴随着页面资源CSS, JS, 图片的加载而爬虫通常只盯着数据接口。3. 伪装策略升级从“像人”到“是人”诊断出问题后解决方案的方向就很明确了让我的爬虫请求看起来尽可能像一个真实浏览器发出的请求。这个过程我称之为“伪装策略升级”。3.1 构建完整的请求头字典第一步我不再使用简单的headers设置而是构建了一个尽可能完整的字典。我打开浏览器的开发者工具F12切换到“网络”(Network)标签找到一次正常的页面请求将其请求头全部复制下来。然后在我的Python脚本中我这样设置import requests import time import random headers { 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, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,image/apng,*/*;q0.8,application/signed-exchange;vb3;q0.7, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, Connection: keep-alive, Upgrade-Insecure-Requests: 1, Sec-Ch-Ua: Not_A Brand;v8, Chromium;v120, Google Chrome;v120, # 注意这部分可能动态变化 Sec-Ch-Ua-Mobile: ?0, Sec-Ch-Ua-Platform: Windows, Sec-Fetch-Site: same-origin, Sec-Fetch-Mode: cors, Sec-Fetch-Dest: empty, Referer: https://www.目标天气网站.com/, # 设置合理的来源页 # Cookie: ..., # 谨慎处理初始请求可能不需要 } # 注意Sec-Ch-Ua (Client Hints) 和 Sec-Fetch-* 头虽然可以手动设置 # 但它们的值具有时效性和浏览器版本特异性过度伪造或使用过期值可能适得其反。 # 一个更稳妥的方法是首次请求先用简单头获取一个页面从中解析出这些值如果网站返回的话再用于后续API请求。3.2 引入会话Session与请求间隔使用requests.Session()对象是一个好习惯。会话对象可以自动管理Cookie保持一些连接状态使得多次请求更像一个“会话”而不是独立的爆破。同时我优化了请求间隔策略。固定的延迟如time.sleep(2)仍然有规律可循。更好的做法是引入随机延迟并模拟人类操作的“思考时间”。import requests import time import random session requests.Session() session.headers.update(headers) # 为整个会话设置基础头 def human_delay(min_s1, max_s4): 模拟人类阅读/操作的不确定延迟 delay random.uniform(min_s, max_s) time.sleep(delay) return delay city_codes [101010100, 101020100, 101280101] # 北京上海广州 for city_code in city_codes: url fhttps://api.目标天气网站.com/v1/weather?city{city_code} try: response session.get(url) response.raise_for_status() # 检查HTTP错误 data response.json() print(f成功获取城市 {city_code} 的数据) # 处理数据... except requests.exceptions.RequestException as e: print(f请求城市 {city_code} 失败: {e}) # 可以考虑更复杂的错误处理如重试、更换代理等 finally: # 在每次请求后加入一个随机的、相对较长的延迟 _delay human_delay(2, 5) # 等待2到5秒 print(f等待 {_delay:.2f} 秒后继续...)3.3 处理Cookie与初始页有些网站的反爬机制是“分步式”的。你可能需要先访问一次首页让服务器种下一些初始Cookie可能是会话ID也可能是一些用于风控的令牌然后携带这些Cookie去访问数据接口才会被允许。我的策略是先GET后API。首先用会话Session对象访问一下网站的首页。会话会自动保存此次请求响应的Set-Cookie。后续对数据接口的请求会自动带上这些Cookie。# 首先访问一下主页面获取并建立会话状态 homepage_url https://www.目标天气网站.com/ try: homepage_resp session.get(homepage_url) homepage_resp.raise_for_status() print(成功访问首页会话状态已建立。) except Exception as e: print(f访问首页失败但可能不影响后续API: {e}) # 然后再用同一个session去请求数据API # ... 后续的 city_codes 循环 ...经过这三步改造——完备的请求头、会话管理、人性化的请求间隔——我再次运行脚本。那个刺眼的403消失了数据又重新流淌了回来。这说明该天气网站的反爬策略主要停留在这一层检测请求的“完备性”和“行为规律性”。4. 深入对抗当基础伪装失效时然而好景不长。几天后403错误又出现了。这次即使我使用了完整的请求头和随机延迟依然无法获取数据。这标志着对抗进入了更深的水域。服务器可能升级了策略或者我触发了更高级别的风控规则。这时就需要祭出更进阶的武器库。4.1 IP代理池的搭建与使用这是应对IP封锁最直接有效的方法。当服务器封禁了你本机的IP地址你就需要换一个IP继续工作。对于个人或小规模爬取可以使用一些免费的代理IP但其稳定性、速度和匿名性都很差。对于商业或稳定的数据获取需求建议使用付费的代理服务它们提供海量IP、高匿性、并可能附带自动切换IP的SDK。使用requests配合代理非常简单proxies { http: http://你的代理IP:端口, https: http://你的代理IP:端口, # 注意很多代理服务器的https协议也用http端口 } # 或者在session中设置 session.proxies.update(proxies) response session.get(url, proxiesproxies)实操心得免费代理陷阱网上搜到的免费代理列表90%以上是不可用的。即使能用速度也慢如蜗牛且很可能被目标网站识别并封禁。不建议在重要项目中使用。付费代理选择选择时关注IP池大小、地理位置、是否高匿Elite或High Anonymous、并发连接数、带宽限制等。有些服务还提供“住宅IP”Residential IP模拟真实家庭用户网络绕过反爬的能力更强。代理健康检查必须建立代理IP的健康检查机制。在每次使用前或定期用一个简单的请求如访问http://httpbin.org/ip测试代理是否存活、速度如何、匿名性是否足够返回的IP是否是代理IP本身。4.2 模拟浏览器环境Selenium与Playwright当网站的反爬虫技术深入到浏览器指纹Canvas, WebGL, Fonts、鼠标移动轨迹、甚至WebDriver属性检测时传统的requests库就力不从心了。这时我们需要动用能够真实控制浏览器进行渲染的工具。Selenium老牌自动化测试工具支持多种浏览器。它可以执行JavaScript渲染完整页面模拟点击、输入等所有用户操作。但速度较慢且容易被检测到存在window.navigator.webdriver属性为true。Playwright后起之秀由微软开发。相比Selenium它更快、更稳定API更现代。最关键的是它可以通过一些启动参数来隐藏自动化特征使其更难以被检测。使用Playwright获取动态渲染内容的示例from playwright.sync_api import sync_playwright def get_weather_with_browser(): with sync_playwright() as p: # 使用Chromium并添加参数尝试隐藏自动化特征 browser p.chromium.launch( headlessFalse, # 调试时可设为False看浏览器操作 args[ --disable-blink-featuresAutomationControlled, ] ) context browser.new_context( viewport{width: 1920, height: 1080}, user_agent你的浏览器UA ) page context.new_page() # 导航到页面 page.goto(https://www.目标天气网站.com/beijing) # 等待必要元素加载可能是某个包含数据的div或script标签 page.wait_for_selector(.weather-data-container, timeout10000) # 直接获取页面渲染后的HTML或者执行JS来提取数据 # 方法1获取整个页面HTML然后用BeautifulSoup解析 html_content page.content() # ... 解析html_content ... # 方法2直接在浏览器上下文中执行JavaScript提取数据 # 假设数据在 window.__INITIAL_STATE__ 中 weather_data page.evaluate(() { return window.__INITIAL_STATE__?.weatherData || null; }) if weather_data: print(f获取到数据: {weather_data}) else: print(未找到数据可能需要更复杂的交互或等待。) browser.close() return weather_data注意使用浏览器自动化工具是“重型武器”资源消耗大、速度慢。它应该是最后的手段用于对付那些数据完全由前端JS渲染、且接口加密极其复杂的网站。对于我遇到的这个天气网站在使用了完备请求头和代理后通常不需要走到这一步。4.3 逆向工程解析JavaScript与加密参数这是爬虫工程师的“终极挑战”。有些网站的数据接口其URL或请求体Payload中包含经过加密或混淆的参数如sign、token、_ts等。这些参数通常由页面加载的JavaScript代码动态计算生成用于验证请求的合法性。要破解这个你需要定位接口在浏览器开发者工具的“网络”面板中找到获取目标数据的XHR/Fetch请求。分析请求仔细查看该请求的“标头”特别是Form Data或Payload和“预览”响应。搜索关键参数在“源代码”Sources面板中全局搜索CtrlShiftF这些关键参数名如sign、token。调试与追踪找到生成这些参数的JS函数通过打断点Breakpoint、控制台Console调试理清其算法逻辑。代码复现将JavaScript算法用Python或其他语言重新实现。这个过程需要扎实的JavaScript基础和耐心。对于这个天气网站我深入检查后发现其核心数据接口并未使用动态Token请求参数是明文的城市代码和时间戳这大大降低了难度。加密参数常见于大型电商、社交平台或金融数据网站。5. 合规的边界与可持续的数据获取策略在成功绕过反爬虫机制重新获取数据之后我并没有感到完全的胜利反而开始思考一个更根本的问题我们这样做的边界在哪里如何可持续地、合规地获取数据5.1 尊重 robots.txt 协议robots.txt是网站放在根目录下的一个文本文件用于告知网络爬虫哪些页面可以抓取哪些不可以。这是一个君子协议。在开始爬取任何网站前都应该先检查其robots.txt。访问https://www.目标天气网站.com/robots.txt你可能会看到类似内容User-agent: * Disallow: /api/ Disallow: /search? Allow: /public/如果它明确Disallow了你要爬取的API路径如/api/那么从法律和道德层面你的爬虫行为就不被该网站允许。尽管技术上可以绕过但这增加了法律风险和被封禁的可能性。5.2 控制爬取频率与数量这是体现“善意爬虫”的关键。即使网站没有明确禁止你也应该将爬取行为对服务器造成的负载降到最低。降低请求频率在非高峰时段如凌晨运行爬虫。将随机延迟设置得足够长例如5-15秒。限制数据范围只爬取你真正需要的数据而不是全站抓取。比如我只关心10个主要城市的天气就不要去遍历全国上千个城市。缓存已获取的数据对于更新不频繁的数据如天气可能每小时更新一次将结果缓存到本地数据库或文件中在一段时间内直接使用缓存避免重复请求。5.3 探索官方API与数据合作这是最理想、最可持续的路径。许多网站包括一些天气服务商实际上提供免费的或付费的官方API。这些API通常有明确的调用限制如每日次数、认证方式API Key和数据结构文档。为什么官方API更好稳定性官方保障接口稳定数据结构清晰。合法性完全合规无法律风险。支持与服务可能有社区支持、技术文档甚至商业支持。功能完整通常会提供比网页端更丰富、更规整的数据。在我这个案例的最后我花时间搜索了一下发现该天气网站确实有一个面向开发者的API中心提供有限的免费额度。虽然免费版可能有调用频率限制但对于个人项目来说完全足够。我最终注册了一个开发者账号获取了API Key将我的爬虫脚本迁移到了官方API上。代码更简洁心里也更踏实。6. 总结从对抗到共存的思维转变这次“被天气网反爬的一天”实际上是一次完整的爬虫攻防实战演练。它让我走过了从基础请求头伪造到会话管理、代理使用再到思考合规性的全过程。技术层面的收获是具体的一套完整的请求头模板、Session对象的最佳实践、对IP代理重要性的认识、以及何时该动用Playwright这样的“核武器”。但更重要的收获是思维层面的爬虫工程师与反爬虫系统之间不应只是一场永无休止的技术对抗。作为数据获取方我们应该优先寻求合作官方API永远是第一选择。恪守善意原则严格遵守robots.txt将请求频率控制在极低水平只取所需数据。准备技术方案当合作路径不通且你的数据获取目的合理合法如个人学习、非商业研究时才动用技术手段去“协商”。此时你的技术储备请求伪装、代理、浏览器自动化就是你的谈判资本。承担相应风险要清楚绕过反爬措施可能违反网站的服务条款导致IP被封、账号被封甚至在极端情况下引发法律问题。最终我放弃了那个与反爬系统斗智斗勇的脚本转而使用官方的、有频限的API。数据流变得稳定而缓慢但我知道这条管道是坚固且被允许的。爬虫之旅终究是一场在技术能力、伦理边界与法律框架之间寻找平衡点的旅行。