خدمات الويب في "منصة خرائط Google" هي مجموعة من واجهات HTTP لخدمات Google التي توفّر بيانات جغرافية لتطبيقات الخرائط.
يوضّح هذا الدليل بعض الممارسات الشائعة المفيدة لإعداد طلبات خدمة الويب ومعالجة ردود الخدمة. يُرجى الرجوع إلى دليل المطوّرين للحصول على المستندات الكاملة حول Directions API (الإصدار القديم).
ما هي خدمة الويب؟
خدمات الويب في "منصة خرائط Google" هي واجهة لطلب بيانات Maps API من خدمات خارجية واستخدام البيانات في تطبيقات "خرائط Google". تم تصميم هذه الخدمات لاستخدامها مع خريطة، وذلك وفقًا لقيود الترخيص الواردة في بنود خدمة "منصة خرائط Google".
تستخدم خدمات الويب في واجهات Maps API طلبات HTTP(S) إلى عناوين URL معيّنة، مع تمرير مَعلمات عناوين URL و/أو بيانات POST بتنسيق JSON كمعلمات للخدمات. وبشكل عام، تعرض هذه الخدمات البيانات في نص الرد بتنسيق JSON أو XML لتحليلها و/أو معالجتها من خلال تطبيقك.
يكون طلب Directions API (الإصدار القديم) عادةً بالتنسيق التالي:
https://maps.googleapis.com/maps/api/directions/output?parameters
حيث يشير output إلى تنسيق الرد (عادةً json أو xml).
ملاحظة: تتطلّب جميع تطبيقات Directions 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 على شكل %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B في عنوان URL. سيتم ترميز السلسلة ? and the Mysterians باستخدام عنوان URL على النحو التالي: %3F+and+the+Mysterians أو %3F%20and%20the%20Mysterians.
الأحرف الشائعة التي يجب ترميزها
في ما يلي بعض الأحرف الشائعة التي يجب ترميزها:
| حرف غير آمن | القيمة المشفرة |
|---|---|
| مسافة | %20 |
| " | %22 |
| < | %3C |
| > | %3E |
| # | %23 |
| % | %25 |
| | | %7C |
قد يكون تحويل عنوان URL الذي تتلقّاه من بيانات أدخلها المستخدم أمرًا صعبًا في بعض الأحيان. على سبيل المثال، قد يُدخل المستخدم عنوانًا على النحو التالي: "شارع 5 وشارع مين". بشكل عام، يجب إنشاء عنوان URL من أجزائه، مع التعامل مع أي بيانات أدخلها المستخدم كأحرف حرفية.
بالإضافة إلى ذلك، يقتصر عدد الأحرف في عناوين URL على 16384 حرفًا لجميع خدمات الويب الثابتة وخدمات الويب في "منصة خرائط Google". في معظم الخدمات، نادرًا ما يتم بلوغ الحدّ الأقصى لعدد الأحرف المسموح به. ومع ذلك، تجدر الإشارة إلى أنّ بعض الخدمات تتضمّن عدة مَعلمات قد تؤدي إلى إنشاء عناوين URL طويلة.
الاستخدام المهذّب لواجهات Google API
يمكن أن تفرض برامج واجهات برمجة التطبيقات المصمّمة بشكل سيئ حملاً أكبر من اللازم على الإنترنت وخوادم Google. يحتوي هذا القسم على بعض أفضل الممارسات لعملاء واجهات برمجة التطبيقات. يمكن أن يساعدك اتّباع أفضل الممارسات هذه في تجنُّب حظر تطبيقك بسبب إساءة استخدام غير مقصودة لواجهات برمجة التطبيقات.
الرقود الأسي الثنائي
في حالات نادرة، قد يحدث خطأ أثناء معالجة طلبك، وقد تتلقّى رمز استجابة 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 هو تنسيق معلومات منظَّمة قديم نسبيًا ويُستخدم لتبادل البيانات. على الرغم من أنّ XML ليس خفيف الوزن مثل JSON، إلا أنّه يوفّر توافقًا أكبر مع اللغات وأدوات أكثر فعالية. على سبيل المثال، تم إنشاء الرمز البرمجي الخاص بمعالجة ملفات XML في Java ضمن حِزم javax.xml.
عند معالجة ردود XML، عليك استخدام لغة طلب بحث مناسبة لاختيار العُقد داخل مستند XML، بدلاً من افتراض أنّ العناصر تقع في مواضع مطلقة ضمن ترميز XML. XPath هي بنية لغوية تصف بشكل فريد العُقد والعناصر ضمن مستند XML. تتيح لك تعبيرات XPath تحديد محتوى معيّن ضمن مستند الرد بتنسيق XML.
تعبيرات XPath
تساعدك معرفة بعض أساسيات XPath في تطوير مخطط تحليل قوي. سيركّز هذا القسم على كيفية استخدام XPath لتحديد عناصر ضمن مستند XML، ما يتيح لك تحديد عناصر متعددة وإنشاء طلبات بحث معقّدة.
تستخدم 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"); } } }