Wie man Bing-Suchergebnisse mit Python abgreift

Bemerkungen: 0

Web-Analysen sind nicht auf Google beschränkt. Bing bietet eine alternative Ansicht der SERP, die für SEO-Forschung, Linkprospektion, Markenüberwachung, Wettbewerbsanalyse und Inhaltsforschung nützlich ist. Python ist ein ideales Werkzeug für diese Art der Automatisierung: ein ausgereiftes Ökosystem, eine einfache Syntax und robuste Bibliotheken für HTML-Parsing und die Arbeit mit JSON ermöglichen es Ihnen, Bing-Suchergebnisse schneller und bequemer zu scrapen.

Warum auf Bing statt auf Google setzen?

Bing verwendet seine eigenen Ranking-Richtlinien und Qualitätssignale, so dass die Ergebnisse oft von denen von Google abweichen. Das ist wertvoll, um zusätzliche Möglichkeiten in der organischen Suche und bei Long-Tail-Anfragen aufzudecken. In seinen Webmaster-Empfehlungen legt Bing den Schwerpunkt auf Relevanz, Qualität/Vertrauen, Nutzerbindung, Aktualität, Geofaktoren und Seitengeschwindigkeit - eine andere Balance von Signalen als Google. Aus diesem Grund rangieren einige Seiten speziell bei Bing höher.

Praktische Anwendungsfälle beim Scrapen von Bing-Suchergebnissen:

  • Erweitern Sie Ihre Linkaufbau-Spenderliste - diese Maschine hebt manchmal Websites hervor, die nicht in den Top 10 von Google erscheinen.
  • Verfolgen Sie PAA ("People also ask") und die universellen SERP-Elemente von Bing (Video, Karussells), um Ihre Content-Strategie anzupassen.

Welche Daten können Sie aus der Bing-Suche extrahieren?

Aus einer "klassischen" SERP können Sie zuverlässig extrahieren:

  • Titel;
  • URL (document link);
  • Schnipsel (Beschreibung);
  • Position in den Ergebnissen (Ordnungsindex);
  • Einige universelle Ergebnisse: "Related/People also ask", eingebettete Bild-/Videoergebnisse (wenn direkt in der Haupt-SERP enthalten).

Wichtig: Das Markup von Bing ändert sich regelmäßig, so dass die Selektoren im folgenden Code möglicherweise angepasst werden müssen.

Rechtliche und ethische Erwägungen beim Scrapen der Bing-Suche

  • Beachten Sie die Nutzungsbedingungen von Microsoft: Für den "offiziellen" Zugriff auf Webdaten bietet Microsoft jetzt Grounding mit Bing Search als Teil von Azure AI Agents an. Die öffentlichen Bing Search-APIs wurden am 11. August 2025 vollständig abgeschaltet.
  • Die Erdung mit Bing Search hat ihre eigenen TOU und Einschränkungen: Sie wird über Azure-Agenten verwendet, und die Ergebnisse werden in den Antworten des Agenten zurückgegeben und nicht als "rohe" JSON-SERP-Daten.
  • Beachten Sie robots.txt und vermeiden Sie die Überlastung von Hosts - die Einhaltung von Robots gehört zu den ethischen Grundlagen des Scraping.

Einrichten der Python-Umgebung für Scraping

Installieren Sie die Grundlagen:

pip install requests beautifulsoup4 lxml fake-useragent selenium
  • requests - HTTP-Client (ermöglicht das Setzen von Headern wie User-Agent);
  • beautifulsoup4 + lxml - HTML-Parsing;
  • fake-user-agent - zufällige UA-Generierung (oder erstellen Sie Ihre eigene Liste);
  • selenium - Rendering dynamischer Blöcke bei Bedarf.

Methode 1 - Scraping von Bing über Requests und BeautifulSoup

Wir werden dies als Basis verwenden, um den Arbeitsablauf zu demonstrieren: GET-Anfragen stellen, einen User-Agent festlegen, Ergebniskarten analysieren und Titel, URL, Snippet und Position sammeln.

import time
import random
from typing import List, Dict
import requests
from bs4 import BeautifulSoup

BING_URL = "https://www.bing.com/search"

HEADERS_POOL = [
    # You can add more — or use fake-useragent
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
    "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5) AppleWebKit/605.1.15 "
    "(KHTML, like Gecko) Version/17.0 Safari/605.1.15",
]

def fetch_serp(query: str, count: int = 10, first: int = 1,
               proxy: str | None = None) -> List[Dict]:
    """
Returns a list of results: title, url, snippet, position.
`first` — starting position (pagination), `count` — how many records to fetch.

    """
    params = {"q": query, "count": count, "first": first}
    headers = {"User-Agent": random.choice(HEADERS_POOL)}
    proxies = {"http": proxy, "https": proxy} if proxy else None

    resp = requests.get(BING_URL, params=params, headers=headers,
                        proxies=proxies, timeout=15)
    resp.raise_for_status()
    soup = BeautifulSoup(resp.text, "lxml")

   # Typical Bing markup: <li class="b_algo"> ... <h2><a href="">Title</a></h2>
    items = []
    for idx, li in enumerate(soup.select("li.b_algo"), start=first):
        a = li.select_one("h2 a")
        if not a:
            continue
        title = a.get_text(strip=True)
        url = a.get("href")
         # Snippet is often in .b_caption p or simply the first <p>
        sn_el = li.select_one(".b_caption p") or li.select_one("p")
        snippet = sn_el.get_text(" ", strip=True) if sn_el else ""
        items.append({
            "position": idx,
            "title": title,
            "url": url,
            "snippet": snippet
        })
    return items

if __name__ == "__main__":
    data = fetch_serp("python web scraping tutorial", count=10)
    for row in data:
        print(f"{row['position']:>2}. {row['title']} -- {row['url']}")
        print(f"   {row['snippet']}\n")

Erläuterung:

  • Verwenden Sie die Parameter count/first für die Paginierung.
  • Die Selektoren li.b_algo h2 a und .b_caption p sind Grundlinien; das Layout kann sich ändern (in DevTools überprüfen).
  • Fügen Sie bei Bedarf einen Proxy hinzu und regeln Sie die Pausen zwischen den Anfragen.
  • Wir werden dieses Beispiel weiter unten noch etwas ausbauen, da es für unsere Zwecke unter den derzeitigen Bedingungen am effektivsten ist.

Methode 2 - Scrapen von Bing-Suchergebnissen über API (Stand: 2025)

Die öffentliche Bing Scraper API von Microsoft wurde im August 2025 eingestellt. Microsoft empfiehlt, mit Bing Search innerhalb von Azure AI Agents auf Grounding zu migrieren.

Was dies in der Praxis bedeutet

  • Der klassische REST-Endpunkt mit "rohen" JSON-SERP-Daten ist für die meisten Entwickler nicht mehr verfügbar.
  • Die Erdung mit Bing Search ist als Tool in einen Azure-Agenten eingebunden; der Agent kann im Web "nachschlagen" und eine zusammengesetzte Antwort zurückgeben. Der Dienst hat seine eigenen TOU und Besonderheiten: Er ist nicht für die Massenextraktion von SERP-Rohdaten konzipiert.

Alternative für rohe SERP in JSON

Verwenden Sie SERP-APIs/Plattformen von Drittanbietern (z. B. Apify Bing Search Scraper), die strukturierte Ergebnisse liefern: Titel, URL, Snippet, Position usw.

Beispiel für eine minimale Apify-Anfrage:

import requests

API_TOKEN = "apify_xxx"  # store in ENV
actor = "tri_angle/bing-search-scraper"
payload = {
    "queries": ["python web scraping tutorial"],
    "countryCode": "US",
    "includeUnfilteredResults": False
}

r = requests.post(
    f"https://api.apify.com/v2/acts/{actor}/runs?token={API_TOKEN}",
    json=payload, timeout=30
)
run = r.json()
# Retrieve dataset items using run['data']['defaultDatasetId']

Apify dokumentiert Unterstützung für organische Ergebnisse, PAA, verwandte Suchanfragen und mehr. Stellen Sie sicher, dass Ihr Anwendungsfall mit den Regeln der Plattform und den Gesetzen Ihrer Gerichtsbarkeit übereinstimmt.

Tipp: Wenn Sie mit dem Azure AI Agents Stack arbeiten und nur geerdete Referenzen für ein LLM benötigen (und nicht rohes JSON), lesen Sie den Leitfaden auf Erdung mit Bing Search.

Methode 3 - Parsing dynamischer Inhalte mit Selenium

Wenn die SERP Karussells, interaktive Blöcke oder durch JavaScript gerenderte Inhalte enthält, wechseln Sie zu Selenium (Headless Chrome/Firefox).

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options

def selenium_bing(query: str, headless: bool = True):
    opts = Options()
    if headless:
        opts.add_argument("--headless=new")
    opts.add_argument("--disable-gpu")
    opts.add_argument("--no-sandbox")
    with webdriver.Chrome(options=opts) as driver:
        driver.get("https://www.bing.com/")
        box = driver.find_element(By.NAME, "q")
        box.send_keys(query)
        box.submit()

        # Consider adding explicit waits via WebDriverWait
        cards = driver.find_elements(By.CSS_SELECTOR, "li.b_algo h2 a")
        results = []
        for i, a in enumerate(cards, start=1):
            results.append({"position": i, "title": a.text, "url": a.get_attribute("href")})
        return results

if __name__ == "__main__":
    print(selenium_bing("site:docs.python.org requests headers"))

Siehe die offizielle Selenium-Dokumente für die Treiberinstallation und WebDriverWait-Beispiele.

Praktische Lösung: Parsing-Strategie und Beispielcode

Für die endgültige Implementierung werden wir Bing Scraping direkt aus HTML durchführen:

  1. Senden Sie HTTP-Anfragen an https://www.bing.com/search.
  2. Legen Sie einen Benutzer-Agenten fest.
  3. Parsen Sie HTML über BeautifulSoup + lxml, um Titel, URLs und Snippets zu extrahieren.

Auf diese Weise benötigen Sie keine Microsoft-Konten und sind nicht an kostenpflichtige APIs von Drittanbietern gebunden. Für die Ergebnisauswahl verwenden wir den Ergebniskarten-Container li.b_algo, der üblicherweise für die organischen Blöcke von Bing verwendet wird.

Arbeitsbeispiel (Paginierung, Verzögerungen, optionaler Proxy)

from __future__ import annotations

import argparse
import csv
import dataclasses
import pathlib
import random
import sys
import time
from typing import List, Optional, Tuple

import requests
from bs4 import BeautifulSoup, FeatureNotFound

BING_URL = "https://www.bing.com/search"

# Pool of user agents
UA_POOL = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
]

@dataclasses.dataclass
class SerpItem:
    position: int
    title: str
    url: str
    snippet: str


def build_session(proxy: Optional[str] = None) -> requests.Session:
    """Create a session with baseline headers and an optional proxy."""
    s = requests.Session()
    s.headers.update(
        {
            "User-Agent": random.choice(UA_POOL),
            "Accept-Language": "uk-UA,uk;q=0.9,en;q=0.8",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        }
    )
    if proxy:
        # Requests proxy dict format: {'http': 'http://host:port', 'https': 'http://host:port'}
        s.proxies.update({"http": proxy, "https": proxy})
    return s


def _soup_with_fallback(html: str) -> BeautifulSoup:
    """Parse HTML with a forgiving fallback chain: lxml -> html.parser -> html5lib (if available)."""
    for parser in ("lxml", "html.parser", "html5lib"):
        try:
            return BeautifulSoup(html, parser)
        except FeatureNotFound:
            continue
    # If none are installed, bs4 will raise; let it propagate
    return BeautifulSoup(html, "html.parser")


def parse_serp_html(html: str, start_pos: int) -> List[SerpItem]:
    """Extract organic results from Bing SERP HTML."""
    soup = _soup_with_fallback(html)
    items: List[SerpItem] = []

    # Organic blocks typically look like <li class="b_algo"> with h2>a and a snippet under .b_caption p or the first <p>.
    for i, li in enumerate(soup.select("li.b_algo"), start=start_pos):
        a = li.select_one("h2 > a")
        if not a:
            continue
        title = (a.get_text(strip=True) or "").strip()
        url = a.get("href") or ""
        p = li.select_one(".b_caption p") or li.select_one("p")
        snippet = (p.get_text(" ", strip=True) if p else "").strip()
        items.append(SerpItem(position=i, title=title, url=url, snippet=snippet))

    return items


def fetch_bing_page(
    session: requests.Session,
    query: str,
    first: int = 1,
    count: int = 10,
    cc: str = "UA",
    setlang: str = "uk",
    timeout: int = 20,
) -> List[SerpItem]:
    """Download one results page and return parsed items."""
    params = {
        "q": query,
        "count": count,   # 10, 15, 20...
        "first": first,   # 1, 11, 21...
        "cc": cc,         # country code for results
        "setlang": setlang,  # interface/snippet language
    }
    r = session.get(BING_URL, params=params, timeout=timeout)
    r.raise_for_status()
    return parse_serp_html(r.text, start_pos=first)


def search_bing(
    query: str,
    pages: int = 1,
    count: int = 10,
    pause_range: Tuple[float, float] = (1.2, 2.7),
    proxy: Optional[str] = None,
    cc: str = "UA",
    setlang: str = "uk",
    timeout: int = 20,
) -> List[SerpItem]:
    """Iterate over pages and return an aggregated list of results."""
    session = build_session(proxy=proxy)
    all_items: List[SerpItem] = []
    first = 1
    for _ in range(pages):
        items = fetch_bing_page(
            session, query, first=first, count=count, cc=cc, setlang=setlang, timeout=timeout
        )
        all_items.extend(items)
        time.sleep(random.uniform(*pause_range))  # polite delay
        first += count
    return all_items


def _normalize_cell(s: str) -> str:
    """Optional: collapse internal whitespace so simple viewers show one‑line cells."""
    # Convert tabs/newlines/multiple spaces to a single space
    return " ".join((s or "").split())


def save_csv(
    items: List[SerpItem],
    path: str,
    excel_friendly: bool = False,
    normalize: bool = False,
    delimiter: str = ",",
) -> int:
    """
Write results to CSV.
— excel_friendly=True -> write UTF‑8 with BOM (utf‑8‑sig) so Excel auto‑detects Unicode.
— normalize=True -> collapse whitespace inside string fields.
— delimiter -> change if your consumer expects ';', etc.
Returns the number of rows written (excluding header).

    """
    p = pathlib.Path(path)
    p.parent.mkdir(parents=True, exist_ok=True)

    encoding = "utf-8-sig" if excel_friendly else "utf-8"

    # newline='' is required so Python's csv handles line endings correctly on all platforms
    with p.open("w", newline="", encoding=encoding) as f:
        writer = csv.DictWriter(
            f,
            fieldnames=["position", "title", "url", "snippet"],
            delimiter=delimiter,
            quoting=csv.QUOTE_MINIMAL,
        )
        writer.writeheader()
        for it in items:
            row = dataclasses.asdict(it)
            if normalize:
                row = {k: _normalize_cell(v) if isinstance(v, str) else v for k, v in row.items()}
            writer.writerow(row)
    return len(items)


def main() -> int:
    ap = argparse.ArgumentParser(description="Bing SERP scraper (Requests + BS4)")
    ap.add_argument("-q", "--query", required=True, help="Search query")
    ap.add_argument("--pages", type=int, default=1, help="Number of pages (x count)")
    ap.add_argument("--count", type=int, default=10, help="Results per page")
    ap.add_argument("--cc", default="UA", help="Country code for results (cc)")
    ap.add_argument("--setlang", default="uk", help="Interface/snippet language (setlang)")
    ap.add_argument("--proxy", help="Proxy, e.g. http://user:pass@host:port")
    ap.add_argument("--csv", help="Path to CSV to save results")
    ap.add_argument(
        "--excel-friendly",
        action="store_true",
        help="Add BOM (UTF‑8‑SIG) so Excel opens the file correctly",
    )
    ap.add_argument(
        "--normalize-cells",
        action="store_true",
        help="Remove line breaks and extra spaces in cells",
    )
    ap.add_argument(
        "--delimiter",
        default=",",
        help="CSV delimiter (default ','); e.g.: ';'",
    )
    args = ap.parse_args()

    try:
        items = search_bing(
            args.query,
            pages=args.pages,
            count=args.count,
            proxy=args.proxy,
            cc=args.cc,
            setlang=args.setlang,
        )
    except requests.HTTPError as e:
        print(f"[ERROR] HTTP error: {e}", file=sys.stderr)
        return 2
    except requests.RequestException as e:
        print(f"[ERROR] Network error: {e}", file=sys.stderr)
        return 2

    if args.csv:
        try:
            n = save_csv(
                items,
                args.csv,
                excel_friendly=args.excel_friendly,
                normalize=args.normalize_cells,
                delimiter=args.delimiter,
            )
            print(f"Saved {n} rows to {args.csv}")
        except OSError as e:
            print(f"[ERROR] Could not write CSV to {args.csv}: {e}", file=sys.stderr)
            return 3
    else:
        for it in items:
            print(f"{it.position:>2}. {it.title} -- {it.url}")
            if it.snippet:
                print("   ", it.snippet[:180])

    return 0


if __name__ == "__main__":
    sys.exit(main())

Beispiel für die Verwendung mit zusätzlichen Parametern und einem Proxy:

python bing_scraper.py -q "Python web scraping" --pages 3 --csv out.csv \
  --proxy "http://username:password@proxy:port"

Was das Skript tut:

  1. Sendet GET-Anfragen an Bing mit kontrollierten Parametern (q, count, first) und Gebietsschemaeinstellungen (cc, setlang).
  2. Setzt User-Agent außer Kraft und fügt Accept-Language für stabilere Snippets hinzu.
  3. Parst HTML über BeautifulSoup(..., "lxml"), lokalisiert die Ergebniskarten li.b_algo und extrahiert Titel, URL und Snippet. Die .select() CSS-Selektoren in BS4 sind ein standardmäßiger, flexibler Ansatz.
  4. Unterstützt einen optionalen Proxy. Für Anfragen ist das korrekte Proxy-Format eine protocol→URL-Zuordnung.

Tipps zur Stabilität:

  • Pausen hinzufügen (zufällige Intervalle zwischen den Anfragen).
  • Drehen Sie den User-Agent (dynamisch oder aus Ihrer Liste). Requests zeigt, wie man Kopfzeilen richtig setzt - wir tun dies im Arbeitsbeispiel.
  • Nutzen Sie bei Bedarf die Proxy-Infrastruktur/IP-Rotation, um innerhalb der Plattformgrenzen effektiv zu skalieren.
  • Halten Sie das Gesamtvolumen der Anfragen in einem vernünftigen Rahmen und überprüfen Sie die Antworten auf CAPTCHA-Aufforderungen.
  • Für komplexe Szenarien sollten Sie verwaltete SERP-APIs (Apify usw.) in Betracht ziehen, die eine Antibot-Infrastruktur umfassen.

Wo Sie mehr über die Tools erfahren können

Tipp: Wenn Sie eine Proxy-Infrastruktur für eine stabilere Datenerfassung benötigen, sollten Sie sich die beste Proxys für Bing.

Wie man beim Scraping von Bing Blocks vermeidet

Wichtige Grundsätze, um sicherzustellen, dass Ihr Abstreifer nicht während seines ersten Zyklus "stirbt":

  • Hinzufügen von Verzögerungen (zufällige Abstände zwischen den Anfragen).
  • Drehen Sie Ihren User-Agent (dynamisch oder aus Ihrer eigenen Liste); die korrekte Art und Weise, Header in Anfragen zu setzen, ist in der Dokumentation beschrieben - wir verwenden den gleichen Ansatz in unserem Arbeitsbeispiel.
  • Verwenden Sie Proxys oder IP-Rotation (unter Beachtung der Nutzungsbedingungen des Dienstes).
  • Begrenzen Sie die Gesamtzahl der Anfragen und überwachen Sie die Antworten auf CAPTCHA-Aufforderungen.
  • Für komplexe Aufgaben sollten Sie verwaltete SERP-APIs (Apify usw.) mit integrierter Antibot-Infrastruktur in Betracht ziehen.

Schlussfolgerung

Das Scraping von Bing ist hilfreich, wenn Sie Ihre Recherchen über Google hinaus ausweiten, zusätzliche Spenderdomänen sammeln, alternative SERP-Funktionen verfolgen und einen unabhängigen Blick auf die Landschaft erhalten möchten. Für eine stabile und "offizielle" Integration empfiehlt Microsoft das Grounding mit Bing Search in Azure AI Agents; es ist aus Sicht der Nutzungsbedingungen sicherer, liefert aber keine rohen JSON-SERP-Daten. Wenn Ihre Aufgabe darin besteht, strukturierte Ergebnisse zu extrahieren, wählen Sie direktes HTML-Parsing über Requests/BS4 oder Selenium, oder verwenden Sie eine spezielle SERP-API. Wählen Sie das richtige Tool für Ihre Aufgabe: schnelles HTML-Parsing für Prototypen, Agenten für LLM-gestützte Antworten und SERP-APIs für größere Sammlungen.

Bemerkungen:

0 Bemerkungen