# Ahrefs Login & Scraping – funktionierender Ansatz ## Problem - `agent-browser` (CLI) wird von Cloudflare Turnstile auf app.ahrefs.com **nach dem Login** blockiert - Login-Seite selbst geht manchmal, aber Dashboard/interne Seiten → Cloudflare Challenge - Headless Browser ohne Anti-Detection werden erkannt ## Lösung: Playwright + playwright-stealth ### Installation (einmalig) ```bash pip install --break-system-packages playwright playwright-stealth playwright install chromium # Xvfb für headless mit Display: apt-get install -y xvfb ``` ### Login-Flow (state speichern) ```python import asyncio, json from playwright.async_api import async_playwright from playwright_stealth import Stealth async def main(): stealth = Stealth() async with stealth.use_async(async_playwright()) as p: browser = await p.chromium.launch(headless=True, args=['--no-sandbox']) ctx = await browser.new_context( user_agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36', viewport={'width': 1280, 'height': 900} ) page = await ctx.new_page() await page.goto('https://app.ahrefs.com/user/login', wait_until='networkidle', timeout=30000) await page.fill('input[name="email"]', 'mkiesewetter9@googlemail.com') await page.fill('input[name="password"]', 'mkiesewetter9@googlemail.com') await page.click('button[type="submit"]') await page.wait_for_url('**/dashboard**', timeout=15000) # State speichern für spätere Sessions await ctx.storage_state(path='/tmp/ahrefs-state.json') print('Logged in & state saved') await browser.close() asyncio.run(main()) ``` ### Daten abrufen (mit gespeichertem State) ```python import asyncio, json from playwright.async_api import async_playwright from playwright_stealth import Stealth async def get_table_data(page): """Scrollt und extrahiert Tabellen-Zeilen""" await page.evaluate('window.scrollTo(0, 800)') await page.wait_for_timeout(3000) rows = await page.evaluate('''() => { const rows = document.querySelectorAll('[class*="tableRow"], [class*="TableRow"], [role="row"], tr'); const data = []; rows.forEach(r => { const t = r.innerText.trim(); if (t && t.length > 5 && t.length < 500 && t.indexOf('Filter') === -1 && t.indexOf('Spalten') === -1) { data.push(t); } }); return data; }''') return rows async def main(): stealth = Stealth() async with stealth.use_async(async_playwright()) as p: browser = await p.chromium.launch(headless=True, args=['--no-sandbox']) ctx = await browser.new_context( user_agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36', viewport={'width': 1280, 'height': 900} ) # Gespeicherten State laden cookies = json.load(open('/tmp/ahrefs-state.json'))['cookies'] await ctx.add_cookies(cookies) page = await ctx.new_page() base = 'mode=subdomains&projectId=2032747&target=manonamission.de%2F&volume_type=monthly' # Beispiel: Top Pages await page.goto(f'https://app.ahrefs.com/site-explorer/top-pages?{base}', wait_until='networkidle', timeout=30000) await page.wait_for_timeout(5000) rows = await get_table_data(page) for r in rows: print(r) await browser.close() asyncio.run(main()) ``` ### Wichtige URLs (mit Projekt-Parametern) - **Dashboard:** `https://app.ahrefs.com/dashboard` - **Site Explorer Overview:** `/site-explorer/overview?{base}` - **Organic Keywords:** `/site-explorer/organic-keywords?{base}` - **Top Pages:** `/site-explorer/top-pages?{base}` - **Backlinks:** `/site-explorer/backlinks?{base}` - **Organic Competitors:** `/site-explorer/organic-competitors?{base}` - **Opportunities:** `/site-explorer/opportunities?{base}` - **Content Gap:** `/site-explorer/content-gap?{base}` ### Wichtige Parameter - `projectId=2032747` – manonamission.de Projekt-ID - `target=manonamission.de%2F` – Ziel-Domain - `mode=subdomains` – inkl. Subdomains - `volume_type=monthly` – monatliches Suchvolumen ### Xvfb (falls nötig) ```bash Xvfb :99 -screen 0 1280x1024x24 & export DISPLAY=:99 ``` Normalerweise nicht nötig – Playwright headless läuft ohne Display. ### Was NICHT funktioniert - ❌ `agent-browser` CLI → Cloudflare Block nach Login - ❌ `web_fetch` → kein JavaScript, useless für SPA - ❌ Direkte URL-Konstruktion ohne `projectId` → "Bitte überprüfe deine Domain" - ❌ `Stealth().use_async(browser.new_context(...))` → falsche Syntax - ✅ `Stealth().use_async(async_playwright())` → korrekt! ### State-Haltbarkeit - `/tmp/ahrefs-state.json` überlebt Reboots NICHT (tmpfs) - Nach Server-Reboot muss neu eingeloggt werden - Falls Session abgelaufen → Login-Flow wiederholen