Scrapfly Scraper

通过API实现反爬虫绕过与JavaScript渲染的网页数据采集。

已扫描
适合谁
需要批量抓取网页数据的技术人员、从事网络数据分析或市场调研的用户
不适合谁
无编程基础的普通用户、希望直接获取结果而无需配置的用户
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @scrapfly/scrapfly-scraper

Skill 说明

命令、参数、文件名以原文为准

Scrapfly 抓取器

使用 Scrapfly 抓取 API 收集网页数据,支持代理轮换、反机器人防护绕过、JavaScript 渲染以及浏览器控制的 JavaScript 场景。

适用场景

  • 抓取网页内容(HTML、JSON、文本、Markdown)
  • 绕过反机器人保护(Cloudflare、DataDome、PerimeterX、Kasada 等)
  • 使用带地理定位的轮换代理收集数据
  • 使用无头浏览器渲染 JavaScript 重度页面
  • 通过 JavaScript 场景控制浏览器执行常见操作(等待选择器、点击、填写表单等)
  • 会话复用,用于保持会话状态的抓取
  • 捕获浏览器 XHR 请求数据

设置

pip install scrapfly-sdk

API 密钥必须通过环境变量 SCRAPFLY_API_KEY 提供,或直接传递给客户端。

API 参考

端点: https://api.scrapfly.io/scrape

HTTP 方法将转发至上游 URL。

要从网页或 API 获取数据,请使用 GET 方法(默认方法)。

如果 API 资源需要其他方法(如 POST),可通过 ScrapeConfigmethod 参数指定。

ScrapflyClient

from scrapfly import ScrapflyClient, ScrapeConfig, ScrapeApiResponse
import os

client = ScrapflyClient(key=os.environ["SCRAPFLY_API_KEY"])

ScrapeConfig 参数

参数类型默认值说明
urlstr必填目标抓取 URL
methodstr"GET"HTTP 方法:GET、POST、PUT、PATCH、HEAD、OPTIONS
headersdictNone自定义 HTTP 头部
cookiesdictNone自定义 cookies(合并到 headers["cookie"]
bodystrNonePOST/PUT/PATCH 的原始请求体(用于预编码负载)
datadictNone表单/JSON 数据,用于 POST/PUT/PATCH;当未提供 body 时按 Content-Type 编码
timeoutintNone请求超时时间(毫秒),默认约 150,000ms
retryboolTrue网络失败时自动重试
countrystrNone代理所在国家(ISO 3166-1 alpha-2 标准,如 "us""de"
proxy_poolstr"public_datacenter_pool"代理池类型:"public_datacenter_pool""public_residential_pool"
sessionstrNone会话 ID,用于跨请求保持 cookies/fingerprint
session_sticky_proxyboolFalse对于指定的 session,保持相同的代理 IP
aspboolFalse启用反抓取保护绕过
render_jsboolFalse启用无头浏览器 JavaScript 渲染(+5 积分)
rendering_waitintNone页面加载后等待时间(毫秒),需配合 render_js=True
rendering_stagestr"complete"浏览器就绪阶段:"complete""domcontentloaded"(需 render_js=True
wait_for_selectorstrNone等待的 CSS/XPath 选择器(需 render_js=True
jsstrNone在浏览器中执行的 JavaScript 代码(自动编码)
js_scenariolistNone浏览器操作列表(点击、填写、滚动、等待等)
auto_scrollboolNone渲染过程中自动滚动页面以加载懒加载内容(需 render_js=True
screenshotsdictNone截图设置:{"fullpage": "png"}{"selector": ".element"}(需 render_js=True
screenshot_flagslist[str]None截图选项:"load_images""dark_mode""block_banners""high_quality""print_media_format"
formatstr"raw"输出格式:"raw""clean_html""json""markdown""text"
format_optionslist[str]None格式修饰符(仅 Markdown 有效):"no_images""no_links""only_content"
extractdictNone要应用在响应上的原始提取规则(编码后作为 extract 发送)
extraction_templatestrNone保存在服务器端的提取模板名称
extraction_ephemeral_templatedictNone一次性使用的内联 JSON 提取模板(前缀为 ephemeral:
extraction_promptstrNone用于 Extraction API 的自然语言指令
extraction_modelstrNone用于 Extraction API 的 LLM 模型名称
cacheboolFalse启用响应缓存
cache_ttlintNone缓存存活时间(秒)
cache_clearboolFalsecache=True 时清除现有缓存响应
dnsboolFalseresult.dns 中收集 DNS 记录和耗时(较慢)
sslboolFalseresult.ssl 中收集 SSL 证书详情(较慢)
debugboolFalse启用调试记录和额外元数据
raise_on_upstream_errorboolTrue上游返回 4xx/5xx HTTP 响应时抛出异常
correlation_idstrNone自定义请求跟踪 ID,用于跨系统追踪
tagslist[str]None自定义标签,用于请求组织与分析
langlist[str]NoneAccept-Language 值,例如 ["en-US", "en"]
osstrNone覆盖浏览器操作系统指纹,例如 "windows""macos"
webhookstrNone命名 webhook,用于异步抓取完成后的回调通知
cost_budgetintNoneASP 重试及附加功能的最大积分消耗

ScrapeApiResponse

response = client.scrape(ScrapeConfig(url="https://httpbin.dev"))

response.content                      # 页面内容(HTML/JSON/文本)
response.scrape_result                # 完整结果字典
response.status_code                  # Scrapfly API HTTP 状态码
response.scrape_result["status_code"] # 上游 HTTP 状态码
response.headers                      # 响应头部
response.context                      # 使用功能的元数据

示例

基础抓取

from scrapfly import ScrapflyClient, ScrapeConfig
import os

client = ScrapflyClient(key=os.environ["SCRAPFLY_API_KEY"])

result = client.scrape(ScrapeConfig(
    url="https://httpbin.dev",
))
print(result.content)

使用反机器人防护和地理定位进行抓取

result = client.scrape(ScrapeConfig(
    url="https://httpbin.dev",
    asp=True,  # 启用 ASP 以绕过反机器人机制
    country="us",  # 将代理与目标域名所在国家匹配
    proxy_pool="public_residential_pool",  # 使用住宅代理池,模拟真实 ISP IP 地址
))

使用 JavaScript 渲染进行抓取

result = client.scrape(ScrapeConfig(
    url="https://web-scraping.dev/products",
    render_js=True,  # 启用云浏览器渲染 JavaScript
    rendering_wait=5000,  # 渲染等待时间(毫秒)
    wait_for_selector="div.product",  # 等待页面上指定选择器出现
))

以 Markdown 格式抓取内容

result = client.scrape(ScrapeConfig(
    url="https://web-scraping.dev/products",
    format="markdown",  # 支持的其他格式包括:json、text、clean_html
))
print(result.content)

发送带请求体的 POST 请求

result = client.scrape(ScrapeConfig(
    url="https://httpbin.dev/anything",
    method="POST",
    headers={"Content-Type": "application/json"},
    body='{"query": "search term"}',
))
print(result.content)

JavaScript 场景(浏览器操作)

result = client.scrape(ScrapeConfig(
    url="https://web-scraping.dev/login",
    render_js=True,
    # 浏览器控制指令
    js_scenario=[
        {
            "fill": {
                "selector": "input[name='username']",
                "clear": True,
                "value": "user123"
            }
        },
        {
            "fill": {
                "selector": "input[name='password']",
                "clear": True,
                "value": "password"
            }
        },
        {
            "click": {
                "selector": "form > button[type='submit']"
            }
        },
        {
            "wait_for_navigation": {
                "timeout": 5000
            }
        }
    ],
    # 请求头设置
    headers={
        "cookie": "cookiesAccepted=true"
    }
))

# 访问登录后的页面元素
print(result.selector.css("div#secret-message::text").get())

内置 Parsel 选择器

result = client.scrape(ScrapeConfig(
    url="https://web-scraping.dev/products",
    render_js=True
))

# 使用内置的 Parsel 选择器
selector = result.selector
for product in selector.css("div.product"):
    print("商品名称:", product.xpath(".//a/text()").get())  # 使用 XPath
    print("商品价格:", product.css("div.price::text").get())  # 使用 CSS 选择器

并发抓取

import asyncio
from scrapfly import ScrapflyClient, ScrapeConfig

client = ScrapflyClient(key=os.environ["SCRAPFLY_API_KEY"])

urls = [
    "https://web-scraping.dev/products",
    "https://web-scraping.dev/products?page=2",
    "https://web-scraping.dev/products?page=3",
]

configs = [ScrapeConfig(url=url) for url in urls]

async def concurrent_scraping():
    results = []
    async for result in client.concurrent_scrape(configs):
        results.append(result)

    for result in results:
        print(result.content[:100])
        print("===========")

asyncio.run(concurrent_scraping())

使用会话实现状态化抓取

# 第一次请求:登录
result = client.scrape(ScrapeConfig(
    url="https://web-scraping.dev/login",
    render_js=True,
    # 浏览器操作指令
    js_scenario=[
        {
            "fill": {
                "selector": "input[name='username']",
                "clear": True,
                "value": "user123"
            }
        },
        {
            "fill": {
                "selector": "input[name='password']",
                "clear": True,
                "value": "password"
            }
        },
        {
            "click": {
                "selector": "form > button[type='submit']"
            }
        },
        {
            "wait_for_navigation": {
                "timeout": 5000
            }
        }
    ],
    # 请求头设置
    headers={
        "cookie": "cookiesAccepted=true"
    },
    session="logged-in-session"
))

# 第二次请求:使用相同会话访问受保护页面
result = client.scrape(ScrapeConfig(
    url="https://web-scraping.dev/login",
    session="logged-in-session"
))

# 使用内置选择器访问内容
print(result.selector.css("div#secret-message::text").get())

缓存响应结果

result = client.scrape(ScrapeConfig(
    url="https://example.com",
    cache=True,
    cache_ttl=3600,  # 缓存时长为 1 小时
))

错误处理

from scrapfly import ScrapflyClient, ScrapeConfig
from scrapfly.errors import (
    ScrapflyError,
    UpstreamHttpClientError,
    UpstreamHttpServerError,
    ScrapflyProxyError,
    ScrapflyThrottleError,
)

try:
    result = client.scrape(ScrapeConfig(url="https://httpbin.dev", asp=True))
except ScrapflyThrottleError as e:
    print(f"请求过于频繁,建议在 {e.retry_delay} 秒后重试")
except UpstreamHttpClientError as e:
    print(f"目标服务器返回 4xx 错误:{e.message}")
except UpstreamHttpServerError as e:
    print(f"目标服务器返回 5xx 错误:{e.message}")
except ScrapflyProxyError as e:
    print(f"代理错误:{e.message}")
except ScrapflyError as e:
    print(f"Scrapfly 错误:{e.message}")

重要注意事项

  • render_js=True 是使用 rendering_waitwait_for_selectorjs_scenarioscreenshots 等功能的前提
  • 使用 asp=True 时,推荐搭配 proxy_pool="public_residential_pool" 以获得更真实的访问行为
  • 使用 format="markdown" 可获取结构清晰的内容,适合用于大模型处理
  • 使用 session 参数可在多个请求间保持状态(如登录状态)
  • concurrent_scrape 方法会自动处理速率限制
S
@scrapfly

已收录 3 个 Skill

相关推荐