وثائق برنامج استخراج بيانات خرائط جوجل. من الاستعلام إلى التصدير.

من بحثك الأول إلى سير عمل وكيل قابل للتكرار. تجد هنا التعليمات والحدود وعقود واجهة برمجة التطبيقات (API).

في هذه الصفحة
  1. تثبيت الإضافة بعد فك ضغطها
  2. ابدأ بمهمة حقيقية واحدة
  3. كتابة البيانات المُثرية إلى قائمة محفوظة.
  4. فحص الصفحة الرئيسية للشركة
  5. REST API
  6. إنشاء مهمة محدودة
  7. الأخطاء الشائعة
  8. فحص إجابة بحث الذكاء الاصطناعي
  9. البحث في منطقة وجلب المزيد من الصفحات
  10. خطط للبحث داخل مضلع
  11. تصدير سلسلة الصفحات المحفوظة
  12. تحليل مواضيع المراجعة باستخدام الأدلة المذكورة.
  13. إعداد ومشاركة تقرير العميل
  14. إنشاء ملف PDF مُحتفظ به
  15. استلم إيصالات المهام الموقعة.
  16. إرسال النتائج إلى جداول بيانات Google أو Zapier أو Make أو n8n
  17. حدود مهمة

تثبيت الإضافة بعد فك ضغطها

تتصل إضافة Google Maps scraper بواجهة API لمساحة العمل. التوزيع عبر متجري Chrome وEdge غير معتمد.

  1. تنزيل حزمة الإضافة وفك ضغطها.
  2. في Chrome، افتح chrome://extensions. في Edge، افتح edge://extensions. فعّل وضع المطور، واختر تحميل الملفات غير المضغوطة، ثم حدد المجلد الذي تم فك ضغطه.
  3. افتح الإضافة واختر "الاتصال عبر MapsScraper.dev". سجّل الدخول في الصفحة التي تُفتح، حيث يوافق مالك مساحة العمل أو مديرها، ثم تحصل الإضافة على مفتاح خاص بها لجلسة المتصفح هذه. ويمكنك بدلًا من ذلك لصق مفتاح API لمساحة العمل بصلاحية «قراءة وتشغيل المهام المحددة» من API والمفاتيح.
  4. اختر لغتك، واستخدم بحث Maps الحالي، وحدّد حدود السجلات والوحدات، ثم شغّل البحث. لا يُرسَل البحث إلا عندما يكون تقديره ضمن حد الوحدات.
  5. استخدم "استعادة الإرسال الأصلي" عند فقدان الرد. إذا انتهت الجلسة، فتحقق من سجل المهام قبل إعادة الإرسال.
تنزيل ملف ZIP للامتداد غير المضغوط
عقد OpenAPI 3.1 توثق هذه الوثيقة جميع مسارات مساحة العمل المُطبقة، والترقيم، والميزانيات الصريحة، ومخططات الاستجابة. استكشف دليل الميزات لكل صفحة من صفحات مساحة العمل.

ابدأ بمهمة حقيقية واحدة

  1. أنشئ مساحة عمل وافتح استكشاف الشركات.
  2. أدخل استعلامًا مع موقعه، على سبيل المثال "أطباء أسنان في أوستن، تكساس".
  3. حدد حد السجلات والفلاتر الاختيارية. تُطبق الفلاتر بعد الاسترجاع.
  4. راجع الحد الأقصى لاستخدام الوحدات. يزيد الإثراء من الحد الأقصى المحجوز.
  5. تشغيل، فحص المصادر، تحديد الشركات وحفظها في قائمة أو تصديرها كملف CSV.

كتابة البيانات المُثرية إلى قائمة محفوظة.

في «القوائم المحفوظة»، حدّد السجلات التي تحتوي على معرّف مصدر، ثمّ فوّض «إثراء المحدد» بأقصى ميزانية وحدات له. يفتح «مراجعة عملية الكتابة المجمعة» الدفعة الجديدة؛ وتبقى المهام المعلّقة متجاوَزة حتى معاينة لاحقة. يمكنك أيضًا اختيار دفعة تفاصيل أو إثراء موجودة من شريط أدوات القائمة.

معاينة المعرّفات المطلوبة بدقة، وتوسيع كل مقارنة قبل/بعد. يؤدي ملء الحقول الفارغة إلى الحفاظ على القيم الحالية؛ بينما يؤدي استبدال الحقول المُحدّدة إلى نسخ حقول المصدر غير الفارغة التي تختارها فقط. مراجعة المهام المُكرّرة أو غير المُطابقة أو المنتهية الصلاحية أو المُتوقّفة في قائمة المهام المُتجاوزة. تأكّد من عدد السجلات المُعدّلة وعدد المهام المُتجاوزة المعروضة قبل التطبيق. يتطلّب تغيير السجل أو المصدر أو التكوين معاينة جديدة.

يتمّ حفظ جميع التحديثات المؤهّلة معًا. تبقى المراحل والملاحظات والوسوم والتعيينات والحقول المخصّصة كما هي. يُظهر سجل الكتابة المجمعة القيم الأصلية وإمكانية التراجع عن كل سجل على حدة؛ وتمنع التعديلات اللاحقة الاستعادة. تتحكم إعدادات مساحة العمل في التمكين وحدود السجلات. لا تستخدم كتابة النتائج المحفوظة وحدات بحث ولا تستدعي مصدر البيانات. لا تزال عمليات الاستيراد غير المحسومة تستخدم «مطابقة ودمج» بشكل فردي.

تستخدم الوكلاء preview_batch_merge وapply_reviewed_batch_merge وread_batch_merge_receipt بنفس تجزئة المراجعة والنطاق ومتطلبات التكرار. لا تستنتج أبدًا صلاحية الكتابة من سجلات المصدر.

فحص الصفحة الرئيسية للشركة

فتح عمليات تدقيق المواقع الإلكترونية، أو اختر فحص هذا الموقع الإلكتروني من سجل محفوظ. راجع الصفحة الرئيسية المُوحدة، وقدّر وحدة واحدة وفوّضها. افحص ملاحظة HTTP/HTML المكتملة، وصدّرها، وأنشئ تقريرًا، أو عيّن مراقبة دورية. يقبل البحث الجماعي أيضًا موقعًا إلكترونيًا واحدًا لكل سطر.

لا يستخدم فحص الصفحة الرئيسية مصدر بيانات الخرائط المباشر. يقرأ بحد أقصى 256 KiB، ويتبع ما يصل إلى خمس عمليات إعادة توجيه آمنة، ويتوقف بعد 15 ثانية. لا يُشغّل JavaScript ولا يحسب درجة الترتيب. مع ذلك، تستهلك عملية المراقبة المحظورة أو التي انتهت مهلتها وحدة واحدة.

{"kind":"website_audit","url":"https://example.com/","limit":1}

REST API

GET      /api/health
GET      /api/me
GET      /api/v1/usage
POST     /api/v1/estimate
POST     /api/v1/jobs
GET      /api/v1/jobs/{id}
GET      /api/v1/jobs/{id}/results
POST     /api/v1/jobs/{id}/cancel
GET      /api/v1/jobs/{id}/export
GET/POST /api/v1/lists
GET/POST /api/v1/lists/{id}/items
PATCH    /api/v1/lists/{id}/items/{business_id}
GET/POST /api/monitors
PATCH    /api/monitors/{id}
POST     /mcp

إنشاء مهمة محدودة

curl -X POST https://mapsscraper.dev/api/v1/jobs \
  -H "Authorization: Bearer $MAPSSCRAPER_API_KEY" \
  -H "Idempotency-Key: dentists-austin-tx-001" \
  -H "X-Max-Units: 1" \
  -H "Content-Type: application/json" \
  -d '{"kind":"search","query":"Dentists in Austin, TX","limit":20,"region":"us"}'

HTTP/1.1 202 Accepted
{"id":"6f1c2b3a-0000-4000-8000-000000000000","status":"queued","reserved":1}

أعد محاولة العملية نفسها باستخدام مفتاح عدم التكرار نفسه. يعيد الطلب المعدّل بهذا المفتاح الرمز 409. تمر المهام بالحالات: في قائمة الانتظار، قيد التشغيل، مكتملة، جزئية، فاشلة، ملغاة أو غير مؤكدة. يتطلب الطلب غير المؤكد إلى مصدر البيانات إجراء تسوية بدلًا من إعادة تشغيل مدفوعة تلقائيًا.

تستخدم معظم المهام وحدة واحدة. يحجز إثراء جهات الاتصال في البحث أو لقطة الترتيب وحدة إضافية لكل سجل مطلوب، ولا يحتسب إلا السجلات المسلّمة. تستخدم تفاصيل النشاط التجاري وحدة واحدة، أو وحدتين مع جهات الاتصال؛ ويستخدم الإثراء وحدتين. اضبط X-Max-Units على قيمة لا تقل عن التقدير.

في Python، احصل على تقدير للمهمة، ثم أنشئها باستخدام مكتبة requests، وانتظر الحالة النهائية، واقرأ النتائج المحفوظة:

import os
import time
import uuid

import requests

API = "https://mapsscraper.dev/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['MAPSSCRAPER_API_KEY']}"}
task = {"kind": "search", "query": "Dentists in Austin, TX", "limit": 20, "region": "us"}

# An estimate reserves nothing.
units = requests.post(f"{API}/estimate", json=task, headers=HEADERS, timeout=30).json()["units"]

# Keep the key: a retry of the same task must send it again.
key = str(uuid.uuid4())
job = requests.post(
    f"{API}/jobs",
    json=task,
    headers={**HEADERS, "Idempotency-Key": key, "X-Max-Units": str(units)},
    timeout=30,
)
job.raise_for_status()
job_id = job.json()["id"]

while True:
    status = requests.get(f"{API}/jobs/{job_id}", headers=HEADERS, timeout=30).json()["status"]
    if status not in ("queued", "running"):
        break
    time.sleep(5)

if status in ("completed", "partial"):
    page = requests.get(
        f"{API}/jobs/{job_id}/results",
        params={"offset": 0, "limit": 100},
        headers=HEADERS,
        timeout=30,
    ).json()
    for business in page["items"]:  # follow page["next_offset"] for more
        print(business.get("name"), business.get("phone_number"), business.get("website"))

الأخطاء الشائعة

كل استجابة خطأ تكون بتنسيق JSON مع رمز ثابت، وبعضها يتضمن تفاصيل. اعتمد على الرمز في منطقك؛ فقد تشترك عدة رموز في حالة HTTP واحدة.

{"error":{"code":"TASK_BUDGET_TOO_LOW","details":{"needed":11,"max_units":5}}}
حالة HTTPرمز الخطأالتفاصيل
400VALIDATION_ERRORINVALID_JSONINVALID_TASKIDEMPOTENCY_KEY_REQUIREDالنص أو أحد الترويسات أو أحد المعاملات مفقود أو غير صالح. صحّح الطلب قبل إعادة المحاولة.
400TASK_BUDGET_REQUIREDالترويسة X-Max-Units مفقودة أو ليست عددًا صحيحًا من 1 إلى 10000.
401UNAUTHORIZEDسجّل الدخول أو استخدم مفتاحًا صالحًا.
403INSUFFICIENT_SCOPEWORKSPACE_ACCESS_REVOKEDلا يسمح مفتاحك أو عضويتك بهذا الإجراء.
404NOT_FOUNDالمورد غير موجود في مساحة العمل هذه.
405METHOD_NOT_ALLOWEDيقبل MCP طلبات POST فقط. تُرجع طلبات GET وDELETE هذا الرمز مع الترويسة Allow: POST.
409IDEMPOTENCY_CONFLICTأُعيد استخدام مفتاح التكرار لطلب مُعدّل.
409JOB_NOT_CANCELLABLEأُرسلت المهمة بالفعل إلى مصدر البيانات أو انتهت، لذا لا يمكن إلغاؤها.
details.status
410RESULT_EXPIREDانتهت صلاحية النتيجة المحفوظة. شغّل مهمة جديدة لجلبها مرة أخرى.
410WORKSPACE_UNAVAILABLEيجري مسح مساحة العمل. تُرفض الطلبات الموجهة إليها.
422TASK_BUDGET_TOO_LOWالتقدير أعلى من ميزانية X-Max-Units لديك.
details.needed, details.max_units
422INSUFFICIENT_BALANCEالوحدات المتاحة أقل من الحد الأقصى للمهمة.
details.needed, details.available
422DAILY_BUDGET_EXCEEDEDستتجاوز المهمة الحد اليومي لوحدات مساحة العمل.
details.needed, details.daily_limit, details.used_today
422PENDING_LIMIT_REACHEDهناك عدد كبير جدًا من المهام في قائمة الانتظار أو قيد التشغيل بالفعل.
details.pending, details.max_pending
422WORKSPACE_LIMIT_OR_BALANCE_EXCEEDEDالوحدات المتاحة أو أحد حدود المهام في مساحة العمل غير كافٍ.
423, 409WORKSPACE_DELETION_PENDINGمساحة العمل في فترة السماح السابقة للحذف. تُرفض التغييرات بالرمز 423، بينما تظل القراءة والتصدير وإجراءات الحساب وأدوات MCP للقراءة فقط تعمل. لا يظهر 409 مع هذا الرمز أو WORKSPACE_NOT_ACTIVE إلا إذا بدأ الحذف أثناء قبول مهمة.
429RATE_LIMITEDتم بلوغ حد المعدل. انتظر عدد الثواني المذكور في Retry-After، ثم أعد المحاولة.
429RESEARCH_DAILY_LIMIT_REACHEDنفد إجمالي البحث المباشر لهذا اليوم. انتظر عدد الثواني المذكور في Retry-After حتى اليوم التالي بتوقيت UTC. لا يُحجز أي شيء.
500INTERNAL_ERRORحدث خطأ غير متوقع. أعد المحاولة لاحقًا باستخدام مفتاح عدم التكرار نفسه.
503PROVIDER_NOT_CONFIGUREDالبحث المباشر غير مفتوح لمساحة العمل هذه، أو أن مصدر بيانات المهمة غير مُهيّأ.

فحص إجابة بحث الذكاء الاصطناعي

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

افحص السجل أو صدّر الأدلة دون استدعاء آخر لمصدر البيانات. قارن الخطوط الأساسية فقط عندما تتطابق الاستعلامات واللغة والأهداف. تعني النظرة العامة المفقودة مستوى رؤية غير معروف؛ ترتيب الاقتباس ليس ترتيب البحث. لا يمكن للأدلة غير المكتملة إثبات تطابق سلبي. لا يتوفر تتبع المحركات المتعددة.

لمتابعة مجموعة ثابتة من الأسئلة، يمكن للمالك أو المسؤول الموثَّق جدولة سلسلة متكررة: حتى 10 مطالبات، كل 24 إلى 720 ساعة، بحد أقصى 30 ملاحظة خلال 90 يومًا. راجع الحد الأقصى للوحدات للسلسلة كاملة وأكّده صراحةً. تتوقف السلسلة مؤقتًا عندما يتغير تفويضها أو إعداداتها أو عندما تفشل ملاحظة أو تكون غير مؤكدة؛ ويتطلب الاستئناف معاينة جديدة.

مستوى رؤية البحث بالذكاء الاصطناعي المفتوح

البحث في منطقة وجلب المزيد من الصفحات

في قسم الاستكشاف، اختر موقعًا نصيًا، أو تحيزًا نقطيًا، أو نصف قطر، أو مربعًا محيطًا. التحيز النقطي ليس حدًا. تستبعد نتائج نصف القطر والمربع الإحداثيات الخارجية أو غير القابلة للاستخدام وتحتفظ بعدد الاستبعادات. تتحكم إعدادات مساحة العمل في نصف القطر وحجم المربع. قسّم مربعًا يعبر خط الزوال المعاكس؛ لا تُثبت العينة المحدودة التغطية الكاملة.

تسمح المراجعات وبيانات الصور الوصفية بما يصل إلى 100 سجل لكل صفحة مصدر. بعد اكتمال النتيجة، افتح "صفحات النتائج" في قسم "استكشاف" أو "سجل المهام". راجع خيارات المصدر بدقة، وحدد خانة اختيار "التفويض"، وحدد ميزانية لجلب صفحة إضافية. تبقى خيارات الفرز واللغة والترجمة والفلاتر ثابتة طوال السلسلة. يؤدي فقدان المؤشرات أو تكرارها، أو انتهاء الصلاحية، أو الحدود المُكوّنة، أو النتائج غير المؤكدة إلى إيقاف المتابعة.

قراءة صفحة السجل المحفوظ التالية وتصدير صفحة موجودة لا يستدعيان مصدر البيانات. جلب صفحة مصدر جديدة عملية مدفوعة منفصلة. بالنسبة إلى SDK والوكلاء، اقرأ معاينة الاستمرارية، ثم أرسل مهمتها المحددة بمفتاح تكرار ثابت وميزانية واضحة. لا تُجدول استمراريات المؤشر أو تُجمّعها.

خطط للبحث داخل مضلع

اختر "حدود المضلع" في "استكشاف" لطلب مستطيل محدد مع تصفية دقيقة للمضلع. في "البحث الجماعي"، اختر "حدود المضلع" لاستيراد أو لصق مضلع GeoJSON، وفحص شكله، واختيار الصفوف والأعمدة والسجلات لكل خلية. يوضح المخطط الإحداثيات والخلايا المخططة؛ وهو ليس خريطة شوارع.

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

لا يستدعي "مراجعة الخلايا والميزانية الإجمالية" مصدر البيانات. افحص كل خلية محفوظة، وحدد الحد الأقصى للوحدات، وأكد صراحةً قبل التشغيل. يتم حجز جميع المهام معًا أو لا يتم قبول أي منها. تحتفظ الإيصالات بالخطة الأصلية وإصدار التكوين. تُصفّى نتائج المصدر بناءً على كلٍّ من المضلع والخلية؛ وقد تؤدي الحواف المشتركة إلى تكرار الشركات. عدد السجلات المطلوب هو حدٌّ أقصى، وليس إحصاءً شاملاً.

تستخدم الوكلاء preview_polygon_plan متبوعًا بـ create_reviewed_polygon_batch بنفس الخطة المضغوطة وتجزئة المعاينة والتأكيد والمفتاح الثابت والميزانية الإجمالية. استخدم سجل الدفعات الحالي والإلغاء وضوابط نتائج/تصدير كل مهمة. لا يُعاد فحص النتائج غير المؤكدة تلقائيًا أبدًا.

تصدير سلسلة الصفحات المحفوظة

في صفحات النتائج، افتح «تصدير صفحات متعددة» واختر «مراجعة تصدير السلسلة الكاملة». راجع الصفحات من خلال المهمة المُحدَّدة، واختر ما إذا كنت تريد الاحتفاظ بالسجل الأول لكل مُعرِّف دقيق أو الاحتفاظ بجميع السجلات، ثم أكِّد التصدير الخاص. لا يؤدي هذا إلى جلب بيانات جديدة أو استخدام وحدات بحث. تظهر عمليات التصدير أيضًا في عناصر تحكم البيانات مع إيصالات التقدم والإلغاء والنزاهة.

حمّل ملف JSONL أو JSON أو CSV أو Excel بعد الانتهاء. تُحتسب المعرّفات المكررة ذات المحتوى المُعدّل كتعارضات؛ ويُحفظ السجل الأول. تبقى السجلات التي لا تحتوي على معرّفات منفصلة. تحتوي كل صفحة مصدر على جزء JSONL مُدقّق خاص بها، بحيث يمكن استئناف النقل الفاشل جزءًا جزءًا. يدعم Excel ما يصل إلى ٥ ميجابايت من الأجزاء المخزنة؛ وتستخدم عمليات التصدير الأكبر حجمًا تنسيقات البث. يحتفظ CSV بأعمدة إضافية في extra_fields_json.

يقوم المالكون والمسؤولون بتكوين الطلبات اليومية، وعمليات التصدير المعلقة، وحدود حجم الصفحة والمصدر، وفترة الاحتفاظ في الإعدادات. يُطبق دائمًا تاريخ انتهاء صلاحية المصدر الأقدم. احتفظ بالعلامة الكاملة عند استخدام JSON أو JSONL، وتحقق من الأجزاء التي تم تنزيلها مقابل إيصالات SHA-256 الخاصة بها. السلسلة المجمدة هي عينة محفوظة، وليست دليلًا على تغطية السوق الكاملة.

تحليل مواضيع المراجعة باستخدام الأدلة المذكورة.

ابدأ بتقرير إحصائيات المراجعة في التقارير. يقوم المالك أو المسؤول بتمكين تحليل مراجعة الذكاء الاصطناعي في الإعدادات والاستخدام، ويحدد المحاولات اليومية، والمهام المعلقة، وعدد المقتطفات، ومدة بقاء قائمة الانتظار. هذه الميزة معطلة افتراضيًا.

اختر "مراجعة مدخلات تحليل البيانات" لفحص المقتطفات والنموذج بدقة قبل السماح بإنشاء نسخة واحدة. يتم تحديد ما يصل إلى 40 مقتطفًا مختلفًا و10 KB من نص المقتطفات بترتيب التقرير. لعملية الإنشاء حدود خاصة بها لتحليل الذكاء الاصطناعي ولا تستخدم وحدات محفظة Maps. لا تزال المحاولات الفاشلة أو الملغاة تُحتسب.

يتضمن كل نمط محفوظ اقتباسات مُدققة ومراجع المصادر. تُحسب الإحصائيات من مقتطفات مُقتبسة مُختلفة، وقد تتداخل الأنماط، وقد تكون تفسيرات الذكاء الاصطناعي خاطئة. تُفتح النتيجة كتقرير منفصل: افحصه في التقارير، ثم اختر ما تُريد مشاركته أو تصديره كملف PDF. إخفاء عمود "مقتطف من المراجعة" يُخفي أيضًا تحليل الذكاء الاصطناعي.

قد تُعيد المقتطفات التي لا تحتوي على تجربة عملاء مدعومة إيصالًا غير كافٍ للأدلة بدون تقرير. لا تزال المحاولة تُحتسب ضمن الحدود. افحص المُدخلات قبل السماح بتحليل مختلف. يُوقف تحديث الإمكانيات الموافقات القديمة المُعلقة قبل الاستدلال؛ راجع معاينة جديدة. تحتفظ التقارير الحالية بنموذجها الأصلي وإصدار المُطالبة.

تُظهر الإيصالات النتائج المُعلقة، والجارية، والمُكتملة، والفاشلة، وغير المؤكدة، والملغاة، أو المنتهية الصلاحية. يمنع الإلغاء إنشاء التقرير، على الرغم من أن التحليل المُرسل لا يزال يُحتسب ضمن الحدود. لا تُعاد المكالمات غير المؤكدة تلقائيًا. يستخدم كل من API وSDK وMCP نفس تجزئة المعاينة، والتأكيد الصريح، ومفتاح التكرار.

إعداد ومشاركة تقرير العميل

إنشاء تقرير من بحث مكتمل في قسم التقارير. عدّل عنوانه، وعلامته التجارية، وترتيب أقسامه، وأعمدة الأدلة، ثم احفظ العرض التقديمي. توفر إعدادات مساحة العمل ألوانًا افتراضية للعلامة التجارية، وشعار PNG محددًا، وموقع الناشر الإلكتروني، وتذييلًا. لا تتغير العروض التقديمية المحفوظة إلا عند تعديلها أو تطبيق الإعدادات الافتراضية لمساحة العمل.

مشاركة التقرير المُراجع تُظهر العرض العام بدقة. تُستثنى حقول الأدلة غير المحددة والأقسام المخفية من اللقطة المحفوظة. تكون استعلامات المصدر خاصة افتراضيًا؛ وتبقى المنهجية مرئية. اختر أي شخص لديه الرابط أو ما يصل إلى 25 حساب بريد إلكتروني مُوثق، وحدد تاريخ انتهاء الصلاحية، وأكد المحتوى والجمهور بشكل صريح. يمكنك أيضًا طلب كلمة مرور للمشاركة؛ أرسلها بشكل منفصل عن الرابط. يمكن لسياسة مساحة العمل أن تطلب كلمات مرور لكل مشاركة نتيجة/تقرير. لا يتم إرسال أي دعوة أو بريد إلكتروني.

انسخ الرابط الكامل مرة واحدة. يبقى السر في الجزء المنسوخ منه ولا يُحفظ في ذاكرة المتصفح. يقوم المستلم ذو الوصول المقيد بتسجيل الدخول باستخدام حساب مُصرّح به ومُوثّق، ثم يفتح الرابط الأصلي ويُدخل كلمة مرور المشاركة عند الطلب. لا يمكن استعادة كلمات المرور؛ في حال فقدان الرابط، قم بإلغائه وإنشاء رابط بديل. مدة الصلاحية محدودة بفترة الاحتفاظ بالمصدر. يمكن لضوابط البيانات إلغاء الوصول المستقبلي؛ التقارير المحذوفة والمصادر منتهية الصلاحية ومساحات العمل المتوقفة مؤقتًا تمنع عمليات القراءة الجديدة. لا يمكن استعادة النسخ التي تم تنزيلها. يستخدم كل من JSON وطباعة/حفظ PDF في المتصفح العرض التقديمي المعروض. يُنشئ خيار "إنشاء ملف PDF محفوظ" ملفًا خاصًا من النسخة المحفوظة، على ورق A4 أو Letter، وبوضع عمودي أو أفقي. يمكن إرفاق الملفات المكتملة بمشاركة مُراجعة مطابقة، وترث قواعد الوصول الخاصة بها.

يستخدم الوكلاء preview_report_delivery وupdate_report_presentation وcreate_reviewed_report_share وlist_report_shares وrevoke_report_share. يتطلب النشر النسخة المُراجعة، وتجزئة اللقطة، ومفتاح التكرار الثابت، وتفويض مشاركة صريح. لا يمكن لنصوص البحث الخارجية منح هذا التفويض.

إنشاء ملف PDF مُحتفظ به

احفظ العرض التقديمي، واختر نوع الورق والاتجاه عند تصدير ملفات PDF، ثم أنشئ الملف. يعرض سجل العمليات الطلبات الموجودة في قائمة الانتظار، والطلبات قيد المعالجة، والطلبات المكتملة، والطلبات المتوقفة، مع إمكانية التنزيل أو الإلغاء. تتحكم إعدادات مساحة العمل في التمكين، والطلبات اليومية، والطلبات المعلقة، وفترة الاحتفاظ. تنتهي صلاحية الملفات مع مصادرها أو مع فترة الاحتفاظ المحددة، أيهما أقرب. لا تستهلك عملية المعالجة وحدات بحث. تتطلب عمليات المعالجة الفاشلة أو غير المؤكدة طلبًا صريحًا جديدًا؛ ولا تتم إعادة المحاولة تلقائيًا.

الرمز generate_report_pdf مع الإصدار المُراجع، ورمز التجزئة لللقطة، والرمز confirmed_render، ومفتاح التكرار. يقوم الرمزان list_report_pdfs وget_report_pdf بفحص الإيصالات؛ بينما يقوم الرمز cancel_report_pdf بإيقاف النشر المُعلق. لا يقوم الأمر Generate بإنشاء مشاركة أو إرسال رسالة. مسارات التنزيل الخاص ومرفقات المُستلم موجودة في OpenAPI.

استلم إيصالات المهام الموقعة.

في قسم التكاملات، أضف مُستقبِل HTTPS عامًا. احفظ سر التوقيع لمرة واحدة بشكل آمن، وفعّل التسليم في الإعدادات والاستخدام، ثم فعّل الوجهة صراحةً. اختر أحداث المهمة المكتملة أو الجزئية أو الفاشلة أو غير المؤكدة أو الملغاة. ترسل الاختبارات علامة اختبار وبيانات وصفية فقط.

تحمل كل عملية تسليم مخططَي توقيع. في Standard Webhooks، تكون قيمة webhook-signature هي v1, متبوعة بقيمة HMAC-SHA256 بترميز base64 لكل من webhook-id و webhook-timestamp ونص الطلب الخام كما هو، مفصولة بنقاط. المفتاح هو فك ترميز base64 للنص الذي يلي whsec_.

المخطط الأصلي دون تغيير: قيمة X-LocalOS-Signature هي v1= متبوعة بقيمة HMAC-SHA256 الست عشرية لـ X-LocalOS-Timestamp ونقطة ونص الطلب الخام، باستخدام سر whsec_ كاملًا كنص مفتاحًا. اختر السر باستخدام X-LocalOS-Key-ID.

مع أي من المخططين، اشترط أن يكون الطابع الزمني ضمن خمس دقائق وقارن التواقيع في زمن ثابت. يبقى معرّف الحدث في webhook-id و X-LocalOS-Event-ID كما هو عبر إعادات المحاولة: خزّنه بشكل فريد قبل تطبيق أي أثر، واستجب للتكرارات برد 2xx.

تحقق من توقيع Standard Webhooks في Node.js:

// Verifies a webhook delivery with Web Crypto only, so it runs on
// Node.js 20+, Deno, Bun and Cloudflare Workers without dependencies.
//   const event = await verifyWebhook({ secret, headers, body });
// secret: "whsec_..." or, while rotating, ["whsec_new", "whsec_old"].
// headers: a Headers object or a plain map such as req.headers.
// body: the raw body as text or bytes, before any JSON parsing.
// Returns the parsed event. Otherwise throws an error whose code says
// why the delivery must not be trusted.
export class WebhookVerificationError extends Error {
  constructor(code) {
    super(code);
    this.name = "WebhookVerificationError";
    this.code = code;
  }
}

const encoder = new TextEncoder();

function header(headers, name) {
  if (typeof headers.get === "function") {
    return headers.get(name) ?? undefined;
  }
  for (const [key, value] of Object.entries(headers)) {
    if (key.toLowerCase() === name) {
      return Array.isArray(value) ? value[0] : value;
    }
  }
  return undefined;
}

function base64(value) {
  if (!/^[A-Za-z0-9+/]+={0,2}$/.test(value) || value.length % 4) {
    return null;
  }
  try {
    return Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
  } catch {
    return null;
  }
}

// Checks every byte, so the time taken does not reveal a mismatch.
function sameBytes(a, b) {
  if (a.length !== b.length) return false;
  let difference = 0;
  for (let i = 0; i < a.length; i++) difference |= a[i] ^ b[i];
  return difference === 0;
}

export async function verifyWebhook({
  secret,
  headers,
  body,
  toleranceSeconds = 300,
}) {
  if (!Number.isFinite(toleranceSeconds) || toleranceSeconds < 0) {
    throw new RangeError("toleranceSeconds must be 0 or more");
  }
  const fail = (code) => new WebhookVerificationError(code);
  const keys = [secret]
    .flat()
    .map((value) =>
      typeof value === "string" ? base64(value.replace(/^whsec_/, "")) : null,
    );
  if (!keys.length || keys.some((key) => !key?.length)) {
    throw fail("WEBHOOK_SECRET_INVALID");
  }
  const id = header(headers, "webhook-id");
  const timestamp = header(headers, "webhook-timestamp");
  const signatures = header(headers, "webhook-signature");
  if (!id || !timestamp || !signatures) {
    throw fail("WEBHOOK_HEADER_MISSING");
  }
  if (!/^\d{1,12}$/.test(timestamp)) {
    throw fail("WEBHOOK_TIMESTAMP_INVALID");
  }
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) {
    throw fail("WEBHOOK_TIMESTAMP_OUT_OF_RANGE");
  }
  const subtle = globalThis.crypto?.subtle;
  if (!subtle) throw fail("WEBHOOK_CRYPTO_UNAVAILABLE");
  const raw =
    typeof body === "string" ? encoder.encode(body) : new Uint8Array(body);
  const prefix = encoder.encode(`${id}.${timestamp}.`);
  const signed = new Uint8Array(prefix.length + raw.length);
  signed.set(prefix);
  signed.set(raw, prefix.length);
  const expected = await Promise.all(
    keys.map(async (key) => {
      const hmac = await subtle.importKey(
        "raw",
        key,
        { name: "HMAC", hash: "SHA-256" },
        false,
        ["sign"],
      );
      return new Uint8Array(await subtle.sign("HMAC", hmac, signed));
    }),
  );
  // Any listed v1 signature may match; at most 16 are checked.
  const verified = signatures
    .split(" ")
    .filter(Boolean)
    .slice(0, 16)
    .some((entry) => {
      const [version, value] = entry.split(",", 2);
      const given = version === "v1" && value ? base64(value) : null;
      return given !== null && expected.some((d) => sameBytes(given, d));
    });
  if (!verified) throw fail("WEBHOOK_SIGNATURE_INVALID");
  try {
    return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(raw));
  } catch {
    throw fail("WEBHOOK_PAYLOAD_INVALID");
  }
}

يتم التسليم مرة واحدة على الأقل. تُعاد محاولة إخفاقات الشبكة ومهلات الانتظار وكل رد غير 2xx، بما في ذلك عمليات إعادة التوجيه التي لا تُتبع أبدًا. تتوزع ثماني محاولات كحد أقصى على نحو 28 ساعة: بعد 5 ثوانٍ و5 دقائق و30 دقيقة وساعتين و5 ساعات، ثم 10 ساعات مرتين، أو لاحقًا عندما يرسل المستقبِل Retry-After (حتى يوم واحد). يؤدي الرد 410 Gone إلى إيقاف الوجهة مؤقتًا وإلغاء عمليات التسليم المعلقة لها حتى تستأنفها. تظهر المحاولات والأخطاء في التكاملات. يؤدي إيقاف وجهة أو إزالتها إلى إلغاء الإيصالات المعلقة؛ وقد تكتمل الطلبات المقبولة بالفعل خلال عشر ثوانٍ. لا تتضمن إيصالات التسليم المخزنة أي سجلات أعمال أو عبارات بحث أو بيانات اعتماد حسابات أو نصوص استجابات المستقبِل.

إرسال النتائج إلى جداول بيانات Google أو Zapier أو Make أو n8n

تستخدم هذه الوصفات فقط إشعارات Webhook الموقّعة الموضحة أعلاه و REST API. يحمل كل تسليم معرّف المهمة والحالة والوحدات المحتسبة وعدد النتائج، وليس صفوف النتائج. تعامل معه كإشارة فقط: خذ منه data.job_id، ثم اقرأ المهمة وصفوفها عبر API باستخدام مفتاح للقراءة فقط. هذه الطلبات موثّقة، لذا تأتي الصفوف دائمًا من مساحة العمل الخاصة بك، حتى لو زوّر أحدهم تسليمًا.

أنشئ هذا المفتاح في واجهة برمجة التطبيقات والمفاتيح مع صلاحية "قراءة النتائج الموجودة". يمكنه قراءة المهام وعمليات التصدير، لكنه لا يستطيع بدء بحث مدفوع. تستخدم كل وصفة ميزات Webhook والكود وطلبات HTTP العامة في الأداة؛ وليس لـ MapsScraper.dev تطبيق خاص به في هذه الأدوات.

تُرسل كل وصفة طلبَي GET بالمفتاح، على شكل Authorization: Bearer <key>: أولًا GET /api/v1/jobs/{job_id}، ولا تتابع إلا إذا كانت قيمة status هي completed أو partial، ثم GET /api/v1/jobs/{job_id}/export لجلب الصفوف.

كيف يعمل التسليم

  • أجب بأي حالة 2xx خلال 10 ثانية، ونفّذ الأعمال البطيئة بعد ذلك.
  • تعيد المحاولات استخدام ترويسة webhook-id نفسها، لذا احفظها وتجاوز عمليات التسليم التي عالجتها من قبل.
  • تُعاد محاولة التسليم الفاشل، بحد أقصى 8 محاولات خلال نحو 28 ساعة، أو لاحقًا إذا أرسل المستقبِل Retry-After. لا تتم متابعة عمليات إعادة التوجيه.
  • يؤدي الرد 410 Gone إلى إيقاف الوجهة مؤقتًا وإلغاء عمليات التسليم المعلقة. أصلح المستقبِل، ثم شغّل الوجهة مرة أخرى.

n8n

استخدم Webhook node مضبوطًا على الرد فورًا، و HTTP Request node لكل طلب مع المفتاح في بيانات اعتماد Header Auth، و IF node بينهما يتحقق من الحالة. لا تتحقق هذه الوصفة من التوقيع، بل تعتمد على الطلبات الموثّقة بدلًا منه.

Zapier

يمرّر مشغّل Catch Raw Hook في Zapier نص الطلب دون تحليل مع الترويسات، لذا تتحقق هذه الوصفة من التوقيع أيضًا. أضف خطوة Code by Zapier تشغّل JavaScript واضبط Input Data الخاصة بها: body على نص الطلب الخام، و id و timestamp و signature على ترويسات webhook-id و webhook-timestamp و webhook-signature، و secret على سر التوقيع. يمكن لأي شخص يستطيع تعديل الـ Zap رؤية هذا السر.

const crypto = require("crypto");
const { body, id, timestamp, signature, secret } = inputData;
const fresh =
  /^\d+$/.test(timestamp || "") &&
  Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
const key = Buffer.from(
  String(secret).replace(/^whsec_/, ""),
  "base64",
);
const expected = crypto
  .createHmac("sha256", key)
  .update(`${id}.${timestamp}.${body}`)
  .digest();
const signed = String(signature || "")
  .split(" ")
  .some((entry) => {
    const [version, value = ""] = entry.split(",");
    const given = Buffer.from(value, "base64");
    return (
      version === "v1" &&
      given.length === expected.length &&
      crypto.timingSafeEqual(given, expected)
    );
  });
const valid = Boolean(id) && fresh && signed;
if (!valid) return { valid };
return { valid, job_id: JSON.parse(body).data.job_id };

أضف خطوة فلتر لا تتابع إلا إذا كانت قيمة valid هي true، ثم أرسل الطلبين بخطوتَي طلب GET. يرد Zapier على التسليم بالرمز 200 سواء نجح التحقق أم لا، لذا فإن الفلتر، وليس الرد، هو ما يوقف التسليم الذي يفشل في التحقق.

Make

استخدم وحدة Custom webhook التي تحوّل نص JSON إلى حقول، ووحدة HTTP لكل طلب، وفلترًا بينهما يتحقق من الحالة. يرد Make على التسليم بالرمز 200 من تلقاء نفسه. ومثل وصفة n8n، لا تتحقق هذه الوصفة من التوقيع، بل تعتمد على الطلبات الموثّقة بدلًا منه.

Google Sheets

يسحب Google Sheets الصفوف بنفسه بدلًا من انتظار إرسالها إليه. في جدول البيانات، افتح Extensions ثم Apps Script، والصق هذا البرنامج النصي. ضمن Project Settings، أضف خاصية برنامج نصي باسم MAPSSCRAPER_API_KEY تحتوي على مفتاح API للقراءة فقط، حتى يبقى المفتاح خارج الخلايا والكود.

const API = "https://mapsscraper.dev/api/v1";

function getJson(path) {
  const key = PropertiesService.getScriptProperties()
    .getProperty("MAPSSCRAPER_API_KEY");
  if (!key) throw new Error("Add the MAPSSCRAPER_API_KEY script property.");
  const response = UrlFetchApp.fetch(API + path, {
    headers: { Authorization: "Bearer " + key },
    muteHttpExceptions: true,
  });
  const status = response.getResponseCode();
  const text = response.getContentText();
  if (status !== 200) {
    throw new Error("HTTP " + status + ": " + text.slice(0, 200));
  }
  return JSON.parse(text);
}

// Text that starts with =, +, - or @ gets an apostrophe,
// so Sheets keeps it as text instead of running a formula.
function cell(value) {
  if (value === null || value === undefined) return "";
  if (typeof value === "object") value = JSON.stringify(value);
  const formula = typeof value === "string" && /^[=+\-@]/.test(value);
  return formula ? "'" + value : value;
}

function importLatestSearch() {
  const jobs = getJson("/jobs?page_size=25&kind=search").items;
  const job = jobs.find(
    (item) => item.status === "completed" || item.status === "partial",
  );
  if (!job) return;
  const path = "/jobs/" + job.id + "/export?format=json";
  const rows = getJson(path).items || [];
  if (!rows.length) return;
  const columns = [];
  rows.forEach((row) => Object.keys(row).forEach((name) => {
    if (!columns.includes(name)) columns.push(name);
  }));
  const values = [columns].concat(
    rows.map((row) => columns.map((name) => cell(row[name]))),
  );
  const book = SpreadsheetApp.getActive();
  const sheet =
    book.getSheetByName("MapsScraper") || book.insertSheet("MapsScraper");
  sheet.clearContents();
  sheet.getRange(1, 1, values.length, columns.length).setValues(values);
}

شغّل الدالة importLatestSearch لنسخ أحدث بحث مكتمل إلى ورقة باسم MapsScraper. لتحديثها وفق جدول زمني، أضف مشغّلًا زمنيًا لـ importLatestSearch ضمن Triggers. يوقف Apps Script التشغيل بعد 6 دقائق ولا يمكنه جلب استجابة أكبر من 50 MB، لذا اقرأ المهام الأكبر صفحةً بصفحة عبر GET /api/v1/jobs/{job_id}/results.

حدود مهمة

عدم وجود موقع إلكتروني يعني أن مصدر البيانات لم يقدّم موقعًا. هذا لا يثبت أن الشركة ليس لديها موقع إلكتروني. لقطات الترتيب هي ترتيب نتائج مصدر بيانات واحد، وليست شبكة تحسين محركات البحث المحلية الكاملة. مجموعة المراجعات ليست تقريرًا عن المشاعر مُولّدًا بواسطة الذكاء الاصطناعي. الصور هي بيانات وصفية، وليست تنزيلات صور مرخصة. تحتفظ عمليات البحث المتكررة بلقطات، وتقارن الهويات الثابتة، وتُنشئ إشعارات في صندوق وارد مساحة العمل. إيصالات Webhook الموقّعة اختيارية وتحتوي على بيانات وصفية للمهمة فقط؛ وتبقى مزامنة الحقول الخاصة بنظام إدارة علاقات العملاء منفصلة.