خدمات الويب في "منصة خرائط Google" هي مجموعة من واجهات HTTP لخدمات Google التي توفّر بيانات جغرافية لتطبيقات الخرائط.
يصف هذا الدليل بعض الممارسات الشائعة المفيدة لإعداد طلبات خدمة الويب ومعالجة استجابات الخدمة. يُرجى الرجوع إلى دليل المطوّر للاطّلاع على المستندات الكاملة حول Geocoding API.
ما هي خدمة الويب؟
خدمات الويب في "منصة خرائط Google" هي واجهة لطلب بيانات Maps API من الخدمات الخارجية واستخدام البيانات في تطبيقات "خرائط Google". تم تصميم هذه الخدمات ليتم استخدامها مع خريطة، وفقًا لقيود الترخيص في بنود خدمة "منصة خرائط Google".
تستخدم خدمات الويب في واجهات برمجة تطبيقات "خرائط Google" طلبات HTTP(S) لعناوين URL معيّنة، وتمرّر مَعلمات عناوين URL و/أو بيانات POST بتنسيق JSON كوسيطات إلى الخدمات. بشكل عام، تعرض هذه الخدمات البيانات في نص الاستجابة إما بتنسيق JSON أو XML لتحليلها و/أو معالجتها بواسطة تطبيقك.
يكون طلب Geocoding API النموذجي بشكل عام على النحو التالي:
https://maps.googleapis.com/maps/api/geocode/output?parameters
حيث يشير output إلى تنسيق الاستجابة (عادةً json أو xml).
ملاحظة: تتطلّب جميع تطبيقات Geocoding API المصادقة. مزيد من المعلومات حول بيانات اعتماد المصادقة
الوصول إلى SSL/TLS
يجب استخدام بروتوكول HTTPS لجميع طلبات "منصة خرائط Google" التي تستخدم مفاتيح واجهة برمجة التطبيقات أو تحتوي على بيانات المستخدمين. قد يتم رفض الطلبات التي يتم إجراؤها عبر بروتوكول HTTP والتي تحتوي على بيانات حساسة.
إنشاء عنوان URL صالح
قد تعتقد أنّ عنوان URL "الصالح" هو أمر بديهي، ولكنّ الأمر ليس كذلك. على سبيل المثال، قد يحتوي عنوان URL الذي يتم إدخاله في شريط العناوين في المتصفّح على أحرف خاصة (مثل "上海+中國")، ويحتاج المتصفّح إلى ترجمة هذه الأحرف داخليًا إلى ترميز مختلف قبل الإرسال.
وبالمثل، فإنّ أي رمز ينشئ أو يقبل إدخال UTF-8 قد يتعامل مع عناوين URL التي تحتوي على أحرف UTF-8 على أنّها "صالحة"، ولكن يجب أيضًا ترجمة هذه الأحرف قبل إرسالها إلى خادم ويب.
تُعرف هذه العملية باسم
ترميز عنوان URL أو الترميز بالنسبة المئوية.
الرموز الخاصة
نحتاج إلى ترجمة الرموز الخاصة لأنّ جميع عناوين URL يجب أن تتوافق مع البنية المحدّدة في مواصفات معرّف الموارد المنتظم (URI). يعني ذلك أنّ عناوين URL يجب أن تحتوي فقط على مجموعة فرعية خاصة من أحرف ASCII، وهي الرموز الأبجدية الرقمية المألوفة وبعض الأحرف المحجوزة لاستخدامها كأحرف تحكّم ضمن عناوين URL. يلخّص هذا الجدول هذه الأحرف:
| ضبط | الأحرف | استخدام عنوان URL |
|---|---|---|
| أحرف أبجدية رقمية | a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 | السلاسل النصية واستخدام المخطط (http) والمنفذ (8080) وما إلى ذلك |
| غير محجوز | - _ . ~ | سلاسل نصية |
| تم الحجز | ! * ' ( ) ; : @ & = + $ , / ? % # [ ] | أحرف التحكّم و/أو السلاسل النصية |
عند إنشاء عنوان URL صالح، يجب التأكّد من أنّه يحتوي فقط على الأحرف المعروضة في الجدول. يؤدي تعديل عنوان URL لاستخدام هذه المجموعة من الأحرف بشكل عام إلى مشكلتَين، إحداهما تتعلق بالحذف والأخرى بالاستبدال:
- إذا كانت الأحرف التي تريد معالجتها خارج المجموعة المذكورة أعلاه على سبيل المثال، يجب ترميز الأحرف باللغات الأجنبية، مثل
上海+中國، باستخدام الأحرف المذكورة أعلاه. وفقًا للعرف الشائع، غالبًا ما يتم تمثيل المسافات (غير المسموح بها في عناوين URL) باستخدام حرف الجمع'+'أيضًا. - تتوفّر الأحرف ضمن المجموعة أعلاه كأحرف محجوزة،
ولكن يجب استخدامها حرفيًا.
على سبيل المثال، يتم استخدام
?ضمن عناوين URL للإشارة إلى بداية سلسلة طلب البحث. إذا أردت استخدام السلسلة "? and the Mysterions"، عليك ترميز الحرف'?'.
يتم ترميز جميع الأحرف التي سيتم ترميزها في عنوان URL
باستخدام حرف '%' وقيمة سداسية عشرية مكوّنة من حرفين
تتوافق مع حرف UTF-8. على سبيل المثال،
سيتم ترميز 上海+中國 في UTF-8 كعنوان URL على النحو التالي:
%E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. يتم ترميز السلسلة ? and the Mysterians بنظام عناوين URL على النحو التالي: %3F+and+the+Mysterians أو %3F%20and%20the%20Mysterians.
الرموز الشائعة التي تحتاج إلى ترميز
في ما يلي بعض الرموز الشائعة التي يجب ترميزها:
| شخصية غير آمنة | القيمة المشفّرة |
|---|---|
| مسافة | %20 |
| " | %22 |
| < | %3C |
| > | %3E |
| # | %23 |
| % | %25 |
| | | %7C |
قد يكون تحويل عنوان URL الذي تتلقّاه من إدخال المستخدم أمرًا صعبًا في بعض الأحيان. على سبيل المثال، قد يُدخل المستخدم عنوانًا مثل "5th&Main St." بشكل عام، يجب إنشاء عنوان URL من أجزائه، مع التعامل مع أي إدخال من المستخدم كأحرف حرفية.
بالإضافة إلى ذلك، يقتصر طول عناوين URL على 16, 384 حرفًا لجميع خدمات الويب وواجهات برمجة التطبيقات الثابتة على الويب في "منصة خرائط Google". بالنسبة إلى معظم الخدمات، نادرًا ما يتم الوصول إلى الحد الأقصى لعدد الأحرف المسموح به. ومع ذلك، يُرجى ملاحظة أنّ بعض الخدمات تحتوي على عدة معلَمات قد تؤدي إلى عناوين URL طويلة.
الاستخدام المهذّب لواجهات Google APIs
يمكن أن تضع برامج عملاء واجهة برمجة التطبيقات المصممة بشكل سيئ عبئًا أكبر من اللازم على كل من الإنترنت وخوادم Google. يحتوي هذا القسم على بعض أفضل الممارسات لعملاء واجهات برمجة التطبيقات. يمكن أن يساعدك اتّباع أفضل الممارسات هذه في تجنُّب حظر تطبيقك بسبب إساءة استخدام واجهات برمجة التطبيقات عن غير قصد.
إدارة الأخطاء وإعادة المحاولة
للحصول على معلومات عن رموز الاستجابة UNKNOWN_ERROR أو OVER_QUERY_LIMIT من Geocoding API، يُرجى الاطّلاع على إدارة الأخطاء وإعادة المحاولة.
الرقود الأسي الثنائي
في حالات نادرة، قد يحدث خطأ أثناء عرض طلبك، وقد تتلقّى رمز استجابة HTTP 4XX أو 5XX، أو قد يتعذّر اتصال TCP بين العميل وخادم Google. غالبًا ما يكون من المفيد إعادة محاولة الطلب لأنّ طلب المتابعة قد ينجح عندما يفشل الطلب الأصلي. ومع ذلك، من المهم عدم تكرار إرسال الطلبات إلى خوادم Google بشكل متكرر. يمكن أن يؤدي سلوك التكرار هذا إلى زيادة الحمل على الشبكة بين العميل وGoogle، ما يسبب مشاكل للعديد من الأطراف.
والنهج الأفضل هو إعادة المحاولة مع زيادة فترات التأخير بين المحاولات. عادةً ما تتم زيادة التأخير بعامل مضاعف مع كل محاولة، وهو نهج يُعرف باسم الرقود الأسي الثنائي.
على سبيل المثال، لنفترض أنّ هناك تطبيقًا يريد إرسال هذا الطلب إلى Time Zone API:
https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&key=YOUR_API_KEYيوضّح مثال Python التالي كيفية إرسال الطلب باستخدام التراجع الأسي:
import json import time import urllib.error import urllib.parse import urllib.request # The maps_key defined below isn't a valid Google Maps API key. # You need to get your own API key. # See https://developers.google.com/maps/documentation/timezone/get-api-key API_KEY = "YOUR_KEY_HERE" TIMEZONE_BASE_URL = "https://maps.googleapis.com/maps/api/timezone/json" def timezone(lat, lng, timestamp): # Join the parts of the URL together into one string. params = urllib.parse.urlencode( {"location": f"{lat},{lng}", "timestamp": timestamp, "key": API_KEY,} ) url = f"{TIMEZONE_BASE_URL}?{params}" current_delay = 0.1 # Set the initial retry delay to 100ms. max_delay = 5 # Set the maximum retry delay to 5 seconds. while True: try: # Get the API response. response = urllib.request.urlopen(url) except urllib.error.URLError: pass # Fall through to the retry loop. else: # If we didn't get an IOError then parse the result. result = json.load(response) if result["status"] == "OK": return result["timeZoneId"] elif result["status"] != "UNKNOWN_ERROR": # Many API errors cannot be fixed by a retry, e.g. INVALID_REQUEST or # ZERO_RESULTS. There is no point retrying these requests. raise Exception(result["error_message"]) if current_delay > max_delay: raise Exception("Too many retry attempts.") print("Waiting", current_delay, "seconds before retrying.") time.sleep(current_delay) current_delay *= 2 # Increase the delay each time we retry. if __name__ == "__main__": tz = timezone(39.6034810, -119.6822510, 1331161200) print(f"Timezone: {tz}")
يجب أيضًا الحرص على عدم وجود رمز إعادة محاولة في سلسلة استدعاء التطبيق يؤدي إلى تكرار الطلبات بشكل متتالٍ وسريع.
الطلبات المتزامنة
قد تبدو الأعداد الكبيرة من الطلبات المتزامنة إلى واجهات برمجة التطبيقات من Google وكأنّها هجمة موزّعة لحجب الخدمة (DDoS) على البنية الأساسية في Google، ويتم التعامل معها وفقًا لذلك. ولتجنّب ذلك، عليك التأكّد من عدم مزامنة طلبات البيانات من واجهة برمجة التطبيقات بين العملاء.
على سبيل المثال، لنفترض أنّ هناك تطبيقًا يعرض الوقت في المنطقة الزمنية الحالية. من المحتمل أن يضبط هذا التطبيق منبهًا في نظام تشغيل العميل لتنبيهه في بداية الدقيقة حتى يمكن تحديث الوقت المعروض. يجب ألا يُجري التطبيق أي طلبات بيانات من واجهة برمجة التطبيقات كجزء من المعالجة المرتبطة بهذا المنبّه.
إنّ إجراء طلبات بيانات من واجهة برمجة التطبيقات استجابةً لمنبّه ثابت هو أمر سيئ لأنّه يؤدي إلى مزامنة طلبات البيانات من واجهة برمجة التطبيقات مع بداية الدقيقة، حتى بين الأجهزة المختلفة، بدلاً من توزيعها بالتساوي بمرور الوقت. سيؤدي التطبيق المصمم بشكل سيئ إلى حدوث ارتفاع في عدد الزيارات بمقدار ستين مرة عن المستويات العادية في بداية كل دقيقة.
بدلاً من ذلك، من الممكن أن يكون التصميم الجيد هو ضبط منبّه ثانٍ على وقت يتم اختياره عشوائيًا. عندما يرنّ هذا المنبّه الثاني، يستدعي التطبيق أي واجهات برمجة تطبيقات يحتاج إليها ويخزّن النتائج. عندما يريد التطبيق تحديث عرضه في بداية الدقيقة، فإنه يستخدم النتائج المخزنة مسبقًا بدلاً من استدعاء واجهة برمجة التطبيقات مرة أخرى. باستخدام هذا الأسلوب، يتم توزيع طلبات البيانات من واجهة برمجة التطبيقات بالتساوي بمرور الوقت. علاوة على ذلك، لا تؤخر طلبات بيانات واجهة برمجة التطبيقات العرض عند تحديث الشاشة.
بالإضافة إلى بداية الدقيقة، هناك أوقات مزامنة شائعة أخرى يجب الحرص على عدم استهدافها، وهي بداية الساعة وبداية كل يوم عند منتصف الليل.
جارٍ معالجة الردود
يتناول هذا القسم كيفية استخراج هذه القيم ديناميكيًا من ردود خدمات الويب.
تقدّم خدمات الويب في "خرائط Google" ردودًا يسهل فهمها، ولكنّها ليست سهلة الاستخدام تمامًا. عند إجراء طلب بحث، بدلاً من عرض مجموعة من البيانات، قد ترغب في استخراج بعض القيم المحددة. بشكل عام، ستحتاج إلى تحليل الردود من خدمة الويب واستخراج القيم التي تهمك فقط.
يعتمد نظام التحليل الذي تستخدمه على ما إذا كنت تعرض الناتج بتنسيق XML أو JSON. يمكن معالجة ردود JSON، التي تكون على شكل كائنات Javascript، ضمن Javascript نفسها على العميل. يجب معالجة ردود XML باستخدام معالج XML ولغة طلب بحث XML لمعالجة العناصر ضمن تنسيق XML. نستخدم XPath في الأمثلة التالية، لأنّه يتوافق بشكل شائع مع مكتبات معالجة XML.
معالجة XML باستخدام XPath
XML هو تنسيق معلومات منظَّمة ناضج نسبيًا يُستخدم لتبادل البيانات. على الرغم من أنها ليست خفيفة مثل JSON، إلا أن XML
توفر المزيد من دعم اللغة وأدوات أكثر قوة. على سبيل المثال، إنّ الرمز البرمجي لمعالجة XML في Java مضمَّن في حزم javax.xml.
عند معالجة استجابات XML، يجب استخدام لغة طلب بحث مناسبة لاختيار العُقد داخل مستند XML، بدلاً من افتراض أنّ العناصر تقع في مواضع مطلقة داخل ترميز XML. XPath هي بنية لغوية لوصف العُقد والعناصر بشكل فريد ضمن مستند XML. تتيح لك تعبيرات XPath تحديد محتوى معيّن ضمن مستند استجابة XML.
تعبيرات XPath
يساعدك الإلمام ببعض أساسيات XPath في تطوير مخطط تحليل قوي. سيركّز هذا القسم على كيفية معالجة العناصر ضمن مستند XML باستخدام XPath، ما يتيح لك معالجة عناصر متعددة وإنشاء طلبات بحث معقّدة.
يستخدم XPath تعبيرات لتحديد العناصر داخل مستند XML، باستخدام صيغة مشابهة لتلك المستخدمة لمسارات الدليل. تحدّد هذه التعبيرات العناصر ضمن شجرة مستندات XML، وهي شجرة هرمية مشابهة لشجرة نموذج العناصر في المستند (DOM). بشكل عام، تكون تعبيرات XPath جشعة، ما يشير إلى أنها ستطابق جميع العُقد التي تطابق المعايير المقدَّمة.
سنستخدم ملف XML المجرّد التالي لتوضيح الأمثلة:
<WebServiceResponse> <status>OK</status> <result> <type>sample</type> <name>Sample XML</name> <location> <lat>37.4217550</lat> <lng>-122.0846330</lng> </location> </result> <result> <message>The secret message</message> </result> </WebServiceResponse>
اختيار العُقد في التعبيرات
تختار اختيارات XPath العُقد. تضم العقدة الجذر
المستند بأكمله. يمكنك اختيار هذه العقدة باستخدام
التعبير الخاص "/". يُرجى العِلم أنّ العقدة الجذرية
ليست العقدة ذات المستوى الأعلى في مستند XML، بل
تقع في مستوى أعلى من هذا العنصر ذي المستوى الأعلى وتتضمّنه.
تمثّل عُقد العناصر العناصر المختلفة ضمن شجرة مستندات XML. على سبيل المثال، يمثّل العنصر <WebServiceResponse> العنصر ذي المستوى الأعلى الذي تم عرضه في نموذج الخدمة أعلاه. يمكنك اختيار عُقد فردية إما من خلال
مسارات مطلقة أو مسارات نسبية، ويتم الإشارة إليها من خلال وجود
أو عدم وجود حرف "/" في البداية.
- المسار المطلق: يختار التعبير "
/WebServiceResponse/result" جميع عُقد<result>التي تكون تابعة للعقدة<WebServiceResponse>. (يُرجى العِلم أنّ كلا العنصرَين ينحدران من العقدة الجذرية "/"). - المسار النسبي من السياق الحالي: يتطابق التعبير "
result" مع أي عناصر<result>ضمن السياق الحالي. بشكل عام، لا داعي للقلق بشأن السياق، لأنّك عادةً تعالج نتائج خدمة الويب من خلال تعبير واحد.
يمكن زيادة أي من هذين التعبيرين من خلال إضافة مسار حرف بدل، يُشار إليه بشرطة مائلة مزدوجة ("//"). يشير حرف البدل هذا إلى أنّه قد يتطابق عنصر واحد أو أكثر في المسار المتداخل. على سبيل المثال، سيتطابق تعبير XPath "//formatted_address" مع جميع العُقد التي تحمل هذا الاسم في المستند الحالي.
يتطابق التعبير //viewport//lat مع جميع عناصر <lat> التي يمكن تتبُّعها إلى <viewport> كعنصر رئيسي.
بشكل تلقائي، تطابق تعبيرات XPath جميع العناصر. يمكنك حصر
التعبير لمطابقة عنصر معيّن من خلال تقديم دالة،
والتي يتم تضمينها بين قوسين مربعين ([]). على سبيل المثال، يعرض تعبير XPath "/GeocodeResponse/result[2] دائمًا
النتيجة الثانية.
| نوع التعبير | |
|---|---|
| Root node | تعبير XPath: "
/"الاختيار:
<WebServiceResponse>
<status>OK</status>
<result>
<type>sample</type>
<name>Sample XML</name>
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
</result>
<result>
<message>The secret message</message>
</result>
</WebServiceResponse>
|
| المسار المطلق | تعبير XPath: "
/WebServiceResponse/result"الاختيار:
<result>
<type>sample</type>
<name>Sample XML</name>
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
</result>
<result>
<message>The secret message</message>
</result>
|
| المسار الذي يتضمّن حرف بدل | تعبير XPath: "
/WebServiceResponse//location"الاختيار:
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
|
| مسار مع دالة منطقية | تعبير XPath: "
/WebServiceResponse/result[2]/message"الاختيار:
<message>The secret message</message>
|
جميع العناصر الثانوية المباشرة لأول result |
تعبير XPath: "
/WebServiceResponse/result[1]/*"الاختيار:
<type>sample</type>
<name>Sample XML</name>
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
|
name لـ result الذي يحتوي على نص type "sample" |
تعبير XPath: "
/WebServiceResponse/result[type/text()='sample']/name"الاختيار:
Sample XML
|
من المهم ملاحظة أنه عند تحديد العناصر، فإنك تحدد العُقد، وليس فقط النص داخل تلك الكائنات. بشكل عام، ستحتاج إلى تكرار جميع العُقد المتطابقة واستخراج النص. يمكنك أيضًا مطابقة عُقد النص مباشرةً، راجِع عُقد النص أدناه.
يُرجى العِلم أنّ XPath يتيح استخدام عُقد السمات أيضًا، ولكن جميع خدمات الويب في "خرائط Google" تقدّم عناصر بدون سمات، لذا لا داعي لمطابقة السمات.
تحديد النص في التعبيرات
يتم تحديد النص داخل مستند XML في تعبيرات XPath
عبر عامل تشغيل عقدة النص. يشير عامل التشغيل "text()"
إلى استخراج النص من العقدة المحدّدة. على سبيل المثال، سيؤدي تعبير XPath "//formatted_address/text()" إلى عرض كل النص ضمن عناصر <formatted_address>.
| نوع التعبير | |
|---|---|
| جميع عُقد النص (بما في ذلك المسافات البيضاء) | تعبير XPath: "
//text()"الاختيار:
sample
Sample XML
37.4217550
-122.0846330
The secret message
|
| تحديد النص | تعبير XPath: "
/WebServiceRequest/result[2]/message/text()"الاختيار:
The secret message
|
| تحديد حسّاس للسياق | تعبير XPath: "
/WebServiceRequest/result[type/text() = 'sample']/name/text()"الاختيار:
Sample XML
|
بدلاً من ذلك، يمكنك تقييم تعبير وعرض مجموعة من العُقد ثم تكرار "مجموعة العُقد" هذه، واستخراج النص من كل عقدة. نستخدم هذا النهج في المثال أدناه.
لمزيد من المعلومات حول XPath، يُرجى الاطّلاع على مواصفات XPath W3C.
تقييم XPath في Java
توفّر Java دعمًا واسعًا لتحليل XML واستخدام تعبيرات XPath ضمن حزمة javax.xml.xpath.*.
لهذا السبب، يستخدم الرمز النموذجي في هذا القسم Java لتوضيح كيفية التعامل مع XML وتحليل البيانات من استجابات خدمة XML.
لاستخدام XPath في رمز Java، عليك أولاً إنشاء مثيل من XPathFactory واستدعاء newXPath() في هذا المصنع لإنشاء كائن XPath
. يمكن لهذا الكائن بعد ذلك معالجة تعبيرات XML وXPath التي تم تمريرها باستخدام طريقة evaluate().
عند تقييم تعبيرات XPath، تأكَّد من تكرار أي "مجموعات عُقد" محتملة قد يتم عرضها. بما أنّ هذه النتائج يتم عرضها كعُقد DOM في رمز Java، عليك تسجيل هذه القيم المتعددة ضمن عنصر NodeList وتكرار هذا العنصر لاستخراج أي نص أو قيم من هذه العُقد.
يوضّح الرمز التالي كيفية إنشاء كائن XPath
وتعيين XML وتعبير XPath له، وتقييم
التعبير لطباعة المحتوى ذي الصلة.
import org.xml.sax.InputSource; import org.w3c.dom.*; import javax.xml.xpath.*; import java.io.*; public class SimpleParser { public static void main(String[] args) throws IOException { XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); try { System.out.print("Web Service Parser 1.0\n"); // In practice, you'd retrieve your XML via an HTTP request. // Here we simply access an existing file. File xmlFile = new File("XML_FILE"); // The xpath evaluator requires the XML be in the format of an InputSource InputSource inputXml = new InputSource(new FileInputStream(xmlFile)); // Because the evaluator may return multiple entries, we specify that the expression // return a NODESET and place the result in a NodeList. NodeList nodes = (NodeList) xpath.evaluate("XPATH_EXPRESSION", inputXml, XPathConstants.NODESET); // We can then iterate over the NodeList and extract the content via getTextContent(). // NOTE: this will only return text for element nodes at the returned context. for (int i = 0, n = nodes.getLength(); i < n; i++) { String nodeString = nodes.item(i).getTextContent(); System.out.print(nodeString); System.out.print("\n"); } } catch (XPathExpressionException ex) { System.out.print("XPath Error"); } catch (FileNotFoundException ex) { System.out.print("File Error"); } } }