网站反爬越狠,它越兴奋!这个自愈式爬虫专治各种不服

📅 2026年05月19日 · 技术

网站反爬越狠,它越兴奋!这个自愈式爬虫专治各种不服

每个网络爬虫刚开始都能正常工作。问题是——它能撑多久不坏?

网站改版了,选择器失效了;加了 Cloudflare,请求被拦截了;反爬升级了,IP 被封了。大部分爬虫团队靠的是"出了事再修"的运维模式,但 Anansi 提供了一条完全不同的路。

"The spider that learns." —— 这是一只会学习的蜘蛛。
💡 核心思想:Anansi 建立在一个完全不同的假设之上——网络是敌对的、不稳定的,而你的爬虫应该能在不需要你介入的情况下自行处理这些问题。

一句话概括 Anansi 的能力

当网站更改布局时,Anansi 自动找到数据并记住修复方案;当页面需要浏览器渲染时,它静默切换到浏览器模式;当反爬检测找上门时,它在网络层模仿 Chrome 的 TLS 指纹——这是大多数爬虫从未考虑过的层面;当重新抓取时,未更改的页面在发出请求前就被跳过;当提取出错时,Pydantic 验证立即捕获,而不是让垃圾数据污染你的数据库。

结果:一个能对付恶意站点、扛得住网站改版、越跑越聪明的爬虫。它还自带 MCP 服务器,任何 LLM 都可以通过对话驱动完整的抓取流程。

核心能力详解

1. 自愈式解析器

CSS 选择器带有置信度分数存储。当某个选择器失效时,四种修复策略会依次尝试——模糊类名匹配、文本模式正则、结构上下文、XPath 回退——胜出者会被持久化供下次使用。

# 提取失败后的修复流程
1. Text-pattern match    — 对元素文本做正则匹配
2. Attribute fuzzy match — Levenshtein 相似 CSS 类名
3. Structural context     — 父/兄弟节点导航
4. XPath fallback        — CSS 转 XPath

2. 结构化数据自动提取

JSON-LD、Open Graph 和 Microdata 会从每个页面自动提取。匹配到 schema.org 标记的字段直接跳过 CSS 评估——它们更稳定,无需选择器维护。

3. TLS/HTTP-2 指纹模仿

企业级反爬系统(Cloudflare、Akamai、DataDome)在检查任何请求头之前,会先对你的 TLS ClientHello 和 HTTP/2 SETTINGS/帧顺序进行指纹识别。使用 impersonate="chrome124",Anansi 通过 curl-cffi 精确重现这两个指纹,再加上按主机预热会话和 Akamai 拦截升级阶梯。

⚠️ 法律提醒:TLS 指纹模仿功能需要安装 tls 扩展包,由操作员把关,仅限授权用途使用。请遵守目标网站的 robots.txt 和服务条款。

4. 自动浏览器升级

每个 HTTP 响应都会检查 SPA 标记、noscript 重定向和异常低的文本密度。JS 壳子会触发静默重试,使用隐身 Playwright 浏览器。该决策会在抓取会话期间按域名缓存。

5. 反爬与 Cloudflare 绕过

浏览器提取器会移除 webdriver 指纹、伪造插件、硬件并发数、音频上下文、字体测量、电池 API 和触摸点,添加 Canvas/WebGL 噪声,自动关闭 GDPR/Cookie 同意横幅,并自动等待 Cloudflare Turnstile 挑战完成。

安装

# 核心安装
pip install "git+https://github.com/mdowis/anansi"

# 浏览器提取(Cloudflare 绕过、JS 渲染)
playwright install chromium

# TLS 指纹模仿扩展
pip install "anansi-scraper[tls] @ git+https://github.com/mdowis/anansi"

# OpenAI/ChatGPT Agents SDK 扩展
pip install "anansi-scraper[openai] @ git+https://github.com/mdowis/anansi"

快速上手

从产品页面提取结构化数据

import asyncio
from anansi import AdaptiveParser
from anansi.parser.adaptive import SelectorConfig

async def main():
    html = ...  # 获取的 HTML

    parser = AdaptiveParser()
    data = await parser.extract(html, {
        "name":  SelectorConfig("h1.product-title", expected_pattern=r"\w+"),
        "price": SelectorConfig(".price-tag", expected_pattern=r"\$[\d,.]+"),
        "sku":   ".product-sku",
    }, url="https://shop.example.com/product/42")

    print(data)
    # {"name": "Widget Pro", "price": "$49.99", "sku": "WGT-001"}

    # 原始结构化数据也可直接获取
    structured = await parser.extract_structured(html)
    print(structured["json_ld"])
    print(structured["open_graph"])

asyncio.run(main())

运行弹性并发爬取

from pydantic import BaseModel
from anansi import Crawler, ProxyManager
from anansi.core import Item, Request, Response
from anansi.spider.spider import Spider

class ProductItem(BaseModel):
    title: str
    price: float
    sku: str | None = None

class ShopSpider(Spider):
    name = "shop"
    start_urls = ["https://shop.example.com/products"]
    item_schema = ProductItem

    async def parse(self, response: Response):
        for link in response.css("a.product-link"):
            yield Request(response.urljoin(link["href"]), callback="parse_product")

    async def parse_product(self, response: Response):
        yield Item({"title": response.css("h1")[0].get_text(),
                    "url": response.url})

pm = ProxyManager(["http://proxy1:8080", "socks5://proxy2:1080"])

crawler = Crawler(
    ShopSpider,
    concurrency=10,
    delay=0.5,
    max_pages=1000,
    proxy_manager=pm,
)

crawler.run()

自适应限速

每次抓取后,Anansi 会调整请求频率:

递增式爬取

ETag、Last-Modified 和内容 MD5 会按 URL 存储。重新爬取时发送条件 GET 头——304 响应直接跳过解析,哈希比较能在没有服务端 ETag 支持的情况下检测变更。Sitemap 的 <lastmod> 日期用于预过滤,在发起网络请求前就跳过未变更的页面。

其他值得关注的功能

适用场景

参考来源

🔧 在线开发者工具 — JSON格式化 · Base64 · UUID生成 · 正则测试 等80+免费工具