Chapitre 11. Services Web HTTP

11.1. Plonger

Nous avons vu le traitement du HTML et le traitement du XML et, au cours de ces chapitres, comment télécharger une page Web et comment analyser du XML à partir d'une URL. Nous allons maintenant plonger dans le sujet plus général des services Web HTTP.

Pour parler simplement, les services Web HTTP sont une manière d'envoyer et de recevoir des données vers et depuis des serveurs distant par la programmation à l'aide des opérations HTTP. Si nous voulons recevoir des données d'un serveur distant, nous utilisons une simple instruction HTTP GET, si nous voulons envoyer des données au serveur, nous utilisons HTTP POST (certaines API de services Web HTTP plus sophistiquées définissent aussi des manières de modifier ou de supprimer des données avec HTTP PUT et HTTP DELETE). En d'autres termes, les «verbes» du protocole HTTP (GET, POST, PUT et DELETE) correspondent directement à des opérations au niveau de l'application pour recevoir, envoyer, modifier et supprimer des données.

L'avantage principale de cette approche est sa simplicité et cette simplicité a rencontré un succès certain sur de nombreux sites. Les données, en général au format XML, peuvent être construites et stockées de manière statique ou générées dynamiquement par un script côté serveur et tous les principaux langages ont une bibliothèque HTTP pour les télécharger. Le débogage est également plus simple car on peut voir les données brutes à l'aide de n'importe quel navigateur Web. Les navigateur récents affichent même le XML formaté et en couleur pour faciliter sa lecture.

Exemples de services Web XML pur par HTTP :

Dans des chapitres suivants, nous explorerons des API qui utilisent HTTP comme moyen de transport pour envoyer et recevoir des données, mais qui ne font pas correspondre la sémantique de l'application et celle du protocole HTTP (elles communiquent tout par un HTTP POST). Mais ce chapitre est centré sur l'utilisation de HTTP GET pour obtenir des données d'un serveur distant et nous verrons plusieurs fonctionnalités du protocole HTTP que nous pouvons utiliser pour obtenir le maximum des services Web HTTP.

Voici une version plus perfectionnée du module openanything que nous avons vu au chapitre précédent:

Exemple 11.1. openanything.py

Si vous ne l’avez pas déjà fait, vous pouvez télécharger cet exemple ainsi que les autres exemples du livre.


import urllib2, urlparse, gzip
from StringIO import StringIO

USER_AGENT = 'OpenAnything/1.0 +http://diveintopython.org/http_web_services/'

class SmartRedirectHandler(urllib2.HTTPRedirectHandler):    
    def http_error_301(self, req, fp, code, msg, headers):  
        result = urllib2.HTTPRedirectHandler.http_error_301(
            self, req, fp, code, msg, headers)              
        result.status = code                                
        return result                                       

    def http_error_302(self, req, fp, code, msg, headers):  
        result = urllib2.HTTPRedirectHandler.http_error_302(
            self, req, fp, code, msg, headers)              
        result.status = code                                
        return result                                       

class DefaultErrorHandler(urllib2.HTTPDefaultErrorHandler):   
    def http_error_default(self, req, fp, code, msg, headers):
        result = urllib2.HTTPError(                           
            req.get_full_url(), code, msg, headers, fp)       
        result.status = code                                  
        return result                                         

def openAnything(source, etag=None, lastmodified=None, agent=USER_AGENT):
    '''URL, filename, or string --> stream

    This function lets you define parsers that take any input source
    (URL, pathname to local or network file, or actual data as a string)
    and deal with it in a uniform manner.  Returned object is guaranteed
    to have all the basic stdio read methods (read, readline, readlines).
    Just .close() the object when you're done with it.

    If the etag argument is supplied, it will be used as the value of an
    If-None-Match request header.

    If the lastmodified argument is supplied, it must be a formatted
    date/time string in GMT (as returned in the Last-Modified header of
    a previous request).  The formatted date/time will be used
    as the value of an If-Modified-Since request header.

    If the agent argument is supplied, it will be used as the value of a
    User-Agent request header.
    '''

    if hasattr(source, 'read'):
        return source

    if source == '-':
        return sys.stdin

    if urlparse.urlparse(source)[0] == 'http':                                      
        # open URL with urllib2                                                     
        request = urllib2.Request(source)                                           
        request.add_header('User-Agent', agent)                                     
        if etag:                                                                    
            request.add_header('If-None-Match', etag)                               
        if lastmodified:                                                            
            request.add_header('If-Modified-Since', lastmodified)                   
        request.add_header('Accept-encoding', 'gzip')                               
        opener = urllib2.build_opener(SmartRedirectHandler(), DefaultErrorHandler())
        return opener.open(request)                                                 
    
    # try to open with native open function (if source is a filename)
    try:
        return open(source)
    except (IOError, OSError):
        pass

    # treat source as string
    return StringIO(str(source))

def fetch(source, etag=None, last_modified=None, agent=USER_AGENT):  
    '''Fetch data and metadata from a URL, file, stream, or string'''
    result = {}                                                      
    f = openAnything(source, etag, last_modified, agent)             
    result['data'] = f.read()                                        
    if hasattr(f, 'headers'):                                        
        # save ETag, if the server sent one                          
        result['etag'] = f.headers.get('ETag')                       
        # save Last-Modified header, if the server sent one          
        result['lastmodified'] = f.headers.get('Last-Modified')      
        if f.headers.get('content-encoding', '') == 'gzip':          
            # data came back gzip-compressed, decompress it          
            result['data'] = gzip.GzipFile(fileobj=StringIO(result['data']])).read()
    if hasattr(f, 'url'):                                            
        result['url'] = f.url                                        
        result['status'] = 200                                       
    if hasattr(f, 'status'):                                         
        result['status'] = f.status                                  
    f.close()                                                        
    return result                                                    

Pour en savoir plus