Un programme doit pouvoir interagir avec des ressources en réseau. Le composant bas niveau utilisé à cet effet est la socket ; il permet d’établir de programmer clients et serveurs TCP ou TCP.
La bibliothèque standard Python comprend des composants de plus haut niveau
pour — par exemple — effectuer des requêtes HTTP.
Ce cours introduit les requêtes HTTP GET.
Une requête HTTP comprend deux parties :
- les entêtes :
- méthode (
GET,POST…) ; - URL ;
- format souhaité pour la réponse :
application/xml,application/jsonoutext/html; - le format des données envoyées (le cas échéant) :
application/xml,application/jsonouapplication/x-www-form-urlencoded(utilisé lors de la soumission d’un formulaire HTML) ; - …
- méthode (
- le corps de la requête (les données envoyées) — uniquement pour un
POSTou unPUT.
La réponse comporte également des entêtes et un corps.
Plusieurs exceptions peuvent survenir lors d’une requette HTTP :
- serveur non joignable (problème réseau, DNS…)
- erreur dans l’URL ou les paramètres de la requête HTTP.
La fonction suivante peut-être utilisée pour effectuer des requêtes HTTP. Elle est pensée pour pouvoir interagir avec des services web.
import sys
from urllib.request import Request, urlopen
from urllib.error import HTTPError, URLError
from typing import Optional
def http_request (url: str, method: str = "GET",
accept: Optional[str] = None,
data_in: Optional[bytes] = None,
content_type: Optional[str] = None) -> Optional[bytes]:
"""
Enveloppe pour simplifier les requêtes HTTP(S).
@param url l'URL de la ressource
@param method la méthode HTTP (GET, POST, PUT, DELETE…)
@param accept le format de la réponse souhaité (text/html,
application/xml, application/json)
@param data_in les donnes à envoyer au serveur
@param content_type le format des données envoyées
(application/xml, application/json,
application/x-www-form-urlencoded)
@return la réponse du serveur
"""
data_out: Optional[bytes] = None
try:
headers = {"User-Agent": "Python"}
if accept is not None:
headers["Accept"] = accept
if content_type is not None:
headers["Content-Type"] = content_type
req = Request(url, data=data_in, headers=headers, method=method.upper())
with urlopen(req) as response:
data_out = response.read()
except HTTPError as e:
print(f"erreur : statut HTTP non OK\n{e.code}: {e.reason}", file=sys.stderr)
except URLError as e:
print(f"erreur : connexion impossible\n{e.reason}", file=sys.stderr)
return data_out
Les données envoyées ou reçues via le protocole HTTP sont de type bytes
(octets) qui doivent être encodées et décodées depuis et vers str, par exemple
en UTF-8.
Encodage str → bytes
try:
octets = texte.encode('utf-8')
except UnicodeEncodeError as e:
print(f"erreur d'encodage UTF-8\n{e}", file=sys.stderr)
Décodage bytes → str
try:
texte = octets.decode('utf-8')
except UnicodeDecodeError as e:
print(f"erreur de décodage UTF-8\n{e}", file=sys.stderr)
Cet exemple utilise la fonction http_request pour effectuer une recherche
sur Wikipedia.
import json
from urllib.request import quote
from typing import Optional, List
def wikipedia_search (search: str) -> Optional[List[str]]:
"""
Recherche sur Wikipédia - cf https://www.mediawiki.org/wiki/API:REST_API
@param search la recherche
@return: liste des resultats (titres) ou None si erreur reseau/API
"""
results = None
search = quote(search) #encodage des termes de la recherche
api = "https://fr.wikipedia.org/w/rest.php/v1/"
url = api + "search/page?limit=10&q=" + search
response_bytes = http_request(url)
if response_bytes is not None:
try:
response_str = response_bytes.decode("utf-8")
data = json.loads(response_str)
#print(json.dumps(data, indent=4)) #pour déboguer
results = []
for r in data["pages"]:
results.append(r["title"])
except UnicodeDecodeError as e:
print(f"erreur : encodage UTF-8 invalide\n{e}", file=sys.stderr)
except json.JSONDecodeError as e:
print(f"erreur : format JSON invalide\n{e}", file=sys.stderr)
return results
if __name__ == "__main__":
search = input("Recherche : ")
titles = wikipedia_search(search)
if titles is not None:
for t in titles:
print(t)
Remarque : la recherche peut comporter des caractères spéciaux (comme l’espace)
qui doivent être remplacés par %xy…, ou xy… est la valeur hexadécimale
du code UTF-8 du caractère (exemple : %20 pour l’espace) ; c’est ce que
fait la fonction urllib.request.quote.