Cara Mengikis Hasil Pencarian Bing dengan Python

Komentar: 0

Analisis web tidak terbatas pada Google. Bing menawarkan tampilan alternatif dari SERP yang berguna untuk penelitian SEO, pencarian tautan, pemantauan merek, analisis kompetitif, dan penelitian konten. Python adalah alat yang ideal untuk otomatisasi semacam ini: ekosistem yang matang, sintaks yang lugas, dan pustaka yang tangguh untuk penguraian HTML dan bekerja dengan JSON memungkinkan Anda untuk mengikis hasil pencarian Bing dengan lebih cepat dan nyaman.

Mengapa Fokus pada Bing Daripada Google?

Bing menggunakan pedoman peringkat dan sinyal kualitasnya sendiri, sehingga hasilnya sering kali berbeda dari Google. Hal ini sangat berguna untuk menemukan peluang tambahan dalam penelusuran organik dan kueri berekor panjang. Dalam rekomendasi webmasternya, Bing menekankan relevansi, kualitas/kepercayaan, keterlibatan pengguna, kesegaran, faktor geografis, dan kecepatan halaman - keseimbangan sinyal yang berbeda dari Google. Itulah mengapa beberapa halaman memiliki peringkat yang lebih tinggi secara khusus di Bing.

Kasus penggunaan praktis saat mengikis hasil pencarian Bing:

  • Memperluas daftar donatur pembangun tautan Anda-mesin ini terkadang mengangkat situs yang tidak muncul di 10 besar Google.
  • Melacak PAA ("Orang juga bertanya") dan elemen SERP universal Bing (video, korsel) untuk menyesuaikan strategi konten Anda.

Data Apa yang Dapat Diekstrak dari Pencarian Bing?

Dari SERP "klasik" Anda dapat mengekstrak dengan andal:

  • Judul;
  • URL (tautan dokumen);
  • Cuplikan (deskripsi);
  • Posisi dalam hasil (indeks ordinal);
  • Beberapa hasil universal: "Terkait/Orang lain juga bertanya", hasil gambar/video yang disematkan (bila disertakan langsung di SERP utama).

Penting: Markup Bing berubah secara berkala, sehingga pemilih dalam kode di bawah ini mungkin perlu penyesuaian.

Pertimbangan Hukum dan Etika Saat Mengikis Pencarian Bing

  • Ikuti Ketentuan Penggunaan Microsoft: untuk akses "resmi" ke data web, Microsoft sekarang menawarkan Grounding dengan Bing Search sebagai bagian dari Agen AI Azure. API Bing Search publik sepenuhnya akan berakhir pada tanggal 11 Agustus 2025.
  • Grounding dengan Bing Search memiliki TOU dan batasannya sendiri: digunakan melalui agen Azure, dan hasilnya kembali dalam respons agen, bukan sebagai data JSON SERP "mentah".
  • Hormati robots.txt dan hindari membebani host secara berlebihan-mengikuti robot adalah etika penggosokan dasar.

Menyiapkan Lingkungan Python Anda untuk Scraping

Instal dasar-dasarnya:

pip install requests beautifulsoup4 lxml fake-useragent selenium
  • permintaan - Klien HTTP (memungkinkan Anda mengatur tajuk seperti User-Agent);
  • beautifulsoup4 + lxml - Penguraian HTML;
  • agen-pengguna palsu - pembuatan UA secara acak (atau buat daftar Anda sendiri);
  • selenium - membuat blok dinamis bila diperlukan.

Metode 1 - Mengikis Bing melalui Permintaan dan BeautifulSoup

Kita akan menggunakan ini sebagai garis dasar untuk mendemonstrasikan alur kerja: menerbitkan permintaan GET, menetapkan Agen-Pengguna, mem-parsing kartu hasil, dan mengumpulkan judul, URL, cuplikan, dan posisi.

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

Penjelasan:

  • Gunakan parameter hitungan/pertama untuk pagination.
  • Pemilih li.b_algo h2 a dan .b_caption p adalah dasar; tata letak dapat berubah (periksa di DevTools).
  • Tambahkan proxy bila diperlukan dan atur jeda di antara permintaan.
  • Kami akan menyempurnakan contoh ini sedikit lebih jauh di bawah ini, karena ini adalah pendekatan yang paling efektif untuk tujuan kita dalam kondisi saat ini.

Metode 2 - Kikis Hasil Pencarian Bing melalui API (status tahun 2025)

API scraper Bing publik Microsoft dihentikan pada Agustus 2025. Microsoft merekomendasikan migrasi ke Grounding dengan Bing Search di dalam Agen AI Azure.

Apa artinya ini dalam praktiknya

  • Titik akhir REST klasik dengan data JSON SERP "mentah" tidak lagi tersedia untuk sebagian besar pengembang.
  • Grounding dengan Bing Search terhubung sebagai alat di dalam agen Azure; agen dapat "mencari" web dan mengembalikan jawaban yang disintesis. Layanan ini memiliki TOU dan spesifikasinya sendiri: layanan ini tidak dirancang untuk ekstraksi massal hasil SERP mentah.

Alternatif untuk SERP mentah dalam JSON

Gunakan API/platform SERP pihak ketiga (misalnya, Apify Bing Search Scraper) yang mengembalikan hasil terstruktur: judul, URL, cuplikan, posisi, dll.

Contoh permintaan Apify minimal:

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 mendukung dokumen untuk hasil organik, PAA, kueri terkait, dan banyak lagi. Pastikan kasus penggunaan Anda sesuai dengan aturan platform dan hukum yurisdiksi Anda.

Tips: Jika Anda bekerja di tumpukan Azure AI Agents dan hanya membutuhkan referensi yang dibumikan untuk LLM (bukan JSON mentah), baca panduan di Membumikan dengan Pencarian Bing.

Metode 3 - Mengurai Konten Dinamis dengan Selenium

Jika SERP menyertakan korsel, blok interaktif, atau konten yang dirender dengan JavaScript, beralihlah ke 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"))

Rujuk ke pejabat Dokumen selenium untuk contoh penginstalan driver dan WebDriverWait.

Solusi Praktis: Strategi Penguraian dan Contoh Kode

Untuk implementasi akhir, kita akan melakukan scraping Bing langsung dari HTML:

  1. Kirim permintaan HTTP ke https://www.bing.com/search.
  2. Tetapkan Agen Pengguna.
  3. Mengurai HTML melalui BeautifulSoup + lxml untuk mengekstrak judul, URL, dan cuplikan.

Dengan cara ini Anda tidak memerlukan akun Microsoft dan Anda tidak terikat dengan API berbayar pihak ketiga. Untuk pemilihan hasil, kami menggunakan wadah kartu hasil li.b_algo, yang biasanya digunakan untuk blok organik Bing.

Contoh Kerja (penomoran halaman, penundaan, proksi opsional)

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

Contoh penggunaan dengan parameter tambahan dan proxy:

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

Apa yang dilakukan skrip:

  1. Mengirimkan permintaan GET ke Bing dengan parameter terkontrol (q, count, first) dan pengaturan lokal (cc, setlang).
  2. Mengesampingkan User-Agent dan menambahkan Accept-Language untuk cuplikan yang lebih stabil.
  3. Mengurai HTML melalui BeautifulSoup(..., "lxml"), mencari kartu hasil li.b_algo, dan mengekstrak judul, url, dan cuplikan. Pemilih CSS .select() di BS4 adalah pendekatan standar dan fleksibel.
  4. Mendukung proxy opsional. Untuk Permintaan, format proxy yang benar adalah pemetaan protokol→URL.

Kiat-kiat stabilitas:

  • Tambahkan jeda (mengacak interval di antara permintaan).
  • Putar Agen-Pengguna (secara dinamis atau dari daftar Anda). Permintaan menunjukkan cara mengatur tajuk dengan benar-kami melakukan ini dalam contoh kerja.
  • Gunakan infrastruktur proxy/rotasi IP bila diperlukan untuk meningkatkan skala secara efektif dalam keterbatasan platform.
  • Jaga agar volume permintaan secara keseluruhan tetap wajar dan periksa respons untuk permintaan CAPTCHA.
  • Untuk skenario yang kompleks, pertimbangkan API SERP terkelola (Apify, dll.) yang menyertakan infrastruktur antibot.

Tempat membaca lebih lanjut tentang alat ini

Tips: Jika Anda membutuhkan infrastruktur proxy untuk pengumpulan data yang lebih stabil, lihat proksi terbaik untuk Bing.

Cara Menghindari Pemblokiran Saat Mengikis Bing

Prinsip-prinsip utama untuk memastikan scraper Anda tidak "mati" selama siklus pertamanya:

  • Tambahkan penundaan (mengacak interval di antara permintaan).
  • Putar User-Agent Anda (secara dinamis atau dari daftar Anda sendiri); cara yang benar untuk mengatur tajuk dalam permintaan dijelaskan dalam dokumentasi - kami menggunakan pendekatan yang sama dalam contoh kerja kami.
  • Gunakan proxy atau rotasi IP (dengan mematuhi ketentuan penggunaan layanan).
  • Batasi jumlah keseluruhan permintaan dan pantau respons untuk permintaan CAPTCHA.
  • Untuk tugas-tugas yang kompleks, pertimbangkan API SERP terkelola (Apify, dll.) dengan infrastruktur antibot bawaan.

Kesimpulan

Scraping Bing sangat membantu ketika Anda ingin memperluas penelitian di luar Google, mengumpulkan domain donor tambahan, melacak fitur SERP alternatif, dan mendapatkan tampilan lanskap yang independen. Untuk integrasi yang stabil dan "resmi", Microsoft mempromosikan Grounding dengan Bing Search di Azure AI Agents; ini lebih aman dari sudut pandang persyaratan layanan tetapi tidak mengembalikan data SERP JSON mentah. Jika tugas Anda adalah mengekstrak hasil terstruktur, pilih penguraian HTML langsung melalui Requests/BS4 atau Selenium, atau gunakan API SERP khusus. Pilih alat untuk pekerjaan itu: penguraian HTML cepat untuk prototipe, agen untuk jawaban yang didasarkan pada LLM, dan API SERP untuk pengumpulan berskala lebih besar.

Komentar:

0 komentar