新闻详情

新闻详情

首页 / 资讯中心 / 详情

爬虫遇到403 Forbidden怎么办?requests与Selenium双方案实战破解

发布时间:2026/9/28 5:41:57来源:尧图网络
爬虫遇到403 Forbidden怎么办?requests与Selenium双方案实战破解
做爬虫最烦的一行输出是什么不是超时不是编码错误而是那醒目的403 Forbidden。我在做一个公开数据归集项目时就栽过跟头requests老老实实带上 UA带上 Referer带上 Accept一切看起来都很正常结果对方服务器给了一个裸 403响应体里连“请求过于频繁”的提示都没有就是纯粹的拒绝。后来试了很多技巧真正解决我手头 80% 403 问题的方案其实就两条路一条是把普通请求的每一个细节打磨到位另一条是直接换 Selenium 上真实浏览器。这篇文章就把这两条路线摊开聊。我不打算只给你贴几个代码片段而是把我从原理到实战、从踩坑到选型的一整套判断方法写出来。适合谁看如果你是用requests写爬虫被反爬拦得怀疑人生的人或者正在纠结“要不要为了一个页面去学 Selenium”的人这篇就是写给你的。1. 403 错误是怎么来的先搞懂服务器在拒绝什么1.1 403 的玄机服务器为什么认识你这个机器人HTTP 状态码 403 翻译成大白话就是你的请求到达了服务器服务器也完全理解你的意图但它就是不让你过。这和 404 有本质区别——404 是“这里没有这个东西”403 是“东西就在这但不给你”。在爬虫场景里触发 403 的原因大致归成三类请求特征异常你的 User-Agent 太老、缺失或明显不是浏览器产生的Headers 字段之间矛盾请求顺序不符合正常人浏览页面的逻辑。访问频率异常短时间大量请求同一接口触发 IP 级别的频率限制。这类 403 往往带有提示比如“请求过快请稍后再试”。浏览器环境异常服务器那边有 JS 检测、浏览器指纹校验、Cookie 合法性校验。你根本没执行 JS或者执行了但指纹对不上它就会认为你不是一个真实用户。第一类最友好改改 Header 基本能过。第二类要靠代理、限速和重试策略。第三类最棘手也是我们后面要聊的 Selenium 主战场。1.2 一个请求从发出到被拒绝中间发生了什么从抽象角度看服务器判断你是否为真人发生在两个层面。第一个层面是HTTP 请求层。requests发出的请求和 Chrome 浏览器发出的请求在协议栈上其实有大量细节差异浏览器的请求头字段更全、字段顺序更接近真实语义、带 Sec-Fetch 系列、带完整的 Accept 和 Accept-Language更关键的是浏览器会先请求 HTML再根据 HTML 里的资源链接发起 CSS、JS、图片等子请求最后才轮到数据接口。普通爬虫是“单刀直入”直接打接口服务器看一眼请求上下文就明白了。第二个层面是执行环境层。现代网站可以在页面里嵌入一段 JS这段 JS 会读取浏览器的 navigator、window、canvas、WebGL 等大量属性然后把计算结果发回服务器。如果计算结果异常或这段 JS 根本没被执行你的请求就会被标上“非真人”标签。普通requests连 JS 都执行不了在这一层天然吃亏。1.3 先判断你的 403 是哪一种拿到一个 403先别急着改代码。我建议你用五分钟做一轮定位判断这能帮你少走很多弯路。先看响应内容。如果响应体里明确写了“Request blocked”“Too many requests”“需要完成安全验证”之类的字样那基本是频率或 JS 挑战类问题。如果响应体是空的、或者只是默认的 nginx 错误页那更可能是请求头或 IP 被拉黑。再看触发条件。今天第一次访问就 403还是爬了 200 条数据之后才开始 403前者多半是请求头或环境问题后者多半是频率问题。最后一个技巧换一个完全干净的 IP 再试一次。如果新 IP 下同样的请求能通说明你原来的 IP 已经被标记了。这三类判断做完你在后面两个方案里就知道该选哪条路。2. 普通请求方案requests 如何破解 4032.1 先把请求头伪装到位很多人的第一个 403 纯粹是“裸请求”造成的。requests.get(url)默认的 User-Agent 是python-requests/x.x.x这是极其明显的爬虫标记服务器看到它基本可以无脑拒绝。解决思路不是随便换一个 UA 就行而是把一整套请求头都补齐形成一个“自洽的浏览器形象”。服务器校验的往往不是单个字段而是多个字段之间的关联性。比如你要带上 Chrome 120 的 UA那 Accept、Accept-Language、Sec-Fetch-Mode、Sec-Ch-Ua 这些字段最好都和 Chrome 120 的实际行为对得上。下面这套是我常用的基础模板import requests 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/avif,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, Sec-Fetch-Dest: document, Sec-Fetch-Mode: navigate, Sec-Fetch-Site: same-origin, Sec-Fetch-User: ?1, } resp requests.get(https://data.example.com/api/list, headersHEADERS) print(resp.status_code)注意这里的两个细节。一是Accept-Encoding别乱填如果你请求了brBrotli压缩但requests没有解压拿到的就是乱码不是 403 的问题却是另一种坑。二是Sec-Fetch-*这几个字段很重要它们是这几年服务器做“请求是否符合浏览器行为”判断的重要依据。2.2 Session 与 Cookie从“每次重新自我介绍”到“服务器记得我”如果你用requests.get直接逐个请求每次都是一个全新的匿名身份。真实用户不是这么浏览网站的他先打开首页服务器种下 Cookie然后他带着这些 Cookie 去访问详情页。服务器通过 Cookie 识别“这个人刚刚来过”。所以爬虫也要模拟这个过程。用requests.Session()代替裸的requests.get()Session 对象会自动保存服务器返回的 Cookie并且在后续请求中自动带上。session requests.Session() session.headers.update(HEADERS) # 先访问入口页拿到基础 Cookie 和必要的隐藏参数 home_resp session.get(https://data.example.com/) print(home_resp.status_code) # 再访问目标接口此时带上了第一步种下的 Cookie api_resp session.get(https://data.example.com/api/list) print(api_resp.status_code)这一步对很多 403 都有效。特别是一些页面通过中间件种 Cookie 做“首次访问合法性校验”的网站你直接打接口必然 403但先逛一圈首页再进接口就能过。2.3 重试、限速与代理请求频率才是大头Header 和 Cookie 只是第一关。很多时候你明明伪装得很好但爬了几百条之后突然 403这就是触发了频率限制。我见过太多人栽在“不加间隔的 for 循环”上。服务器端的频率判断通常是滑动窗口机制比如 60 秒内同一个 IP 超过 30 次请求就拉黑。你要做的很简单在每次请求之间加随机延迟让请求节奏看起来更像真人。import time import random from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry session requests.Session() session.headers.update(HEADERS) # 配置重试策略遇到 403/5xx 时指数退避重试 retry Retry( total3, status_forcelist[403, 500, 502, 503], backoff_factor1, # 第1次等1秒第2次等2秒第3次等4秒 allowed_methods[GET, POST], ) adapter HTTPAdapter(max_retriesretry) session.mount(http://, adapter) session.mount(https://, adapter) # 先进首页拿 Cookie session.get(https://data.example.com/) for page in range(1, 11): resp session.get(fhttps://data.example.com/api/list?page{page}) if resp.status_code 403: print(f第 {page} 页被拒睡觉后重试) time.sleep(30) else: print(f第 {page} 页 OK状态 {resp.status_code}) # 随机间隔避免固定节奏被识别 time.sleep(random.uniform(2, 5))关于代理我多说一句。当 IP 被标记之后立刻换代理池轮询是对的但有几条红线要注意不要用免费代理池里的高匿名代理去访问带账号体系的网站不要用一个代理短时间内高频请求不要每次请求都换一个不同地区的代理——一个正常用户不会在 10 秒内从北京跳到上海再跳到广州。代理的轮换策略应该是低频慢换而不是每次请求都换。另外采购代理的时候优先选“住宅代理”或者“ISP 代理”数据中心代理 IP 在风控严格的网站上命中率偏高。2.4 普通请求的局限为什么有些 403 你搞不定到这里普通请求方案的几条腿就都站上了完整的请求头、Session 保持 Cookie、重试退避、随机限速、代理轮换。这套组合拳能解决掉相当一部分 403但我必须说句实话它存在天花板。天花板主要有三层无法执行 JS。有些网站的反爬逻辑全部写在 JS 里页面加载时会通过 JS 生成一个动态 Token 拼在请求头或 URL 上。你连 JS 都不执行自然拿不到这个 Token。无法伪造浏览器指纹。WebGL 渲染结果、Canvas 指纹、字体列表、屏幕分辨率、时区这些属性在普通请求里完全不存在。服务器可以要求“客户端必须上报一套完整且合理的浏览器指纹”普通请求直接出局。无法模拟复杂交互。点击、滚动、拖拽、悬停、输入这些行为在普通请求里做不到。如果服务器要求“先滚动到页面底部数据接口才会被触发”普通请求依然出局。遇到这三类情况你与其跟请求头死磕不如切换思路。3. Selenium 方案用真实浏览器解决“行为识别”类 4033.1 Selenium 到底做了什么和普通请求的差别是什么Selenium 的本质是通过 WebDriver 协议驱动一个真实的浏览器实例。它不是一个“模拟浏览器”它就是浏览器本身。Chrome 会真实启动JS 会真实执行CSS 会真实加载Cookie 和指纹由 Chromium 内核自动生成。普通请求和 Selenium 在服务器眼里完全是两种东西维度普通请求 (requests)Selenium 浏览器JS 执行不执行完整执行浏览器指纹缺失真实 Chromium 指纹请求上下文单请求直达完整加载流程HTML→CSS→JS→接口速度毫秒级秒级资源占用极低每个实例数百 MB 内存维护成本低中等驱动管理、版本兼容适用场景纯接口、无强校验强 JS 校验、复杂交互、数据动态渲染从这张表能看出一个明显结论Selenium 不是为了“更快”而存在的是为了“更像真人”而存在的。3.2 Selenium 4 环境搭建与最小可用脚本Selenium 4 相比老版本最大的变化是内置了 Service 管理不再需要手动设置 executable_path。我用它的时候通常会搭配webdriver-manager自动下载配对的 Chromedriver省去手动维护驱动的麻烦。pip install selenium webdriver-manager最小可用脚本长这样from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from webdriver_manager.chrome import ChromeDriverManager options Options() # 无头模式后台运行不弹窗 options.add_argument(--headlessnew) options.add_argument(--window-size1920,1080) options.add_argument(--disable-gpu) options.add_argument(--no-sandbox) # 关键参数去掉自动化控制标志 options.add_argument(--disable-blink-featuresAutomationControlled) service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice, optionsoptions) try: driver.get(https://data.example.com/) # 显性等待等目标元素出现在页面中 items WebDriverWait(driver, 10).until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, .data-row)) ) for item in items: print(item.text) finally: driver.quit()这段脚本里有几个点值得解释一下。--disable-blink-featuresAutomationControlled的作用是去掉 Chromium 里一个叫navigator.webdriver的自动标记。正常浏览器这个属性是undefined而 WebDriver 控制的浏览器里它是true。很多网站的反爬脚本第一件事就是查这个属性看见true直接判死刑。加这个参数能去掉大部分场景下的自动化标记。--headlessnew是新版无头模式。老的无头模式--headless在某些版本里会被特殊处理产生一些指纹差异。新无头模式已经非常接近有头浏览器。WebDriverWait是必须养成的习惯。很多新手用time.sleep(3)硬等页面加载这既慢又不稳。显性等待让程序在目标元素出现的那一刻立即继续既不浪费时间也不会因为固定时长不够而误杀。3.3 无头模式与反检测配置为什么你打开了页面还是 403用 Selenium 还是被 403这是最让人崩溃的场景。我遇到过不少次排查到最后都是“浏览器指纹”问题。先说一个常见的坑无头模式本身可能被识别。老版本的--headless有很多特征比如navigator.plugins为空、navigator.languages不完整、时区和语言不匹配。新版--headlessnew改善很大但也不敢说百分百。更隐蔽的是指纹一致性问题。你的 Chrome 版本是 120但系统的时区是东八区语言是 zh-CN屏幕分辨率是 1920×1080……如果某些属性之间互相矛盾比如 Chrome 的Accept-Language显示 en-US但navigator.language是 zh-CN就会被风控模型标记为可疑。有两类开源工具可以解决这个问题一类是Stealth.js / selenium-stealth。它会在页面加载前注入一段 JS把navigator.webdriver、navigator.plugins、navigator.languages、chrome.runtime等数十个自动化特征全部用真实值覆盖。这样相当于给自动化浏览器做了一层“套皮”。另一类是undetected-chromedriver。它比标准 Selenium 更进一步直接 patch 了 Chromedriver 的底层通信过程让浏览器不认为自己是被 WebDriver 控制的。实测下来它在应对商业级风控时的成功率更高。但我必须给一段提醒工具只能降低被识别的概率不能保证 100% 不被识别。更重要的是用这些工具去绕过自己无权访问的内容是不道德且可能违法的事情。我在这里介绍它们是为了让你在自动化测试、处理自己有权限的公开数据场景时理解背后的原理。真正的长期爬虫项目靠的是频率控制、IP 管理和合理的采集节奏不是硬刚风控。3.4 Selenium 性能代价与正确使用姿势Selenium 真正的痛点在性能和稳定性。性能方面一个 Chrome 实例启动要一两秒打开一个复杂页面又要几秒单个请求动辄 3-8 秒是普通请求的几十倍。内存占用更是夸张一个无头 Chrome 常驻内存 300-500MB。你要开 10 个并发实例基本就是一台小服务器了。稳定性方面WebDriver 的版本兼容性是个老难题。Chrome 自动更新到某个新版本你的 Chromedriver 没跟上启动就报错。webdriver-manager能解决一部分但很多企业的内网环境根本访问不了自动下载地址。另外浏览器偶尔会因为页面 JS 报错、内存溢出、网络波动而崩溃你的爬虫程序必须做好异常兜底。所以正确的使用姿势是把 Selenium 当作“攻坚”工具而不是“常规”工具。先用普通请求跑 80% 的接口碰到普通请求搞不定的 403再降级到 Selenium 处理。这也是我要在第四章详细演示的思路。4. 实操对比同一个目标两种方案分别怎么解 4034.1 场景设定与合规边界为了把两种方案放在同一起跑线上比较我们用同一个假设场景一个公开展示的数据列表页页面数据通过异步接口加载接口只有带上合法 Cookie 才能访问直接裸请求返回 403。先声明合规边界这个场景只讨论公开数据的采集不涉及登录后隐私数据、付费内容、评论区个人信息等敏感点。你在跟进任何目标时永远要先看对方的 robots.txt 和服务条款采集频率保持克制——别把别人的服务器打崩这是爬虫从业者的基本素养。4.2 普通请求完整示例与效果分析先写普通请求版本。这个版本的核心是补齐 Headers、用 Session 先逛首页拿 Cookie、加随机延迟和重试。import time import random import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, Accept: application/json, text/plain, */*, Accept-Language: zh-CN,zh;q0.9, Referer: https://data.example.com/, Origin: https://data.example.com, } session requests.Session() session.headers.update(HEADERS) retry Retry(total2, status_forcelist[403, 500, 502], backoff_factor0.5) adapter HTTPAdapter(max_retriesretry) session.mount(https://, adapter) # 第一步访问首页拿下种子 Cookie entry session.get(https://data.example.com/, timeout10) print(首页状态:, entry.status_code, Cookie数:, len(session.cookies)) # 第二步带 Cookie 请求数据接口 data_url https://data.example.com/api/list?page1 resp session.get(data_url, timeout10) print(数据接口状态:, resp.status_code) if resp.status_code 200: data resp.json() print(拿到数据条数:, len(data.get(items, []))) else: print(响应体前 200 字符:, resp.text[:200])在大多数情况下这套配置能解决“裸请求被 403”的问题。它的耗时是多少两次请求加起来不到 0.5 秒非常轻量。但如果服务器在后端校验了更复杂的指纹或者首页必须在浏览器里执行 JS 才能种下真正的有效 Cookie这个方案就会失败——表现为首页状态是 200但拿到的 Cookie 是无效的数据接口继续 403。4.3 Selenium 完整示例与效果分析接着是 Selenium 版本。这个版本的核心是用真实浏览器执行 JS、生成完整指纹、走完页面整个加载流程然后把数据提取出来。import time from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from webdriver_manager.chrome import ChromeDriverManager options Options() options.add_argument(--headlessnew) options.add_argument(--window-size1920,1080) options.add_argument(--disable-blink-featuresAutomationControlled) options.add_argument(--no-sandbox) options.add_argument(--disable-dev-shm-usage) options.add_argument(--langzh-CN) # 通过 CDP 注入 stealth 脚本隐藏自动化特征 STEALTH_JS Object.defineProperty(navigator, webdriver, {get: () undefined}); Object.defineProperty(navigator, languages, {get: () [zh-CN, zh]}); Object.defineProperty(navigator, plugins, {get: () [1, 2, 3, 4, 5]}); service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice, optionsoptions) try: driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, {source: STEALTH_JS}) driver.get(https://data.example.com/) print(页面标题:, driver.title) # 等待表格数据渲染出来 rows WebDriverWait(driver, 10).until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, table tbody tr)) ) print(拿到数据行数:, len(rows)) for row in rows[:3]: print(row.text) finally: driver.quit()这个版本的优点很明显只要页面能出现在浏览器里数据基本就能拿。缺点也同样明显慢每次都启动一个浏览器重内存占用高。而且selenium-stealth这类库只对特定版本的 Chromedriver 有效版本一升级可能就失效需要不断维护。4.4 两种方案的核心数据对比与决策标准我用一个协作过的测试项目记录过两种方案的对比数据场景就是上面这种公开列表页对比项普通请求优化后Selenium无头首次请求成功率60%-85%95% 以上单次请求耗时0.2s-0.5s3s-8s内存占用可忽略300MB10 万条数据预计耗时2-4 小时1-3 天维护成本低高被 IP 封禁的风险高请求快容易触发频率低请求慢更接近真人这里的决策标准其实很清晰目标是纯 JSON 接口页面本身没有复杂 JS 校验 → 用普通请求把 Header、Cookie、频率三件事做好就够。页面需要 JS 执行后才能加载数据但数据本身不复杂 → 用 Selenium 或者考虑用 Playwright 的 sync API性能更好。数据量大、需要长时间稳定采集 → 优先考虑普通请求加代理池让 Selenium 只做“攻坚”而不是“常态”。网站风控极强、普通请求怎么调都过不了 → 再上 Selenium 加 stealth同时严格限速一次只跑少量并发。5. 混合方案与进阶思路把两者优势结合起来5.1 先用普通请求探接口再用 Selenium 兜底真正成熟的项目很少只依赖一种方案。我自己的习惯是设计一套“降级链路”先用普通请求去探测目标接口遇到 403 就暂停然后用 Selenium 打开同一个页面观察它的真实网络请求长什么样拿到正常请求所需的 Cookie、Header、参数格式再回到普通请求里把这些信息补上。有限因为 Selenium 主要解决了“请求头最缺什么”的问题而不是“每次都慢”。伪代码大概是这个逻辑# 第一步尝试普通请求 resp requests.get(data_url, headersHEADERS, cookiessession.cookies) if resp.status_code ! 403: return parse_json(resp.json()) # 第二步403了降级到 Selenium selenium_result get_cookie_and_params_by_selenium() # 第三步用 Selenium 拿到的信息重新走普通请求 session.cookies.update(selenium_result[cookies]) headers {**HEADERS, **selenium_result[headers]} resp requests.get(data_url, headersheaders, cookiessession.cookies)这套逻辑的核心价值在于能用普通请求解决的 90% 场景就不必为一个页面昂贵地启动浏览器。Selenium 在这个链路里更像是一个“侦察兵”负责把最难拿的参数和 Cookie 取回来后续的批量请求交给轻量级方案。5.2 拿 Selenium 的 Cookie 喂给 requests这是实际项目里最常用的技巧之一。一些网站需要登录或完整加载页面后才在 Cookie 里种下有效的会话凭证。与其让 Selenium 一页一页地采集不如让它干点更值钱的事登录并拿到有效 Cookie然后退出后续所有批量请求都用requests带着这批 Cookie 打接口。Selenium 获取 Cookie 的写法# 登录后或页面加载完成后 cookies driver.get_cookies() # 转换成 requests 可用的格式 cookie_dict {c[name]: c[value] for c in cookies} # 打印出来保存或直接更新给 Session print(cookie_dict)拿到之后直接灌入 requests 的 Sessionsession requests.Session() session.cookies.update(cookie_dict) resp session.get(https://data.example.com/api/list, headersHEADERS)这种“浏览器拿凭证、请求库跑量”的组合是我目前见过的、在保证速度和稳定性之间权衡得最好的方案。代价是 Cookie 有有效期通常几十分钟到几天不等所以需要在程序里设计“Cookie 过期自动续期”的机制——比如请求返回 401 或 403 时重新拉起 Selenium 刷新 Cookie。5.3 从被动挨打到主动防御合规的“反反爬”思路聊了这么多绕过思路最后必须聊一个更有价值的问题如何用“自身行为习惯”降低被 403 的概率。真正稳定的爬虫靠的不是某个神奇的 Header 或强大的 stealth 脚本而是让自己在服务器眼里看起来像个“有耐心的正常用户”。我见过很多项目从开始就注定要失败因为它们的设计思路就是“别人正常浏览需要 10 分钟才能看完的内容我要在 10 秒内拿完”这不叫爬虫这叫攻击。合规且可持续的做法是频率上限单个 IP 对同一站点的请求频率控制在 1-5 次/秒以下甚至更低。时间窗口只在目标网站的“低峰期”跑大批量任务比如凌晨 2-5 点对大多数站来说是低峰期。随机化请求间隔加随机抖动避免固定 3 秒、固定 5 秒这种机器人节奏。数据量规划设定每天采集上限而不是“一次全拉完”。宁可跑三天也别惹火服务器。会话保活一批请求之间定期访问页面首页让会话看起来持续活跃。把这几件事做好你甚至会发现很多“反爬”压根不会对你触发——因为你的流量特征早就融入普通用户了。6. 常见问题与排查技巧实录6.1 403 问题速查表我把这几年遇到过的 403 场景做一个速查表方便你对照排查现象可能原因排查方向裸请求就 403UA 太明显 / Header 缺失补全 Header用浏览器 UA换了 UA 还是 403需要 Cookie / 首次访问校验先访问首页拿 Cookie再访问接口爬了一会儿开始 403频率过高触发限流加随机延迟、上代理池降低 QPS代理下 403数据中心 IP 被标记换住宅代理或 ISP 代理首页 200接口 403接口有独立风控检查 Referer / Origin / 接口 TokenSelenium 打开页面后被 403自动化标记被识别加 stealth、去掉 webdriver 标记Selenium 间歇性 403浏览器指纹不一致检查时区、语言、分辨率是否匹配请求验证码页面综合风控等级较高降频、换 IP、暂停几小时再继续6.2 几个我踩过的坑第一个坑是无头模式下字体缺失。用--headlessnew跑某个页面时页面渲染的 DOM 结构不完整导致定位不到元素。排查半天发现是服务器根据浏览器能识别的字体列表做了一次语音检测无头环境字体库不全被判定为异常。解决办法是装常用字体或者使用有头模式跑关键页面。第二个坑是URL 拼接编码。有次反复 403后来发现是请求 URL 里的一个参数包含特殊字符requests自动编码后的路径和浏览器不一致服务器判定 URL 签名不合法。排查了很久才发现是分号变成了%3B的问题。这个需要程序员对接口设计的理解足够细。第三个坑是Session 过期时间比想象中短。一个网站种下的 Cookie 有效期只有 10 分钟。我配置了代理池但每个请求都换新 IP服务器认为用户在频繁切换身份直接触发风控。后来改成“一个 IP 保持一段时间再切”问题就消失了。第四个坑是Selenium 版本兼容。Chrome 某天自动升级到 124而机器上的 Chromedriver 还停留在 120启动直接抛SessionNotCreatedException。这个问题的解决思路就是引入webdriver-manager让驱动版本和本地浏览器版本自动对齐。6.3 经验心得什么项目真的该用 Selenium聊到这儿我直接给出我的个人判断标准。如果一个项目满足以下任意两条我就不会死磕普通请求直接上浏览器方案目标页面通过 JS 动态渲染数据接口调用逻辑极其复杂或根本无法从页面源码直接定位网站存在“首次访问必须执行一段 JS 生成动态 Token”的机制采集的数据量不大但对成功率和完整度要求很高你每天只需要跑一两次单次跑完就停。反过来如果目标是纯 JSON 接口、数据量大、需要长期跑我依然建议把大部分精力花在普通请求上——把 Header 抄准、把频率控稳、把代理池用好这套方案的速度和成本是 Selenium 无法比的。在我实际的项目里最常用到的其实不是某个单一技术而是一套“降级链路”的思维先用最轻量的方案试不行再上更重的方案而每一层方案之间通过 Cookie 和参数的传递互相衔接。这套思路让我的爬虫项目很少因为一个 403 而彻底卡死。最后再分享一个小技巧写爬虫的时候建议给每个请求打上日志记录时间戳、目标 URL、状态码、用了哪个代理、Cookie 是否过期。没有日志的爬虫就像没有仪表盘的飞机你根本不知道它在哪一步失联的。等你的 403 问题排查积累多了你会发现大部分“灵异事件”背后其实都有一个非常具体的原因只差你多看几行日志。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

个人备案网站避坑指南 保姆级建站教程拆解费用 2026/9/28 7:34:43

个人备案网站避坑指南 保姆级建站教程拆解费用

个人备案网站避坑指南 保姆级建站教程拆解费用 域名服务器搞不懂,备案流程像迷宫?别慌,这份 个人备案网站 的 保姆级建站教程 ,专治各种“小白焦虑”。…

阅读更多 →
膜蛋白分析七库串联:从Uniprot到TMHMM的完整流程指南 2026/9/28 7:34:43

膜蛋白分析七库串联:从Uniprot到TMHMM的完整流程指南

1. 为什么要凑齐这七个数据库?——膜蛋白分析的完整拼图做膜蛋白研究的人大多有过这种体验:好不容易把一条序列拿到手,接下来却不知道该往哪儿查。翻Uniprot?只有注释信息,没有结构;跑TMHMM?只给…

阅读更多 →
Android热修复方案选型与工程化落地:从原理到实践 2026/9/28 7:34:43

Android热修复方案选型与工程化落地:从原理到实践

1. 热修复到底解决什么问题1.1 线上故障的“最后一公里”之痛做过移动端开发的人应该都有这种经历:应用上线后,用户反馈页面白屏、支付失败、数据错乱,产品经理在群里连发“怎么回事”、“什么时候能修”,而你盯着 Android 系统的…

阅读更多 →
Windows 11 下 ISE 14.7 与 ModelSim 的联合仿真配置与避坑指南 2026/9/28 7:34:43

Windows 11 下 ISE 14.7 与 ModelSim 的联合仿真配置与避坑指南

1. 为什么现在还有人在折腾 ISE 与 ModelSim 的这套组合1.1 哪些人必须用 ISE 14.7先别急着吐槽“都什么年代了还在用 ISE”。作为搞 FPGA 的人,你迟早会遇到这类需求:单位里还躺着几块 Spartan-6、Virtex-5 的老开发板,导师给的毕业设计题目…

阅读更多 →
ASRPRO进阶开发实战:串口通信、多线程与ADC采集三大难点全解析 2026/9/28 7:34:43

ASRPRO进阶开发实战:串口通信、多线程与ADC采集三大难点全解析

做语音产品最怕的就是“能识别但联不上”。我见过太多人卡在天问block的图形化界面里,把ASRPRO当独立语音模块用,一旦需要跟ESP32S3、STM32这类主控打交道,或者要采集电压、光线、电量等模拟量,就不知道怎么把语音、外设、逻辑串在…

阅读更多 →
AlgoNote「算法通关手册」题解:搜索二维矩阵(LeetCode 0074)——对角线分治与二分查找 2026/9/28 7:34:36

AlgoNote「算法通关手册」题解:搜索二维矩阵(LeetCode 0074)——对角线分治与二分查找

教程文档知识库 【免费下载链接】AlgoNote ⛽️「算法通关手册」:从零开始的「算法与数据结构」学习教程,200 道「算法面试热门题目」,1000 道「LeetCode 题目解析」,持续更新中! 项目地址: https://gitcod…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉