Python ile Bing Arama Sonuçları Nasıl Kazınır?

Yorumlar: 0

Web analitiği Google ile sınırlı değildir. Bing, SEO araştırması, bağlantı arama, marka izleme, rekabet analizi ve içerik araştırması için yararlı olan alternatif bir SERP görünümü sunar. Python bu tür bir otomasyon için ideal bir araçtır: olgun bir ekosistem, basit sözdizimi ve HTML ayrıştırma ve JSON ile çalışma için sağlam kütüphaneler, Bing arama sonuçlarını daha hızlı ve daha rahat bir şekilde kazımanıza olanak tanır.

Neden Google Yerine Bing'e Odaklanalım?

Bing kendi sıralama yönergelerini ve kalite sinyallerini kullanır, bu nedenle sonuçlar genellikle Google'dan farklıdır. Bu, organik aramada ve uzun kuyruklu sorgularda ek fırsatları ortaya çıkarmak için değerlidir. Bing, web yöneticisi önerilerinde alaka düzeyi, kalite/güven, kullanıcı etkileşimi, tazelik, coğrafi faktörler ve sayfa hızı gibi Google'dan farklı bir sinyal dengesini vurgular. Bu nedenle bazı sayfalar özellikle Bing'de daha üst sıralarda yer alır.

Bing arama sonuçlarını kazırken pratik kullanım durumları:

  • Bağlantı kurma bağışçı listenizi genişletin - bu motor bazen Google'ın ilk 10'unda görünmeyen siteleri yükseltir.
  • İçerik stratejinizi ayarlamak için PAA ("İnsanlar da soruyor") ve Bing'in evrensel SERP öğelerini (video, karuseller) izleme.

Bing Aramasından Hangi Verileri Çıkarabilirsiniz?

"Klasik" bir SERP'ten güvenilir bir şekilde çıkarabilirsiniz:

  • Başlık;
  • URL (belge bağlantısı);
  • Snippet (açıklama);
  • Sonuçlardaki konum (sıra indeksi);
  • Bazı evrensel sonuçlar: "İlgili/İnsanlar da soruyor", gömülü resim/video sonuçları (doğrudan ana SERP'e dahil edildiğinde).

Önemli: Bing'in işaretlemesi periyodik olarak değişir, bu nedenle aşağıdaki koddaki seçicilerin değiştirilmesi gerekebilir.

Bing Aramasını Kazırken Dikkat Edilmesi Gereken Yasal ve Etik Hususlar

  • Microsoft'un Kullanım Koşullarını takip edin: Web verilerine "resmi" erişim için Microsoft artık Azure AI Agent'ların bir parçası olarak Bing Search ile Grounding sunuyor. Genel Bing Arama API'leri 11 Ağustos 2025 tarihinde tamamen kullanıma sunuldu.
  • Bing Search ile topraklamanın kendi TOU'su ve kısıtlamaları vardır: Azure aracıları aracılığıyla kullanılır ve sonuçlar "ham" JSON SERP verileri yerine aracının yanıtlarında geri gelir.
  • Robots.txt'ye saygı gösterin ve ana bilgisayarları aşırı yüklemekten kaçının - robotlara uymak temel kazıma etiğidir.

Kazıma için Python Ortamınızı Kurma

Temel bilgileri yükleyin:

pip install requests beautifulsoup4 lxml fake-useragent selenium
  • requests - HTTP istemcisi (User-Agent gibi başlıkları ayarlamanızı sağlar);
  • beautifulsoup4 + lxml - HTML ayrıştırma;
  • fake-useragent - rastgele UA oluşturma (veya kendi listenizi oluşturun);
  • selenium - gerektiğinde dinamik bloklar oluşturun.

Yöntem 1 - İstekler ve BeautifulSoup aracılığıyla Bing'i kazıma

İş akışını göstermek için bunu temel olarak kullanacağız: GET istekleri yayınlayın, bir User-Agent ayarlayın, sonuç kartlarını ayrıştırın ve başlık, URL, snippet ve konum toplayın.

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

Açıklama:

  • Sayfalandırma için count/first parametrelerini kullanın.
  • Seçiciler li.b_algo h2 a ve .b_caption p taban çizgisidir; düzen değişebilir (DevTools'ta inceleyin).
  • Gerektiğinde bir proxy ekleyin ve istekler arasındaki duraklamaları düzenleyin.
  • Bu örneği aşağıda biraz daha geliştireceğiz, çünkü mevcut koşullar altında amaçlarımız için en etkili yaklaşım budur.

Yöntem 2 - Bing Arama Sonuçlarını API aracılığıyla kazıyın (2025'te durum)

Microsoft'un genel Bing kazıyıcı API'si Ağustos 2025'te kullanımdan kaldırılmıştır. Microsoft, Azure Yapay Zeka Aracıları içinde Bing Search ile Grounding'e geçmenizi önerir.

Bu pratikte ne anlama geliyor

  • "Ham" JSON SERP verilerine sahip klasik REST uç noktası artık çoğu geliştirici için mevcut değildir.
  • Bing Search ile topraklama, bir Azure aracısının içinde bir araç olarak bağlanır; aracı web'i "arayabilir" ve sentezlenmiş bir yanıt döndürebilir. Hizmetin kendi TOU'su ve özellikleri vardır: ham SERP sonuçlarının toplu olarak çıkarılması için tasarlanmamıştır.

JSON'da ham SERP için alternatif

Yapılandırılmış sonuçlar döndüren üçüncü taraf SERP API'lerini/platformlarını (örneğin, Apify Bing Search Scraper) kullanın: başlık, URL, snippet, konum vb.

Minimal Apify talep örneği:

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 belgeleri organik sonuçlar, PAA, ilgili sorgular ve daha fazlası için destek sağlar. Kullanım durumunuzun platform kurallarına ve yetki alanınızdaki yasalara uygun olduğundan emin olun.

İpucu: Azure AI Agents yığınında çalışıyorsanız ve bir LLM için yalnızca topraklanmış referanslara ihtiyacınız varsa (ham JSON yerine) Bing Arama ile Topraklama.

Yöntem 3 - Selenium ile Dinamik İçeriği Ayrıştırma

SERP'de karuseller, etkileşimli bloklar veya JavaScript tarafından işlenen içerik varsa Selenium'a (Headless Chrome/Firefox) geçin.

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

Resmi belgeye bakınız Selenium dokümanları sürücü kurulumu ve WebDriverWait örnekleri için.

Pratik Çözüm: Ayrıştırma Stratejisi ve Örnek Kod

Son uygulama için Bing kazıma işlemini doğrudan HTML'den gerçekleştireceğiz:

  1. HTTP isteklerini https://www.bing.com/search adresine gönderin.
  2. Bir Kullanıcı Aracısı ayarlayın.
  3. Başlıkları, URL'leri ve snippet'leri ayıklamak için BeautifulSoup + lxml aracılığıyla HTML'yi ayrıştırın.

Bu şekilde Microsoft hesaplarına ihtiyacınız olmaz ve üçüncü taraf ücretli API'lere bağlı kalmazsınız. Sonuç seçimi için, Bing'in organik blokları için yaygın olarak kullanılan li.b_algo sonuç kartı kapsayıcısını kullanıyoruz.

Çalışma Örneği (sayfalandırma, gecikmeler, isteğe bağlı 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())

Ekstra parametreler ve bir proxy ile örnek kullanım:

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

Senaryonun yaptığı şey:

  1. Bing'e kontrollü parametreler (q, count, first) ve yerel ayarlar (cc, setlang) ile GET istekleri gönderir.
  2. User-Agent'ı geçersiz kılar ve daha kararlı snippet'ler için Accept-Language ekler.
  3. BeautifulSoup(..., "lxml") aracılığıyla HTML'yi ayrıştırır, li.b_algo sonuç kartlarını bulur ve başlık, url ve snippet'i çıkarır. BS4'teki .select() CSS seçicileri standart, esnek bir yaklaşımdır.
  4. İsteğe bağlı bir proxy'yi destekler. İstekler için doğru proxy biçimi bir protokol→URL eşlemesidir.

Denge ipuçları:

  • Duraklamalar ekleyin (istekler arasındaki aralıkları rastgele ayarlayın).
  • User-Agent'ı döndürün (dinamik olarak veya listenizden). İstekler, başlıkların nasıl doğru ayarlanacağını gösterir - bunu çalışma örneğinde yapıyoruz.
  • Platform sınırlamaları dahilinde etkili bir şekilde ölçeklendirmek için gerektiğinde proxy altyapısı/IP rotasyonu kullanın.
  • Genel talep hacimlerini makul düzeyde tutun ve CAPTCHA istemleri için yanıtları kontrol edin.
  • Karmaşık senaryolar için, antibot altyapısı içeren yönetilen SERP API'lerini (Apify, vb.) düşünün.

Araçlar hakkında daha fazla bilgi için

  • İstekler: başlıklar, proxy'ler, zaman aşımları - resmi̇ belgeler.
  • BeautifulSoup: .select() ve CSS seçicileri.
  • lxml: BS4 için HTML ayrıştırıcısı düzeltildi.

İpucu: Daha istikrarlı veri toplama için proxy altyapısına ihtiyacınız varsa Bing için en iyi proxy'ler.

Bing'i Kazırken Bloklardan Nasıl Kaçınılır?

Kazıyıcınızın ilk döngüsü sırasında "ölmemesini" sağlamak için temel ilkeler:

  • Gecikmeler ekleyin (istekler arasındaki aralıkları rastgele ayarlayın).
  • User-Agent'ınızı döndürün (dinamik olarak veya kendi listenizden); isteklerde başlıkları ayarlamanın doğru yolu belgelerde açıklanmıştır - çalışma örneğimizde de aynı yaklaşımı kullanıyoruz.
  • Proxy veya IP rotasyonu kullanın (hizmetin kullanım koşullarına uyarak).
  • CAPTCHA istemleri için toplam istek sayısını sınırlayın ve yanıtları izleyin.
  • Karmaşık görevler için, yerleşik antibot altyapısına sahip yönetilen SERP API'lerini (Apify, vb.) düşünün.

Sonuç

Araştırmayı Google'ın ötesine genişletmek, ek bağışçı alan adları toplamak, alternatif SERP özelliklerini izlemek ve manzaranın bağımsız bir görünümünü elde etmek istediğinizde Bing'i kazımak yararlıdır. İstikrarlı ve "resmi" entegrasyon için Microsoft, Azure Yapay Zeka Aracılarında Bing Arama ile Topraklamayı teşvik eder; hizmet şartları açısından daha güvenlidir ancak ham JSON SERP verilerini döndürmez. Göreviniz yapılandırılmış sonuçları ayıklamaksa, Requests/BS4 veya Selenium aracılığıyla doğrudan HTML ayrıştırmayı seçin veya özel bir SERP API'si kullanın. İşe uygun aracı seçin: prototipler için hızlı HTML ayrıştırma, LLM temelli yanıtlar için aracılar ve daha büyük ölçekli toplama için SERP API'leri.

Yorumlar:

0 yorumlar