Comment récupérer les résultats de recherche de Bing avec Python

Commentaires: 0

L'analyse web ne se limite pas à Google. Bing offre une vue alternative de la SERP qui est utile pour la recherche SEO, la prospection de liens, la surveillance de la marque, l'analyse concurrentielle et la recherche de contenu. Python est un outil idéal pour ce type d'automatisation: un écosystème mature, une syntaxe simple et des bibliothèques robustes pour l'analyse HTML et le travail avec JSON vous permettent de récupérer les résultats de recherche de Bing plus rapidement et plus facilement.

Pourquoi se concentrer sur Bing plutôt que sur Google?

Bing utilise ses propres règles de classement et signaux de qualité, de sorte que les résultats diffèrent souvent de ceux de Google. Cette différence est précieuse pour découvrir des opportunités supplémentaires dans la recherche organique et les requêtes de longue traîne. Dans ses recommandations aux webmasters, Bing met l'accent sur la pertinence, la qualité et la confiance, l'engagement des utilisateurs, la fraîcheur, les facteurs géographiques et la vitesse des pages, soit un équilibre de signaux différent de celui de Google. C'est pourquoi certaines pages sont mieux classées sur Bing.

Cas pratiques de récupération des résultats de recherche de Bing:

  • Élargissez votre liste de donateurs pour la création de liens - ce moteur élève parfois des sites qui n'apparaissent pas dans le top 10 de Google.
  • Suivre le PAA ("People also ask") et les éléments universels du SERP de Bing (vidéo, carrousels) pour ajuster votre stratégie de contenu.

Quelles données pouvez-vous extraire de Bing Search?

A partir d'un SERP "classique", vous pouvez extraire de manière fiable:

  • Titre;
  • URL (document link);
  • Extrait (description);
  • Position dans les résultats (indice ordinal);
  • Quelques résultats universels: "Related/People also ask", résultats d'images/vidéos intégrées (lorsqu'elles sont incluses directement dans le SERP principal).

Important: le balisage de Bing change périodiquement, de sorte que les sélecteurs du code ci-dessous peuvent nécessiter des ajustements.

Considérations juridiques et éthiques lorsque l'on scrape Bing Search

  • Suivez les conditions d'utilisation de Microsoft: pour un accès "officiel" aux données web, Microsoft propose désormais le Grounding avec Bing Search dans le cadre d'Azure AI Agents. Les API publiques de Bing Search ont été entièrement supprimées le 11 août 2025.
  • La mise à jour avec Bing Search a ses propres CGU et contraintes: elle est utilisée par des agents Azure, et les résultats sont renvoyés dans les réponses de l'agent plutôt que sous forme de données SERP JSON "brutes".
  • Respectez le fichier robots.txt et évitez de surcharger les hôtes - le respect des robots est une règle de base de l'éthique du scraping.

Configuration de l'environnement Python pour le scraping

Installer les bases:

pip install requests beautifulsoup4 lxml fake-useragent selenium
  • requests - client HTTP (permet de définir des en-têtes tels que User-Agent);
  • beautifulsoup4 + lxml - Analyse HTML;
  • fake-useragent - génération aléatoire d'UA (ou construction de votre propre liste);
  • selenium - rend les blocs dynamiques lorsque c'est nécessaire.

Méthode 1 - Scraping Bing via Requests et BeautifulSoup

Nous l'utiliserons comme base pour démontrer le flux de travail: émettre des requêtes GET, définir un User-Agent, analyser les cartes de résultats et collecter le titre, l'URL, l'extrait et la position.

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")

Explication:

  • Utiliser les paramètres count/first pour la pagination.
  • Les sélecteurs li.b_algo h2 a et .b_caption p sont des lignes de base; la mise en page peut être modifiée (inspecter dans DevTools).
  • Ajoutez un proxy si nécessaire et régulez les pauses entre les requêtes.
  • Nous améliorerons cet exemple un peu plus loin, car il s'agit de l'approche la plus efficace pour nos objectifs dans les conditions actuelles.

Méthode 2 - Récupérer les résultats de recherche de Bing via l'API (état en 2025)

L'API Bing scraper publique de Microsoft a été retirée en août 2025. Microsoft recommande de migrer vers le Grounding avec Bing Search dans Azure AI Agents.

Ce que cela signifie en pratique

  • Le point de terminaison REST classique avec des données SERP JSON "brutes" n'est plus disponible pour la plupart des développeurs.
  • La mise à la terre avec Bing Search est connectée comme un outil à l'intérieur d'un agent Azure; l'agent peut "chercher" sur le web et renvoyer une réponse synthétisée. Le service a ses propres conditions d'utilisation et spécificités: il n'est pas conçu pour l'extraction en masse de résultats bruts de SERP.

Alternative pour les SERP bruts en JSON

Utiliser des API/plateformes SERP tierces (par exemple, Apify Bing Search Scraper) qui renvoient des résultats structurés: titre, URL, extrait, position, etc.

Exemple de demande d'Apify minimale:

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 documente la prise en charge des résultats organiques, de l'AAP, des requêtes connexes, etc. Assurez-vous que votre cas d'utilisation est conforme aux règles de la plateforme et aux lois de votre juridiction.

Conseil: si vous travaillez dans la pile Azure AI Agents et n'avez besoin que de références mises à la terre pour un LLM (plutôt que de JSON brut), lisez le guide sur les Mise à la terre avec Bing Search.

Méthode 3 - Analyse du contenu dynamique avec Selenium

Lorsque la SERP comprend des carrousels, des blocs interactifs ou du contenu rendu par JavaScript, passez à 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"))

Se référer au document officiel Documentation sur Selenium pour l'installation du pilote et les exemples de WebDriverWait.

Solution pratique: Stratégie d'analyse et exemple de code

Pour la mise en œuvre finale, nous effectuerons du scraping Bing directement à partir de HTML:

  1. Envoyer des requêtes HTTP à https://www.bing.com/search.
  2. Définir un User-Agent.
  3. Analyse du HTML via BeautifulSoup + lxml pour extraire les titres, les URL et les extraits.

Ainsi, vous n'avez pas besoin de comptes Microsoft et vous n'êtes pas lié à des API payantes de tiers. Pour la sélection des résultats, nous utilisons le conteneur de carte de résultats li.b_algo, qui est généralement utilisé pour les blocs organiques de Bing.

Exemple de travail (pagination, délais, proxy optionnel)

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())

Exemple d'utilisation avec des paramètres supplémentaires et un proxy:

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

Ce que fait le texte:

  1. Envoie des requêtes GET à Bing avec des paramètres contrôlés (q, count, first) et des paramètres linguistiques (cc, setlang).
  2. Remplace User-Agent et ajoute Accept-Language pour des extraits plus stables.
  3. Analyse le HTML via BeautifulSoup(..., "lxml"), localise les cartes de résultats li.b_algo, et extrait le titre, l'url, et le snippet. Les sélecteurs CSS .select() de BS4 sont une approche standard et flexible.
  4. Prend en charge un proxy optionnel. Pour les demandes, le format correct du proxy est une correspondance protocole→URL.

Conseils de stabilité:

  • Ajouter des pauses (intervalles aléatoires entre les demandes).
  • Rotation de l'agent utilisateur (dynamiquement ou à partir de votre liste). Requests montre comment définir correctement les en-têtes - nous le faisons dans l'exemple de travail.
  • Utiliser l'infrastructure proxy/la rotation IP lorsque cela est nécessaire pour s'adapter efficacement aux limites de la plate-forme.
  • Veillez à ce que le volume global des demandes reste raisonnable et vérifiez les réponses aux questions du CAPTCHA.
  • Pour les scénarios complexes, envisagez des API SERP gérées (Apify, etc.) qui incluent une infrastructure antibot.

Pour en savoir plus sur les outils

  • Demandes: en-têtes, proxies, délais - documents officiels.
  • BeautifulSoup les fonctions suivantes sont utilisées: .select() et les sélecteurs CSS.
  • lxml: correction de l'analyseur HTML pour BS4.

Conseil: si vous avez besoin d'une infrastructure proxy pour une collecte de données plus stable, consultez la page meilleurs proxies pour Bing.

Comment éviter les blocages lors du scraping de Bing

Principes clés pour s'assurer que votre racleur ne "meurt" pas au cours de son premier cycle:

  • Ajouter des délais (randomiser les intervalles entre les demandes).
  • Faites tourner votre User-Agent (dynamiquement ou à partir de votre propre liste); la manière correcte de définir les en-têtes dans les requêtes est décrite dans la documentation - nous utilisons la même approche dans notre exemple de travail.
  • Utiliser des proxys ou une rotation d'IP (en respectant les conditions d'utilisation du service).
  • Limitez le nombre total de demandes et surveillez les réponses aux questions CAPTCHA.
  • Pour les tâches complexes, envisagez des API SERP gérées (Apify, etc.) avec une infrastructure antibot intégrée.

Conclusion

Le scraping de Bing est utile lorsque vous souhaitez étendre la recherche au-delà de Google, collecter d'autres domaines donateurs, suivre d'autres caractéristiques des SERP et obtenir une vue indépendante du paysage. Pour une intégration stable et "officielle", Microsoft encourage le Grounding avec Bing Search dans Azure AI Agents; c'est plus sûr du point de vue des conditions de service, mais cela ne renvoie pas les données JSON brutes des SERP. Si votre tâche consiste à extraire des résultats structurés, choisissez l'analyse HTML directe via Requests/BS4 ou Selenium, ou utilisez une API SERP spécialisée. Choisissez l'outil qui convient: l'analyse HTML rapide pour les prototypes, les agents pour les réponses fondées sur le LLM et les API SERP pour la collecte à grande échelle.

Commentaires:

0 Commentaires