خانه/مقالات/چگونه نتایج جستجوی گوگل را با پایتون استخراج کنیم
وب اسکریپینگ
سلنیوم
برگشت به صفحه مقاله ها
چگونه نتایج جستجوی گوگل را با پایتون استخراج کنیم

چگونه نتایج جستجوی گوگل را با پایتون استخراج کنیم

مقاله می‌گوید اسکرپ نتایج گوگل با پایتون به‌خاطر تغییرات دائمی HTML و ضدبات‌های گوگل سخت و شکننده می‌شود. یک روش عملی با Requests و BeautifulSoup برای استخراج عنوان/لینک/اسنیپت و صفحه‌بندی همراه با ریت‌لیمیت و Retry پیشنهاد می‌کند. در پایان می‌گوید برای استفاده پایدار و مقیاس، بهتر است سراغ APIهای آماده مثل Asanscrape بروید.
آسان اسکریپ
آسان اسکریپ
1404-12-08

اسکرپ نتایج جستجوی گوگل با پایتون (Google SERP Scraping)

استخراج نتایج جستجوی گوگل (SERP) یکی از کاربردی‌ترین مهارت‌ها برای سئو، تحلیل رقبا، تحقیق کلمات کلیدی، مانیتورینگ برند و حتی ساخت دیتاست‌های تحقیقاتی است. ایده ساده است: یک عبارت را در گوگل جستجو می‌کنید، لینک‌ها و عنوان‌ها و توضیحات را می‌بینید، و حالا می‌خواهید همین کار را با پایتون و به‌صورت خودکار انجام دهید.

اما در عمل، اسکرپ گوگل مثل اسکرپ یک وب‌سایت ساده نیست: ساختار HTML پیچیده و تو در توست، بخش‌هایی مثل تبلیغات و باکس‌های ویژه ممکن است قاطی نتایج شوند، و مهم‌تر از همه گوگل به‌شدت روی تشخیص ربات‌ها حساس است. در این مقاله یک مسیر «واقع‌بینانه» برای ساخت اسکرپر SERP با پایتون می‌سازیم: از ساخت URL و پارس HTML تا صفحه‌بندی، ریت‌لیمیت، و پایدارسازی. اگر هدف‌تان استفاده پایدار و مقیاس‌پذیر است، در انتهای مقاله هم راهکار آماده‌تر را معرفی می‌کنیم (از جمله اسکریپر گوگل سرچ Asanscrape).

 

اسکرین‌شات نتایج جستجوی گوگل و اجزای SERP

 

 

نتایج گوگل دقیقاً چه چیزهایی هستند و چرا اسکرپ کردنشان سخت می‌شود؟

وقتی وارد گوگل می‌شوید و یک کلمه را سرچ می‌کنید، خروجی فقط «۱۰ لینک آبی» نیست. بسته به نوع کوئری، ممکن است با این اجزا روبه‌رو شوید: نتایج ارگانیک، تبلیغات، باکس People Also Ask، فیچر اسنیپت، ویدیو/تصویر، نقشه، سایت‌لینک‌ها، باکس‌های خبری و ده‌ها ماژول دیگر. هر کدام می‌توانند HTML متفاوتی داشته باشند.

چالش اصلی اینجاست که گوگل برای جلوگیری از سوءاستفاده، ساختار صفحه را دائماً تغییر می‌دهد و از کلاس‌های CSS پویا (Dynamic) استفاده می‌کند؛ یعنی اگر اسکرپر شما به کلاس‌ها یا سلکتورهای شکننده وابسته باشد، خیلی زود می‌شکند. بنابراین معمولاً بهتر است روی نشانه‌های پایدارتر (مثل وجود تگ h3 برای عنوان نتیجه، یا الگوی لینک‌ها) تکیه کنید و در عین حال آماده تغییرات باشید.

از طرف دیگر، گوگل رفتارهای غیرعادی را سریع تشخیص می‌دهد: درخواست‌های زیاد از یک IP، هدرهای غیرواقعی، پارامترهای عجیب مثل درخواست ۱۰۰ نتیجه در هر صفحه، یا تکرار بیش از حد یک الگو می‌تواند باعث نمایش CAPTCHA یا بلاک شدن شود.

 

نمایی از HTML نتایج گوگل در Inspect و تگ‌های h3 و a

 

ملاحظات قانونی، اخلاقی و ریسک‌های فنی

قبل از اینکه وارد کدنویسی شوید، سه نکته را جدی بگیرید:

  • گوگل معمولاً در «شرایط استفاده» محدودیت‌هایی برای اسکرپ خودکار قائل است. حتی اگر داده عمومی باشد، ممکن است از نظر قرارداد/ToS مشکل داشته باشد.
  • در بسیاری از کشورها «اسکرپ داده عمومی» فی‌نفسه غیرقانونی نیست، اما شیوه جمع‌آوری، حجم ترافیک، و نوع داده (به‌خصوص داده شخصی) اهمیت دارد. اگر استفاده شما تجاری یا بزرگ‌مقیاس است، مشورت حقوقی ارزش دارد.
  • از نظر فنی هم باید بدانید: ساخت یک اسکرپر پایدار برای گوگل یعنی جنگ دائمی با تغییرات HTML، بلاک شدن IP، کپچا، و نوسانات نتایج.

اگر هدف شما فقط «یادگیری» است، ساخت اسکرپر آموزشی عالی است. اما اگر هدف شما «استفاده پایدار در محصول/کسب‌وکار» است، معمولاً APIهای آماده یا سرویس‌های تخصصی (که مدیریت پروکسی/کپچا/پایداری را انجام می‌دهند) هزینه را به‌شدت کاهش می‌دهند. در بخش پایانی، نمونه عملی استفاده از API را می‌بینید.

معماری پیشنهادی یک اسکرپر SERP که بعداً زمین نخورد

برای اینکه اسکرپر شما از یک اسکریپت ساده به یک ابزار قابل اتکا نزدیک شود، بهتر است معماری را این‌طور ببینید:

  • Request Layer: ساخت URL جستجو، تنظیم هدرها، مدیریت کوکی/ریدایرکت، تایم‌اوت و ریت‌لیمیت
  • Parsing Layer: استخراج عنوان/لینک/اسنیپت با منطق مقاوم (نه صرفاً کلاس‌های CSS)
  • Pagination Layer: رفتن به صفحات بعدی با پارامتر start و کنترل تعداد نتایج با num
  • Data Pipeline: تمیزکاری (Dedup)، نرمال‌سازی URLها، و خروجی گرفتن (CSV/JSON/DB)
  • Reliability: Retry، بک‌آف، لاگ، و در صورت نیاز همزمانی (Concurrency)
  • Anti-block: مدیریت پراکسی/چرخش IP، هدرهای واقعی، و رفتار انسانی‌تر

اگر این معماری را درست بچینید، بعداً می‌توانید منبع داده را هم عوض کنید: مثلاً به جای پارس HTML، از یک سرویس آماده استفاده کنید و همچنان بقیه پایپ‌لاین شما (ذخیره‌سازی، تحلیل، مانیتورینگ) بدون تغییر باقی بماند. این دقیقاً جایی است که سرویس‌هایی مثل Google Search Scraper در Asanscrape می‌توانند نقش «Request + Anti-block + Pagination» را ساده‌تر کنند.

 

دیاگرام معماری اسکرپر نتایج گوگل: درخواست، پارس، صفحه‌بندی، ذخیره

 

آموزش قدم‌به‌قدم: اسکرپ یک صفحه نتایج گوگل با Requests و BeautifulSoup

در این بخش یک اسکرپر «ساده اما قابل توسعه» می‌سازیم. هدف ما استخراج نتایج ارگانیک رایج است: عنوان (Title)، لینک (URL)، و در صورت امکان اسنیپت (Snippet). توجه کنید که HTML گوگل ثابت نیست؛ پس این کد را باید یک نقطه شروع بدانید، نه یک راه‌حل ابدی.

۱) نصب پیش‌نیازها


pip install requests beautifulsoup4 lxml

۲) ساخت URL جستجو

حداقل چیزی که نیاز دارید پارامتر q است. برای صفحه‌بندی معمولاً از start استفاده می‌شود (مثلاً ۰، ۱۰، ۲۰). برای کنترل تعداد نتایج هم پارامتر num وجود دارد (گاهی تا ۱۰۰ هم درخواست می‌شود، ولی ممکن است باعث بلاک یا خروجی ناقص شود).

۳) ارسال درخواست با هدرهای واقعی

حداقل یک User-Agent واقعی بگذارید. همچنین تایم‌اوت و مدیریت خطا را فراموش نکنید.

۴) پارس کردن HTML و استخراج نتیجه‌ها

در بسیاری از حالت‌ها، عنوان نتیجه داخل h3 است و لینک داخل a. اما لینک‌های گوگل گاهی ریدایرکت هستند (مثل /url?q=...) و باید URL واقعی را از پارامتر q بیرون بکشید.


import time
import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse, parse_qs, urljoin
GOOGLE_BASE = "https://www.google.com/search"
def extract_real_url(href: str) -> str:
    """
    لینک‌های گوگل ممکن است به شکل /url?q=https://example.com&sa=...
    باشند. این تابع URL واقعی را بیرون می‌کشد.
    """
    if not href:
        return ""
    full = urljoin("https://www.google.com", href)
    parsed = urlparse(full)
    qs = parse_qs(parsed.query)
    if "q" in qs and qs["q"]:
        return qs["q"][0]
    if parsed.scheme in ("http", "https") and parsed.netloc:
        return full
    return full
def build_google_url(query: str, start: int = 0, num: int = 10) -> str:
    return f"{GOOGLE_BASE}?q={requests.utils.quote(query)}&start={start}&num={num}"
def scrape_google_page(query: str, start: int = 0, num: int = 10, sleep_s: float = 1.0):
    headers = {
        "User-Agent": (
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/122.0.0.0 Safari/537.36"
        ),
        "Accept-Language": "fa-IR,fa;q=0.9,en-US;q=0.8,en;q=0.7",
    }
    url = build_google_url(query, start=start, num=num)
    r = requests.get(url, headers=headers, timeout=20)
    r.raise_for_status()
    soup = BeautifulSoup(r.text, "lxml")
    results = []
    seen = set()
    for h3 in soup.find_all("h3"):
        a = h3.find_parent("a")
        if not a or not a.get("href"):
            continue
        raw_href = a.get("href")
        real_url = extract_real_url(raw_href)
        title = h3.get_text(strip=True)
        if not real_url.startswith("http"):
            continue
        if "google.com" in urlparse(real_url).netloc:
            continue
        key = (title, real_url)
        if key in seen:
            continue
        seen.add(key)
        snippet = ""
        container = h3.find_parent("div")
        if container:
            text = container.get_text(" ", strip=True)
            if text and title in text:
                snippet = text.replace(title, "", 1).strip()
        results.append({
            "title": title,
            "url": real_url,
            "snippet": snippet
        })
    time.sleep(sleep_s)
    return results
if __name__ == "__main__":
    data = scrape_google_page("اسکرپ گوگل با پایتون", start=0, num=10)
    for i, item in enumerate(data, 1):
        print(i, item["title"], item["url"])

مراحل اجرای عملی (چک‌لیست)

  1. یک کوئری انتخاب کنید و با start=0 اولین صفحه را بگیرید.
  2. از روی HTML، عنوان‌ها را با h3 پیدا کنید و لینک‌های نزدیک را استخراج کنید.
  3. اگر لینک به شکل ریدایرکت گوگل بود، URL واقعی را از پارامتر q بیرون بکشید.
  4. روی خروجی Dedup انجام دهید و موارد مشکوک (لینک‌های داخلی گوگل) را حذف کنید.
  5. برای صفحه بعدی start را ۱۰ تا ۱۰ تا زیاد کنید.

صفحه‌بندی، کنترل لوکیشن و پایدارسازی اسکرپر در مقیاس

وقتی از «یک صفحه» عبور می‌کنید، تازه چالش واقعی شروع می‌شود. اینجا چند نکته کلیدی برای مقیاس و پایداری:

۱) صفحه‌بندی با start و num

گوگل معمولاً نتایج را دسته‌های حدود ۱۰تایی می‌دهد. بنابراین برای رفتن به صفحه بعدی، start را به صورت ۰، ۱۰، ۲۰، ۳۰… تنظیم کنید. اگر num را تغییر می‌دهید، بهتر است start هم متناسب با همان افزایش یابد (مثلاً start=page*num).

۲) کنترل لوکیشن (کشور/منطقه)

نتایج گوگل وابسته به لوکیشن هستند. یکی از پارامترهای رایج برای شبیه‌سازی کشور، gl است (مثلاً gl=US برای آمریکا). برای پروژه‌های حساس‌تر به لوکیشن، معمولاً باید علاوه بر پارامترها، IP/پروکسی را هم متناسب با همان کشور تنظیم کنید تا نتایج طبیعی‌تر شوند.

۳) ضدبلاک (Anti-bot) به زبان ساده

  • ریت‌لیمیت: درخواست‌ها را با فاصله زمانی بفرستید (مثلاً ۱ تا ۵ ثانیه، با کمی تصادفی‌سازی).
  • Retry + Backoff: اگر 429 یا پاسخ مشکوک گرفتید، با تأخیر بیشتر دوباره تلاش کنید.
  • هدرهای واقعی: User-Agent و Accept-Language منطقی بگذارید.
  • پروکسی/چرخش IP: برای حجم بالا معمولاً ضروری است؛ وگرنه دیر یا زود CAPTCHA می‌آید.
  • تشخیص صفحه کپچا: اگر در HTML عبارت‌هایی مثل “unusual traffic” یا فرم‌های غیرمعمول دیدید، آن درخواست را بلاک‌شده فرض کنید و مسیر را عوض کنید.

۴) همزمانی (Concurrency) با احتیاط

همزمانی می‌تواند سرعت را بالا ببرد، ولی اگر کنترل نشود خیلی سریع شما را وارد محدوده رفتار مشکوک می‌کند. اگر همزمانی می‌خواهید، تعداد تردها کم باشد و حتماً با پروکسی/ریت‌لیمیت همراه شود.


from concurrent.futures import ThreadPoolExecutor, as_completed
def scrape_many_pages(query: str, pages: int = 3, num: int = 10, max_workers: int = 3):
    starts = [p * num for p in range(pages)]
    all_results = []
    with ThreadPoolExecutor(max_workers=max_workers) as ex:
        futures = [ex.submit(scrape_google_page, query, start=s, num=num, sleep_s=1.0) for s in starts]
        for f in as_completed(futures):
            try:
                all_results.extend(f.result())
            except Exception as e:
                print("Page failed:", e)
    uniq = {}
    for r in all_results:
        uniq[r["url"]] = r
    return list(uniq.values())

ذخیره‌سازی خروجی: از CSV ساده تا دیتابیس

اگر خروجی‌تان را ذخیره نکنید، اسکرپ کردن عملاً ارزش عملی کمی دارد. ساده‌ترین گزینه CSV است؛ اما برای پروژه‌های جدی‌تر، JSON و دیتابیس منطقی‌تر می‌شود.

  • CSV: مناسب گزارش‌گیری سریع و اکسل، ولی برای داده‌های تو در تو یا توسعه‌های آینده محدود است.
  • JSON: برای نگهداری ساختار و ارسال به سرویس‌های دیگر عالی است.
  • DB: اگر می‌خواهید تاریخچه نتایج، مقایسه رتبه‌ها، یا مانیتورینگ روزانه داشته باشید، دیتابیس (PostgreSQL/SQLite) انتخاب بهتری است.

import csv
from pathlib import Path
def save_to_csv(filename: str, rows: list[dict]):
    path = Path(filename)
    if not rows:
        return
    write_header = not path.exists()
    with path.open("a", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=rows[0].keys())
        if write_header:
            w.writeheader()
        w.writerows(rows)

راه پایدارتر برای محصول و مقیاس: استفاده از API اسنسکرپ (Asanscrape) برای Google Search

اگر هدف شما «استفاده پایدار» است (نه فقط تمرین آموزشی)، معمولاً چالش‌های اصلی این‌ها هستند: بلاک و کپچا، مدیریت پروکسی و لوکیشن، صفحه‌بندی قابل اعتماد، و خروجی ساخت‌یافته. اینجا دقیقاً جایی است که Asanscrape به‌عنوان یک API آماده کمک می‌کند: شما به جای جنگیدن با HTML و ضدبات‌ها، یک درخواست API می‌زنید و خروجی JSON ساخت‌یافته تحویل می‌گیرید.

 

 

نمونه بلاک یا کپچا در اسکرپ نتایج گوگل و اهمیت پایداری

 

 

Asanscrape چه کمکی می‌کند؟

  • خروجی استاندارد و قابل پردازش (عنوان، لینک، اسنیپت و…)
  • پشتیبانی از صفحه‌بندی با pagination_token (بدون درگیری با start/HTML)
  • مناسب برای ساخت ابزارهای سئو: مانیتورینگ رتبه، بررسی رقبا، تحقیق کلمات کلیدی
  • کاهش ریسک شکست اسکرپر به خاطر تغییرات HTML گوگل

لینک داخلی مهم: اگر دقیقاً دنبال اسکرپ نتایج گوگل هستید، این صفحه مربوط به ربات گوگل سرچ در Asanscrape است: اسکریپر گوگل سرچ. همچنین اگر می‌خواهید همه ربات‌ها/اسکرپرهای آماده را ببینید: لیست همه اسکرپرها.

نمونه استفاده از API اسنسکرپ (Robot: google search)


import requests
BASE_URL = "[https://backend.asanscrape.com](https://backend.asanscrape.com)"
API_KEY = ""
url = f"{BASE_URL}/robots/create/task/wait-for-result/"
headers = {
"accept": "*/*",
"Authorization": f"token {API_KEY}",
"Content-Type": "application/json"
}
payload = {
"robot": "google search",
"inputs": {
"keyword": "asanscrape",
"domain": "google.com",
"from_": "2024-01-01  (optional)",
"to_": "2024-12-31  (optional)"
},
"pagination_token": "NEXT_TOKEN (optional)"
}
response = requests.request("POST", url, headers=headers, json=payload)
print(response.json()) 

نمونه خروجی (Example Response)


{
  "data": {
    "request": {
      "status": true,
      "status_code": 200,
      "request_data": "{\"count\": 1.0, \"keyword\": \"\\u062f\\u0627\\u062f\\u0647 \\u0627\\u0633\\u062a\\u062e\\u0631\\u0627\\u062c\", \"domain\": \"google.com\"}"
    },
    "response": [
      {
        "title": "استخراج داده چیست؟ روش‌ها و تکنیک‌های موثر",
        "url": "https://****/پردازش-داده-ها/استخراج-داده-data-extraction-چیست؟/",
        "snippet": "Oct 14, 2025 — استخراج داده فرآیند جمع‌آوری سیستماتیک داده‌ها ...",
        "favicon_text": "****"
      }
    ],
    "next_page_token": "gAAAAABpnkN6..."
  },
  "error": {}
}

چطور در پروژه‌تان از Asanscrape استفاده کنید؟

  1. API Key را داخل پروژه قرار دهید (ترجیحاً با متغیر محیطی).
  2. برای هر کلمه کلیدی، یک تسک با Robot «google search» بسازید.
  3. خروجی را بخوانید و در دیتابیس/CSV ذخیره کنید.
  4. اگر next_page_token داشتید، برای صفحه بعدی همان را به‌عنوان pagination_token بفرستید.

جمع‌بندی: ساخت اسکرپر دستی با پایتون برای یادگیری فوق‌العاده است (و در پروژه‌های کوچک هم جواب می‌دهد)، اما برای مقیاس و پایداری، بهترین تصمیم معمولاً این است که «مسائل زیرساختی و ضدبات» را به یک سرویس تخصصی بسپارید. اگر می‌خواهید سریع‌تر، مطمئن‌تر و با خروجی ساخت‌یافته‌تر به داده‌های SERP برسید، API اسنسکرپ مسیر را کوتاه و پایدار می‌کند.

لینک‌های داخلی پیشنهادی برای ادامه مسیر:

مقاله‌های مرتبط