Chapter 13
Using Web Services
Once it became easy to retrieve documents and parse documents over HTTP using
programs, it did not take long to develop an approach where we started producing
documents that were specifically designed to be consumed by other programs (i.e.,
not HTML to be displayed in a browser).
There are two common formats that we use when exchanging data across the web.
eXtensible Markup Language (XML) has been in use for a very long time and
is best suited for exchanging document-style data. When programs just want to
exchange dictionaries, lists, or other internal information with each other, they
use JavaScript Object Notation (JSON) (see [Link]). We will look at both
formats.
13.1 eXtensible Markup Language - XML
XML looks very similar to HTML, but XML is more structured than HTML. Here
is a sample of an XML document:
<person>
<name>Chuck</name>
<phone type="intl">
+1 734 303 4456
</phone>
<email hide="yes" />
</person>
Each pair of opening (e.g., <person>) and closing tags (e.g., </person>) represents
an element or node with the same name as the tag (e.g., person). Each element
can have some text, some attributes (e.g., hide), and other nested elements. If
an XML element is empty (i.e., has no content), then it may be depicted by a
self-closing tag (e.g., <email />).
Often it is helpful to think of an XML document as a tree structure where there is
a top element (here: person), and other tags (e.g., phone) are drawn as children
of their parent elements.
159
160 CHAPTER 13. USING WEB SERVICES
person
name phone email
type=intl hide=yes
+1 734
Chuck
303 4456
Figure 13.1: A Tree Representation of XML
13.2 Parsing XML
Here is a simple application that parses some XML and extracts some data elements
from the XML:
import [Link] as ET
data = '''
<person>
<name>Chuck</name>
<phone type="intl">
+1 734 303 4456
</phone>
<email hide="yes" />
</person>'''
tree = [Link](data)
print('Name:', [Link]('name').text)
print('Attr:', [Link]('email').get('hide'))
# Code: [Link]
The triple single quote ('''), as well as the triple double quote ("""), allow for
the creation of strings that span multiple lines.
Calling fromstring converts the string representation of the XML into a “tree” of
XML elements. When the XML is in a tree, we have a series of methods we can
call to extract portions of data from the XML string. The find function searches
through the XML tree and retrieves the element that matches the specified tag.
Name: Chuck
Attr: yes
Using an XML parser such as ElementTree has the advantage that while the
XML in this example is quite simple, it turns out there are many rules regarding
13.3. LOOPING THROUGH NODES 161
valid XML, and using ElementTree allows us to extract data from XML without
worrying about the rules of XML syntax.
13.3 Looping through nodes
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 [Link] as ET
input = '''
<stuff>
<users>
<user x="2">
<id>001</id>
<name>Chuck</name>
</user>
<user x="7">
<id>009</id>
<name>Brent</name>
</user>
</users>
</stuff>'''
stuff = [Link](input)
lst = [Link]('users/user')
print('User count:', len(lst))
for item in lst:
print('Name', [Link]('name').text)
print('Id', [Link]('id').text)
print('Attribute', [Link]('x'))
# Code: [Link]
The findall method retrieves a Python list of subtrees that represent the user
structures in the XML tree. Then we can write a for loop that looks at each of
the user nodes, and prints the name and id text elements as well as the x attribute
from the user node.
User count: 2
Name Chuck
Id 001
Attribute 2
Name Brent
Id 009
Attribute 7
162 CHAPTER 13. USING WEB SERVICES
It is important to include all parent level elements in the findall statement except
for the top level element (e.g., users/user). Otherwise, Python will not find any
desired nodes.
import [Link] as ET
input = '''
<stuff>
<users>
<user x="2">
<id>001</id>
<name>Chuck</name>
</user>
<user x="7">
<id>009</id>
<name>Brent</name>
</user>
</users>
</stuff>'''
stuff = [Link](input)
lst = [Link]('users/user')
print('User count:', len(lst))
lst2 = [Link]('user')
print('User count:', len(lst2))
lst stores all user elements that are nested within their users parent. lst2 looks
for user elements that are not nested within the top level stuff element where
there are none.
User count: 2
User count: 0
13.4 JavaScript Object Notation - JSON
The JSON format was inspired by the object and array format used in the
JavaScript language. But since Python was invented before JavaScript, Python’s
syntax for dictionaries and lists influenced the syntax of JSON. So the format of
JSON is nearly identical to a combination of Python lists and dictionaries.
Here is a JSON encoding that is roughly equivalent to the simple XML from above:
{
"name" : "Chuck",
"phone" : {
"type" : "intl",
"number" : "+1 734 303 4456"
13.5. PARSING JSON 163
},
"email" : {
"hide" : "yes"
}
}
You will notice some differences. First, in XML, we can add attributes like “intl”
to the “phone” tag. In JSON, we simply have key-value pairs. Also the XML
“person” tag is gone, replaced by a set of outer curly braces.
In general, JSON structures are simpler than XML because JSON has fewer ca-
pabilities than XML. But JSON has the advantage that it maps directly to some
combination of dictionaries and lists. And since nearly all programming languages
have something equivalent to Python’s dictionaries and lists, JSON is a very nat-
ural format to have two cooperating programs exchange data.
JSON is quickly becoming the format of choice for nearly all data exchange between
applications because of its relative simplicity compared to XML.
13.5 Parsing JSON
We construct our JSON by nesting dictionaries and lists as needed. In this example,
we represent a list of users where each user is a set of key-value pairs (i.e., a
dictionary). So we have a list of dictionaries.
In the following program, we use the built-in json library to parse the JSON and
read through the data. Compare this closely to the equivalent XML data and code
above. The JSON has less detail, so we must know in advance that we are getting a
list and that the list is of users and each user is a set of key-value pairs. The JSON
is more succinct (an advantage) but also is less self-describing (a disadvantage).
import json
data = '''
[
{ "id" : "001",
"x" : "2",
"name" : "Chuck"
} ,
{ "id" : "009",
"x" : "7",
"name" : "Brent"
}
]'''
info = [Link](data)
print('User count:', len(info))
for item in info:
print('Name', item['name'])
164 CHAPTER 13. USING WEB SERVICES
print('Id', item['id'])
print('Attribute', item['x'])
# Code: [Link]
If you compare the code to extract data from the parsed JSON and XML you will
see that what we get from [Link]() is a Python list which we traverse with
a for loop, and each item within that list is a Python dictionary. Once the JSON
has been parsed, we can use the Python index operator to extract the various bits
of data for each user. We don’t have to use the JSON library to dig through the
parsed JSON, since the returned data is simply native Python structures.
The output of this program is exactly the same as the XML version above.
User count: 2
Name Chuck
Id 001
Attribute 2
Name Brent
Id 009
Attribute 7
In general, there is an industry trend away from XML and towards JSON for web
services. Because the JSON is simpler and more directly maps to native data struc-
tures we already have in programming languages, the parsing and data extraction
code is usually simpler and more direct when using JSON. But XML is more self-
descriptive than JSON and so there are some applications where XML retains an
advantage. For example, most word processors store documents internally using
XML rather than JSON.
13.6 Application Programming Interfaces
We now have the ability to exchange data between applications using Hypertext
Transport Protocol (HTTP) and a way to represent complex data that we are send-
ing back and forth between these applications using eXtensible Markup Language
(XML) or JavaScript Object Notation (JSON).
The next step is to begin to define and document “contracts” between applications
using these techniques. The general name for these application-to-application con-
tracts is Application Program Interfaces (APIs). When we use an API, generally
one program makes a set of services available for use by other applications and
publishes the APIs (i.e., the “rules”) that must be followed to access the services
provided by the program.
When we begin to build our programs where the functionality of our program
includes access to services provided by other programs, we call the approach a
Service-oriented architecture (SOA). An SOA approach is one where our overall
application makes use of the services of other applications. A non-SOA approach
is where the application is a single standalone application which contains all of the
code necessary to implement the application.
13.7. SECURITY AND API USAGE 165
Auto Hotel Airline
Rental Reservation Reservation
Service Service Service
API
API API
Travel
Application
Figure 13.2: Service-oriented architecture
We see many examples of SOA when we use the web. We can go to a single web
site and book air travel, hotels, and automobiles all from a single site. The data
for hotels is not stored on the airline computers. Instead, the airline computers
contact the services on the hotel computers and retrieve the hotel data and present
it to the user. When the user agrees to make a hotel reservation using the airline
site, the airline site uses another web service on the hotel systems to actually make
the reservation. And when it comes time to charge your credit card for the whole
transaction, still other computers become involved in the process.
A Service-oriented architecture has many advantages, including: (1) we always
maintain only one copy of data (this is particularly important for things like hotel
reservations where we do not want to over-commit) and (2) the owners of the data
can set the rules about the use of their data. With these advantages, an SOA
system must be carefully designed to have good performance and meet the user’s
needs.
When an application makes a set of services in its API available over the web, we
call these web services.
13.7 Security and API usage
It is quite common that you need an API key to make use of a vendor’s API. The
general idea is that they want to know who is using their services and how much
each user is using. Perhaps they have free and pay tiers of their services or have a
policy that limits the number of requests that a single individual can make during
a particular time period.
Sometimes once you get your API key, you simply include the key as part of POST
data or perhaps as a parameter on the URL when calling the API.
166 CHAPTER 13. USING WEB SERVICES
Other times, the vendor wants increased assurance of the source of the requests
and so they expect you to send cryptographically signed messages using shared
keys and secrets. A very common technology that is used to sign requests over
the Internet is called OAuth. You can read more about the OAuth protocol at
[Link].
Thankfully there are a number of convenient and free OAuth libraries so you can
avoid writing an OAuth implementation from scratch by reading the specification.
These libraries are of varying complexity and have varying degrees of richness. The
OAuth web site has information about various OAuth libraries.
13.8 Glossary
API Application Program Interface - A contract between applications that defines
the patterns of interaction between two application components.
ElementTree A built-in Python library used to parse XML data.
JSON JavaScript Object Notation - A format that allows for the markup of struc-
tured data based on the syntax of JavaScript Objects.
SOA Service-Oriented Architecture - When an application is made of components
connected across a network.
XML eXtensible Markup Language - A format that allows for the markup of
structured data.