276 lines
9.4 KiB
Python
276 lines
9.4 KiB
Python
"""
|
||
Example: Using Table Extraction Strategies in Crawl4AI
|
||
|
||
This example demonstrates how to use different table extraction strategies
|
||
to extract tables from web pages.
|
||
"""
|
||
|
||
import asyncio
|
||
import pandas as pd
|
||
from crawl4ai import (
|
||
AsyncWebCrawler,
|
||
CrawlerRunConfig,
|
||
CacheMode,
|
||
DefaultTableExtraction,
|
||
NoTableExtraction,
|
||
TableExtractionStrategy
|
||
)
|
||
from typing import Dict, List, Any
|
||
|
||
|
||
async def example_default_extraction():
|
||
"""Example 1: Using default table extraction (automatic)."""
|
||
print("\n" + "="*50)
|
||
print("Example 1: Default Table Extraction")
|
||
print("="*50)
|
||
|
||
async with AsyncWebCrawler() as crawler:
|
||
# No need to specify table_extraction - uses DefaultTableExtraction automatically
|
||
config = CrawlerRunConfig(
|
||
cache_mode=CacheMode.BYPASS,
|
||
table_score_threshold=7 # Adjust sensitivity (default: 7)
|
||
)
|
||
|
||
result = await crawler.arun(
|
||
"https://en.wikipedia.org/wiki/List_of_countries_by_GDP_(nominal)",
|
||
config=config
|
||
)
|
||
|
||
if result.success and result.tables:
|
||
print(f"Found {len(result.tables)} tables")
|
||
|
||
# Convert first table to pandas DataFrame
|
||
if result.tables:
|
||
first_table = result.tables[0]
|
||
df = pd.DataFrame(
|
||
first_table['rows'],
|
||
columns=first_table['headers'] if first_table['headers'] else None
|
||
)
|
||
print(f"\nFirst table preview:")
|
||
print(df.head())
|
||
print(f"Shape: {df.shape}")
|
||
|
||
|
||
async def example_custom_configuration():
|
||
"""Example 2: Custom table extraction configuration."""
|
||
print("\n" + "="*50)
|
||
print("Example 2: Custom Table Configuration")
|
||
print("="*50)
|
||
|
||
async with AsyncWebCrawler() as crawler:
|
||
# Create custom extraction strategy with specific settings
|
||
table_strategy = DefaultTableExtraction(
|
||
table_score_threshold=5, # Lower threshold for more permissive detection
|
||
min_rows=3, # Only extract tables with at least 3 rows
|
||
min_cols=2, # Only extract tables with at least 2 columns
|
||
verbose=True
|
||
)
|
||
|
||
config = CrawlerRunConfig(
|
||
cache_mode=CacheMode.BYPASS,
|
||
table_extraction=table_strategy,
|
||
# Target specific tables using CSS selector
|
||
css_selector="div.main-content"
|
||
)
|
||
|
||
result = await crawler.arun(
|
||
"https://example.com/data",
|
||
config=config
|
||
)
|
||
|
||
if result.success:
|
||
print(f"Found {len(result.tables)} tables matching criteria")
|
||
|
||
for i, table in enumerate(result.tables):
|
||
print(f"\nTable {i+1}:")
|
||
print(f" Caption: {table.get('caption', 'No caption')}")
|
||
print(f" Size: {table['metadata']['row_count']} rows × {table['metadata']['column_count']} columns")
|
||
print(f" Has headers: {table['metadata']['has_headers']}")
|
||
|
||
|
||
async def example_disable_extraction():
|
||
"""Example 3: Disable table extraction when not needed."""
|
||
print("\n" + "="*50)
|
||
print("Example 3: Disable Table Extraction")
|
||
print("="*50)
|
||
|
||
async with AsyncWebCrawler() as crawler:
|
||
# Use NoTableExtraction to skip table processing entirely
|
||
config = CrawlerRunConfig(
|
||
cache_mode=CacheMode.BYPASS,
|
||
table_extraction=NoTableExtraction() # No tables will be extracted
|
||
)
|
||
|
||
result = await crawler.arun(
|
||
"https://example.com",
|
||
config=config
|
||
)
|
||
|
||
if result.success:
|
||
print(f"Tables extracted: {len(result.tables)} (should be 0)")
|
||
print("Table extraction disabled - better performance for non-table content")
|
||
|
||
|
||
class FinancialTableExtraction(TableExtractionStrategy):
|
||
"""
|
||
Custom strategy for extracting financial tables with specific requirements.
|
||
"""
|
||
|
||
def __init__(self, currency_symbols=None, **kwargs):
|
||
super().__init__(**kwargs)
|
||
self.currency_symbols = currency_symbols or ['$', '€', '£', '¥']
|
||
|
||
def extract_tables(self, element, **kwargs):
|
||
"""Extract only tables that appear to contain financial data."""
|
||
tables_data = []
|
||
|
||
for table in element.xpath(".//table"):
|
||
# Check if table contains currency symbols
|
||
table_text = ''.join(table.itertext())
|
||
has_currency = any(symbol in table_text for symbol in self.currency_symbols)
|
||
|
||
if not has_currency:
|
||
continue
|
||
|
||
# Extract using base logic (could reuse DefaultTableExtraction logic)
|
||
headers = []
|
||
rows = []
|
||
|
||
# Extract headers
|
||
for th in table.xpath(".//thead//th | .//tr[1]//th"):
|
||
headers.append(th.text_content().strip())
|
||
|
||
# Extract rows
|
||
for tr in table.xpath(".//tbody//tr | .//tr[position()>1]"):
|
||
row = []
|
||
for td in tr.xpath(".//td"):
|
||
cell_text = td.text_content().strip()
|
||
# Clean currency values
|
||
for symbol in self.currency_symbols:
|
||
cell_text = cell_text.replace(symbol, '')
|
||
row.append(cell_text)
|
||
if row:
|
||
rows.append(row)
|
||
|
||
if headers or rows:
|
||
tables_data.append({
|
||
"headers": headers,
|
||
"rows": rows,
|
||
"caption": table.xpath(".//caption/text()")[0] if table.xpath(".//caption") else "",
|
||
"summary": table.get("summary", ""),
|
||
"metadata": {
|
||
"type": "financial",
|
||
"has_currency": True,
|
||
"row_count": len(rows),
|
||
"column_count": len(headers) if headers else len(rows[0]) if rows else 0
|
||
}
|
||
})
|
||
|
||
return tables_data
|
||
|
||
|
||
async def example_custom_strategy():
|
||
"""Example 4: Custom table extraction strategy."""
|
||
print("\n" + "="*50)
|
||
print("Example 4: Custom Financial Table Strategy")
|
||
print("="*50)
|
||
|
||
async with AsyncWebCrawler() as crawler:
|
||
# Use custom strategy for financial tables
|
||
config = CrawlerRunConfig(
|
||
cache_mode=CacheMode.BYPASS,
|
||
table_extraction=FinancialTableExtraction(
|
||
currency_symbols=['$', '€'],
|
||
verbose=True
|
||
)
|
||
)
|
||
|
||
result = await crawler.arun(
|
||
"https://finance.yahoo.com/",
|
||
config=config
|
||
)
|
||
|
||
if result.success:
|
||
print(f"Found {len(result.tables)} financial tables")
|
||
|
||
for table in result.tables:
|
||
if table['metadata'].get('type') == 'financial':
|
||
print(f" ✓ Financial table with {table['metadata']['row_count']} rows")
|
||
|
||
|
||
async def example_combined_extraction():
|
||
"""Example 5: Combine table extraction with other strategies."""
|
||
print("\n" + "="*50)
|
||
print("Example 5: Combined Extraction Strategies")
|
||
print("="*50)
|
||
|
||
from crawl4ai import LLMExtractionStrategy, LLMConfig
|
||
|
||
async with AsyncWebCrawler() as crawler:
|
||
# Define schema for structured extraction
|
||
schema = {
|
||
"type": "object",
|
||
"properties": {
|
||
"page_title": {"type": "string"},
|
||
"main_topic": {"type": "string"},
|
||
"key_figures": {
|
||
"type": "array",
|
||
"items": {"type": "string"}
|
||
}
|
||
}
|
||
}
|
||
|
||
config = CrawlerRunConfig(
|
||
cache_mode=CacheMode.BYPASS,
|
||
# Table extraction
|
||
table_extraction=DefaultTableExtraction(
|
||
table_score_threshold=6,
|
||
min_rows=2
|
||
),
|
||
# LLM extraction for structured data
|
||
extraction_strategy=LLMExtractionStrategy(
|
||
llm_config=LLMConfig(provider="openai"),
|
||
schema=schema
|
||
)
|
||
)
|
||
|
||
result = await crawler.arun(
|
||
"https://en.wikipedia.org/wiki/Economy_of_the_United_States",
|
||
config=config
|
||
)
|
||
|
||
if result.success:
|
||
print(f"Tables found: {len(result.tables)}")
|
||
|
||
# Tables are in result.tables
|
||
if result.tables:
|
||
print(f"First table has {len(result.tables[0]['rows'])} rows")
|
||
|
||
# Structured data is in result.extracted_content
|
||
if result.extracted_content:
|
||
import json
|
||
structured_data = json.loads(result.extracted_content)
|
||
print(f"Page title: {structured_data.get('page_title', 'N/A')}")
|
||
print(f"Main topic: {structured_data.get('main_topic', 'N/A')}")
|
||
|
||
|
||
async def main():
|
||
"""Run all examples."""
|
||
print("\n" + "="*60)
|
||
print("CRAWL4AI TABLE EXTRACTION EXAMPLES")
|
||
print("="*60)
|
||
|
||
# Run examples
|
||
await example_default_extraction()
|
||
await example_custom_configuration()
|
||
await example_disable_extraction()
|
||
await example_custom_strategy()
|
||
# await example_combined_extraction() # Requires OpenAI API key
|
||
|
||
print("\n" + "="*60)
|
||
print("EXAMPLES COMPLETED")
|
||
print("="*60)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main()) |