العودة إلى المدونة

أتمتة جمع العملاء من خرائط جوجل باستخدام n8n

Ilyas Yıldırım
Ilyas Yıldırım
قراءة 10 دقيقة

نعم، يمكن لـ n8n أتمتة جمع العملاء المحتملين من خرائط جوجل من دون نقرات متكررة أو برنامج خاص. يبدأ المسار مهمة في BasedOnB عبر واجهة REST، ويفحص حالتها على فترات محدودة، ثم يجلب كل صفحات النتائج ويرسل الشركات إلى Google Sheets أو نظام CRM.

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

إذا لم تحدد الحقول أو الشريحة بعد، فابدأ بدليل إنشاء قائمة عملاء من خرائط جوجل. يفترض هذا الشرح أن الفئة والمدينة والحجم والوجهة معروفة.

ما الذي تحتاج إليه؟

  • نسخة n8n تستطيع إرسال طلبات HTTPS إلى الإنترنت.
  • مفتاح BasedOnB API بصلاحية قراءة مهام الاستخراج وكتابتها. يبدأ المفتاح بـ bdb_live_.
  • جدول أو قائمة CRM تحمل عمودًا لمعرف خارجي ثابت.
  • معرف حملة أو حدث مصدر يصلح لمفتاح idempotency.
  • عبارة بحث، ودولة، وإمارة أو منطقة، ومدينة، وعدد سجلات مستهدف.

أنشئ المفتاح من لوحة التحكم واحفظه في Header Auth Credentials داخل n8n. لا تضع المفتاح الحقيقي في Edit Fields أو ملف workflow مصدر أو صورة شاشة أو رسالة دعم. تعرض وثائق BasedOnB API الحالية نقاط الاتصال وحقول الرد.

راع حدود العمل قبل الجدولة. تقبل المهمة 10 استعلامات كحد أقصى. لا يمكن أن يملك الحساب أكثر من مهمتين مفتوحتين، ولا يمكن أن يتجاوز مجموع أهداف المهام المفتوحة 5,000 سجل. تعد حالات pending وsubmitted وrunning مفتوحة. ويعيد تجاوز هذه الحدود رد سعة بحالة 429.

شكل المسار باختصار

  1. يستقبل Schedule Trigger أو Webhook الحملة.
  2. يرتب Edit Fields القيم ويضيف مفتاح idempotency ثابتًا.
  3. يرسل HTTP Request الطلب POST /api/v1/scrapes.
  4. يفحص IF قيمة statusCode، ثم يحول الرد الناجح إلى حالة polling من عنصر واحد.
  5. يزيد Wait وEdit Fields قيمة attempt في فرع الحالة.
  6. يستدعي HTTP Request المسار GET /api/v1/scrapes/:id، ثم يدمج Merge الرد مع الحالة.
  7. يفصل Switch أخطاء HTTP وdone وfailed وcancelled والحالات المفتوحة.
  8. بعد done يجلب HTTP Request صفحات النتائج ويفحص statusCode أولًا.
  9. ينقسم رد كل صفحة مرة واحدة: يذهب body.results إلى Split Out، ويفحص عنصر تحكم واحد body.page.
  10. يطلب فرع التحكم cursor التالي مرة واحدة ويعيد الرد إلى نقطة التقسيم نفسها.

تشرح وثائق عقدة HTTP Request الرسمية المصادقة والرؤوس والمعاملات. وتشرح وثائق عقدة Wait التوقف المؤقت واستئناف التنفيذ.

1. إعداد طلب ثابت

أضف Edit Fields بعد المشغل مباشرة. يمكن أن يكون جسم طلب دبي هكذا:

{
  "query": "property management companies",
  "country": "AE",
  "state": "AE.03",
  "city": "Dubai",
  "target_leads": 250
}

يحدد query نوع النشاط. تحدد حقول الموقع المنطقة. يحدد target_leads عدد سجلات الشركات المطلوب، ويجب أن يناسب الرصيد وحدود المهام المفتوحة.

أضف حقل idempotency_key مثل dubai-property-2026-08-batch-01. يكون معرف الحملة في CRM أو معرف الحدث الذي بدأ المسار أفضل من نص يدوي. لا تستخدم الوقت الحالي. فإذا أعاد n8n التشغيل بوقت جديد، قد يرى الخادم طلبًا جديدًا وينشئ مهمة أخرى.

2. بدء مهمة الاستخراج

اضبط HTTP Request كما يلي:

  • Method: POST
  • URL: https://www.basedonb.com/api/v1/scrapes
  • Authentication: بيانات Header Auth ترسل Authorization: Bearer bdb_live_...
  • Header: قيمة Idempotency-Key من Edit Fields
  • Body Content Type: JSON
  • Body: الحقول الخمسة السابقة
  • Options > Response > Include Response Headers and Status: مفعّل
  • Options > Response > Never Error: مفعّل
  • Options > Response > Response Format: JSON

تجعل هذه الخيارات n8n يعيد الردود غير 2xx في output بدل إيقاف العقدة. يحتوي العنصر الكامل على statusCode وheaders وbody. ابدأ بالتفرع حسب $json.statusCode. لا ينتقل إلى منطق المهمة إلا رد 2xx. أنشئ له عنصر Poll State في Edit Fields يحمل job_id = {{$json.body.id}} وjob_status = {{$json.body.status}} وattempt = 0. أرسل الرموز الأخرى إلى فرع خطأ HTTP المشترك.

غالبًا تعيد المهمة الجديدة حالة submitted، لكن نتيجة متاحة مسبقًا قد تعيد done فورًا. لذلك اقرأ $json.body.status بعد فحص 2xx وقبل الانتقال إلى Wait.

يعيد المفتاح نفسه مع الجسم نفسه المهمة الأصلية. أما استخدام المفتاح نفسه مع مدينة أو استعلام أو هدف مختلف فينتج تعارضًا. احفظ المفتاح وجسمه كسجل واحد.

3. Polling محدود باستخدام Wait وSwitch

استخدم عنصر Poll State الذي أنشئ بعد POST الناجح. وزع حقل job_status كما يلي:

  • done: انتقل إلى النتائج.
  • failed أو cancelled: انتقل إلى فرع الخطأ النهائي.
  • pending أو submitted أو running: انتقل إلى Wait.

يمكن ضبط Wait على 15 ثانية. بعد الانتظار استخدم Edit Fields للاحتفاظ بـ job_id واضبط attempt = {{$json.attempt + 1}}. صل عنصر الحالة الوحيد بفرعين. يستقبل Input 1 في عقدة Merge العنصر مباشرة. ويستدعي الفرع الآخر:

GET https://www.basedonb.com/api/v1/scrapes/{{$json.job_id}}

استخدم بيانات Header Auth نفسها وخيارات Response الثلاثة في عقدة POST. صل رد HTTP الخاص بالحالة إلى Input 2 في Merge. اضبط Merge على Mode: Combine وCombine By: Position. يحتفظ العنصر المدمج عندها بـ job_id وattempt إلى جانب statusCode وbody، فلا يمحو رد GET العداد.

بعد Merge افحص $json.statusCode قبل قراءة الجسم. تذهب الردود غير 2xx إلى فرع خطأ HTTP المشترك. في رد 2xx يقرأ Switch الحقل $json.body.status ويفصل done وfailed وcancelled والحالة المفتوحة. ويفحص IF منفصل في الفرع المفتوح $json.attempt قبل الرجوع إلى Wait.

ضع IF آخر قبل الرجوع إلى Wait. عندما يبلغ attempt الحد الذي اخترته، أوقف الفحص وأرسل تنبيهًا يحمل معرف المهمة. مثلًا، تحد 40 محاولة بفاصل 15 ثانية مدة تنفيذ هذا المسار، لكنها لا تعد بأن كل مهمة تنتهي خلال عشر دقائق. لا تلغ مهمة الخادم تلقائيًا عند الحد، فقد تنتهي لاحقًا.

لا تعد فرعي failed وcancelled إلى الحلقة. احفظ الحالة ومعرف المهمة والحملة ومعرف الطلب إن توفر. افحص سبب الخطأ قبل إرسال مهمة جديدة.

4. جلب كل صفحات النتائج

لا تطلب النتائج إلا بعد done:

GET https://www.basedonb.com/api/v1/scrapes/{{$json.id}}/results

أضف معامل limit=500. فعّل Include Response Headers and Status وNever Error، واختر JSON في Response Format مثل عقد HTTP Request الأخرى. افحص $json.statusCode أولًا وأرسل الردود غير 2xx إلى فرع الخطأ المشترك. يكون الرد الكامل الناجح هكذا:

{
  "statusCode": 200,
  "body": {
    "id": "scrape-job-id",
    "status": "done",
    "results": [],
    "page": {
      "limit": 500,
      "next_cursor": "next-page-token",
      "has_more": true,
      "total": 1250
    }
  }
}

احتفظ برد الصفحة الناجح كعنصر واحد وصله بفرعين. يستخدم فرع البيانات Split Out على body.results، ثم يربط الصفوف ويكتبها بطريقة upsert. لا يمر فرع التحكم عبر Split Out. يقرأ IF فيه $json.body.page.has_more، لذلك يعمل مرة لكل صفحة لا مرة لكل شركة.

في فرع true، يضبط Edit Fields القيمتين job_id = {{$json.body.id}} وcursor = {{$json.body.page.next_cursor}}. تستدعي عقدة HTTP Request باسم Next Results النقطة نفسها مع limit=500 وهذا cursor وخيارات Response نفسها. أعد ردها إلى فحص statusCode ونقطة الفرعين نفسها التي استخدمتها الصفحة الأولى. ينهي فرع false ترقيم الصفحات. تحتاج 1,250 نتيجة إلى ثلاثة طلبات لأن الحد الأقصى للصفحة 500.

لا تخترع رقم صفحة ولا تفترض أن total هو عدد صفوف الرد الأول. اتبع cursor الذي يعيده الخادم. اكتب كل صفحة قبل طلب التالية حتى يبقى لديك موضع واضح للاستكمال.

5. ربط الحقول مع Sheets أو CRM

في فرع البيانات، استخدم Split Out لتحويل body.results إلى عناصر. بعد ذلك حدد الربط في Edit Fields:

حقل BasedOnBحقل الوجهة
place_idexternal_id
titlecompany_name
categorycategory
addressaddress
phonephone
websitewebsite
ratinggoogle_rating
reviews_countgoogle_review_count

استخدم place_id لمنع التكرار. اختر upsert في CRM بدل create بلا فحص. وفي Google Sheets ابحث عن المفتاح أولًا، ثم حدّث الصف أو أضفه. يؤدي append المباشر إلى تكرار الشركات عند إعادة تنفيذ workflow.

استخدم upsert واحفظ معرف المهمة وcursor لكل صفحة. يستطيع فرع التحكم ذو العنصر الواحد جلب الصفحة التالية بينما يكتب فرع البيانات. إذا احتجت ترتيبًا صارمًا، اجعل التحكم ينتظر إشارة واحدة تفيد اكتمال الصفحة من عقدة الوجهة. لا تصل IF الخاص بالصفحة التالية بعد Split Out، وإلا سيطلب cursor نفسه مرة لكل نتيجة.

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

أخطاء شائعة

لأن Never Error مفعّل، يجب أن تتفرع كل عقدة HTTP أولًا حسب $json.statusCode. في الردود غير 2xx افحص $json.body.error.code و$json.body.error.details؛ يبقى جسم الخطأ متاحًا في هذا الفرع المقصود.

401 Unauthorized: المفتاح مفقود أو غير صالح أو ملغى. افحص بيانات الاعتماد ولا تطبع السر في سجلات التنفيذ.

403 Forbidden: المفتاح صالح لكنه لا يحمل الصلاحية المطلوبة. استخدم أقل صلاحيات قراءة وكتابة مناسبة.

402 Payment Required: يحتاج الرصيد أو الاشتراك أو الدفع إلى إجراء. أوقف الفرع وأبلغ مالك الحساب.

409 Conflict: استُخدم مفتاح idempotency مع جسم مختلف. قارن المفتاح والجسم المحفوظين. لا تعد المحاولة بالمفتاح نفسه إلا بعد إعادة الجسم الأصلي. استخدم مفتاحًا ثابتًا جديدًا لمهمة جديدة مقصودة.

429 Too Many Requests: افحص $json.body.error.details.reason. تعني too_many_open_scrapes انتظار انتهاء واحدة من المهمتين المفتوحتين. وتعني open_leads_limit الانتظار أو خفض الهدف الجديد حتى لا يتجاوز مجموع المهام المفتوحة 5,000 سجل. إذا غاب reason فتعامل معه كحد لمعدل الطلبات، واستخدم رأس Retry-After أو $json.body.error.details.retry_after_seconds في فرع Wait محدود.

نتائج فارغة: افحص الحالة. قد تعيد نقطة النتائج صفحة فارغة ما دامت المهمة نشطة.

يعرض دليل الأتمتة مع Zapier وMake تدفقات أخرى تعتمد الأحداث. تبقى هوية المهمة والحالات النهائية والكتابة بلا تكرار مهمة في كل أداة.

Polling أم webhook؟

يسهل بناء polling ومراجعته. يناسب المهام الفردية والجداول قليلة التكرار. لكنه يرسل طلبات حالة متكررة، ويجعل تنفيذ n8n يحتفظ بعداد المحاولات.

يناسب webhook المهام المتكررة أو التي تختلف مدتها. يستقبل n8n حدث النهاية، ويتحقق من التوقيع، ثم يبدأ جلب النتائج. تظل معالجة الخطأ وcursor وupsert مطلوبة. تتغير طريقة اكتشاف النهاية فقط.

ابدأ بـ polling محدود إذا كان يوضح حالات المهمة لفريقك. انتقل إلى webhook عندما تصبح طلبات الفحص عبئًا تشغيليًا.

الأسئلة الشائعة

هل يستطيع n8n جمع عملاء محتملين من خرائط جوجل تلقائيًا؟

نعم. يستطيع n8n استدعاء واجهة BasedOnB REST لبدء بحث عن الشركات، والانتظار أثناء التنفيذ، وجلب كل صفحات النتائج، ثم إرسال الصفوف إلى Google Sheets أو نظام CRM. احفظ مفتاح API داخل بيانات اعتماد n8n، لا داخل حقل عادي في المسار.

ما عقد n8n المطلوبة لهذا المسار؟

استخدم مشغلًا، وEdit Fields، وHTTP Request، وWait، وIF أو Switch، وMerge، وSplit Out، ثم عقدة النظام الهدف. يحفظ Merge عداد polling مع رد الحالة. ويبقى Split Out في فرع البيانات فقط حتى يحتفظ فرع التحكم في الصفحات بعنصر واحد.

لماذا يجب إرسال Idempotency-Key؟

يجعل إعادة طلب POST آمنة. عند إرسال المفتاح نفسه مع الطلب نفسه، تعيد الخدمة المهمة الأصلية بدل إنشاء مهمة ثانية. استخدم معرف حملة أو حدث مصدر محفوظًا، ولا تنشئ المفتاح من الوقت الحالي.

كم مرة يجب أن يفحص n8n حالة المهمة؟

ضع عقدة Wait بين طلبات الحالة وحدد عددًا أقصى للمحاولات. فترة ثابتة من 10 إلى 20 ثانية بداية عملية، لكنها ليست وعدًا بوقت الانتهاء. عند بلوغ الحد أوقف الحلقة وأرسل تنبيهًا إلى المسؤول.

كيف أجلب أكثر من 500 نتيجة؟

اطلب 500 صف كحد أقصى، واقرأ body.page.has_more وbody.page.next_cursor من رد HTTP الكامل. اجعل ترقيم الصفحات في فرع تحكم يحمل عنصرًا واحدًا، واستمر حتى تصبح has_more مساوية لـ false.

هل أستخدم polling أم webhook في n8n؟

يكون polling أبسط للمسار الأول وللمهام المتفرقة. يناسب webhook الموقّع المهام المتكررة أو الطويلة، لأن n8n ينتظر حدث الاكتمال بدل تكرار طلبات الحالة. وفي الحالتين يجب جلب النتائج بصفحات cursor.

ماذا أفعل إذا أصبحت المهمة failed أو cancelled؟

عامل failed وcancelled كحالتين نهائيتين. لا تطلب النتائج ولا تعيد هذا الفرع إلى Wait. احفظ معرف المهمة وحالتها، وأبلغ المسؤول، ثم قرر بصورة منفصلة إن كان إرسال مهمة جديدة مناسبًا.

ابدأ بمهمة صغيرة يمكن استعادتها

اختر فئة واحدة ومدينة واحدة وجدولًا يمكنك مراجعته. يحصل الحساب الجديد في BasedOnB مرة واحدة على 50 رصيد تصدير من دون بطاقة. استخدمها للتحقق من ربط الحقول ومفتاح idempotency وفرع الخطأ وupsert قبل زيادة العدد.

احفظ معرف المهمة بجوار كل صف مستورد. تجعل هذه المعلومة الصغيرة مراجعة الأتمتة وإصلاحها أسهل بكثير.