提取 JSON(无 LLM)

Crawl4AI 最强大的功能之一是无需依赖大型语言模型即可从网站中提取结构化 JSON。Crawl4AI 提供了几种无需 LLM 的提取策略:

  1. 通过 CSS 或 XPath 选择器进行基于模式的提取JsonCssExtractionStrategyJsonXPathExtractionStrategy
  2. 正则表达式提取RegexExtractionStrategy用于快速模式匹配

这些方法让您可以立即提取数据(即使是从复杂或嵌套的 HTML 结构中提取数据),而无需 LLM 的成本、延迟或环境影响。

为什么要避免使用 LLM 进行基本提取?

  1. 更快、更便宜:无需 API 调用或 GPU 开销。
  2. 更低的碳足迹:LLM 推理可能耗能。基于模式的提取几乎是零碳排放的。
  3. 精准且可重复:CSS/XPath 选择器和正则表达式模式会完全按照您的指定执行。LLM 的输出可能会有所不同,甚至可能产生幻觉。
  4. 轻松扩展:对于数千页,基于模式的提取可以快速并行运行。

下面,我们将探讨如何创建这些模式,并将它们与 JsonCssExtractionStrategy(如果您更喜欢 XPath,则可以使用 JsonXPathExtractionStrategy)结合使用。我们还将重点介绍嵌套字段和基本元素属性等高级功能。


1. 基于模式的提取简介

模式定义:

  1. 标识页面上每个“容器”元素(例如,产品行、博客文章卡)的基本选择器。
  2. 描述要捕获的每条数据(文本、属性、HTML 块等)要使用哪些 CSS/XPath 选择器的字段。
  3. 嵌套或列表类型适用于重复或分层结构。

例如,如果您有一个产品列表,每个产品可能都有名称、价格、评论和“相关产品”。对于一致、结构化的页面来说,这种方法比 LLM 更快、更可靠。


2. 简单示例:加密货币价格

让我们从一个简单的基于模式的提取开始,使用JsonCssExtractionStrategy下面是一个从网站提取加密货币价格的代码片段(类似于旧版 Coinbase 示例)。请注意,我们没有调用任何 LLM:

import json
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode
from crawl4ai import JsonCssExtractionStrategy

async def extract_crypto_prices():
    # 1. Define a simple extraction schema
    schema = {
        "name": "Crypto Prices",
        "baseSelector": "div.crypto-row",    # Repeated elements
        "fields": [
            {
                "name": "coin_name",
                "selector": "h2.coin-name",
                "type": "text"
            },
            {
                "name": "price",
                "selector": "span.coin-price",
                "type": "text"
            }
        ]
    }

    # 2. Create the extraction strategy
    extraction_strategy = JsonCssExtractionStrategy(schema, verbose=True)

    # 3. Set up your crawler config (if needed)
    config = CrawlerRunConfig(
        # e.g., pass js_code or wait_for if the page is dynamic
        # wait_for="css:.crypto-row:nth-child(20)"
        cache_mode = CacheMode.BYPASS,
        extraction_strategy=extraction_strategy,
    )

    async with AsyncWebCrawler(verbose=True) as crawler:
        # 4. Run the crawl and extraction
        result = await crawler.arun(
            url="https://example.com/crypto-prices",

            config=config
        )

        if not result.success:
            print("Crawl failed:", result.error_message)
            return

        # 5. Parse the extracted JSON
        data = json.loads(result.extracted_content)
        print(f"Extracted {len(data)} coin entries")
        print(json.dumps(data[0], indent=2) if data else "No data found")

asyncio.run(extract_crypto_prices())

亮点:

  • :告诉我们每个“项目”(加密行)在哪里。
  • :两个字段(coin_nameprice ) 使用简单的 CSS 选择器。
  • 每个字段定义一个type(例如,textattributehtmlregex , ETC。)。

不需要法学硕士学位,数百或数千个项目的性能几乎是即时的。


XPath 示例raw://HTML

下面是一个简短的示例,演示了 XPath 提取以及raw://方案。我们将直接传递一个虚拟 HTML(无网络请求),并在CrawlerRunConfig

import json
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai import JsonXPathExtractionStrategy

async def extract_crypto_prices_xpath():
    # 1. Minimal dummy HTML with some repeating rows
    dummy_html = """
    <html>
      <body>
        <div class='crypto-row'>
          <h2 class='coin-name'>Bitcoin</h2>
          <span class='coin-price'>$28,000</span>
        </div>
        <div class='crypto-row'>
          <h2 class='coin-name'>Ethereum</h2>
          <span class='coin-price'>$1,800</span>
        </div>
      </body>
    </html>
    """

    # 2. Define the JSON schema (XPath version)
    schema = {
        "name": "Crypto Prices via XPath",
        "baseSelector": "//div[@class='crypto-row']",
        "fields": [
            {
                "name": "coin_name",
                "selector": ".//h2[@class='coin-name']",
                "type": "text"
            },
            {
                "name": "price",
                "selector": ".//span[@class='coin-price']",
                "type": "text"
            }
        ]
    }

    # 3. Place the strategy in the CrawlerRunConfig
    config = CrawlerRunConfig(
        extraction_strategy=JsonXPathExtractionStrategy(schema, verbose=True)
    )

    # 4. Use raw:// scheme to pass dummy_html directly
    raw_url = f"raw://{dummy_html}"

    async with AsyncWebCrawler(verbose=True) as crawler:
        result = await crawler.arun(
            url=raw_url,
            config=config
        )

        if not result.success:
            print("Crawl failed:", result.error_message)
            return

        data = json.loads(result.extracted_content)
        print(f"Extracted {len(data)} coin rows")
        if data:
            print("First item:", data[0])

asyncio.run(extract_crypto_prices_xpath())

要点:

  1. 用来代替JsonCssExtractionStrategy
  2. 以及每个字段的"selector"使用 XPath 而不是 CSS。
  3. 让我们通过dummy_html无需真正的网络请求——方便本地测试。
  4. 一切(包括提取策略)都在CrawlerRunConfig

这样,您就可以保持配置的独立性,说明 XPath 用法,并演示直接 HTML 输入的原始方案——同时避免传递extraction_strategy直接arun()


3. 高级模式和嵌套结构

实际网站通常会包含嵌套或重复的数据,例如包含产品的类别,而产品本身又包含评论或功能列表。为此,我们可以定义嵌套或列表(甚至是 nested_list)字段。

电子商务 HTML 示例

我们在 GitHub 上有一个示例电子商务 HTML 文件(示例):

https://gist.githubusercontent.com/githubusercontent/2d7b8ba3cd8ab6cf3c8da771ddb36878/raw/1ae2f90c6861ce7dd84cc50d3df9920dee5e1fd2/sample_ecommerce.html
This snippet includes categories, products, features, reviews, and related items. Let's see how to define a schema that fully captures that structure without LLM.

schema = {
    "name": "E-commerce Product Catalog",
    "baseSelector": "div.category",
    # (1) We can define optional baseFields if we want to extract attributes 
    # from the category container
    "baseFields": [
        {"name": "data_cat_id", "type": "attribute", "attribute": "data-cat-id"}, 
    ],
    "fields": [
        {
            "name": "category_name",
            "selector": "h2.category-name",
            "type": "text"
        },
        {
            "name": "products",
            "selector": "div.product",
            "type": "nested_list",    # repeated sub-objects
            "fields": [
                {
                    "name": "name",
                    "selector": "h3.product-name",
                    "type": "text"
                },
                {
                    "name": "price",
                    "selector": "p.product-price",
                    "type": "text"
                },
                {
                    "name": "details",
                    "selector": "div.product-details",
                    "type": "nested",  # single sub-object
                    "fields": [
                        {
                            "name": "brand",
                            "selector": "span.brand",
                            "type": "text"
                        },
                        {
                            "name": "model",
                            "selector": "span.model",
                            "type": "text"
                        }
                    ]
                },
                {
                    "name": "features",
                    "selector": "ul.product-features li",
                    "type": "list",
                    "fields": [
                        {"name": "feature", "type": "text"} 
                    ]
                },
                {
                    "name": "reviews",
                    "selector": "div.review",
                    "type": "nested_list",
                    "fields": [
                        {
                            "name": "reviewer", 
                            "selector": "span.reviewer", 
                            "type": "text"
                        },
                        {
                            "name": "rating", 
                            "selector": "span.rating", 
                            "type": "text"
                        },
                        {
                            "name": "comment", 
                            "selector": "p.review-text", 
                            "type": "text"
                        }
                    ]
                },
                {
                    "name": "related_products",
                    "selector": "ul.related-products li",
                    "type": "list",
                    "fields": [
                        {
                            "name": "name", 
                            "selector": "span.related-name", 
                            "type": "text"
                        },
                        {
                            "name": "price", 
                            "selector": "span.related-price", 
                            "type": "text"
                        }
                    ]
                }
            ]
        }
    ]
}

关键要点:

  • 嵌套与列表:
  • 表示单个子对象(如details)。
  • 表示多个项目,包括简单的字典或单个文本字段。
  • 意味着重复的复杂对象(如products或者reviews)。
  • 基本字段:我们可以通过以下方式从容器元素中提取属性"baseFields"。 例如,"data_cat_id"可能是data-cat-id="elect123"
  • 变换:我们还可以定义一个transform如果我们想要小写/大写,去除空格,甚至运行自定义函数。

运行提取

import json
import asyncio
from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
from crawl4ai import JsonCssExtractionStrategy

ecommerce_schema = {
    # ... the advanced schema from above ...
}

async def extract_ecommerce_data():
    strategy = JsonCssExtractionStrategy(ecommerce_schema, verbose=True)

    config = CrawlerRunConfig()

    async with AsyncWebCrawler(verbose=True) as crawler:
        result = await crawler.arun(
            url="https://gist.githubusercontent.com/githubusercontent/2d7b8ba3cd8ab6cf3c8da771ddb36878/raw/1ae2f90c6861ce7dd84cc50d3df9920dee5e1fd2/sample_ecommerce.html",
            extraction_strategy=strategy,
            config=config
        )

        if not result.success:
            print("Crawl failed:", result.error_message)
            return

        # Parse the JSON output
        data = json.loads(result.extracted_content)
        print(json.dumps(data, indent=2) if data else "No data found.")

asyncio.run(extract_ecommerce_data())

如果一切顺利,您将获得一个结构化的 JSON 数组,每个“类别”都包含一个数组products. 每件产品包括detailsfeaturesreviews等。所有这些都不需要法学硕士学位。


4. RegexExtractionStrategy - 基于模式的快速提取

Crawl4AI 现在提供强大的新零 LLM 提取策略:RegexExtractionStrategy 。该策略使用预编译的正则表达式,可以快速提取电子邮件、电话号码、URL、日期等常见数据类型。

主要特点

  • 零 LLM 依赖:无需任何 AI 模型调用即可提取数据
  • 极快:使用预编译的正则表达式模式来实现最佳性能
  • 内置模式:包括常见数据类型的现成模式
  • 自定义模式:添加您自己的正则表达式模式以进行特定于域的提取
  • LLM 辅助模式生成:可选择使用一次 LLM 来生成优化模式,然后重复使用它们而无需进一步调用 LLM

简单示例:提取公共实体

最简单的开始方式是使用内置模式目录:

import json
import asyncio
from crawl4ai import (
    AsyncWebCrawler,
    CrawlerRunConfig,
    RegexExtractionStrategy
)

async def extract_with_regex():
    # Create a strategy using built-in patterns for URLs and currencies
    strategy = RegexExtractionStrategy(
        pattern = RegexExtractionStrategy.Url | RegexExtractionStrategy.Currency
    )

    config = CrawlerRunConfig(extraction_strategy=strategy)

    async with AsyncWebCrawler() as crawler:
        result = await crawler.arun(
            url="https://example.com",
            config=config
        )

        if result.success:
            data = json.loads(result.extracted_content)
            for item in data[:5]:  # Show first 5 matches
                print(f"{item['label']}: {item['value']}")
            print(f"Total matches: {len(data)}")

asyncio.run(extract_with_regex())

可用的内置模式

提供这些常见模式作为 IntFlag 属性,以便于组合:

# Use individual patterns
strategy = RegexExtractionStrategy(pattern=RegexExtractionStrategy.Email)

# Combine multiple patterns
strategy = RegexExtractionStrategy(
    pattern = (
        RegexExtractionStrategy.Email | 
        RegexExtractionStrategy.PhoneUS | 
        RegexExtractionStrategy.Url
    )
)

# Use all available patterns
strategy = RegexExtractionStrategy(pattern=RegexExtractionStrategy.All)

可用的模式包括:-Email - 电子邮件地址 -PhoneIntl - 国际电话号码 -PhoneUS - 美国格式的电话号码 -Url - HTTP/HTTPS URL -IPv4 IPv4地址IPv6IPv6地址Uuid- UUID -Currency - 货币价值(美元、欧元等)-Percentage - 百分比值 -Number - 数值 -DateIso - ISO 格式日期 -DateUS - 美国格式的日期 -Time24h - 24 小时格式时间 -PostalUS - 美国邮政编码 -PostalUK - 英国邮政编码 -HexColor - HTML 十六进制颜色代码 -TwitterHandle Twitter 账号Hashtag- 标签 -MacAddr - MAC地址 -Iban - 国际银行账号 -CreditCard - 信用卡号码

自定义图案示例

为了更有针对性的提取,您可以提供自定义模式:

import json
import asyncio
from crawl4ai import (
    AsyncWebCrawler,
    CrawlerRunConfig,
    RegexExtractionStrategy
)

async def extract_prices():
    # Define a custom pattern for US Dollar prices
    price_pattern = {"usd_price": r"\$\s?\d{1,3}(?:,\d{3})*(?:\.\d{2})?"}

    # Create strategy with custom pattern
    strategy = RegexExtractionStrategy(custom=price_pattern)
    config = CrawlerRunConfig(extraction_strategy=strategy)

    async with AsyncWebCrawler() as crawler:
        result = await crawler.arun(
            url="https://www.example.com/products",
            config=config
        )

        if result.success:
            data = json.loads(result.extracted_content)
            for item in data:
                print(f"Found price: {item['value']}")

asyncio.run(extract_prices())

LLM辅助模式生成

对于复杂或特定于站点的模式,您可以使用一次 LLM 来生成优化模式,然后保存并重复使用它而无需进一步的 LLM 调用:

import json
import asyncio
from pathlib import Path
from crawl4ai import (
    AsyncWebCrawler,
    CrawlerRunConfig,
    RegexExtractionStrategy,
    LLMConfig
)

async def extract_with_generated_pattern():
    cache_dir = Path("./pattern_cache")
    cache_dir.mkdir(exist_ok=True)
    pattern_file = cache_dir / "price_pattern.json"

    # 1. Generate or load pattern
    if pattern_file.exists():
        pattern = json.load(pattern_file.open())
        print(f"Using cached pattern: {pattern}")
    else:
        print("Generating pattern via LLM...")

        # Configure LLM
        llm_config = LLMConfig(
            provider="openai/gpt-4o-mini",
            api_token="env:OPENAI_API_KEY",
        )

        # Get sample HTML for context
        async with AsyncWebCrawler() as crawler:
            result = await crawler.arun("https://example.com/products")
            html = result.fit_html

        # Generate pattern (one-time LLM usage)
        pattern = RegexExtractionStrategy.generate_pattern(
            label="price",
            html=html,
            query="Product prices in USD format",
            llm_config=llm_config,
        )

        # Cache pattern for future use
        json.dump(pattern, pattern_file.open("w"), indent=2)

    # 2. Use pattern for extraction (no LLM calls)
    strategy = RegexExtractionStrategy(custom=pattern)
    config = CrawlerRunConfig(extraction_strategy=strategy)

    async with AsyncWebCrawler() as crawler:
        result = await crawler.arun(
            url="https://example.com/products",
            config=config
        )

        if result.success:
            data = json.loads(result.extracted_content)
            for item in data[:10]:
                print(f"Extracted: {item['value']}")
            print(f"Total matches: {len(data)}")

asyncio.run(extract_with_generated_pattern())

此模式允许您:1. 使用一次 LLM 为您的特定站点生成高度优化的正则表达式 2. 将模式保存到磁盘以供重复使用 3. 在生产中仅使用正则表达式(无需进一步的 LLM 调用)提取数据

提取结果格式

RegexExtractionStrategy以一致的格式返回结果:

[
  {
    "url": "https://example.com",
    "label": "email",
    "value": "contact@example.com",
    "span": [145, 163]
  },
  {
    "url": "https://example.com",
    "label": "url",
    "value": "https://support.example.com",
    "span": [210, 235]
  }
]

每场比赛包括:-url :源 URL -label :匹配的模式名称(例如“email”、“phone_us”) -value :提取的文本 -span :源内容中的开始和结束位置


5. 为什么“没有法学硕士”通常更好

  1. 零幻觉:基于模式的提取不会猜测文本。它要么找到,要么找不到。
  2. 保证结构:相同的模式或正则表达式在多个页面上产生一致的 JSON,因此您的下游管道可以依赖稳定的密钥。
  3. 速度:对于大规模爬取,基于 LLM 的提取速度可能会慢 10 到 1000 倍。
  4. 可扩展:添加或更新字段只需调整模式或正则表达式,而不是重新调整模型。

什么时候你会考虑攻读法学硕士?如果网站结构极其混乱,或者你需要人工智能摘要,那么就有可能。但对于重复或一致的数据模式,请务必先尝试使用模式或正则表达式。


6. 基本元素属性和附加字段

提取属性很容易(比如hrefsrc , 或者data-xxx)从您的基础或嵌套元素中使用:

{
  "name": "href",
  "type": "attribute",
  "attribute": "href",
  "default": null
}

您可以在baseFields(从主容器元素中提取)或每个字段的子列表中。如果您需要将项目的链接或 ID 存储在父级中,这将特别有用<div>


7. 整合:更大的示例

假设有一个博客网站。我们有一个模式,可以从每张帖子中提取 URL(通过baseFields"attribute": "href"),加上标题、日期、摘要和作者:

schema = {
  "name": "Blog Posts",
  "baseSelector": "a.blog-post-card",
  "baseFields": [
    {"name": "post_url", "type": "attribute", "attribute": "href"}
  ],
  "fields": [
    {"name": "title", "selector": "h2.post-title", "type": "text", "default": "No Title"},
    {"name": "date", "selector": "time.post-date", "type": "text", "default": ""},
    {"name": "summary", "selector": "p.post-summary", "type": "text", "default": ""},
    {"name": "author", "selector": "span.post-author", "type": "text", "default": ""}
  ]
}

然后运行JsonCssExtractionStrategy(schema)获取博客文章对象数组,每个对象都包含"post_url""title""date""summary""author"


8. 技巧与最佳实践

  1. 在 Chrome DevTools 或 Firefox 的 Inspector 中检查 DOM 以找到稳定的选择器。
  2. 从简单开始:验证是否可以提取单个字段。然后添加嵌套对象或列表等复杂性。
  3. 在进行大规模爬取之前,在部分 HTML 或测试页面上测试您的架构。
  4. 如果网站动态加载内容,请与 JS 执行结合使用。您可以通过js_code或者wait_forCrawlerRunConfig
  5. 查看日志verbose=True:如果您的选择器关闭或者您的模式格式不正确,它通常会显示警告。
  6. 如果需要容器元素的属性(例如,hrefdata-id ),尤其是对于“父”项。
  7. 性能:对于大页面,请确保选择器尽可能窄。
  8. 首先考虑使用正则表达式:对于电子邮件、URL 和日期等简单数据类型,RegexExtractionStrategy往往是最快的方法。

9. 模式生成实用程序

虽然手动创建模式功能强大且精确,但 Crawl4AI 现在提供了一个便捷的实用程序,可以使用 LLM 自动生成提取模式。这在以下情况下尤其有用:

  1. 您正在处理一个新的网站结构,并希望快速入门
  2. 您需要提取复杂的嵌套数据结构
  3. 您希望避免 CSS/XPath 选择器语法的学习曲线

使用模式生成器

模式生成器可作为静态方法在JsonCssExtractionStrategyJsonXPathExtractionStrategy。您可以选择 OpenAI 的 GPT-4 或开源 Ollama 进行模式生成:

from crawl4ai import JsonCssExtractionStrategy, JsonXPathExtractionStrategy
from crawl4ai import LLMConfig

# Sample HTML with product information
html = """
<div class="product-card">
    <h2 class="title">Gaming Laptop</h2>
    <div class="price">$999.99</div>
    <div class="specs">
        <ul>
            <li>16GB RAM</li>
            <li>1TB SSD</li>
        </ul>
    </div>
</div>
"""

# Option 1: Using OpenAI (requires API token)
css_schema = JsonCssExtractionStrategy.generate_schema(
    html,
    schema_type="css", 
    llm_config = LLMConfig(provider="openai/gpt-4o",api_token="your-openai-token")
)

# Option 2: Using Ollama (open source, no token needed)
xpath_schema = JsonXPathExtractionStrategy.generate_schema(
    html,
    schema_type="xpath",
    llm_config = LLMConfig(provider="ollama/llama3.3", api_token=None)  # Not needed for Ollama
)

# Use the generated schema for fast, repeated extractions
strategy = JsonCssExtractionStrategy(css_schema)

LLM 提供者选项

  1. OpenAI GPT-4(openai/gpt4o )
  2. 默认提供程序
  3. 需要 API 令牌
  4. 通常提供更准确的模式
  5. 通过环境变量设置:OPENAI_API_KEY
  6. 奥拉玛(ollama/llama3.3 )
  7. 开源替代方案
  8. 无需 API 令牌
  9. 自托管选项
  10. 适合开发和测试

模式生成的好处

  1. 一次性成本:虽然模式生成使用 LLM,但这只是一次性成本。生成的模式可以重复使用,进行无限次提取,无需进一步调用 LLM。
  2. 智能模式识别:LLM 分析 HTML 结构并识别常见模式,通常产生比手动尝试更强大的选择器。
  3. 自动嵌套:自动检测复杂的嵌套结构并在模式中正确表示。
  4. 学习工具:生成的模式是学习如何编写自己的模式的绝佳示例。

最佳实践

  1. 审查生成的模式:虽然生成器很智能,但在生产中使用生成的模式之前,请务必审查并测试它。
  2. 提供代表性 HTML:示例 HTML 越能代表整体结构,生成的模式就越准确。
  3. 同时考虑 CSS 和 XPath:尝试两种模式类型并选择最适合您特定情况的类型。
  4. 缓存生成的模式:由于生成使用 LLM,因此保存成功的模式以供重用。
  5. API 令牌安全:切勿对 API 令牌进行硬编码。请使用环境变量或安全配置管理。
  6. 明智地选择提供商:
  7. 使用 OpenAI 实现生产质量模式
  8. 使用 Ollama 进行开发、测试或需要自托管解决方案时

10. 结论

借助 Crawl4AI 的无 LLM 提取策略 -JsonCssExtractionStrategyJsonXPathExtractionStrategy ,现在RegexExtractionStrategy- 您可以构建强大的管道:

  • 抓取任何一致站点的结构化数据。
  • 支持嵌套对象、重复列表或基于模式的提取。
  • 快速可靠地扩展到数千页。

选择正确的策略:

  • 使用RegexExtractionStrategy用于快速提取常见数据类型,如电子邮件、电话、URL、日期等。
  • 使用JsonCssExtractionStrategy或者JsonXPathExtractionStrategy对于具有清晰 HTML 模式的结构化数据
  • 如果两者都需要:首先使用 JSON 策略提取结构化数据,然后在特定字段上使用正则表达式

请记住:对于重复的结构化数据,您无需付费或等待法学硕士学位 (LLM)。精心设计的架构和正则表达式模式可以让您更快、更干净、更经济地获取数据——这正是 Crawl4AI 的真正威力所在。

最后更新时间:2025年5月2日


以上就是 JSON 数据提取(无需法学硕士)的全部内容!您已经了解了基于模式的方法(CSS 或 XPath)和正则表达式如何能够处理从简单列表到深度嵌套的产品目录等各种数据——即时且开销极小。尽情享受构建强大的数据抓取工具,为您的数据管道生成一致、结构化的 JSON 数据吧!


> Feedback