[2026-06-12 09:10:55] [INFO] ============================================================
[2026-06-12 09:10:55] [INFO] Universal Scraping Architect — Hybrid Pipeline START
[2026-06-12 09:10:55] [INFO] ============================================================
[2026-06-12 09:10:55] [INFO] Routing: greenstack.io → Mode 2 (HTML static, BS4 local)
[2026-06-12 09:10:55] [INFO] Routing: organicos.app → Mode 1 (SPA/JS, Firecrawl API)
[2026-06-12 09:10:55] [INFO] Routing: ecopos.es → Mode 2 (HTML static, BS4 local)
[2026-06-12 09:11:00] [INFO] robots.txt greenstack.io → ALLOWED ✓
[2026-06-12 09:11:01] [INFO] Attempt 1/3: GET https://greenstack.io/pricing
[2026-06-12 09:11:02] [INFO] HTTP 200 — content length: 48,231 chars.
[2026-06-12 09:11:02] [INFO] Target table found: table#pricing-plans
[2026-06-12 09:11:02] [INFO] Parsed table: 4 rows, 6 columns.
[2026-06-12 09:11:02] [INFO] Columns after normalisation: ['plan_name', 'price_monthly_eur', 'price_annual_eur', 'max_skus', 'users_included', 'support_tier']
[2026-06-12 09:11:02] [INFO] Validation passed. 4 rows, 6 columns. ✓
[2026-06-12 09:12:10] [INFO] Firecrawl API call → organicos.app/planes (mode: scrape, formats: ['markdown','extract'])
[2026-06-12 09:12:38] [INFO] Firecrawl tokens used: 2,841 / budget: 10,000
[2026-06-12 09:12:41] [INFO] Parsed __NEXT_DATA__ pricing JSON. 4 plans found.
[2026-06-12 09:12:41] [INFO] Validation passed. 4 rows, 6 columns. ✓
[2026-06-12 09:13:55] [INFO] robots.txt ecopos.es → ALLOWED ✓
[2026-06-12 09:13:56] [INFO] Attempt 1/3: GET https://ecopos.es/tarifas
[2026-06-12 09:13:58] [INFO] HTTP 200 — content length: 31,774 chars.
[2026-06-12 09:13:58] [INFO] Target table found: table[data-id=tariff-compare]
[2026-06-12 09:13:59] [INFO] Parsed table: 4 rows, 6 columns. Validation passed. ✓
[2026-06-12 09:14:00] [INFO] Merging 3 DataFrames → unified competitor_pricing.csv
[2026-06-12 09:14:00] [INFO] Dedup check: 0 duplicates found.
[2026-06-12 09:14:01] [INFO] Running validate_extraction.py extraction_summary.json --json
[2026-06-12 09:14:01] [INFO] {"status": "ok", "message": "JSON is valid and well-formed."}
[2026-06-12 09:14:01] [INFO] Validator exit code: 0 ✓
[2026-06-12 09:14:01] [INFO] Output saved: competitor_pricing.csv (12 rows)
[2026-06-12 09:14:01] [INFO] Output saved: extraction_summary.json
[2026-06-12 09:14:01] [INFO] ============================================================
[2026-06-12 09:14:01] [INFO] EXTRACTION COMPLETE — 12 rows · 3 sources · exit 0
[2026-06-12 09:14:01] [INFO] ============================================================
📦 extraction_summary.json (validate_extraction.py → status: ok)
{
"pipeline_run": {
"timestamp": "2026-06-12T09:14:01Z",
"client": "LeafBox SaaS",
"objective": "competitor_pricing_monitoring",
"total_sources": 3,
"total_records": 12,
"duplicates": 0,
"validation_status": "ok",
"validator_exit_code": 0
},
"routing": {
"firecrawl_sources": ["organicos.app"],
"local_bs4_sources": ["greenstack.io", "ecopos.es"],
"firecrawl_tokens_used": 2841,
"api_cost_saved_pct": 66.7
},
"sources": [
{ "domain": "greenstack.io", "mode": "local_bs4", "plans": 4, "robots_ok": true, "status": "ok" },
{ "domain": "organicos.app", "mode": "firecrawl", "plans": 4, "robots_ok": true, "status": "ok" },
{ "domain": "ecopos.es", "mode": "local_bs4", "plans": 4, "robots_ok": true, "status": "ok" }
],
"output_files": {
"csv": "competitor_pricing.csv",
"log": "extraction_run.log",
"summary_json": "extraction_summary.json"
}
}