Caso de uso: LeadFlow CRM — scraping paginado de empresas en InfoJobs, exportación JSON/CSV lista para importar, auditoría anti-detección y muestra de 20 leads extraídos.
#!/usr/bin/env python3 """ Auto-generated Playwright scraping script. Target: https://www.infojobs.net/empresas Generated: 2026-06-12T18:11:22 Client: LeadFlow CRM — extraccion de leads para SDRs """ import asyncio, json, random, csv, os from playwright.async_api import async_playwright # ── Configuración ────────────────────────────────────────── URL = "https://www.infojobs.net/empresas" CONTAINER = ".company-card" FIELDS = { "company_name": ".company-name", "company_sector": ".company-sector", "company_city": ".company-city", "company_size": ".company-size", "company_url": ".company-url a[href]", } NEXT_SELECTOR = "button[aria-label='Siguiente página']" MAX_PAGES = 100 USER_AGENTS = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120.0.0.0", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0.0.0", ] # ── Extracción de items por página ───────────────────────── async def extract_items(page, container_sel, field_map): items = [] cards = await page.query_selector_all(container_sel) for card in cards: item = {} for name, selector in field_map.items(): el = await card.query_selector(selector) if el: item[name] = (await el.text_content() or "").strip() else: item[name] = None items.append(item) return items # ── Paginación con delay humano ──────────────────────────── async def scrape_all_pages(page, max_pages=MAX_PAGES): all_items = [] for page_num in range(max_pages): print(f"Scraping página {page_num + 1}...") items = await extract_items(page, CONTAINER, FIELDS) all_items.extend(items) next_btn = page.locator(NEXT_SELECTOR) if await next_btn.count() == 0 or await next_btn.is_disabled(): break await next_btn.click() await page.wait_for_load_state("networkidle") await asyncio.sleep(random.uniform(0.8, 2.5)) # delay humano return all_items # ── Main ─────────────────────────────────────────────────── async def main(): async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context( viewport={"width": 1920, "height": 1080}, user_agent=random.choice(USER_AGENTS), ) page = await context.new_page() # Eliminar flag navigator.webdriver await page.add_init_script( "Object.defineProperty(navigator,'webdriver',{get:()=>undefined});" ) await page.goto(URL, wait_until="networkidle") data = await scrape_all_pages(page) # Exportar JSON with open("leads.json", "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) # Exportar CSV para LeadFlow CRM import if data: with open("leads.csv", "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=data[0].keys()) writer.writeheader() writer.writerows(data) print(f"✓ {len(data)} empresas extraídas → leads.json + leads.csv") await browser.close() if __name__ == "__main__": asyncio.run(main())
plugins, languages y platform.random.choice(). Buena práctica.navigator.webdriver sobreescrito a undefined via add_init_script(). Check crítico superado.random.uniform(0.8, 2.5) entre páginas. Patrón de comportamiento humano detectado.| # | Empresa | Sector | Ciudad | Tamaño | URL |
|---|---|---|---|---|---|
| 001 | Factorial HR | SaaS / RRHH | Barcelona | 201–500 | factorial.es |
| 002 | Holaluz | Energía | Barcelona | 51–200 | holaluz.com |
| 003 | Cobee | Fintech / RRHH | Madrid | 11–50 | cobee.io |
| 004 | Paack | Logística Tech | Barcelona | 201–500 | paack.co |
| 005 | TravelPerk | SaaS / Travel | Barcelona | 501–1000 | travelperk.com |
| 006 | Glovo | Delivery / Tech | Barcelona | +1000 | glovoapp.com |
| 007 | Signaturit | LegalTech | Barcelona | 51–200 | signaturit.com |
| 008 | Qonto España | Fintech | Madrid | 51–200 | qonto.com |
| 009 | Habitissimo | Marketplace | Palma | 51–200 | habitissimo.es |
| 010 | Plenitude | Energía | Madrid | 201–500 | plenitude.com |
| 011 | Incode Technologies | Identity Tech | Madrid | 51–200 | incode.com |
| 012 | Typeform | SaaS / Forms | Barcelona | 201–500 | typeform.com |
| 013 | Clarity AI | FinTech / ESG | Madrid | 51–200 | clarity.ai |
| 014 | Flywire | Fintech / Pagos | Barcelona | 201–500 | flywire.com |
| 015 | Verifai | RegTech | Valencia | 11–50 | verifai.com |
| 016 | Sennelier ES | Retail / B2B | Sevilla | 11–50 | sennelier.es |
| 017 | Amenitiz | HotelTech | Barcelona | 51–200 | amenitiz.io |
| 018 | Pockyt | Payments | Madrid | 11–50 | pockyt.io |
| 019 | Nuclia | AI / Search | Barcelona | 11–50 | nuclia.com |
| 020 | Bdeo | InsurTech | Madrid | 51–200 | bdeo.co |
leads.json y leads.csv listos para importar en LeadFlow CRM
# LeadFlow CRM — scraping diario 07:00 0 7 * * 1-5 cd /home/deploy/leadflow && \ HEADLESS=true python3 leadflow_scraper.py \ >> logs/scraper.log 2>&1
4 SDRs × 2.5h/día de búsqueda manual = 10h de trabajo eliminadas diariamente. A 25 €/h eso son 250 €/día ahorrados — 65.000 €/año. El script se ejecuta en ~40s con 0 intervención humana. Coste de implementación: 1–2h de setup inicial.