ko
English
Español
中國人
Tiếng Việt
Deutsch
Українська
Português
Français
भारतीय
Türkçe
Italiano
Gaeilge
اردو
Indonesia
Polski 웹 분석은 Google에만 국한되지 않습니다. Bing은 SEO 연구, 링크 탐색, 브랜드 모니터링, 경쟁 분석 및 콘텐츠 조사에 유용한 SERP에 대한 대안적인 보기를 제공합니다. Python은 이러한 종류의 자동화에 이상적인 도구로, 성숙한 에코시스템, 간단한 구문, HTML 구문 분석 및 JSON 작업을 위한 강력한 라이브러리를 통해 Bing 검색 결과를 보다 빠르고 편리하게 스크랩할 수 있습니다.
Bing은 자체 순위 가이드라인과 품질 신호를 사용하므로 결과가 Google과 다른 경우가 많습니다. 이는 자연 검색 및 롱테일 쿼리에서 추가적인 기회를 발견하는 데 유용합니다. 웹마스터 추천에서 Bing은 관련성, 품질/신뢰도, 사용자 참여, 최신성, 지리적 요인, 페이지 속도를 강조하는데, 이는 Google과는 다른 신호의 균형입니다. 그렇기 때문에 일부 페이지가 Bing에서 특히 더 높은 순위를 차지하는 것입니다.
Bing 검색 결과를 스크랩할 때의 실제 사용 사례:
"클래식" SERP에서 안정적으로 추출할 수 있습니다:
중요: Bing의 마크업은 주기적으로 변경되므로 아래 코드의 선택기를 조정해야 할 수 있습니다.
기본 사항을 설치합니다:
pip install requests beautifulsoup4 lxml fake-useragent selenium
이를 워크플로우 데모의 기준으로 삼아 GET 요청을 발행하고, 사용자 에이전트를 설정하고, 결과 카드를 구문 분석하고, 제목, URL, 스니펫 및 위치를 수집하겠습니다.
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")
설명:
Microsoft의 공용 Bing 스크레이퍼 API는 2025년 8월에 사용 중단되었습니다. Microsoft는 Azure AI 에이전트 내에서 Bing Search를 사용한 Grounding으로 마이그레이션할 것을 권장합니다.
이것이 실제로 의미하는 것
제목, URL, 스니펫, 위치 등 구조화된 결과를 반환하는 타사 SERP API/플랫폼(예: Apify Bing 검색 스크레이퍼)을 사용하세요.
최소 Apify 요청 예시:
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']
오가닉 결과, PAA, 관련 쿼리 등에 대한 문서 지원을 제공합니다. 사용 사례가 플랫폼 규칙과 관할 지역의 법률을 준수하는지 확인하세요.
팁: Azure AI 에이전트 스택에서 작업하고 원시 JSON이 아닌 LLM에 대한 접지된 참조만 필요한 경우 다음에 대한 가이드를 읽어보세요. Bing 검색으로 접지하기.
SERP에 캐러셀, 대화형 블록 또는 자바스크립트로 렌더링된 콘텐츠가 포함된 경우, 셀레늄(헤드리스 크롬/파이어폭스)으로 전환하세요.
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"))
공식 셀레늄 문서 드라이버 설치 및 WebDriverWait 예제를 참조하십시오.
최종 구현을 위해 HTML에서 직접 Bing 스크래핑을 수행하겠습니다:
이렇게 하면 Microsoft 계정이 필요하지 않으며 타사 유료 API에 종속되지 않습니다. 결과 선택에는 Bing의 오가닉 블록에 일반적으로 사용되는 결과 카드 컨테이너 li.b_algo를 사용합니다.
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())
추가 매개변수와 프록시를 사용한 사용 예시입니다:
python bing_scraper.py -q "Python web scraping" --pages 3 --csv out.csv \
--proxy "http://username:password@proxy:port"
스크립트의 기능
안정성 팁:
도구에 대해 자세히 알아볼 수 있는 곳
팁: 보다 안정적인 데이터 수집을 위해 프록시 인프라가 필요한 경우, 다음을 확인하세요 Bing을 위한 최고의 프록시.
스크레이퍼가 첫 번째 주기 동안 "죽지 않도록" 하는 핵심 원칙입니다:
Bing 스크래핑은 Google을 넘어 연구를 확장하고, 추가 제공자 도메인을 수집하고, 대체 SERP 기능을 추적하고, 환경에 대한 독립적인 시각을 얻고자 할 때 유용합니다. 안정적이고 "공식적인" 통합을 위해 Microsoft는 Azure AI 에이전트에서 Bing Search와의 접목을 권장하며, 이는 서비스 약관 관점에서 더 안전하지만 원시 JSON SERP 데이터를 반환하지 않습니다. 구조화된 결과를 추출해야 하는 작업이라면 Requests/BS4 또는 Selenium을 통한 직접 HTML 구문 분석을 선택하거나 특수 SERP API를 사용하세요. 프로토타입을 위한 빠른 HTML 구문 분석, LLM 기반 답변을 위한 에이전트, 대규모 수집을 위한 SERP API 등 작업에 적합한 도구를 선택하세요.
댓글: 0