Google People Also Ask und Autocomplete für die Keyword-Recherche scrapen – Python-Leitfaden

Praktischer Leitfaden zum Scrapen von Google Autocomplete, People Also Ask und Related Searches mit Python, httpx und Playwright – inklusive ProxyHat-Setup, Dedup-Logik und CSV-Export für die Keyword-Recherche.

Scrape Google People Also Ask and Autocomplete for Keyword Research (Python Guide)

Wer People Also Ask und Autocomplete für die Keyword-Recherche scrapen will, greift auf drei kostenlose, aber extrem ergiebige Google-Datenquellen zu: die Autocomplete-Suggest-API, die PAA-Box (People Also Ask) und die Related-Searches-Leiste. Dieser Leitfaden zeigt, wie du mit Python, httpx und Playwright daraus hunderte Longtail-Keywords pro Seed generierst – und warum residential Proxies mit Geo-Targeting unverzichtbar sind, sobald du skaliert.

Warum People Also Ask und Autocomplete für die Keyword-Recherche scrapen?

Google veröffentlicht keine vollständigen Suchvolumina, aber die Autocomplete-Vorschläge, die PAA-Fragen und die Related Searches sind öffentlich sichtbar und spiegeln echtes Suchverhalten wider. Anders als bei kommerziellen Keyword-Tools zahlst du nichts pro Query und bekommst dafür die exakt gleichen Vorschläge, die ein echter Nutzer in der Google-Suche sieht. Für SEO-Teams, die FAQ-Schemata bauen, Content-Lücken finden oder Longtail-Cluster identifizieren wollen, sind diese drei Quellen die effizientesten freien Datenpunkte überhaupt.

Die drei Quellen lassen sich sauber nach Suchintenz unterscheiden:

QuelleEndpoint / DOMIntenz-SignalTypische Ausbeute pro Seed
Autocompletesuggestqueries.google.com/complete/search?client=chrome&q=Was Nutzer eintippen (informational + transactional)10 Vorschläge pro Buchstabenkombination
People Also AskSERP-Box mit Akkordeon-ElementenExplizite Fragen (FAQ-freundlich)4–8 initiale Fragen, rekursiv erweiterbar
Related SearchesUnterer SERP-BereichThematisch benachbarte Queries8 Vorschläge

Technischer Kontext: Warum Google diese Daten frei gibt – und trotzdem drosselt

Die Autocomplete-API ist technisch kein offizielles öffentliches API, sondern der Endpoint, den der Chrome-Browser selbst ansteuert, um Suchvorschläge zu laden. Die Struktur ist dokumentiertes Verhalten, kein Vertrag – Google kann das Format jederzeit ändern. Der Endpoint google.com liefert bei client=chrome ein kompaktes JSON mit UTF-8-kodierten Vorschlägen, während client=firefox ein etwas anderes Array-Layout zurückgibt.

PAA-Fragen sind Teil der regulären SERP und werden serverseitig gerendert. Jedes Akkordeon-Element lädt beim Aufklappen per XHR weitere Fragen nach – das ist der Grund, warum einfache requests-Aufrufe hier scheitern und ein Headless-Browser wie Playwright nötig wird. Die offizielle Playwright-Dokumentation beschreibt die page.click- und page.wait_for_selector-Muster, die du für das rekursive Aufklappen brauchst.

Das eigentliche Problem bei Skalierung: Google drosselt pro IP aggressiv. In der Praxis siehst du nach etwa 50–100 Requests pro IP innerhalb von 10 Minuten CAPTCHAs oder leere Responses. Hinzu kommt Locale-Bias: Autocomplete-Vorschläge unterscheiden sich stark je nach Land und Stadt. Ein Query „pizza“ liefert in Berlin andere Vorschläge als in Chicago. Wer sauber lokalisierte Keyword-Sets bauen will, braucht residential Proxies mit Country- und City-Targeting.

Proxy-Setup mit ProxyHat: Geo-Targeting für saubere Suggestions

ProxyHat bietet residential Proxies, die du über den Username steuerst. Für Deutschland-Berlin-Sessions sieht die Konfiguration so aus:

# HTTP-Proxy mit Geo-Targeting (Berlin, Deutschland)
HTTP_PROXY = "http://user-country-DE-city-berlin:pass@gate.proxyhat.com:8080"

# SOCKS5-Variante für Playwright / Browser-Kontexte
SOCKS5_PROXY = "socks5://user-country-DE-city-berlin:pass@gate.proxyhat.com:1080"

# Sticky Session für konsistente SERP-Ergebnisse
STICKY_PROXY = "http://user-country-DE-session-abc123:pass@gate.proxyhat.com:8080"

Sticky Sessions sind wichtig, wenn du mehrere SERP-Seiten derselben Query hintereinander lädst und dabei nicht die IP wechseln willst – sonst liefert Google dir inkonsistente Layouts. Für Autocomplete-Massencabrufe nutzt du besser rotierende IPs, also ohne -session--Flag. Details zu den Standorten findest du unter /de/locations, Pricing unter /de/pricing.

Code-Block 1: Autocomplete mit curl und ProxyHat testen

Bevor du Python baust, prüfe den Endpoint manuell. Der Chrome-Client liefert JSON, der Firefox-Client ein flacheres Array:

# Autocomplete über ProxyHat mit Berlin-IP abrufen
curl -x "http://user-country-DE-city-berlin:pass@gate.proxyhat.com:8080" \
  "https://suggestqueries.google.com/complete/search?client=chrome&q=best%20running%20shoes" \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
  -H "Accept-Language: de-DE,de;q=0.9"

Die Antwort ist ein Array: [query, [suggestions], [types], [metadata]]. Die Vorschläge sind bereits UTF-8-dekodiert, du musst nur das Array flachklopfen.

Code-Block 2: Seed-zu-Longtail-Expander mit httpx

Ein einzelnes Seed-Keyword liefert 10 Vorschläge. Wenn du das Seed mit den Buchstaben a–z und Modifikatoren wie „how", „what", „best", „vs" kombinierst, bekommst du aus einem Seed schnell 260+ Longtail-Keywords. Hier die httpx-Implementierung mit Retry-Logik und Proxy-Rotation:

import httpx
import asyncio
import itertools
from typing import List

PROXY = "http://user-country-DE-city-berlin:pass@gate.proxyhat.com:8080"
AUTOCOMPLETE_URL = "https://suggestqueries.google.com/complete/search"

HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36",
    "Accept-Language": "de-DE,de;q=0.9,en;q=0.8",
}

MODIFIERS = ["", "how ", "what ", "best ", "vs ", "why ", "is ", "buy "]
LETTERS = [chr(c) for c in range(ord("a"), ord("z") + 1)]

async def fetch_suggestions(client: httpx.AsyncClient, query: str) -> List[str]:
    params = {"client": "chrome", "q": query}
    for attempt in range(3):
        try:
            r = await client.get(AUTOCOMPLETE_URL, params=params, headers=HEADERS, timeout=10.0)
            if r.status_code == 200 and r.json():
                return r.json()[1] or []
        except (httpx.HTTPError, httpx.JSONDecodeError) as e:
            print(f"retry {attempt} for '{query}': {e}")
            await asyncio.sleep(1.5 * (attempt + 1))
    return []

async def expand_seed(seed: str) -> List[str]:
    prefixes = [f"{seed} {l}" for l in LETTERTERS] + [f"{m}{seed}" for m in MODIFIERS]
    async with httpx.AsyncClient(proxy=PROXY) as client:
        tasks = [fetch_suggestions(client, p) for p in prefixes]
        results = await asyncio.gather(*tasks, return_exceptions=True)
    suggestions = set()
    for r in results:
        if isinstance(r, list):
            suggestions.update(r)
    return sorted(suggestions)

if __name__ == "__main__":
    keywords = asyncio.run(expand_seed("running shoes"))
    print(f"{len(keywords)} Keywords gefunden")

Achtung: 33 parallele Requests sind schon im Drosselbereich. Setze einen Semaphore(10) um die Concurrency zu begrenzen, sonst bekommst du 429s.

Code-Block 3: PAA-Fragen mit Playwright rekursiv aufklappen

PAA ist der schwierigste Teil, weil die Fragen in einem Akkordeon liegen und erst beim Klicken weitere Fragen nachladen. Playwright mit SOCKS5-Proxy ist hier der Standardweg:

from playwright.async_api import async_playwright
import asyncio
import json

SOCKS5 = "socks5://user-country-DE-city-berlin:pass@gate.proxyhat.com:1080"
QUERY = "best running shoes for flat feet"

def extract_paa(page):
    return page.evaluate("""() => {
      const items = [...document.querySelectorAll('div[role="listitem"]')];
      return items.map(el => {
        const q = el.querySelector('span[jsname]')?.innerText || '';
        const cite = el.querySelector('a[href]')?.href || '';
        return {question: q, source: cite};
      }).filter(x => x.question);
    }""")

async def scrape_paa(query: str, max_depth: int = 4):
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=True,
            proxy={"server": SOCKS5}
        )
        ctx = await browser.new_context(locale="de-DE")
        page = await ctx.new_page()
        await page.goto(f"https://www.google.com/search?q={query}&hl=de&gl=de", wait_until="domcontentloaded")
        await page.wait_for_selector('div[role="listitem"]', timeout=15000)

        seen = set()
        for depth in range(max_depth):
            accordions = await page.query_selector_all('div[role="listitem"] div[jsname]')
            for acc in accordions[:8]:
                try:
                    await acc.click(timeout=3000)
                    await page.wait_for_timeout(800)
                except Exception:
                    continue
            rows = await extract_paa(page)
            for r in rows:
                if r["question"] not in seen:
                    seen.add(r["question"])
        await browser.close()
        return list(seen)

if __name__ == "__main__":
    questions = asyncio.run(scrape_paa(QUERY))
    print(json.dumps(questions, ensure_ascii=False, indent=2))

Die wait_for_timeout(800) ist wichtig – Google lädt die Folgefragen asynchron nach. Zu schnelles Klicken überspringt Fragen. Die source-URL ist gleichzeitig ein Backlink-Kandidat für dein FAQ-Content-Marketing.

Code-Block 4: Related Searches extrahieren

Related Searches sind das einfachste Ziel – sie stehen als statische Links unten auf der SERP:

async def scrape_related(page, query):
    await page.goto(f"https://www.google.com/search?q={query}&hl=de&gl=de", wait_until="domcontentloaded")
    await page.wait_for_selector('a[href*="/search?q="], div[id*="related"]', timeout=10000)
    return await page.evaluate("""() => {
      const links = [...document.querySelectorAll('a[href*="/search?q="]')];
      const related = new Set();
      for (const a of links) {
        const u = new URL(a.href);
        const q = u.searchParams.get('q');
        if (q && !q.startsWith('http')) related.add(q);
      }
      return [...related];
    }""")

Code-Block 5: Dedup, Intent-Clustering und CSV-Export

Roh-Listen sind wertlos. Du musst normalisieren, dedupen und nach Suchintenz clustern. Hier eine kompakte Pipeline:

import csv
import re
from collections import defaultdict

INTENT_RULES = {
    "transactional": [r"\b(kaufen|bestellen|price|buy|deal|gutschein)\b"],
    "navigational":  [r"\b(login|official|website|site:)\b"],
    "informational": [r"\b(wie|was|warum|how|what|why|guide|tutorial)\b"],
}

def normalize(q: str) -> str:
    return re.sub(r"\s+", " ", q.strip().lower())

def classify(q: str) -> str:
    for intent, patterns in INTENT_RULES.items():
        if any(re.search(p, q) for p in patterns):
            return intent
    return "commercial"

def build_dataset(autocomplete, paa, related):
    raw = autocomplete + [p["question"] if isinstance(p, dict) else p for p in paa] + related
    seen = {}
    for q in raw:
        n = normalize(q)
        if n and n not in seen:
            seen[n] = {"keyword": n, "intent": classify(n), "source": "mixed"}
    return list(seen.values())

def export_csv(rows, path="keywords.csv"):
    with open(path, "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=["keyword", "intent", "source"])
        w.writeheader()
        w.writerows(rows)

if __name__ == "__main__":
    rows = build_dataset(
        autocomplete=["best running shoes", "running shoes sale"],
        paa=[{"question": "How to choose running shoes?", "source": ""}],
        related=["running shoes vs trainers"],
    )
    export_csv(rows)
    print(f"{len(rows)} Keywords exportiert")

Für ernsthaftes Intent-Clustering lohnt sich danach ein Embedding-Modell (z. B. sentence-transformers), das die Keywords semantisch gruppiert. Das übersteigt aber den Rahmen dieses Leitfadens – die Regel-basierte Klassifikation reicht für einen ersten FAQ-Plan.

Häufige Fehler und Edge Cases

  • Zu hohe Concurrency ohne Proxy-Rotation – nach ~80 Requests / 10 Min / IP liefert Google leere Arrays oder 429. Drossel auf 5–10 gleichzeitige Requests.
  • Falscher User-Agent – der Chrome-Endpoint erwartet einen Chrome-UA. Mit einem Python-requests-Default-UA bekommst du manchmal HTML statt JSON.
  • Locale-Header ignoriertAccept-Language und gl-Parameter müssen zur Proxy-Geo passen, sonst mischt Google Regionen.
  • PAA-Selektor bricht – Google ändert die DOM-Struktur regelmäßig. Nutze robuste Selektoren wie div[role="listitem"] statt kryptischer jsname-Attribute.
  • Kein Retry bei Transient-Errors – ein Circuit Breaker (z. B. 3 Fails → 60 s Pause) verhindert, dass du ganze Proxy-Pools verbrennst.

Ethik und Skalierung: öffentliche Daten, aber höflich

Autocomplete-Vorschläge und PAA-Fragen sind öffentlich sichtbare Daten – es gibt keine Paywall und keine Authentifizierung. Trotzdem solltest du throttle politely: 1–2 Requests pro Sekunde pro IP, Pausen zwischen Bursts, und Respekt vor robots.txt. Für produktive Workloads im großen Stil bevorzugt Google offizielle APIs – die Custom Search JSON API deckt allerdings keine Autocomplete-Daten ab, weshalb Scraping für diese spezifische Datenquelle die einzige praktische Option bleibt.

Rechtlich relevant: In der EU greift die DSGVO nicht für anonyme Suchvorschläge, aber personenbezogene Queries (z. B. Namenssuchen) solltest du herausfiltern. Für kommerzielle Keyword-Datenbanken empfiehlt sich eine anonymisierte Speicherung ohne Timestamps, die einzelne Nutzer identifizierbar machen könnten.

Key Takeaways

  • Autocomplete, PAA und Related Searches decken drei verschiedene Intenz-Signale ab – kombiniere sie, nicht nur eine Quelle.
  • Ein Seed-Keyword mit a–z- und Modifikator-Prefixen liefert 200+ Longtail-Vorschläge – das ist der billigste Keyword-Funnel, den es gibt.
  • PAA ist rekursiv: Jeder Klick lädt neue Fragen, also klappre 3–4 Level tief auf.
  • Residential Proxies mit City-Targeting (z. B. user-country-DE-city-berlin) sind nötig für saubere lokalisierte Suggestions und um IP-Drosseln zu umgehen.
  • Normalisiere, dedupe und klassifiziere nach Intenz, bevor du exportierst – Rohlisten sind nicht produktionsreif.
  • Höflich drosseln: 1–2 req/s/IP, offizielle APIs bevorzugen, wo sie existieren.

Wenn du das Setup produktiv drehen willst, schau dir unsere Web-Scraping-Use-Cases und die SERP-Tracking-Seite an. Die vollständige ProxyHat-Dokumentation findest du unter docs.proxyhat.com.

Bereit loszulegen?

Zugang zu über 50 Mio. Residential-IPs in über 148 Ländern mit KI-gesteuerter Filterung.

Preise ansehenResidential Proxies
← Zurück zum Blog