Ctx Scrapling¶
ctx_scrapling -- mvp.ctx_scrapling
Cluster: Context & Retrieval | Type: component | MCP Tools: 25
Overview¶
Resilient web scraper with a two-tier fallback chain: Scrapling (JS rendering + anti-bot evasion) when installed, then httpx + regex HTML processing. Supports CSS selector targeting, plain-text extraction, and Markdown conversion via an internal regex transformer. Both backends honour a configurable timeout and return a structured ScrapeOutput with URL, title, content, content type, and character count.
When to use:
- Fetching live web content for a retrieval or grounding pipeline
- Extracting article text or documentation from URLs without a separate scraping service
- Getting Markdown-formatted content from HTML pages for downstream LLM consumption
Outbound URL safety and deployment egress
ctx_scrapling performs SSRF-oriented URL safety checks before outbound fetches. Both the direct block and MCP fetch/crawl paths reject non-HTTP schemes, localhost, literal non-public IPs, DNS names that resolve to non-public IPs, and redirects to non-public targets. DNS failures fail closed.
This code-level guard reduces common SSRF and cloud-metadata risks, but it should not be the only control in production. Hosted or multi-user deployments should also enforce network-layer egress policy, for example blocking RFC1918 ranges, link-local metadata endpoints, loopback, and internal service networks from the container, VM, or VPC firewall layer.
Example:
from mvp.ctx_scrapling import CtxScraplingBlock, ScrapeInput
block = CtxScraplingBlock(name="scraper")
result = block.infer(ScrapeInput(url="https://example.com", extract_markdown=True))
# result.value.content → Markdown text; result.value.title → page title
Works well with: ctx_markitdown, ctx_langextract, ctx_rag
Public API¶
CtxScraplingDecisionError(ValueError)¶
The LLM did not produce a usable, validated search-rerank decision.
ScraplingRerankDecision¶
Validated advisory rerank verdict over a returned search-record set.
| Field | Type | Default |
|---|---|---|
ordered_indices | tuple[int, ...] | required |
dropped_indices | tuple[int, ...] | () |
rationale | str | '' |
eligible_fingerprint | str | '' |
confidence | float | 0.0 |
degraded | bool | False |
raw_response | str | '' |
LLMScraplingRerankRuntime¶
Provider-neutral search-result-rerank runtime backed by G6's LLM caller.
Constructor:
| Parameter | Type | Default |
|---|---|---|
llm | LLMCaller \| None | None |
Methods:
rerank(query: str, records: list[Any]) -> ScraplingRerankDecision¶
CtxScraplingRerankPatternRuntime¶
Stateless, load-bearing returned-set-ceiling enforcement.
Methods:
enforce_eligibility(decision: ScraplingRerankDecision, records: list[Any]) -> tuple[list[Any], bool, bool]¶
CtxScraplingPlanner¶
Runtime-first advisory result-rerank facade with returned-order fallback.
Constructor:
| Parameter | Type | Default |
|---|---|---|
runtime | ScraplingRerankRuntime \| None | None |
pattern_runtime | CtxScraplingRerankPatternRuntime \| None | None |
Methods:
rerank(query: str, records: list[Any]) -> list[Any]¶
ScrapeInput(BaseModel)¶
Input to CtxScraplingBlock.
| Field | Type | Default |
|---|---|---|
url | str | required |
selector | str | '' |
extract_markdown | bool | True |
timeout_sec | float | 10.0 |
run_mode | Literal['beta', 'production'] | 'beta' |
reviewer_signature | str | '' |
ScrapeOutput(BaseModel)¶
Output from CtxScraplingBlock.
| Field | Type | Default |
|---|---|---|
url | str | required |
title | str | '' |
content | str | required |
content_type | str | 'text' |
char_count | int | required |
degraded | bool | False |
degradation_reason | str \| None | None |
completion_state | Literal['verified', 'qualified-draft', 'blocked-escalated'] | 'qualified-draft' |
warning_card | dict[str, Any] | Field(default_factory=dict) |
evidence | dict[str, Any] | Field(default_factory=dict) |
request_id | str | '' |
task_id | str | '' |
run_id | str | '' |
backend_attempted | str | '' |
backend_used | str | '' |
CtxScraplingBlock(AIBlock[ScrapeInput, ScrapeOutput, None])¶
Resilient web scraping block.
| Field | Type | Default |
|---|---|---|
name | str | 'ctx_scrapling' |
resource_bounds | ResourceBounds \| None | None |
usage | ResourceUsage | field(default_factory=ResourceUsage) |
Methods:
infer(data: ScrapeInput) -> Result[ScrapeOutput]¶
MCPScraplingRecord(BaseModel)¶
| Field | Type | Default |
|---|---|---|
id | str | required |
record_type | str | required |
key | str | required |
value | str | required |
tags | list[str] | Field(default_factory=list) |
timestamp | str | required |
metadata | dict[str, Any] | Field(default_factory=dict) |
MCPScraplingInput(BaseModel)¶
| Field | Type | Default |
|---|---|---|
op | Literal['fetch_static', 'fetch_dynamic', 'fetch_stealth', 'fetch_post', 'fetch_multi', 'parse_css', 'parse_xpath', 'parse_text_search', 'parse_regex', 'parse_table', 'crawl_start', 'crawl_page', 'crawl_status', 'crawl_results', 'crawl_stop', 'store_page', 'retrieve_page', 'query_pages', 'store_selector', 'search_store', 'extract_links', 'extract_metadata', 'auto_selector', 'smart_extract', 'scrapling_info'] | required |
url | str | '' |
urls | list[str] | Field(default_factory=list) |
headers | dict[str, str] | Field(default_factory=dict) |
body | str | '' |
timeout | float | 30.0 |
fetcher_type | str | 'static' |
html | str | '' |
selector | str | '' |
pattern | str | '' |
text_query | str | '' |
session_id | str | '' |
allowed_domains | list[str] | Field(default_factory=list) |
max_depth | int | 3 |
max_pages | int | 100 |
start_url | str | '' |
page_id | str | '' |
title | str | '' |
content | str | '' |
content_type | str | '' |
status_code | int | 200 |
domain | str | '' |
tags | list[str] | Field(default_factory=list) |
metadata_json | str | '{}' |
selector_name | str | '' |
selector_type | str | 'css' |
selector_value | str | '' |
description | str | '' |
query | str | '' |
limit | int | 50 |
top_k | int | 5 |
offset | int | 0 |
fields | list[str] | Field(default_factory=list) |
domain_filter | str | '' |
extraction_prompt | str | '' |
agentic_rerank | bool \| None | None |
run_mode | Literal['beta', 'production'] | 'beta' |
reviewer_signature | str | '' |
MCPScraplingOutput(BaseModel)¶
| Field | Type | Default |
|---|---|---|
op | str | required |
key | str | '' |
value | str | '' |
found | bool | False |
count | int | 0 |
records | list[MCPScraplingRecord] | Field(default_factory=list) |
content | str | '' |
title | str | '' |
url | str | '' |
status_code | int | 0 |
links | list[str] | Field(default_factory=list) |
items | list[dict[str, Any]] | Field(default_factory=list) |
selector_generated | str | '' |
summary | str | '' |
message | str | '' |
metadata | dict[str, Any] | Field(default_factory=dict) |
degraded | bool | False |
degradation_reason | str | '' |
completion_state | Literal['verified', 'qualified-draft', 'blocked-escalated'] | 'qualified-draft' |
warning_card | dict[str, Any] | Field(default_factory=dict) |
evidence | dict[str, Any] | Field(default_factory=dict) |
request_id | str | '' |
task_id | str | '' |
run_id | str | '' |
agentic_evidence | dict[str, Any] | Field(default_factory=dict) |
CtxScraplingMCPBlock(AIBlock[MCPScraplingInput, MCPScraplingOutput, dict])¶
Full-featured web scraping block with SQLite persistence.
| Field | Type | Default |
|---|---|---|
name | str | 'ctx_scrapling_mcp' |
state | dict \| None | None |
db_path | str | ':memory:' |
resource_bounds | ResourceBounds \| None | None |
usage | ResourceUsage | field(default_factory=ResourceUsage) |
agentic_planner | CtxScraplingPlanner \| None | None |
Methods:
infer(data: MCPScraplingInput) -> Result[MCPScraplingOutput]¶
ScraplingStore¶
Sync SQLite scrapling store with 8 tables.
Constructor:
| Parameter | Type | Default |
|---|---|---|
db_path | str | ':memory:' |
Methods:
add_page(url: str, domain: str, title: str, html: str, content: str, content_type: str, status_code: int, fetcher_type: str, tags: list[str], metadata_json: str = '{}') -> str¶
get_page(url: str = '', page_id: str = '') -> dict[str, Any] | None¶
query_pages(domain: str = '', tags: list[str] | None = None, query: str = '', limit: int = 50) -> list[dict[str, Any]]¶
add_item(page_id: str, url: str, selector: str, data: dict, extraction_method: str, tags: list[str]) -> str¶
query_items(page_id: str = '', limit: int = 50) -> list[dict[str, Any]]¶
upsert_selector(name: str, selector_type: str, selector_value: str, description: str, domain: str, tags: list[str]) -> str¶
get_selector(name: str) -> dict[str, Any] | None¶
query_selectors(domain: str = '', limit: int = 50) -> list[dict[str, Any]]¶
create_session(start_url: str, allowed_domains: list[str], max_depth: int, max_pages: int, fetcher_type: str, config_json: str = '{}') -> str¶
get_session(session_id: str) -> dict[str, Any] | None¶
pop_queue(session_id: str) -> dict[str, Any] | None¶
complete_queue_item(queue_id: str) -> None¶
add_to_queue(session_id: str, url: str, depth: int, parent_url: str) -> str¶
increment_session_pages(session_id: str) -> None¶
increment_session_items(session_id: str, count: int = 1) -> None¶
stop_session(session_id: str) -> None¶
get_queue_stats(session_id: str) -> dict[str, int]¶
add_crawl_item(session_id: str, url: str, data: dict) -> str¶
query_crawl_items(session_id: str, limit: int = 50, offset: int = 0) -> list[dict[str, Any]]¶
add_link(source_url: str, target_url: str, anchor_text: str, domain: str, session_id: str = '') -> str¶
query_links(source_url: str = '', domain: str = '', limit: int = 50) -> list[dict[str, Any]]¶
upsert_extraction_rule(name: str, description: str, url_pattern: str, selector_value: str, selector_type: str, output_fields: list[str], domain: str, tags: list[str]) -> str¶
text_search(query: str, top_k: int = 5) -> list[dict[str, Any]]¶
TF-IDF search across pages and selectors.
count_all() -> dict[str, int]¶
Functions¶
eligible_fingerprint(records: list[Any]) -> str¶
sha256 over the returned record texts (order-sensitive).
validate_scrapling_rerank_decision(decision: ScraplingRerankDecision, n_eligible: int, expected_fingerprint: str) -> None¶
Returned-set / anti-injection guard for a search-rerank decision.
planner_is_llm_trusted(planner: Any) -> bool¶
Whether the BLOCK may report
llm_used=Trueforplanner.
MCP Tools¶
| Operation | Source |
|---|---|
fetch_static | scrapling_mcp |
fetch_dynamic | scrapling_mcp |
fetch_stealth | scrapling_mcp |
fetch_post | scrapling_mcp |
fetch_multi | scrapling_mcp |
parse_css | scrapling_mcp |
parse_xpath | scrapling_mcp |
parse_text_search | scrapling_mcp |
parse_regex | scrapling_mcp |
parse_table | scrapling_mcp |
crawl_start | scrapling_mcp |
crawl_page | scrapling_mcp |
crawl_status | scrapling_mcp |
crawl_results | scrapling_mcp |
crawl_stop | scrapling_mcp |
store_page | scrapling_mcp |
retrieve_page | scrapling_mcp |
query_pages | scrapling_mcp |
store_selector | scrapling_mcp |
search_store | scrapling_mcp |
extract_links | scrapling_mcp |
extract_metadata | scrapling_mcp |
auto_selector | scrapling_mcp |
smart_extract | scrapling_mcp |
scrapling_info | scrapling_mcp |