أفضل ممارسات كشط نتائج البحث الموجهة جغرافياً: دليل المطورين

دليل عملي للمطورين حول كشط نتائج جوجل الموجهة جغرافياً باستخدام معاملات gl وhl وuule، مع أمثلة كود Python قابلة للتشغيل وبروكسي ProxyHat السكنية المطابقة للموقع الجغرافي.

Geo-Targeted SERP Scraping Best Practices for Local SEO Data
En este artículo

ما هي أفضل ممارسات كشط نتائج البحث الموجهة جغرافياً؟

عندما تبحث عن عبارة مثل "أفضل صانع قهوة" من نيويورك مقابل لوس أنجلوس، تُرجع جوجل نتائج مختلفة تمامًا — متاجر محلية، إعلانات، وحتى ترتيب النتائج العضوية يتغير. السبب هو أن جوجل تستخدم إشارات جغرافية متعددة لتحديد ما يراه المستخدم: عنوان IP، موقع GPS (على الأجهزة المحمولة)، إعدادات اللغة، ومعاملات الاستعلام الصريحة مثل gl وhl وuule. أفضل ممارسات كشط نتائج البحث الموجهة جغرافياً هي مجموعة من التقنيات التي تضمن أن البيانات التي تجمعها تعكس بدقة ما يراه مستخدم حقيقي في موقع جغرافي محدد — وليس مجرد تقريب على مستوى الدولة.

الاستهداف على مستوى الدولة وحده غير كافٍ لتحليلات SEO المحلية. متجر في بروكلين لا يظهر في نتائج بحث مستخدم في مانهاتن بنفس الترتيب، رغم أنهما في نفس المدينة من الناحية الإدارية. تحتاج إلى دقة على مستوى المدينة أو الحي، وهذا يتطلب فهمًا عميقًا لمعاملات التوطين في جوجل ومطابقتها مع بروكسي سكني من نفس المنطقة الجغرافية.

لماذا تُرجع جوجل نتائج مختلفة حسب الموقع؟

جوجل تتبنى نهجًا متعدد الطبقات للتوطين. كل طبقة تؤثر على ترتيب النتائج:

  • عنوان IP: يحدد الدولة (وأحيانًا المدينة) الافتراضية للبحث.
  • معامل gl: يفرض الدولة المستهدفة صراحةً (مثل gl=us).
  • معامل hl: لغة الواجهة (مثل hl=en أو hl=ar).
  • معامل google_domain: النطاق المستخدم (مثل google.com أو google.co.uk).
  • معامل lr: تقييد لغة المستندات (مثل lr=lang_ar).
  • معامل uule: تحديد الموقع الجغرافي الدقيق على مستوى المدينة.
  • معامل pws=0: تعطيل التخصيص للحصول على خط أساسي غير مخصص.

المشكلة أن جوجل تتحقق من تطابق هذه المعاملات مع جغرافية عنوان IP. إذا أرسلت gl=us وuule يشير إلى مدينة نيويورك، لكن طلبك يأتي من IP في فرانكفورت، فإن جوجل قد تتجاهل المعاملات أو تُرجع نتائج منقوصة أو تعرض تحدي CAPTCHA. هذا هو السبب الأساسي لاستخدام بروكسي سكنية مطابقة — راجع وثائق جوجل الرسمية حول نتائج البحث المحلي لفهم أعمق.

بناء قيمة uule: البنية التقنية

معامل uule هو الأقوى والأقل فهمًا. يتيح تحديد موقع جغرافي دقيق على مستوى المدينة أو الحي. بنيته كالتالي:

uule = w+CAIQICI + base64(canonical_place_name)

حيث:

  • w+CAIQICI — بادئة ثابتة (8 أحرف) تسبق كل قيمة uule صالحة.
  • الجزء التالي هو مفتاح طول (بايت واحد يمثل طول النص المشفر).
  • ثم base64 للاسم الكنوني للمكان (مثل New York,New York,United States).

على سبيل المثال، لتشفير Brooklyn,New York,United States:

import base64

place = "Brooklyn,New York,United States"
encoded = base64.b64encode(place.encode("utf-8")).decode("ascii")
uule = "w+CAIQICI" + encoded
print(uule)
# الناتج: w+CAIQICIQnJvb2tseW4sTmV3IFlvcmssVW5pdGVkIFN0YXRlcw==

راجع مقالة ويكيبيديا عن Geotargeting لمزيد من السياق حول كيفية استخدام منصات الإعلان والبحث لاستهداف الموقع.

مطابقة المعاملات مع البروكسي: القاعدة الذهبية

القاعدة بسيطة: المعاملات والـ IP يجب أن تتحدث بنفس اللغة الجغرافية. إذا كنت تستهدف gl=us مع uule لشيكاغو، يجب أن يأتي طلبك من IP سكني في الولايات المتحدة، ويفضل أن يكون في شيكاغو نفسها. هذا يقلل من احتمالية رصد جوجل للتناقض ويحسن معدل النجاح بشكل كبير.

مع ProxyHat، يمكنك توجيه الطلبات عبر بروكسي سكنية مطابقة للموقع المستهدف باستخدام معاملات اسم المستخدم:

  • user-country-US-city-newyork:pass — IP سكني في نيويورك.
  • user-country-DE-city-berlin:pass — IP سكني في برلين.
  • user-country-GB-city-london:pass — IP سكني في لندن.

راجع صفحة مواقع ProxyHat لقائمة الدول والمدن المدعومة، وصفحة الأسعار لتفاصيل الباقات.

مثال 1: كشط SERP موجّه جغرافياً باستخدام requests والبروكسي الخام

import base64
import requests
from urllib.parse import urlencode

def build_uule(place: str) -> str:
    """بناء قيمة uule من اسم مكان كنوني."""
    encoded = base64.b64encode(place.encode("utf-8")).decode("ascii")
    return "w+CAIQICI" + encoded

def scrape_serp_raw(query: str, place: str, country: str, lang: str, proxy_user: str, proxy_pass: str):
    uule = build_uule(place)
    params = {
        "q": query,
        "gl": country,
        "hl": lang,
        "uule": uule,
        "pws": "0",
        "num": "50",
    }
    url = f"https://www.google.com/search?{urlencode(params)}"
    proxy_url = f"http://{proxy_user}:{proxy_pass}@gate.proxyhat.com:8080"
    proxies = {"http": proxy_url, "https": proxy_url}
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Accept-Language": f"{lang},{lang};q=0.9",
    }
    try:
        resp = requests.get(url, headers=headers, proxies=proxies, timeout=30)
        resp.raise_for_status()
        return resp.text
    except requests.exceptions.RequestException as e:
        print(f"خطأ: {e}")
        return None

# استخدام بروكسي سكني في نيويورك
html = scrape_serp_raw(
    query="best coffee shop",
    place="New York,New York,United States",
    country="us",
    lang="en",
    proxy_user="user-country-US-city-newyork",
    proxy_pass="YOUR_PASSWORD",
)

مثال 2: نفس الطلب باستخدام ProxyHat SDK

from proxyhat import ProxyHatClient
import base64
from urllib.parse import urlencode

ph = ProxyHatClient(username="YOUR_USERNAME", password="YOUR_PASSWORD")

def build_uule(place: str) -> str:
    encoded = base64.b64encode(place.encode("utf-8")).decode("ascii")
    return "w+CAIQICI" + encoded

def scrape_serp_sdk(query: str, place: str, country: str, lang: str):
    uule = build_uule(place)
    params = {
        "q": query,
        "gl": country,
        "hl": lang,
        "uule": uule,
        "pws": "0",
        "num": "50",
    }
    url = f"https://www.google.com/search?{urlencode(params)}"
    # ProxyHat SDK يدير البروكسي والدوران تلقائيًا
    session = ph.session(country=country.upper(), city=place.split(",")[0].lower().replace(" ", ""))
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Accept-Language": f"{lang},{lang};q=0.9",
    }
    try:
        resp = session.get(url, headers=headers, timeout=30)
        resp.raise_for_status()
        return resp.text
    except Exception as e:
        print(f"خطأ ProxyHat SDK: {e}")
        return None

html = scrape_serp_sdk(
    query="best coffee shop",
    place="Chicago,Illinois,United States",
    country="us",
    lang="en",
)

مثال 3: استخدام curl_cffi مع انتحال Chrome

from curl_cffi import requests as cffi_requests
import base64
from urllib.parse import urlencode

def build_uule(place: str) -> str:
    encoded = base64.b64encode(place.encode("utf-8")).decode("ascii")
    return "w+CAIQICI" + encoded

def scrape_serp_cffi(query, place, country, lang, proxy_user, proxy_pass):
    uule = build_uule(place)
    params = {"q": query, "gl": country, "hl": lang, "uule": uule, "pws": "0", "num": "50"}
    url = f"https://www.google.com/search?{urlencode(params)}"
    proxy = f"http://{proxy_user}:{proxy_pass}@gate.proxyhat.com:8080"
    try:
        resp = cffi_requests.get(
            url,
            impersonate="chrome120",
            proxies={"http": proxy, "https": proxy},
            timeout=30,
        )
        resp.raise_for_status()
        return resp.text
    except Exception as e:
        print(f"خطأ curl_cffi: {e}")
        return None

html = scrape_serp_cffi(
    query="plumber near me",
    place="Houston,Texas,United States",
    country="us",
    lang="en",
    proxy_user="user-country-US-city-houston",
    proxy_pass="YOUR_PASSWORD",
)

curl_cffi مع impersonate="chrome120" يُرسل بصمة TLS مطابقة لمتصفح Chrome حقيقي، مما يقلل من احتمالية رصد البوت. هذا مهم بشكل خاص لـ أفضل ممارسات كشط نتائج البحث الموجهة جغرافياً في الولايات المتحدة حيث تكون أنظمة كشف جوجل أكثر صرامة.

مثال 4: تحليل النتائج العضوية والحزمة المحلية باستخدام selectolax

from selectolax.parser import HTMLParser
import json

def parse_serp(html: str) -> dict:
    tree = HTMLParser(html)
    results = {"organic": [], "local_pack": []}
    
    # النتائج العضوية
    for node in tree.css("div.g"):
        title_node = node.css_first("h3")
        link_node = node.css_first("a[href]")
        if title_node and link_node:
            results["organic"].append({
                "title": title_node.text(strip=True),
                "url": link_node.attributes.get("href", ""),
            })
    
    # الحزمة المحلية (Local Pack)
    for node in tree.css("div.rllt__link"):
        name_node = node.css_first("span")
        if name_node:
            results["local_pack"].append({
                "name": name_node.text(strip=True),
            })
    
    # طريقة بديلة للحزمة المحلية
    for node in tree.css("div[role=\"heading\"]"):
        aria = node.attributes.get("aria-level")
        if aria == "3":
            results["local_pack"].append({"name": node.text(strip=True)})
    
    return results

data = parse_serp(html)
print(json.dumps(data, indent=2, ensure_ascii=False))

هذا المحلل يستخرج النتائج العضوية والحزمة المحلية (Local Pack) — وهي العناصر التي تظهر في الأعلى للبحث المحلي مع خريطة وأسماء أعمال. للحصول على دقة أعلى، راجع حالة استخدام تتبع SERP.

مثال 5: جلسة ثابتة (Sticky Session) للصفحات المتعددة

import requests
import base64
from urllib.parse import urlencode

def build_uule(place: str) -> str:
    encoded = base64.b64encode(place.encode("utf-8")).decode("ascii")
    return "w+CAIQICI" + encoded

def scrape_multi_page(query, place, country, lang, max_pages=5):
    uule = build_uule(place)
    session_id = "serp-nyc-001"  # معرف جلسة ثابت
    proxy_user = f"user-country-{country.upper()}-city-{place.split(',')[0].lower().replace(' ', '')}-session-{session_id}"
    proxy_pass = "YOUR_PASSWORD"
    proxy_url = f"http://{proxy_user}:{proxy_pass}@gate.proxyhat.com:8080"
    proxies = {"http": proxy_url, "https": proxy_url}
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Accept-Language": f"{lang},{lang};q=0.9",
    }
    all_results = []
    
    for page in range(max_pages):
        start = page * 10
        params = {
            "q": query,
            "gl": country,
            "hl": lang,
            "uule": uule,
            "pws": "0",
            "start": str(start),
        }
        url = f"https://www.google.com/search?{urlencode(params)}"
        try:
            resp = requests.get(url, headers=headers, proxies=proxies, timeout=30)
            if resp.status_code == 200:
                all_results.append({"page": page + 1, "html": resp.text})
            elif resp.status_code == 429:
                print(f"صفحة {page+1}: 429 — انتظار 60 ثانية")
                import time; time.sleep(60)
                continue
            else:
                print(f"صفحة {page+1}: HTTP {resp.status_code}")
                break
        except Exception as e:
            print(f"خطأ في الصفحة {page+1}: {e}")
            break
    
    return all_results

pages = scrape_multi_page(
    query="dentist",
    place="Miami,Florida,United States",
    country="us",
    lang="en",
    max_pages=5,
)

الجلسة الثابتة تضمن أن جميع الطلبات تخرج من نفس عنوان IP السكني، مما يقلل من التناقض الذي قد ترصده جوجل عند التنقل بين الصفحات.

التراجع وإعادة المحاولة عند CAPTCHA و429

import time
import random
import requests
from urllib.parse import urlencode

def scrape_with_retry(url, headers, proxies, max_retries=5):
    backoff = 2
    for attempt in range(max_retries):
        try:
            resp = requests.get(url, headers=headers, proxies=proxies, timeout=30)
            if resp.status_code == 200:
                return resp.text
            elif resp.status_code == 429:
                wait = backoff ** attempt + random.uniform(0, 1)
                print(f"429: انتظار {wait:.1f}s قبل المحاولة {attempt+1}")
                time.sleep(wait)
            elif resp.status_code == 302 and "google.com/sorry" in resp.headers.get("location", ""):
                print(f"CAPTCHA في المحاولة {attempt+1} — تبديل البروكسي")
                # تبديل البروكسي: إنهاء الجلسة الحالية
                time.sleep(backoff ** attempt)
            else:
                print(f"HTTP {resp.status_code} في المحاولة {attempt+1}")
                time.sleep(backoff)
        except requests.exceptions.RequestException as e:
            print(f"استثناء: {e}")
            time.sleep(backoff ** attempt)
    return None

# استخدام مع تجمع بروكسي لكل دولة
country_pools = {
    "us": ["user-country-US-city-newyork", "user-country-US-city-chicago", "user-country-US-city-miami"],
    "de": ["user-country-DE-city-berlin", "user-country-DE-city-munich"],
    "gb": ["user-country-GB-city-london", "user-country-GB-city-manchester"],
}

def get_proxy_for_country(country: str, password: str):
    pool = country_pools.get(country.lower(), [])
    if not pool:
        return None
    user = random.choice(pool)
    url = f"http://{user}:{password}@gate.proxyhat.com:8080"
    return {"http": url, "https": url}

هذا النمط يطبق تراجع أُسي مع تمايز (Exponential Backoff with Jitter) — معيار صناعي موصى به. عند مواجهة 429، نتضاعف وقت الانتظار مع إضافة عشوائية لتفادي تأثير الرعد (Thundering Herd). عند مواجهة CAPTCHA، نبدل البروكسي من تجمع الدولة المعنية.

تجمعات البروكسي لكل دولة: بنية الإنتاج

في بيئات الإنتاج، يجب أن يكون لديك تجمع بروكسي منفصل لكل سوق مستهدف. هذا يضمن:

  • عزل الأعطال: إذا تم حظر IP في سوق واحد، لا يؤثر على الأسواق الأخرى.
  • معدل نجاح أعلى: كل طلب يخرج من IP في نفس الدولة المستهدفة.
  • إدارة التكاليف: يمكنك تخصيص حجم التجمع حسب حجم البيانات المطلوبة لكل سوق.

مع ProxyHat، يمكنك تحقيق ذلك عبر معاملات اسم المستخدم. على سبيل المثال، للحصول على 100 جلسة متزامنة للسوق الأمريكي، استخدم user-country-US-session-{unique_id} مع معرفات فريدة لكل طلب. راجع حالة استخدام كشط الويب لمزيد من التفاصيل.

أخطاء شائعة وحالات حافة

  • عدم تطابق gl مع google_domain: استخدام gl=de مع google.com بدلاً من google.de يسبب نتائج غير متسقة. اجعلها متسقة.
  • نسيان pws=0: بدون هذا المعامل، قد تُرجع جوجل نتائج مخصصة بناءً على سجل البحث المرتبط بالـ IP، مما يفسد خط الأساس.
  • استخدام بروكسي مركز بيانات: جوجل ترصد IP مراكز البيانات بسهولة وتعرض تحديات CAPTCHA بشكل متكرر. استخدم بروكسي سكنية.
  • عدم تدوير User-Agent: تكرار نفس User-Agent عبر آلاف الطلبات هو إشارة واضحة للبوت. دوّرها بشكل واقعي.
  • تجاهل lr: إذا كنت تجمع بيانات باللغة العربية، أضف lr=lang_ar لتقييد المستندات.
  • uule غير صحيح: اسم المكان يجب أن يكون كنونيًا ومطابقًا لما تستخدمه جوجل. اختبر القيمة قبل الإنتاج.

اعتبارات قانونية وأخلاقية

كشط نتائج جوجل يخضع لشروط خدمة جوجل التي تقيّد الوصول الآلي. قبل بناء أي نظام كشط:

هذا الدليل لأغراض تعليمية. استشر مستشارًا قانونيًا قبل نشر أي نظام كشط في الإنتاج. راجع أيضًا وثائق ProxyHat لتفاصيل التكامل.

النقاط الرئيسية

  • الدقة على مستوى المدينة ضرورية: الاستهداف على مستوى الدولة لا يكفي لتحليلات SEO المحلية — استخدم uule مع gl وhl.
  • المعاملات والـ IP يجب أن تتطابق: جوجل تتحقق من تطابق جغرافية المعاملات مع عنوان IP. استخدم بروكسي سكنية من نفس المنطقة.
  • uule = w+CAIQICI + base64(canonical_place_name): بادئة ثابتة + base64 للاسم الكنوني للمكان.
  • أضف pws=0 دائمًا: لتعطيل التخصيص والحصول على خط أساسي قابل للتكرار.
  • استخدم curl_cffi مع انتحال Chrome: لتقليل بصمة البوت عبر مطابقة بصمة TLS.
  • طبق تراجعًا أُسيًا مع تمايز: عند 429 أو CAPTCHA، مع تبديل البروكسي من تجمع الدولة.

Preguntas frecuentes

ما هي أفضل ممارسات كشط نتائج البحث الموجهة جغرافياً؟

هي مجموعة من التقنيات التي تضمن جمع بيانات نتائج بحث جوجل تعكس بدقة ما يراه مستخدم حقيقي في موقع جغرافي محدد. تشمل استخدام معاملات gl وhl وuule لتحديد الدولة والمدينة واللغة، وإضافة pws=0 لتعطيل التخصيص، وتوجيه الطلبات عبر بروكسي سكنية من نفس المنطقة الجغرافية المستهدفة لضمان تطابق المعاملات مع عنوان IP.

لماذا تُعد ممارسات كشط نتائج البحث الموجهة جغرافياً مهمة لمستخدمي البروكسي؟

لأن جوجل تتحقق من تطابق معاملات التوطين (gl، uule) مع جغرافية عنوان IP. إذا كانت المعاملات تشير إلى مدينة معينة لكن الـ IP من دولة أخرى، قد تتجاهل جوجل المعاملات أو تعرض CAPTCHA. البروكسي السكنية المطابقة للموقع تضمن نتائج دقيقة ومعدل نجاح أعلى يصل إلى 99% مقارنة ببروكسي مراكز البيانات.

أي نوع من البروكسي يعمل بشكل أفضل لكشط نتائج البحث الموجهة جغرافياً؟

البروكسي السكنية (Residential Proxies) هي الأفضل لأنها تستخدم عناوين IP حقيقية مخصصة من مزودي إنترنت شرعيين، مما يجعلها تبدو كمستخدمين حقيقيين. يمكن توجيهها إلى مدينة محددة باستخدام معاملات ProxyHat مثل user-country-US-city-newyork. البروكسي الجوالة أيضًا فعالة لكنها أغلى، بينما بروكسي مراكز البيانات تُرصد بسرعة من قبل جوجل.

كيف تتجنب الحظر عند تنفيذ كشط نتائج البحث الموجهة جغرافياً؟

استخدم بروكسي سكنية مطابقة للموقع المستهدف، دوّر User-Agent بشكل واقعي، طبق تراجعًا أُسيًا مع تمايز عند استلام 429 أو CAPTCHA، استخدم curl_cffi مع انتحال Chrome لمطابقة بصمة TLS، أبقِ معدل الطلبات منخفضًا (1-2 طلب في الثانية لكل IP)، واستخدم جلسات ثابتة للصفحات المتعددة لتجنب تبديل IP في منتصف التصفح.

كيف أبني قيمة معامل uule لتحديد مدينة محددة في جوجل؟

قيمة uule تتكون من بادئة ثابتة w+CAIQICI متبوعة بـ base64 للاسم الكنوني للمكان. على سبيل المثال، لتشفير Brooklyn,New York,United States، تستخدم base64.b64encode(place.encode('utf-8')) ثم تضيف الناتج إلى البادئة. يجب أن يكون اسم المكان كنونيًا ومطابقًا لما تستخدمه جوجل داخليًا.

¿Listo para empezar?

Proxies residenciales, ISP y móviles en más de 148 países. Creá una cuenta gratis.

Crear cuenta gratis
← Volver al Blog