Χρήση Υπηρεσιών Ιστού

Από τη στιγμή που έγινε εύκολη η ανάκτηση και η ανάλυση εγγράφων μέσω HTTP, χρησιμοποιώντας προγράμματα, δεν χρειάστηκε πολύς χρόνος για να αναπτύξουμε μια προσέγγιση παραγωγής εγγράφων σχεδιασένων ειδικά για κατανάλωση από άλλα προγράμματα (δηλαδή, όχι HTML για εμφάνιση σε πρόγραμμα περιήγησης ).

Υπάρχουν δύο κοινές μορφές που χρησιμοποιούμε κατά την ανταλλαγή δεδομένων στον ιστό. Η eXtensible Markup Language (XML) ή Επεκτάσιμη Γλώσσα Σήμανσης χρησιμοποιείται εδώ και πολύ καιρό και είναι η καταλληλότερη για έγγραφα τυποποιημένα για ανταλλαγή δεδομένων. Όταν τα προγράμματα θέλουν απλώς να ανταλλάξουν λεξικά, λίστες ή άλλες εσωτερικές πληροφορίες μεταξύ τους, χρησιμοποιούν JavaScript Object Notation (JSON) ή Σημειογραφία Αντικειμένου JavaScript (δείτε www.json.org). Θα εξετάσουμε και τις δύο μορφές.

eXtensible Markup Language - XML

Η XML μοιάζει πολύ με την HTML, αλλά η XML είναι πιο δομημένη. Ακολουθεί ένα δείγμα εγγράφου XML:

<άτομο>
  <όνομα>Chuck</όνομα>
  <τηλέφωνο τύπος="εσωτερικό">
    +1 734 303 4456
  </τηλέφωνο>
  <email κρυφό="ναι" />
</άτομο>

Κάθε ζεύγος ετικετών ανοίγματος (π.χ. <άτομο>) και κλεισίματος (π.χ. </άτομο>) αντιπροσωπεύει ένα στοιχείο ή κόμβο με το ίδιο όνομα με αυτό της ετικέτας (π.χ. άτομο). Κάθε στοιχείο μπορεί να έχει κάποιο κείμενο, ορισμένα χαρακτηριστικά (π.χ. κρυφό) και άλλα εμφωλευμένα στοιχεία. Εάν ένα στοιχείο XML είναι κενό (δηλαδή, δεν έχει περιεχόμενο), μπορεί να απεικονίζεται από μια ετικέτα που κλείνει αυτόματα (π.χ. <email />).

Συχνά είναι χρήσιμο να σκεφτόμαστε ένα έγγραφο XML ως μια δομή δέντρου, όπου υπάρχει ένα κορυφαίο στοιχείο (εδώ το: άτομο) και οι άλλες ετικέτες (π.χ. τηλέφωνο) σχεδιάζονται ως παιδιά των στοιχείων γονέα τους.

Μια αναπαράσταση δέντρου της XML
Μια αναπαράσταση δέντρου της XML

Ανάλυση XML

Εδώ είναι μια απλή εφαρμογή, που αναλύει ορισμένα δεδομένα XML και εξάγει κάποια στοιχεία δεδομένων από το XML:

import xml.etree.ElementTree as ET

data = '''
<άτομο>
  <όνομα>Chuck</όνομα>
  <τηλέφωνο τύπος="εσωτερικό">
    +1 734 303 4456
  </τηλέφωνο>
  <email κρυφό="ναι" />
</άτομο>'''

tree = ET.fromstring(data)
print('Όνομα:', tree.find('όνομα').text)
print('Χαρακτηριστικό:', tree.find('email').get('κρυφό'))

# Code: http://www.gr.py4e.com/code3/xml1.py

Το τριπλό μονό εισαγωγικό ('''), καθώς και το τριπλό διπλό εισαγωγικό ("""), επιτρέπουν τη δημιουργία συμβολοσειρών που εκτείνονται σε πολλές γραμμές.

Η κλήση fromstring μετατρέπει την αναπαράσταση συμβολοσειράς του XML σε ένα “δέντρο” στοιχείων XML. Όταν το XML αναπαρασταθεί με δέντρο, έχουμε μια σειρά από μεθόδους που μπορούμε να καλέσουμε για να εξαγάγουμε τμήματα δεδομένων από τη συμβολοσειρά XML. Η συνάρτηση find πραγματοποιεί αναζήτηση στο δέντρο XML και ανακτά το στοιχείο που ταιριάζει με την καθορισμένη ετικέτα.

Όνομα: Chuck
Χαρακτηριστικό:: yes

Το XML σε αυτό το παράδειγμα είναι αρκετά απλό, αποδεικνύεται όμως ότι υπάρχουν πολλοί κανόνες σχετικά με έγκυρη XML. Η χρήση ενός αναλυτή XML, όπως το ElementTree, μας επιτρέπει να εξαγάγουμε δεδομένα από το XML χωρίς να ανησυχούμε για τους περίπλοκους, σε κάποιες περιπτώσεις, κανόνες σύνταξης XML.

Προσπέλαση των κόμβων με επανάληψη

Often the XML has multiple nodes and we need to write a loop to process all of the nodes. In the following program, we loop through all of the user nodes:

import xml.etree.ElementTree as ET

input = '''
<stuff>
  <χρήστες>
    <χρήστης x="2">
      <id>001</id>
      <όνομα>Chuck</όνομα>
    </χρήστης>
    <χρήστης x="7">
      <id>009</id>
      <όνομα>Brent</όνομα>
    </χρήστης>
  </χρήστες>
</stuff>'''

stuff = ET.fromstring(input)
lst = stuff.findall('χρήστες/χρήστης')
print('Πλήθος χρηστών:', len(lst))

for item in lst:
    print('Όνομα', item.find('όνομα').text)
    print('Id', item.find('id').text)
    print('Χαρακτηριστικό', item.get('x'))

# Code: http://www.gr.py4e.com/code3/xml2.py

Η μέθοδος findall ανακτά μια λίστα Python με υποδέντρα που αντιπροσωπεύουν τις δομές χρήστης στο δέντρο XML. Στη συνέχεια, μπορούμε να γράψουμε ένα βρόχο for, που εξετάζει κάθε κόμβο χρήστη και εκτυπώνει τα στοιχεία κειμένου όνομα και id καθώς και το χαρακτηριστικό x του κόμβου (χρήστης).

Πλήθος χρηστών: 2
Όνομα Chuck
Id 001
Χαρακτηριστικό 2
Όνομα Brent
Id 009
Χαρακτηριστικό 7

Είναι σημαντικό να συμπεριληφθούν όλα τα στοιχεία γονικού επιπέδου στη δήλωση findall, εκτός από το στοιχείο ανώτατου επιπέδου (π.χ. χρήστες/χρήστης). Διαφορετικά, η Python δεν θα βρει κανέναν, επιθυμητό, κόμβο.

import xml.etree.ElementTree as ET

input = '''
<stuff>
  <χρήστες>
    <χρήστης x="2">
      <id>001</id>
      <όνομα>Chuck</όνομα>
    </χρήστης>
    <χρήστης x="7">
      <id>009</id>
      <όνομα>Brent</όνομα>
    </χρήστης>
  </χρήστες>
</stuff>'''

stuff = ET.fromstring(input)

lst = stuff.findall('χρήστες/χρήστης')
print('Πλήθος χρηστών:', len(lst))

lst2 = stuff.findall('χρήστης')
print('Πλήθος χρηστών:', len(lst2))

lst αποθηκεύει όλα τα στοιχεία user τα οποία είναι εμφωλευμένα στον γονέα users. lst2 αναζητά τα στοιχεία user τα οποία δεν είναι εμφωλευμένα, μέσα στο στοιχείο ανώτατου επιπέδου stuff, τα οποία δεν υπάρχουν.

User count: 2
User count: 0

JavaScript Object Notation - JSON

Η μορφή JSON εμπνεύστηκε από τη μορφή αντικειμένου και πίνακα, που χρησιμοποιείται στη γλώσσα JavaScript. Αλλά δεδομένου ότι η Python επινοήθηκε πριν από την JavaScript, η σύνταξη της Python για λεξικά και λίστες επηρέασε τη σύνταξη του JSON. Έτσι, η μορφή του JSON είναι σχεδόν πανομοιότυπη με έναν συνδυασμό λιστών και λεξικών Python.

Εδώ παραθέτω μια κωδικοποίηση JSON, που είναι περίπου ισοδύναμη με την απλή XML παραπάνω:

{
  "όνομα" : "Chuck",
  "τηλέφωνο" : {
    "τύπος" : "εσωτερικό",
    "αριθμός" : "+1 734 303 4456"
   },
   "email" : {
     "κρυφό" : "ναι"
   }
}

Θα παρατηρήσετε κάποιες διαφορές. Αρχικά, στην XML, μπορούμε να προσθέσουμε χαρακτηριστικά, όπως το “εσωτερικό” στην ετικέτα “τηλέφωνο”. Στο JSON, έχουμε απλώς ζεύγη κλειδιού-τιμής. Επίσης, η ετικέτα “άτομο” της XML έχει ακυρωθεί και αντικαθίσταται από ένα ζεύγος εξωτερικών αγκίστρων.

Γενικά, οι δομές JSON είναι απλούστερες από την XML επειδή το JSON έχει λιγότερες δυνατότητες από το XML. Αλλά το JSON έχει το πλεονέκτημα ότι αντιστοιχίζεται απευθείας σε κάποιο συνδυασμό λεξικών και λιστών. Και, δεδομένου ότι σχεδόν όλες οι γλώσσες προγραμματισμού έχουν κάτι αντίστοιχο με τα λεξικά και τις λίστες της Python, το JSON είναι μια πολύ φυσική μορφή για να ανταλλάσσουν δεδομένα δύο συνεργαζόμενα προγράμματα.

Το JSON γίνεται ταχύτατα η μορφή που επιλέγεται, για σχεδόν το σύνολο των δεδομένων που ανταλλάσσονται μεταξύ εφαρμογών, λόγω της σχετικής απλότητάς του σε σύγκριση με την XML.

Ανάλυση JSON

Κατασκευάζουμε το JSON μας με εμφωλευμένα λεξικά και λίστες όπως απαιτείται. Σε αυτό το παράδειγμα, αναπαριστούμε μια λίστα χρηστών, όπου κάθε χρήστης είναι ένα σύνολο ζευγών κλειδιών-τιμών (δηλαδή, ένα λεξικό). Έχουμε λοιπόν μια λίστα με λεξικά.

Στο παρακάτω πρόγραμμα, χρησιμοποιούμε την ενσωματωμένη βιβλιοθήκη json για να αναλύσουμε το JSON και να διαβάσουμε τα δεδομένα. Συγκρίνετε προσεκτικά αυτό το πρόγραμμα και τα δεδομένα εισόδου του με τα ισοδύναμα δεδομένα και τον κώδικα XML παραπάνω. Το JSON έχει λιγότερες λεπτομέρειες, επομένως πρέπει να γνωρίζουμε εκ των προτέρων ότι λαμβάνουμε μια λίστα και ότι η λίστα είναι χρηστών και κάθε χρήστης είναι ένα σύνολο ζευγών κλειδιών-τιμών. Το JSON είναι πιο συνοπτικό (πλεονέκτημα) αλλά είναι επίσης λιγότερο αυτο-περιγραφικό (ένα μειονέκτημα).

import json

data = '''
[
  { "id" : "001",
    "x" : "2",
    "όνομα" : "Chuck"
  } ,
  { "id" : "009",
    "x" : "7",
    "όνομα" : "Brent"
  }
]'''

info = json.loads(data)
print('Πλήθος χρηστών:', len(info))

for item in info:
    print('Όνομα', item['όνομα'])
    print('Id', item['id'])
    print('Χαρακτηριστικό', item['x'])

# Code: http://www.gr.py4e.com/code3/json2.py

Εάν συγκρίνετε τον κώδικα για την εξαγωγή δεδομένων από το αναλυμένο JSON και το XML, θα δείτε ότι αυτό που λαμβάνουμε από το json.loads() είναι μια λίστα Python, την οποία διασχίζουμε με έναν βρόχο for και κάθε στοιχείο σε αυτήν τη λίστα είναι ένα λεξικό Python. Αφού αναλυθεί το JSON, μπορούμε να χρησιμοποιήσουμε τον τελεστή ευρετηρίου Python για να εξαγάγουμε τα διάφορα bit δεδομένων για κάθε χρήστη. Δεν χρειάζεται να χρησιμοποιήσουμε τη βιβλιοθήκη JSON για να διερευνήσουμε το αναλυμένο JSON, καθώς τα δεδομένα που επιστρέφονται είναι απλώς εγγενείς δομές Python.

Η έξοδος αυτού του προγράμματος είναι ακριβώς η ίδια με την παραπάνω έκδοση XML.

Πλήθος χρηστών: 2
Όνομα Chuck
Id 001
Χαρακτηριστικό 2
Όνομα Brent
Id 009
Χαρακτηριστικό 7

Σε γενικές γραμμές, υπάρχει μια τάση του κλάδου να απομακρύνεται από την XML και να υιοθετεί το JSON για τις υπηρεσίες Ιστού. Επειδή το JSON είναι απλούστερο και αντιστοιχίζεται πιο άμεσα σε εγγενείς δομές δεδομένων που έχουμε ήδη στις γλώσσες προγραμματισμού, ο κώδικας ανάλυσης και εξαγωγής δεδομένων είναι συνήθως απλούστερος και πιο άμεσος όταν χρησιμοποιείται JSON. Αλλά η XML είναι πιο αυτοπεριγραφική από την JSON και έτσι υπάρχουν ορισμένες εφαρμογές όπου η XML διατηρεί ένα πλεονέκτημα. Για παράδειγμα, οι περισσότεροι επεξεργαστές κειμένου αποθηκεύουν έγγραφα εσωτερικά χρησιμοποιώντας XML αντί JSON.

Διεπαφές προγραμματισμού εφαρμογών

Τώρα έχουμε τη δυνατότητα να ανταλλάσσουμε δεδομένα μεταξύ εφαρμογών, χρησιμοποιώντας το Πρωτόκολλο Μεταφοράς HyperText (HTTP) και έναν τρόπο να αναπαραστήσουμε σύνθετα δεδομένα, που ανταλλάσουμε μεταξύ αυτών των εφαρμογών χρησιμοποιώντας τη Γλώσσα Επεκτάσιμης Σήμανσης (XML) ή τη Σημειογραφία Αντικειμένων JavaScript (JSON).

Το επόμενο βήμα είναι να αρχίσουμε να ορίζουμε και να τεκμηριώνουμε “συμβάσεις” μεταξύ εφαρμογών χρησιμοποιώντας αυτές τις τεχνικές. Η γενική ονομασία για αυτές τις από εφαρμογής σε εφαρμογή συμβάσεις είναι Διεπαφές Προγράμματος Εφαρμογής ή Application Program Interfaces (APIs). Όταν χρησιμοποιούμε ένα API, γενικά ένα πρόγραμμα καθιστά διαθέσιμο ένα σύνολο υπηρεσιών, για χρήση από άλλες εφαρμογές και δημοσιεύει τα API (δηλαδή τους “κανόνες”) που πρέπει να ακολουθούνται για την πρόσβαση στις υπηρεσίες που παρέχονται από αυτό.

Όταν αρχίζουμε να χτίζουμε τα προγράμματά μας, όπου η λειτουργικότητα του προγράμματός μας περιλαμβάνει πρόσβαση σε υπηρεσίες που παρέχονται από άλλα προγράμματα, ονομάζουμε την προσέγγιση Αρχιτεκτονική προσανατολισμένη στις υπηρεσίες ή Service-oriented architecture (SOA). Μια προσέγγιση SOA είναι μια προσέγγιση όπου η συνολική μας εφαρμογή κάνει χρήση των υπηρεσιών άλλων εφαρμογών. Μια προσέγγιση που δεν είναι SOA είναι αυτή όπου η εφαρμογή είναι μια ενιαία, αυτόνομη εφαρμογή, που περιέχει όλο τον απαραίτητο κώδικα για την υλοποίηση της εφαρμογής.

Βλέπουμε πολλά παραδείγματα SOA όταν χρησιμοποιούμε τον ιστό. Μπορούμε να μεταβούμε σε έναν ιστότοπο και να κάνουμε κράτηση για αεροπορικά εισιτήρια, ξενοδοχεία και αυτοκίνητα, όλα από έναν μόνο ιστότοπο. Τα δεδομένα για τα ξενοδοχεία δεν αποθηκεύονται στους υπολογιστές της αεροπορικής εταιρείας. Αντίθετα, οι υπολογιστές της αεροπορικής εταιρείας επικοινωνούν με τις υπηρεσίες στους υπολογιστές του ξενοδοχείου, ανακτούν τα δεδομένα του ξενοδοχείου και τα παρουσιάζουν στον χρήστη. Όταν ο χρήστης συμφωνεί να κάνει μια κράτηση ξενοδοχείου, χρησιμοποιώντας τον ιστότοπο της αεροπορικής εταιρείας, ο ιστότοπος της αεροπορικής εταιρείας χρησιμοποιεί μια άλλη υπηρεσία web, στα συστήματα του ξενοδοχείου, για να υλοποιήσει πραγματικά την κράτηση. Και όταν έρθει η ώρα να χρεώσετε την πιστωτική σας κάρτα για ολόκληρη τη συναλλαγή, και πάλι άλλοι υπολογιστές εμπλέκονται στη διαδικασία.

Αρχιτεκτονική Προσανατολισμένη σις Υπηρεσίες (Service-oriented architecture)

Μια αρχιτεκτονική προσανατολισμένη στις υπηρεσίες έχει πολλά πλεονεκτήματα, όπως: (1) διατηρούμε πάντα μόνο ένα αντίγραφο δεδομένων (αυτό είναι ιδιαίτερα σημαντικό για πράγματα όπως κρατήσεις ξενοδοχείων, όπου δεν θέλουμε να κάνουμε υπερβολική δέσμευση) και (2) οι κάτοχοι των δεδομένα μπορούν να ορίσουν τους κανόνες σχετικά με τη χρήση των δεδομένων τους. Με αυτά τα πλεονεκτήματα, ένα σύστημα SOA πρέπει να είναι προσεκτικά σχεδιασμένο ώστε να έχει καλή απόδοση και να καλύπτει τις ανάγκες του χρήστη.

Όταν μια εφαρμογή διαθέτει ένα σύνολο υπηρεσιών στο API της μέσω του ιστού, την ονομάζουμε υπηρεσίες Ιστού (web services).

Ασφάλεια και χρήση API

Είναι αρκετά συνηθισμένο να χρειάζεστε ένα κλειδί API για να χρησιμοποιήσετε το API ενός προμηθευτή. Η γενική ιδέα είναι ότι θέλουν να γνωρίζουν ποιος χρησιμοποιεί τις υπηρεσίες τους και πόσο τις χρησιμοποιεί ο κάθε χρήστης. Ίσως έχουν δωρεάν και επί πληρωμή επίπεδα των υπηρεσιών τους ή έχουν μια πολιτική που περιορίζει τον αριθμό των αιτημάτων που μπορεί να υποβάλει ένα άτομο κατά τη διάρκεια μιας συγκεκριμένης χρονικής περιόδου.

Μερικές φορές, μόλις λάβετε το κλειδί API σας, απλώς συμπεριλάβετε το κλειδί ως μέρος των δεδομένων POST ή ίσως ως παράμετρο στη διεύθυνση URL κατά την κλήση του API.

Άλλες φορές, ο πωλητής θέλει αυξημένη διασφάλιση της προέλευσης των αιτημάτων και έτσι περιμένει από εσάς να στείλετε κρυπτογραφικά υπογεγραμμένα μηνύματα χρησιμοποιώντας κοινόχρηστα κλειδιά και μυστικά. Μια πολύ κοινή τεχνολογία που χρησιμοποιείται για την υπογραφή αιτημάτων μέσω Διαδικτύου ονομάζεται OAuth. Μπορείτε να διαβάσετε περισσότερα για το πρωτόκολλο OAuth στο www.oauth.net.

Ευτυχώς, υπάρχουν πολλές βολικές και δωρεάν βιβλιοθήκες OAuth, ώστε να μπορείτε να αποφύγετε να γράψετε μια υλοποίηση OAuth, από την αρχή διαβάζοντας τις προδιαγραφές. Αυτές οι βιβλιοθήκες είναι ποικίλης πολυπλοκότητας και έχουν διαφορετικούς βαθμούς πλούτου. Ο ιστότοπος του OAuth έχει πληροφορίες σχετικά με διάφορες βιβλιοθήκες OAuth.

Γλωσσάριο

API
Application Program Interface (Διεπαφές Προγράμματος Εφαρμογή) - Ένα συμβόλαιο μεταξύ εφαρμογών που ορίζει τα πρότυπα αλληλεπίδρασης μεταξύ δύο στοιχείων εφαρμογής.
ElementTree
Μια ενσωματωμένη βιβλιοθήκη Python που χρησιμοποιείται για την ανάλυση δεδομένων XML.
JSON
JavaScript Object Notation (Σημειογραφία Αντικειμένου JavaScript) - Μια μορφή που επιτρέπει τη σήμανση δομημένων δεδομένων με βάση τη σύνταξη των αντικειμένων JavaScript.
SOA
Service-Oriented Architecture (Αρχιτεκτονική προσανατολισμένη στις υπηρεσίες) - Όταν μια εφαρμογή αποτελείται από στοιχεία συνδεδεμένα σε ένα δίκτυο.
XML
eXtensible Markup Language (Επεκτάσιμη Γλώσσα Σήμανσης) - Μια μορφή που επιτρέπει τη σήμανση δομημένων δεδομένων.

Εφαρμογή 1: Υπηρεσία ιστού γεωκωδικοποίησης Google

Η Google διαθέτει μια εξαιρετική υπηρεσία ιστού που μας επιτρέπει να χρησιμοποιούμε τη μεγάλη βάση δεδομένων γεωγραφικών πληροφοριών της. Μπορούμε να υποβάλουμε μια συμβολοσειρά γεωγραφικής αναζήτησης όπως “Ann Arbor, MI” στο API γεωκωδικοποίησης της και να ζητήσουμε από την Google να επιστρέψει την καλύτερη εικασία για το πού θα βρούμε τη συμβολοσειρά αναζήτησής μας σε έναν χάρτη και να μας ενημερώσει για τα γειτονικά ορόσημα.

Η υπηρεσία γεωκωδικοποίησης είναι δωρεάν αλλά περιορισμένης χρήση, επομένως δεν μπορείτε να κάνετε απεριόριστη χρήση του API σε μια εμπορική εφαρμογή. Ωστόσο, εάν έχετε κάποια δεδομένα έρευνας, όπου ένας τελικός χρήστης έχει εισαγάγει μια τοποθεσία σε ένα πλαίσιο εισαγωγής ελεύθερης μορφής, μπορείτε να χρησιμοποιήσετε αυτό το API για να καθαρίσετε τα δεδομένα σας αρκετά καλά.

Όταν χρησιμοποιείτε ένα δωρεάν API, όπως το API γεωκωδικοποίησης της Google, θα πρέπει να δείχνετε σεβασμό στη χρήση αυτών των πόρων. Εάν πάρα πολλοί άνθρωποι κάνουν κατάχρηση της υπηρεσίας, η Google ενδέχεται να απορρίψει ή να περιορίσει σημαντικά τη δωρεάν υπηρεσία της.

Μπορείτε να διαβάσετε την ηλεκτρονική τεκμηρίωση για αυτήν την υπηρεσία, αλλά είναι αρκετά απλή και μπορείτε ακόμη και να τη δοκιμάσετε χρησιμοποιώντας ένα πρόγραμμα περιήγησης πληκτρολογώντας την ακόλουθη διεύθυνση URL στο πρόγραμμα περιήγησής σας:

http://maps.googleapis.com/maps/api/geocode/json?address=Ann+Arbor%2C+MI

Φροντίστε να αφαιρέσετε τυχόν κενά από τη διεύθυνση URL προτού την επικολλήσετε στο πρόγραμμα περιήγησής σας.

Η παρακάτω είναι μια απλή εφαρμογή, για να ζητήσει από τον χρήστη μια συμβολοσειρά αναζήτησης, να καλέσει το API γεωκωδικοποίησης της Google και να εξαγάγει πληροφορίες από το JSON που επιστράφηκε.

import urllib.request, urllib.parse, urllib.error
import json
import ssl

api_key = False
# Εάν διαθέτετε κλειδί API Google Places, πληκτρολογήστε το εδώ
# api_key = 'AIzaSy___IDByT70'
# https://developers.google.com/maps/documentation/geocoding/intro

if api_key is False:
    api_key = 42
    serviceurl = 'http://py4e-data.dr-chuck.net/json?'
else :
    serviceurl = 'https://maps.googleapis.com/maps/api/geocode/json?'

# Αγνόησε τα σφάλματα πιστοποιητικού SSL
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

while True:
    address = input('Εισαγάγετε τοποθεσία: ')
    if len(address) < 1: break

    parms = dict()
    parms['address'] = address
    if api_key is not False: parms['key'] = api_key
    url = serviceurl + urllib.parse.urlencode(parms)

    print('Ανάκτηση', url)
    uh = urllib.request.urlopen(url, context=ctx)
    data = uh.read().decode()
    print('Ανακτήθηκαν', len(data), 'χαρακτήρες')

    try:
        js = json.loads(data)
    except:
        js = None

    if not js or 'status' not in js or js['status'] != 'OK':
        print('==== Αποτυχία ανάκτησης ====')
        print(data)
        continue

    print(json.dumps(js, indent=4))

    lat = js['results'][0]['geometry']['location']['lat']
    lng = js['results'][0]['geometry']['location']['lng']
    print('πλάτος', lat, 'μήκος', lng)
    location = js['results'][0]['formatted_address']
    print(location)

# Code: http://www.gr.py4e.com/code3/geojson.py

Το πρόγραμμα παίρνει τη συμβολοσειρά αναζήτησης και κατασκευάζει μια διεύθυνση URL με τη συμβολοσειρά αναζήτησης, ως σωστά κωδικοποιημένη παράμετρο, και στη συνέχεια χρησιμοποιεί το urllib για να ανακτήσει το κείμενο από το API γεωκωδικοποίησης της Google. Σε αντίθεση με μια σταθερή ιστοσελίδα, τα δεδομένα που λαμβάνουμε εξαρτώνται από τις παραμέτρους που στέλνουμε και τα γεωγραφικά δεδομένα που είναι αποθηκευμένα στους διακομιστές της Google.

Μόλις ανακτήσουμε τα δεδομένα JSON, τα αναλύουμε με τη βιβλιοθήκη json και κάνουμε μερικούς ελέγχους για να βεβαιωθούμε ότι λάβαμε καλά δεδομένα και, στη συνέχεια, εξάγουμε τις πληροφορίες που αναζητούμε.

Η έξοδος του προγράμματος είναι η εξής (μερικά από τα JSON που επιστράφηκαν έχουν αφαιρεθεί):

$ python3 geojson.py
Εισαγάγετε τοποθεσία: Ann Arbor, MI
Ανάκτηση http://py4e-data.dr-chuck.net/json?address=Ann+Arbor%2C+MI&key=42
Ανακτήθηκαν 1736 χαρακτήρες
{
    "results": [
        {
            "address_components": [
                {
                    "long_name": "Ann Arbor",
                    "short_name": "Ann Arbor",
                    "types": [
                        "locality",
                        "political"
                    ]
                },
                {
                    "long_name": "Washtenaw County",
                    "short_name": "Washtenaw County",
                    "types": [
                        "administrative_area_level_2",
                        "political"
                    ]
                },
                {
                    "long_name": "Michigan",
                    "short_name": "MI",
                    "types": [
                        "administrative_area_level_1",
                        "political"
                    ]
                },
                {
                    "long_name": "United States",
                    "short_name": "US",
                    "types": [
                        "country",
                        "political"
                    ]
                }
            ],
            "formatted_address": "Ann Arbor, MI, USA",
            "geometry": {
                "bounds": {
                    "northeast": {
                        "lat": 42.3239728,
                        "lng": -83.6758069
                    },
                    "southwest": {
                        "lat": 42.222668,
                        "lng": -83.799572
                    }
                },
                "location": {
                    "lat": 42.2808256,
                    "lng": -83.7430378
                },
                "location_type": "APPROXIMATE",
                "viewport": {
                    "northeast": {
                        "lat": 42.3239728,
                        "lng": -83.6758069
                    },
                    "southwest": {
                        "lat": 42.222668,
                        "lng": -83.799572
                    }
                }
            },
            "place_id": "ChIJMx9D1A2wPIgR4rXIhkb5Cds",
            "types": [
                "locality",
                "political"
            ]
        }
    ],
    "status": "OK"
}
πλάτος 42.2808256 μήκος -83.7430378
Ann Arbor, MI, USA
Εισαγάγετε τοποθεσία:

Μπορείτε να κάνετε λήψη του www.gr.py4e.com/code3/geoxml.py για να εξερευνήσετε την παραλλαγή XML του API γεωκωδικοποίησης Google.

Άσκηση 1: Αλλάξτε είτε το geojson.py είτε το geoxml.py για να εκτυπώσετε τον κωδικό χώρας δύο χαρακτήρων από τα δεδομένα που ανακτήθηκαν. Προσθέστε έλεγχο σφαλμάτων, ώστε το πρόγραμμά σας να μην ανιχνεύει εάν ο κωδικός χώρας δεν υπάρχει. Μόλις το θέσετε σε λειτουργία, αναζητήστε “Atlantic Ocean” και βεβαιωθείτε ότι μπορεί να χειριστεί τοποθεσίες που δεν βρίσκονται σε καμία χώρα.

Εφαρμογή 2: Twitter

Καθώς το Twitter API γινόταν όλο και πιο πολύτιμο, το Twitter μετατράπηκε από ένα ανοιχτό και δημόσιο API σε ένα API που απαιτούσε τη χρήση υπογραφών OAuth σε κάθε αίτημα API.

Για αυτό το επόμενο δείγμα προγράμματος, πραγματοποιήστε λήψη των αρχείων twurl.py, hidden.py, oauth.py και twitter1.py από www.gr.py4e.com/code και αποθηκεύστε τα όλα σε ένα φάκελο στον υπολογιστή σας.

Για να χρησιμοποιήσετε αυτά τα προγράμματα θα χρειαστεί να έχετε λογαριασμό Twitter και να εξουσιοδοτήσετε τον κώδικα Python ως εφαρμογή, να ένα κλειδί (key), ένα μυστικό (secret), ένα διακριτικό (token) και ένα μυστικό διακριτικού (token secret). Θα επεξεργαστείτε το αρχείο hidden.py και θα προσθέσετε αυτές τις τέσσερις συμβολοσειρές στις κατάλληλες μεταβλητές του αρχείου:

# Κρατήστε αυτό το αρχείο ξεχωριστά

# https://apps.twitter.com/
# Δημιουργήστε νέο App και πάρτε τις τέσσερις συμβολοσειρές

def oauth():
    return {"consumer_key": "h7Lu...Ng",
            "consumer_secret": "dNKenAC3New...mmn7Q",
            "token_key": "10185562-eibxCp9n2...P4GEQQOSGI",
            "token_secret": "H0ycCFemmC4wyf1...qoIpBo"}

# Code: http://www.gr.py4e.com/code3/hidden.py

Η πρόσβαση στην υπηρεσία ιστού Twitter γίνεται χρησιμοποιώντας μια διεύθυνση URL όπως αυτή:

https://api.twitter.com/1.1/statuses/user_timeline.json

Αλλά μόλις προστεθούν όλες οι πληροφορίες ασφαλείας, η διεύθυνση URL θα μοιάζει περισσότερο με:

https://api.twitter.com/1.1/statuses/user_timeline.json?count=2
&oauth_version=1.0&oauth_token=101...SGI&screen_name=drchuck
&oauth_nonce=09239679&oauth_timestamp=1380395644
&oauth_signature=rLK...BoD&oauth_consumer_key=h7Lu...GNg
&oauth_signature_method=HMAC-SHA1

Μπορείτε να διαβάσετε την προδιαγραφή OAuth, εάν θέλετε να μάθετε περισσότερα σχετικά με τη σημασία των διαφόρων παραμέτρων που προστίθενται για την κάλυψη των απαιτήσεων ασφαλείας του OAuth.

Για τα προγράμματα που εκτελούμε με το Twitter, αποκρύπτουμε όλη την πολυπλοκότητα στα αρχεία oauth.py και twurl.py. Απλώς ορίζουμε τα μυστικά στο hidden.py και μετά στέλνουμε το επιθυμητό URL στη συνάρτηση twurl.augment() και ο κώδικας της βιβλιοθήκης προσθέτει όλες τις απαραίτητες παραμέτρους στη διεύθυνση URL για εμάς.

Αυτό το πρόγραμμα ανακτά το χρονοδιάγραμμα (timeline) για έναν συγκεκριμένο χρήστη του Twitter και μας το επιστρέφει σε μορφή JSON σε μια συμβολοσειρά. Εκτυπώνουμε απλώς τους πρώτους 250 χαρακτήρες της συμβολοσειράς:

import urllib.request, urllib.parse, urllib.error
import twurl
import ssl

# https://apps.twitter.com/
# Create App and get the four strings, put them in hidden.py

TWITTER_URL = 'https://api.twitter.com/1.1/statuses/user_timeline.json'

# Ignore SSL certificate errors
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

while True:
    print('')
    acct = input('Enter Twitter Account:')
    if (len(acct) < 1): break
    url = twurl.augment(TWITTER_URL,
                        {'screen_name': acct, 'count': '2'})
    print('Retrieving', url)
    connection = urllib.request.urlopen(url, context=ctx)
    data = connection.read().decode()
    print(data[:250])
    headers = dict(connection.getheaders())
    # print headers
    print('Remaining', headers['x-rate-limit-remaining'])

# Code: http://www.gr.py4e.com/code3/twitter1.py

Όταν το πρόγραμμα εκτελείται, παράγει την ακόλουθη έξοδο:

Enter Twitter Account:drchuck
Retrieving https://api.twitter.com/1.1/ ...
[{"created_at":"Sat Sep 28 17:30:25 +0000 2013","
id":384007200990982144,"id_str":"384007200990982144",
"text":"RT @fixpert: See how the Dutch handle traffic
intersections: http:\/\/t.co\/tIiVWtEhj4\n#brilliant",
"source":"web","truncated":false,"in_rep
Remaining 178

Enter Twitter Account:fixpert
Retrieving https://api.twitter.com/1.1/ ...
[{"created_at":"Sat Sep 28 18:03:56 +0000 2013",
"id":384015634108919808,"id_str":"384015634108919808",
"text":"3 months after my freak bocce ball accident,
my wedding ring fits again! :)\n\nhttps:\/\/t.co\/2XmHPx7kgX",
"source":"web","truncated":false,
Remaining 177

Enter Twitter Account:

Μαζί με τα επιστρεφόμενα δεδομένα χρονοδιαγράμματος, το Twitter επιστρέφει επίσης μεταδεδομένα σχετικά με το αίτημα στις κεφαλίδες απόκρισης HTTP. Ειδικότερα, μια κεφαλίδα, x-rate-limit-remaining, μας ενημερώνει πόσα ακόμη αιτήματα μπορούμε να υποβάλουμε προτού απόκλειστουμε για ένα σύντομο χρονικό διάστημα. Μπορείτε να δείτε ότι οι ανακτήσεις που απομένουν μειώνονται κατά μία κάθε φορά που υποβάλλουμε ένα αίτημα στο API.

Στο παρακάτω παράδειγμα, ανακτούμε τους φίλους Twitter ενός χρήστη, αναλύουμε το JSON που επιστράφηκε και εξάγουμε ορισμένες από τις πληροφορίες σχετικά με τους φίλους. Επίσης, απορρίπτουμε το JSON μετά την ανάλυση και το “εκτυπώνουμε όμορφα” με μια εσοχή τεσσάρων χαρακτήρων για να μας επιτρέψει να διαπεράσουμε τα δεδομένα όταν θέλουμε να εξαγάγουμε περισσότερα πεδία.

import urllib.request, urllib.parse, urllib.error
import twurl
import json
import ssl

# https://apps.twitter.com/
# Create App and get the four strings, put them in hidden.py

TWITTER_URL = 'https://api.twitter.com/1.1/friends/list.json'

# Ignore SSL certificate errors
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

while True:
    print('')
    acct = input('Enter Twitter Account:')
    if (len(acct) < 1): break
    url = twurl.augment(TWITTER_URL,
                        {'screen_name': acct, 'count': '5'})
    print('Retrieving', url)
    connection = urllib.request.urlopen(url, context=ctx)
    data = connection.read().decode()

    js = json.loads(data)
    print(json.dumps(js, indent=2))

    headers = dict(connection.getheaders())
    print('Remaining', headers['x-rate-limit-remaining'])

    for u in js['users']:
        print(u['screen_name'])
        if 'status' not in u:
            print('   * No status found')
            continue
        s = u['status']['text']
        print('  ', s[:50])

# Code: http://www.gr.py4e.com/code3/twitter2.py

Εφόσον το JSON γίνεται ένα σύνολο από ένθετες λίστες και λεξικά Python, μπορούμε να χρησιμοποιήσουμε έναν συνδυασμό της λειτουργίας ευρετηρίου και των βρόχων for για να διατρέξουμε στις επιστρεφόμενες δομές δεδομένων με πολύ λίγο κώδικα Python.

Η έξοδος του προγράμματος έχει ως εξής (ορισμένα από τα στοιχεία δεδομένων συντομεύονται για να χωρούν στη σελίδα):

Enter Twitter Account:drchuck
Retrieving https://api.twitter.com/1.1/friends ...
Remaining 14
{
  "next_cursor": 1444171224491980205,
  "users": [
    {
      "id": 662433,
      "followers_count": 28725,
      "status": {
        "text": "@jazzychad I just bought one .__.",
        "created_at": "Fri Sep 20 08:36:34 +0000 2013",
        "retweeted": false,
      },
      "location": "San Francisco, California",
      "screen_name": "leahculver",
      "name": "Leah Culver",
    },
    {
      "id": 40426722,
      "followers_count": 2635,
      "status": {
        "text": "RT @WSJ: Big employers like Google ...",
        "created_at": "Sat Sep 28 19:36:37 +0000 2013",
      },
      "location": "Victoria Canada",
      "screen_name": "_valeriei",
      "name": "Valerie Irvine",
    }
  ],
 "next_cursor_str": "1444171224491980205"
}
leahculver
   @jazzychad I just bought one .__.
_valeriei
   RT @WSJ: Big employers like Google, AT&amp;T are h
ericbollens
   RT @lukew: sneak peek: my LONG take on the good &a
halherzog
   Learning Objects is 10. We had a cake with the LO,
scweeker
   @DeviceLabDC love it! Now where so I get that "etc

Enter Twitter Account:

Το τελευταίο κομμάτι της εξόδου είναι αυτό όπου βλέπουμε τον βρόχο for να διαβάζει τους πέντε πιο πρόσφατους “φίλους” του λογαριασμού Twitter @drchuck και να εκτυπώνει το πιο πρόσφατο status του κάθε φίλου. Υπάρχουν πολλά περισσότερα διαθέσιμα δεδομένα στο JSON που επιστράφηκε. Αν κοιτάξετε στην έξοδο του προγράμματος, μπορείτε επίσης να δείτε ότι το “βρείτε τους φίλους (find the friends)” ενός συγκεκριμένου λογαριασμού έχει διαφορετικό όριο χρέωσης από τον αριθμό των ερωτημάτων χρονοδιαγράμματος, που επιτρέπεται να εκτελούμε ανά χρονική περίοδο.

Αυτά τα ασφαλή κλειδιά API επιτρέπουν στο Twitter να είναι σίγουρο ότι γνωρίζει ποιος χρησιμοποιεί το API και τα δεδομένα του και σε ποιο επίπεδο. Η προσέγγιση περιορισμού πρόσβασης μάς επιτρέπει να κάνουμε απλές, ανακτήσεις προσωπικών δεδομένων, αλλά δεν μας επιτρέπει να δημιουργήσουμε ένα προϊόν που αντλεί δεδομένα από το API τους, εκατομμύρια φορές την ημέρα.


Αν εντοπίσετε κάποιο λάθος σε αυτό το βιβλίο μην διστάσετε να μου στείλετε τη διόρθωση στο Github.