反爬拦截不再重试: 判定为反爬类错误(403/429/503/Cloudflare/captcha/验证页等)直接放弃该页, 节省重试时间; 验证页持续8秒即判定反爬, 不再干等满超时(默认60s)

- engine.is_anti_crawl_error: 反爬错误信号关键词判定
- _retry_crawl: 反爬错误直接 break, entry.error 标注'反爬拦截, 跳过重试'
- _wait_page_settle/_settle_wait: 连续8秒检测到验证页即判定反爬尽早返回
- 集成测试: 反爬页 attempts=1 且 8.1s 放弃; 非反爬错误仍 attempts=retry_count+1 正常重试
This commit is contained in:
2026-08-12 09:37:23 +08:00
parent 796e667533
commit 0482284cc0
+28
View File
@@ -36,6 +36,19 @@ _CHALLENGE_MARKS = [
"checking your browser", "enable javascript and cookies",
]
# 反爬拦截类错误信号: 命中后不再重试 (重试无意义且拖慢任务)
_ANTI_CRAWL_MARKS = [
"403", "429", "503", "forbidden", "access denied", "too many requests",
"captcha", "cloudflare", "challenge", "verify you are human",
"just a moment", "blocked", "被反爬拦截",
]
def is_anti_crawl_error(err):
"""判断错误是否属于反爬拦截 (此类失败重试也无法通过, 直接放弃该页)"""
s = str(err or "").lower()
return any(m in s for m in _ANTI_CRAWL_MARKS)
def is_challenge_page(title, html):
"""判断是否仍在反爬验证页 (仅关键词启发, 避免误伤正常小页面)"""
@@ -56,6 +69,7 @@ def safe_name(url, idx):
def _settle_wait(page, timeout_s):
"""等待页面稳定(无 stop/pause 检查, 供试爬取等独立流程使用)"""
last_title, stable = "", 0
challenge_hits = 0
start = time.time()
while time.time() - start < timeout_s:
time.sleep(1)
@@ -65,8 +79,12 @@ def _settle_wait(page, timeout_s):
except Exception:
continue
if is_challenge_page(title, html):
challenge_hits += 1
if challenge_hits >= 8: # 持续 8 秒仍是验证页, 判定反爬, 尽早放弃
return True, title, html
stable = 0
continue
challenge_hits = 0
if title == last_title:
stable += 1
if stable >= 2 and len(html) > 1000:
@@ -306,6 +324,7 @@ class CrawlJob:
def _wait_page_settle(self, page, timeout_s):
last_title, stable = "", 0
challenge_hits = 0
start = time.time()
while time.time() - start < timeout_s:
if self._stop.is_set():
@@ -318,8 +337,12 @@ class CrawlJob:
except Exception:
continue # 正在跳转
if is_challenge_page(title, html):
challenge_hits += 1
if challenge_hits >= 8: # 持续 8 秒仍是验证页, 判定反爬, 尽早放弃
return True, title, html
stable = 0
continue
challenge_hits = 0
if title == last_title:
stable += 1
if stable >= 2 and len(html) > 1000:
@@ -460,6 +483,11 @@ class CrawlJob:
entry["error"] = str(e)
entry["crawl_time"] = store.now_str()
self._log("warn", f"{attempt + 1}次失败 {url}: {e}")
if is_anti_crawl_error(e):
# 反爬拦截: 重试也过不去, 直接放弃, 不再消耗重试次数
entry["error"] = f"反爬拦截, 跳过重试: {e}"
self._log("warn", f"判定为反爬拦截, 放弃重试: {url}")
break
if attempt < retries:
self._wait_if_paused()
t0 = time.time()