如何在 Scrapy 中配置自定义代理(适用于生产环境)

最后更新于 August 11, 2026
Hand-drawn Scrapy crawler routing through a proxy pool, retrying a 503 route and completing through a 200 route
AI 摘要
- 构建自定义 Scrapy 代理中间件,负责分配已批准的代理线路、安全附加认证、记录失败原因,并在重试时保留请求元数据。 - 理解下载器中间件的执行顺序,尤其是自定义代理逻辑如何与 HttpProxyMiddleware、RetryMiddleware、重定向和异常处理协同工作。 - 通过有限次数、冷却时间、代理健康状态以及面向目标站点的策略来做轮换,而不是每次请求都随机选一个代理。 - 区分代理认证失败、连接错误、DNS 问题、目标站点 403 响应和限流,让每种情况都得到正确处理。 - 使用适用于生产环境的安全措施,包括密钥存储、并发控制、可观测性、会话一致性,以及当没有可用的已批准代理时的失败即关闭策略。

你的 Scrapy 爬虫在 100 个测试页面上表现完美,到了 10,000 页就直接崩了。那其实不一定是爬虫代码有 bug——更像是网络问题披着爬虫 bug 的外衣。大家第一时间想到的解决办法通常是代理,而把代理塞进 request.meta 也就三十秒的事。

问题在于,这个三十秒的方案往往也是很多入门教程的终点。它们会告诉你 meta={"proxy": "http://IP:PORT"},或者给你一个中间件类,然后就结束了。却很少提到代理在抓取途中失效怎么办、凭证如何避免泄露到 Git 历史里、或者为什么重试请求可能又回到了同一个坏代理。这些才是生产环境真正要考虑的点:失败即关闭的配置、密钥管理、在重试时有意识地选择代理、协议边界限制,以及可衡量的成本权衡。

Scrapy 里的代理中间件是什么?

代理中间件是一段位于 Scrapy 下载器流程中的代码,它会在请求真正发出去之前,决定这个请求看起来来自哪个 IP。Scrapy 自带了一个内置的 HttpProxyMiddleware,它会读取 request.meta 里的 proxy 字段,必要时附加代理认证信息,然后把请求交给真正负责建立连接的下载器处理。自定义代理中间件并不是替代这个传输步骤——它更像是一个“选择器”,负责在内置机制接管之前,先决定要塞入哪个代理。这个区别比看上去重要得多,也是“我的自定义中间件怎么没生效”这类问题的主要来源。

为什么代理对生产级 Scrapy 项目很重要?

代理会改变网络路径和表面上的来源 IP。这在合法的地域测试、分发已授权的请求流量、隔离网络故障时都很有帮助。但它并不会赋予你访问权限、绕过速率限制,或者保证一定能访问成功。到底要不要给爬虫加代理,取决于目标站点、其使用条款、请求频率,以及任务对稳定性的要求。

并不是所有代理都一样,选错档位本身就可能引发生产事故:

代理类型可靠性速度被识别风险典型成本
免费公共代理波动极大不稳定往往很高不收费,但安全和运维风险很大
数据中心代理取决于服务商和目标站点通常较快取决于目标站点常按流量或 IP 计费
住宅代理取决于服务商和目标站点不稳定取决于目标站点通常按流量计费
ISP 代理取决于服务商和目标站点不稳定取决于目标站点由供应商具体定价

免费代理尤其值得单独提醒,因为实际风险非常高。那项为期 30 个月的 Free Proxies Unmasked 研究 跟踪了 11 家服务商的 64 万多个地址:其中 34.5% 至少曾经可用一次,16,923 个会篡改内容。这个结果足以对公共代理列表敲响强烈的安全警钟,但它并不能直接代表每一个当前列表、付费代理池、目标站点或工作负载的故障率。

代理也不是现代反爬检测的万能解法。像 Cloudflare 这类服务会从几十种信号评估请求——TLS 指纹、请求头一致性、JavaScript 执行情况、行为模式等等,其中 IP 只是输入之一。即使你用了干净的住宅代理,如果请求头前后不一致、又没有 cookie 容器,照样可能被标记。别一上来就围着“只要轮换 IP 就行”这个思路搭整套架构。

开始前需要准备什么

  • 难度: 中级
  • 所需时间: 完整生产级配置约 30–45 分钟,快速测试约 5 分钟
  • 你需要准备: Python 3.9+、已安装 Scrapy(本文基于 Scrapy 2.17.0 测试,该版本于 2026 年 7 月发布)、一个通过 scrapy startproject 创建并可运行的爬虫,以及至少一个代理入口(测试时任何数据中心代理服务商的免费试用都可以)

第 1 步:用 Request Meta 参数测试代理

验证代理是否可用的最快方法,就是先别管中间件架构,直接试一下。

Scrapy 内置的 HttpProxyMiddleware 会直接读取 request.meta 里的 proxy 字段,并把请求通过它发出去。不用改 settings,也不用写中间件类——只要一个参数就够了。

import scrapy

class ProxyTestSpider(scrapy.Spider):
    name = "proxy_test"
    start_urls = ["https://httpbin.org/ip"]

    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(
                url,
                meta={"proxy": "http://203.0.113.10:8080"},
                callback=self.parse,
            )

    def parse(self, response):
        self.logger.info(response.text)

scrapy runspider proxy_test.py 运行。若代理正常,httpbin.org/ip 返回的会是代理 IP,而不是你自己的 IP——这就说明代理通了。如果请求一直卡住,最后报 TCP connection timed out,那通常就是代理挂了或者根本连不上。说白了,这种情况比代理服务商愿意承认的要常见得多。

这种方式适合一次性爬虫或者快速测试。一旦你有不止一个爬虫,它就会立刻变得不够用,因为你要在五个文件里重复写同一个代理字符串。

第 2 步:构建自定义代理中间件

只要不是单个爬虫,代理逻辑最好集中到一个地方。可以在项目的 middlewares.py 中创建一个 ProxyMiddleware 类:

from scrapy.exceptions import NotConfigured

class ProxyMiddleware:
    def __init__(self, proxy_url):
        self.proxy_url = proxy_url

    @classmethod
    def from_crawler(cls, crawler):
        proxy_url = crawler.settings.get("PROXY_URL")
        if not proxy_url:
            raise NotConfigured("PROXY_URL is required; refusing silent direct fallback")
        return cls(proxy_url=proxy_url)

    def process_request(self, request, spider):
        if "proxy" not in request.meta:
            request.meta["proxy"] = self.proxy_url

然后在 settings.py 里注册它:

import os

PROXY_URL = os.environ.get("PROXY_URL")

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.ProxyMiddleware": 350,
}

注意这里用了 from_crawler 类方法,而不是普通的 __init__。这正是 Scrapy 推荐的读取配置方式,而且我们后面在第 4 步讲密钥管理时还会再次用到这个入口。这样做的好处很直接:改一个配置,项目里所有爬虫都会自动使用新的代理。再也不用在五个文件里到处搜索替换失效的 IP 了。

第 3 步:搞清楚中间件执行顺序,别让代理悄悄失效

这一部分几乎所有其他教程都会轻描淡写地带过一句“把优先级设成 350 就行,相信我”。但 Scrapy 的下载器中间件是按照固定顺序执行的,如果你不理解这个顺序,代理配置会出现很多看起来和代理完全没关系的 bug。

请求方向的钩子(process_request)按优先级升序执行——数字越小越先执行。响应方向的钩子(process_responseprocess_exception)则按优先级降序执行——数字越大越先执行,然后逐层往回展开。

请求流向(升序):
  Spider → [100] RobotsTxt → [300] HttpAuth → [350] 你的代理中间件
         → [500] UserAgent → [550] Retry → [700] Cookies → [750] HttpProxy
         → Downloader

响应流向(降序):
  Downloader → [750] HttpProxy → [700] Cookies → [550] Retry
             → [500] UserAgent → [350] 你的代理中间件 → [300] HttpAuth
             → [100] RobotsTxt → Spider

下面是 Scrapy 内置中间件当前真实的默认优先级表(已按 2.17.0 验证):

中间件默认优先级
RobotsTxtMiddleware100
HttpAuthMiddleware300
DownloadTimeoutMiddleware350
DefaultHeadersMiddleware400
UserAgentMiddleware500
RetryMiddleware550
RedirectMiddleware600
CookiesMiddleware700
HttpProxyMiddleware750
DownloaderStats850
HttpCacheMiddleware900

RetryMiddleware 安排一次重试时,它会复制失败请求,包括其元数据,然后这个新请求会重新进入下载器中间件链。选择器和优先级 550 的数值关系并不能自动保证代理轮换。只有当选择器代码识别到这是重试请求,并且主动覆盖掉复制过来的 meta["proxy"] 时,轮换才会真正发生。把选择器放在 350 只是因为它会在内置传输步骤 750 之前运行,这样比较方便,但它本身并不等于轮换机制。

Scrapy 请求与响应路径穿过优先级 350、550 和 750,并在 503 重试时重新进入中间件链

常见的中间件顺序错误

  • 把你的中间件设置成和 HttpProxyMiddleware 一样的优先级(750): 这会造成竞争条件,最后由 Scrapy 的字典顺序(而不是你的逻辑)决定哪个中间件先处理请求。症状就是:代理行为时好时坏,完全说不清原因。
  • 在轮换选择器里用 setdefault()if "proxy" not in request.meta 重试时复制过来的请求会保留旧代理。症状是每次重试都还是同一条失败路线。修复方式:识别这是不是重试请求(例如 retry_times > 0),然后明确覆盖选择器负责的代理值。
  • 完全禁用 HttpProxyMiddleware 有些教程会让你把 "scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": None,理由是“自定义中间件已经处理好了”。其实并没有——你的自定义中间件只是选择器,不是传输层。禁用内置中间件意味着代理认证头不会被附加,请求会悄悄以未认证状态发出,或者干脆发不出去。

第 4 步:别把代理凭证硬编码进去

我看过的几乎所有高排名 Scrapy 代理教程,都会把 http://username:password@proxy.example.com:8080 直接写进 Python 文件里。这等于把一个凭证永久留在 Git 历史中,之后每次克隆、fork,甚至谁手滑 print() 到日志里,都可能把它泄露出去。

这件事其实可以分成三个层级:

方法安全性灵活性适合场景
直接硬编码在 spider/settings.py很差——密钥留在仓库里只适合本地快速测试
使用 http_proxy 环境变量(Scrapy 原生支持)更好——不在代码里低(单个代理)CI/CD 流水线、Docker
.env 文件 + python-dotenv + from_crawler最好——不在代码里,且可按环境配置高(支持多个代理、轮换)生产级爬虫

第三种方式值得认真搭建。先安装 python-dotenv,然后创建一个 .env 文件,并立刻把它加入 .gitignore——我是认真的,现在就做:

PROXY_USER=myuser
PROXY_PASSWORD=my$ecret!Pass
PROXY_HOST=proxy.example.com
PROXY_PORT=8080

然后在 settings.py 顶部加载它:

from dotenv import load_dotenv
import os

load_dotenv()

PROXY_USER = os.getenv("PROXY_USER")
PROXY_PASSWORD = os.getenv("PROXY_PASSWORD")
PROXY_HOST = os.getenv("PROXY_HOST")
PROXY_PORT = os.getenv("PROXY_PORT")

接着在中间件里通过 from_crawler 安全读取,这正好能解决论坛里经常有人问的那个问题:“如果每次运行 scrapy crawl 前都得换凭证,该怎么设置?”

import os
from urllib.parse import quote

class SecureProxyMiddleware:
    def __init__(self, user, password, host, port):
        self.user = quote(user, safe="")
        self.password = quote(password, safe="")
        self.host = host
        self.port = port

    @classmethod
    def from_crawler(cls, crawler):
        settings = crawler.settings
        return cls(
            user=settings.get("PROXY_USER"),
            password=settings.get("PROXY_PASSWORD"),
            host=settings.get("PROXY_HOST"),
            port=settings.get("PROXY_PORT"),
        )

    def process_request(self, request, spider):
        proxy_url = f"http://{self.user}:{self.password}@{self.host}:{self.port}"
        request.meta["proxy"] = proxy_url

注意这里对凭证做了 urllib.parse.quote()。如果你的密码里包含 @:/(密码生成器特别爱加这些字符),URL 解析就会出问题,除非先做百分号编码。这是一个一行代码就能避免的坑,能省掉你一个小时去排查“invalid proxy URL”错误——而那个错误其实跟代理本身一点关系都没有。

请把凭证留在日志之外,并用真实但虚假的示例值测试百分号编码。HTTPS 隧道、SOCKS5,以及非拉丁字符的凭证,都会有各自的处理边界;不要想当然地认为某一种认证方式在所有协议上都通用,除非你有锁定版本的集成测试。

从加锁的环境文件,经由百分号编码的代理配置,进入 Scrapy 请求元数据的安全流程

第 5 步:加入代理轮换

单个代理——哪怕质量很好——对同一个网站发 5,000 次请求后,也迟早会被盯上。你需要一个代理池。

方案 A——自己实现。 真的很简单:

import random

class RotatingProxyMiddleware:
    def __init__(self, proxy_pool):
        self.proxy_pool = proxy_pool

    @classmethod
    def from_crawler(cls, crawler):
        return cls(proxy_pool=crawler.settings.getlist("PROXY_POOL"))

    def process_request(self, request, spider):
        request.meta["proxy"] = random.choice(self.proxy_pool)

这个方案适合基础使用,但它完全不知道哪些代理是真正活着的。每个请求都在掷骰子。

方案 B——使用 scrapy-rotating-proxies 这个第三方包开箱即带有封禁检测和自动退避机制:

pip install scrapy-rotating-proxies

这里我得坦白提醒一句:它在 PyPI 上的最新版本 是 0.6.2,发布时间还是 2019 年,而且项目标记为 Alpha。它不一定就和现代 Scrapy 不兼容,但“2019 年后没有持续维护”绝不等于“已经在 2026 年的生产流量里被充分验证”。记得锁版本、拿真实目标站点做测试,也不要默认它能处理带认证的代理端点——它大多做不到。

第 6 步:构建容错型代理中间件(检测坏代理)

这一节通常会被竞争教程直接跳过,但它正是 demo 和能在无人值守情况下连续跑 6 小时的系统之间的区别。

场景大多数教程怎么处理这个中间件增加了什么
代理返回 407不提将其视为代理认证失败
目标站点返回 403/429经常混在一起讲把策略/限流反馈与代理健康状态分开
代理超时不提可配置超时阈值、健康分值衰减
所有代理都挂了不提优雅回退,或者记录警告后暂停爬取
代理抖动(间歇性失效)不提重新加入代理池前先冷却一段时间
import time
import random
from scrapy.exceptions import IgnoreRequest

class FaultTolerantProxyMiddleware:
    MAX_FAILURES = 3
    COOLDOWN_SECONDS = 300

    def __init__(self, proxy_pool):
        self.pool = {p: {"failures": 0, "banned_until": 0} for p in proxy_pool}

    @classmethod
    def from_crawler(cls, crawler):
        return cls(proxy_pool=crawler.settings.getlist("PROXY_POOL"))

    def _healthy_proxies(self):
        now = time.time()
        return [p for p, s in self.pool.items() if s["banned_until"] < now]

    def process_request(self, request, spider):
        healthy = self._healthy_proxies()
        if not healthy:
            spider.logger.warning("All proxies unhealthy — pausing crawl")
            raise IgnoreRequest("No healthy proxies available")
        request.meta["proxy"] = random.choice(healthy)

    def process_response(self, request, response, spider):
        proxy = request.meta.get("proxy")
        if proxy and response.status == 407:
            self._mark_failure(proxy)
        return response

    def process_exception(self, request, exception, spider):
        proxy = request.meta.get("proxy")
        if proxy:
            self._mark_failure(proxy)

    def _mark_failure(self, proxy):
        state = self.pool[proxy]
        state["failures"] += 1
        if state["failures"] >= self.MAX_FAILURES:
            state["banned_until"] = time.time() + self.COOLDOWN_SECONDS
            state["failures"] = 0

这里有几点是我在实际实现中学到的:不要把每个 403 都当成代理死了——RFC 9110 对 403 的定义是“服务器理解了请求,但拒绝执行”,这很可能只是你的请求头或会话状态看起来可疑,和代理本身无关。相比之下,407 明确表示代理拒绝了你的认证,这信号更强也更具体。把这些信号混成一锅乱炖,最后只会白白浪费好代理。

在生产环境里,把 Scrapy 的临时重试默认值显式写出来,方便审查者看清楚你的策略:

RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408, 429]
RETRY_TIMES = 2

这些是 Scrapy 2.17 的默认值。不要一股脑把 403 或 407 也加进去:403 是目标站点拒绝访问,可能有很多原因;407 则是代理认证问题,靠普通请求重试并不能解决。如果某个特定目标契约确实要求对某个状态码重试,请把原因写清楚,并单独测试。

407 认证失败、429 限流、503 重试、200 成功以及代理不可用时关闭闸门的不同处理方式

第 7 步:一种可度量的“先直连、再代理”模式

某些已授权目标站点可能会直接返回有效内容,而代理只需要在出现文档化的临时响应之后才启用。这样可以减少走代理的字节数,但并不存在一个可通用套用的节省比例,也没有可移植的“直连成功率”。在采用这个模式前,先对自己的工作负载做测量。

可以使用 Scrapy 公共的 get_retry_request() 辅助函数,并且只对你明确分类过的、与目标站点相关的状态码做升级处理。下面这个示例把 429 和 503 当作可重试的压力信号;它刻意不包含 403 和 407。

from scrapy.downloadermiddlewares.retry import get_retry_request

class CostAwareEscalationMiddleware:
    def process_response(self, request, response, spider):
        if response.status not in {429, 503}:
            return response

        retry = get_retry_request(
            request,
            spider=spider,
            reason=f"proxy_escalation_{response.status}",
            max_retry_times=2,
        )
        if retry is None:
            return response

        current_tier = request.meta.get("proxy_tier", "direct")
        if current_tier == "direct":
            retry.meta["proxy"] = spider.settings["DATACENTER_PROXY"]
            retry.meta["proxy_tier"] = "datacenter"
        elif current_tier == "datacenter":
            retry.meta["proxy"] = spider.settings["RESIDENTIAL_PROXY"]
            retry.meta["proxy_tier"] = "residential"
        else:
            return response
        return retry

你需要记录直连有效内容比例、代理有效内容比例、每条成功记录消耗的字节数、每次成功对应的重试次数,以及额外延迟。只有在直连访问本身是被授权的、并且系统在需要代理时会明确失败关闭,这种“先直连”的设计才算可接受。这里节省下来的,是实际测得的代理流量减少,而不是凭空假设的百分比。

什么时候自己管理代理不值得?

我先说清楚,免得让人误会整篇文章都在反对自建:上面这些内容都是真实且有用的工程实践。对于很多项目——高吞吐爬取、自定义管道、以及需要完全掌控请求调度的场景——这确实是正确选择。

但如果你的真实目标是“从这个页面拿到结构化数据”,而不是“自己运维代理基础设施”,那抽取 API 可能会是更合适的边界。Thunderbit 当前的 POST /extract 文档 支持传入页面 URL 和可选的 JSON Schema;如果不提供 schema,服务也可以根据页面内容自动生成。这个接口还提供 nonebasicfull 三种渲染模式,以及超时和加载后等待时间控制。纯提示词抽取并不在当前支持的请求能力范围内,因此生产集成应当基于文档中定义的 schema 契约来构建。这个方案只是把抽取接口收敛到一次请求中,并不意味着它能对所有反爬或验证码目标做出统一承诺。

| 因素 | 自建 Scrapy + 代理 | 基于 API 的抽取(例如 Thunderbit) | |---|---|---|---| | 搭建成本 | 高——中间件、轮换、重试逻辑都要自己做 | 低——一次 API 调用加 schema 就行 | | 网络/渲染行为 | 由你配置处理器、代理、请求头和延迟 | 通过文档化的 API 选项控制 | | 维护成本 | 你自己负责选择器、代理池健康和目标站点变化 | 你负责 schema 质量、校验和集成行为 | | 成本模型 | 代理费用 + 计算资源 + 工程时间 | 当前文档显示每页 20 个单位(核对于 2026-08-10) | | 控制力 | 完全可控——自定义管道和中间件链 | 受限于 API 能力 | | 适合场景 | 复杂爬取、高并发、自定义逻辑 | 定向抽取、原型验证、数据补全 |

如果你主要是在抓取用于线索列表、商品数据或研究用途的结构化页面,可以用 Thunderbit Chrome Extension 或 API 做一个代表性试跑,对比有效记录数、延迟、单位消耗和维护时间。如果你的项目需要自定义爬取图谱和完整管道控制,Scrapy 依然更适合。

延伸阅读

提示与常见坑

  • 提示: 在真正接目标站点之前,先用 httpbin.org/ip 测一下代理。这样可以在增加复杂度之前,最快确认路由是否真的生效。
  • 坑点: 把代理写在 request.headers 里,而不是 request.meta 里。这是一个非常常见、又很容易被忽略的错误——HttpProxyMiddleware 只会读取 meta["proxy"],而基于 header 的写法会静默失败,几乎没有明显报错。
  • 坑点: 以为 HTTP 和 HTTPS 代理的配置方式完全一样。指向 HTTPS 目标站点的 HTTP 代理 URL,通常会通过兼容的处理器走 CONNECT 隧道;但把代理端点本身写成 https://,则是另一种支持度更低的配置——这两者不要混为一谈。
  • 提示: 如果你需要 SOCKS5 支持,先确认你的 Scrapy 版本对应的下载处理器能力。Scrapy 2.17 的实验性 Httpx 处理器通过 httpx[socks] 提供了 SOCKS5 支持,但它仍然标注为实验性——不要在没有自行测试的情况下把它当作生产依赖。

其他方法

除了 scrapy-rotating-proxies 之外,还有一些团队会把所有代理逻辑交给供应商网关来处理——也就是一个代理 URL,由服务商在后台完成轮换、会话粘性和地域定位。这样会牺牲一点控制权,但能少写很多中间件代码。在你从零自建代理池之前,值得先把这个方案的成本算清楚。

结论

在 Scrapy 中设置代理只要一行。但要让这套配置在真实生产抓取中稳定运行,你需要明确的失败策略、受保护的凭证、经过测试的处理器边界,以及在重试时会主动替换已复制代理元数据的选择器逻辑。如果你只带走两个习惯,那就记住这两个:当必须使用代理时,要失败即关闭;还有,永远不要把代理密码提交到 Python 文件里。

常见问题

如何在 Scrapy 中配置带认证的自定义代理? 使用 protocol://username:password@host:port 这种 URL 格式,但如果用户名或密码里含有特殊字符,要先用 urllib.parse.quote() 做百分号编码。生产环境中,最好通过 from_crawler 类方法从环境变量读取这些凭证,而不是直接写死在代码里。

Scrapy 自定义代理中间件应该用什么优先级? 350 是一个常见的选择器优先级,因为它会在 HttpProxyMiddleware 的 750 之前执行。但这并不能保证轮换发生。只有当选择器识别到这是复制出来的重试请求,并覆盖掉原先的 meta["proxy"] 值时,重试请求才会获得新的代理。

Scrapy 里怎么自动处理坏代理? 编写一个中间件,在 process_responseprocess_exception 中跟踪每个代理的失败次数;当失败达到阈值后把它从可用池中移除,并在冷却一段时间后再重新加入,而不是永久封禁。

Scrapy 可以使用 SOCKS5 代理吗? Scrapy 2.17 的实验性 HttpxDownloadHandler 在安装 httpx[socks] 后文档中说明支持 SOCKS5。默认的 HTTP/1.1 处理器不支持 SOCKS 代理。请先锁定处理器和版本,并做集成测试,再把这条路径当作生产方案。

“先直连、再代理”的模式到底能省多少代理成本? 没有统一适用的比例。你需要测量:有多少已授权请求能直接返回有效内容、每一层代理实际传输了多少字节、每个成功结果平均重试几次,以及增加了多少延迟。观察到的代理流量减少量就是你的节省;如果直连访问没有授权,或者结果无效,就不要使用这个模式。

Ke
Ke
Thunderbit 首席技术官 | 高级数据科学家与机器学习专家 Ke Shen 拥有近十年的机器学习和数据科学经验,毕业于哥伦比亚大学,曾任 Walmart Labs 高级数据科学家。他在 Python、R、Java 和统计学方面拥有深厚且备受同行认可的专业能力,并分享如何将复杂的 AI 算法从理论落地到生产级架构的实战经验。
Topics
Scrapy 代理中间件Python 网页爬取代理轮换
目录
Thunderbit · AI 网页数据代理

1 次点击 中从任何页面提取数据

受到超过 250,000+ 用户的信赖
提供免费计划
使用 AI 提取数据
轻松将数据传输到 Google Sheets、Airtable 或 Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week