 
          Introduction
Les fonctionnalités essentielles à aborder lorsqu’on souhaite apprendre et utiliser très rapidement Python :
- Python - Comprendre et démystifier virtualenv (Publication : Janvier 2020)
- Lire et écrire des fichiers JSON, traiter des données JSON avec Python (Publication : Avril 2020)
- Manipuler les arguments d’un programme Python avec les packages argparse et getopt (Publication : Avril 2020)
- Configuration applicative : variables d’environnement, fichiers ini et YAML (Publication : Avril 2020)
- Gestion des requêtes HTTP avec les packages requests et httplib2 (Publication : Avril 2020)
Dans ce chapître, comment gérer les requêtes HTTP dans un programme Python.
Un programme PHP de démo rpc-articles-indexing.php envoie dans un format JSON les 10 derniers articles à indexer (colonne data_ixgoo est à null) :
rpc-articles-indexing.php
<?php
  $conn=mysqli_connect('localhost','sqlpac_ro','********','sqlpac',40000);
  mysqli_set_charset($conn,"utf8");
  
  if(!$conn) {
    die('Connexion error : ' . mysqli_connect_error());
  }
  
  $sql  = "select filename, id_lang from articles where date_ixgoo is null ";
  $sql .= " order by date_ol desc limit 10 ";
  
  $data = array();
  
  $get_articles = mysqli_query($conn, $sql);
  
  if($get_articles)
  {
     foreach ($get_articles as $row) {
        $data[] = $row;
     }
  }
  
  print json_encode($data);
?>En interrogeant https://www.sqlpac.com/rpc/rpc-articles-indexing.php, les données produites avec json_encode
          ont le format suivant : 
[
  {"filename":"mariadb-columnstore-1.2.3-installation-standalone-ubuntu-premiers-pas.html","id_lang":"fr"},
  {"filename":"mariadb-columnstore-1.2.3-standalone-installation-ubuntu-getting-started.html","id_lang":"en"},
  {"filename":"influxdb-v2-prise-en-main-installation-preparation-migration-version-1.7.html","id_lang":"fr"},
  {"filename":"influxdb-v2-getting-started-setup-preparing-migration-from-version-1.7.html","id_lang":"en"}
]Voyons comment réaliser les requêtes HTTP dans un programme Python.
2 packages très utiles sont disponibles : requests et httplib2.
          Un autre package est disponible: urllib2, mais il nécessite plus de code.
Package requests
Installation
Si il n’est pas installé dans son environnement virtuel Python, installer le package requests avec pip :
pip3 search requestsrequests (2.23.0) - Python HTTP for Humans.
pip3 install requestsInstalling collected packages: urllib3, chardet, certifi, idna, requests Successfully installed certifi-2020.4.5.1 chardet-3.0.4 idna-2.9 requests-2.23.0 urllib3-1.25.9
Une simple requête GET avec requests
Dans le programme Python, il faut juste importer le package requests et appeler la méthode get :
import requests r = requests.get('https://www.sqlpac.com/sqlpac/rpc-articles-indexing.php') print(r.status_code) print(r.headers) print(r.text)200 {'Date': 'Thu, 16 Apr 2020 14:59:04 GMT', 'Content-Type': 'text/html; charset=UTF-8', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Server': 'Apache', 'X-Powered-By': 'PHP/7.3', 'Vary': 'Accept-Encoding', 'Content-Encoding': 'gzip', 'X-IPLB-Instance': '30837', 'Set-Cookie': 'SERVERID108286=102098|Xpic6|Xpic6; path=/' } [ {"filename":"influxdb-v2-prise-en-main-installation-preparation-migration-version-1.7.html","id_lang":"fr"}, {"filename":"influxdb-v2-getting-started-setup-preparing-migration-from-version-1.7.html","id_lang":"en"}, {"filename":"linux-ubuntu-fail2ban-installation-configuration-iptables.html","id_lang":"fr"} ]
Si facile que le code n’a pas besoin de commentaires.
requests - Ajouter des paramètres à la requête GET
Améliorons la requête dans le programme PHP pour y ajouter des critères : https://www.sqlpac.com/rpc/rpc-articles-indexing.php?section=oracle&year=2006
  $sql  = "select filename, id_lang from articles where date_ixgoo is null ";
  if (isset($_GET["section"])) { $sql .= " and filename like '".$_GET["section"]."%'"; }
  if (isset($_GET["year"]))    { $sql .= " and date_ol between '".$_GET["year"]."-01-01' and '".$_GET["year"]."-12-31'"; }
  $sql .= " order by date_ol desc limit 10";Pour envoyer les critères :
import requests q = {'section':'oracle', 'year':2006} r = requests.get('https://www.sqlpac.com/sqlpac/rpc-articles-indexing.php', params=q ) print(r.status_code) print(r.text)200 [ {"filename":"oracle-resultats-procedure-stockee-vers-ms-sql.html","id_lang":"fr"}, {"filename":"oracle-trigger-systeme-after-logon.html","id_lang":"fr"} ]
Pas besoin d’importer le package json, un décodeur JSON est intégré avec la méthode json :
import requests q = {'section':'oracle', 'year':2006} r = requests.get('https://www.sqlpac.com/sqlpac/rpc-articles-indexing.php', params=q ) jresult = r.json() print(type(jresult)) print(jresult[0]["filename"])<class 'list'> oracle-resultats-procedure-stockee-vers-ms-sql.html
requests - La méthode POST
Pour envoyer des données avec la méthode POST, utiliser la méthode post avec l’argument data, aussi simple que la méthode get et son
          argument params:
Le programme PHP rpc-update-article.php met à jour une table en utilisant les variables POST envoyées par le programme Python
          et retourne au format JSON les résultats au format JSON (nombre de lignes affectées ou code erreur): 
rpc-update-article.php
<?php
  $resp = array();
  
  if (! isset($_POST["filename"]) || ! isset($_POST["datets"])) {
     $resp[0]["returncode"] = -1;
     $resp[0]["reason"] = "Missing parameter, filename or timestamp";
  } else {
     $sql = "update articles set date_ixgoo='".$_POST["datets"]."' where filename='".$_POST["filename"]."'";
     
     $conn=mysqli_connect('localhost','sqlpac_ro','********','sqlpac',40000);
     mysqli_set_charset($conn,"utf8");
     
     if ( ! $conn ) {
        $resp[0]["returncode"] = -2;
        $resp[0]["reason"] = "Connexion to database issue";
     }
     else {
        $sql = "update articles set date_ixgoo='".$_POST["datets"]."' where filename='".$_POST["filename"]."'";
        if ( ! mysqli_query($conn,$sql) ) {
           $resp[0]["returncode"] = -2;
           $resp[0]["errorcode"] = mysqli_errno($conn);
           $resp[0]["reason"] = mysqli_error($conn);      
        } else {
           $resp[0]["returncode"] = mysqli_affected_rows($conn);
           $resp[0]["filename"] = $_POST["filename"];
           $resp[0]["datets"] = $_POST["datets"];
        }
        mysqli_close($conn);
     }
     
  }
  print json_encode($resp);
?>Les données sont envoyées avec le code suivant :
import requests formdata = {'filename':'python-http-queries-with-packages-requests-httplib2.html', 'datets':'2020-04-16'} p = requests.post('https://www.sqlpac.com/sqlpac/rpc-update-article.php', data=formdata) print(p.status_code) print(p.json())200 [{'returncode': 1, 'filename': 'python-http-queries-with-packages-requests-httplib2', 'datets':'2020-04-16'}]
Le package requests est puissant pour l’upload de fichiers avec la méthode POST, il n’y a qu’à utiliser l’argument  files :
import requests
formdata = {'filename':'python-http-queries-with-packages-requests-httplib2.html', 'datets':'2020-04-16'}
uploadfiles = {'file': open('file1.txt', 'rb'), 'file': open('file2.txt', 'rb')}
p = requests.post('https://www.sqlpac.com/sqlpac/rpc-update-article.php', data=formdata, files=uploadfiles)requests - Désactivation de la vérification du certificat SSL
Ajouter l’option verify=False pour désactiver la validation du certificat SSL lors de l’utilisation de la méthode 
          get ou post :
import requests r = requests.get('https://www.sqlpac.com/sqlpac/rpc-articles-indexing.php', verify=False )InsecureRequestWarning: Unverified HTTPS request is being made to host 'www.sqlpac.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
requests et l’authentification basique (basic HTTP authentication)
Quand un répertoire web est protégé avec une authentification basique (fichiers .htaccess et .htpasswd), utiliser 
          l’argument auth=HTTPBasicAuth('user','password') en important HTTPBasicAuth
import requests from requests.auth import HTTPBasicAuth r = requests.get('https://www.sqlpac.com/rpc/send-data.php', auth=HTTPBasicAuth('sqlpac', '*********')) print(r.status_code)200
D’autres méthodes d’authentification peuvent bien entendu être utilisées avec requests : Digest, Oauth…
Package httplib2
Étudions un autre package: httplib2. Le package requests est si puissant et simple que l’on pourrait conclure
          que nous avons tout ce qu’il nous faut avec celui-ci mais le package httplib2 doit aussi être étudié car les exemples de code
          pour les API Google utilisent ce package, et il y a un aspect qui est rarement abordé dans les documentations et tutoriaux
          à propos de requests : le mécanisme de mise en cache (caching), disponible en natif avec httplib2.
Installation
Si il n’est pas installé dans l’environnement virtuel Python, installer le package httplib2 avec pip :
pip3 search httplib2httplib2 (0.17.2) - A comprehensive HTTP client library.
pip3 install httplib2Installing collected packages: httplib2 Successfully installed httplib2-0.17.2
Comparé au package requests, httplib2 est autonome et ne requiert pas de dépendances.
          Le package requests dépend de chardet, urllib3 et d’autres.
La requête GET avec httplib2
En utilisant les programmes PHP de démo lors de l’exploration du package requests, pour lancer une requête GET :
import httplib2 http = httplib2.Http() r = http.request("https://www.sqlpac.com/sqlpac/rpc-articles-indexing.php", method="GET") print(r)({'date': 'Thu, 16 Apr 2020 16:20:09 GMT', 'content-type': 'text/html; charset=UTF-8', 'transfer-encoding': 'chunked', 'connection': 'keep-alive', 'server': 'Apache', 'x-powered-by': 'PHP/7.3', 'vary': 'Accept-Encoding', 'x-iplb-instance': '30846', 'set-cookie': 'SERVERID108286=102098|XpjaH|XpjaH; path=/', 'status': '200', 'content-length': '904', '-content-encoding': 'gzip', 'content-location': 'https://www.sqlpac.com/sqlpac/rpc-articles-indexing.php'}, b'[{"filename":"influxdb-v2-prise-en-main-installation-preparation-migration-version-1.7.html","id_lang":"fr"}, … ]')
Les résultats sont moins faciles à exploiter que ceux obtenus avec le package requests.
2 objets sont retournés:
- L’entête (headers), appelé aussi réponse : class 'httplib2.Response'
- Le contenu de la réponse : class 'bytes'
Les objets de la réponse (headers, contenu) peuvent être séparés avec la syntaxe suivante :
import httplib2 http = httplib2.Http() (headers, content) = http.request("https://www.sqlpac.com/sqlpac/rpc-articles-indexing.php", method="GET") print(headers.status)200
httplib2 ne fournit pas de traduction native en JSON comme le fait le package requests avec la méthode json,
          le package json doit être importé et utilisé : 
import json import httplib2 http = httplib2.Http() (headers, content) = http.request("https://www.sqlpac.com/sqlpac/rpc-articles-indexing.php", method="GET") if (headers.status==200) : jdata = json.loads(content) for elt in jdata: print("%s %s" % (elt["filename"], elt["id_lang"]))ms-sql-server-2016-dbcc-clonedatabase-usage.html fr ms-sql-server-2016-using-dbcc-clonedatabase.html en
httplib2 - Les requêtes GET avec des paramètres
Les paramètres doivent être donnés dans l’URL, aussi il ne faut pas oublier d’encoder la chaîne de requête (query string) avec urlencode :
import httplib2 import json from urllib.parse import urlencode params = { "section": "oracle", "year": 2006 } (headers, content) = http.request("https://www.sqlpac.com/sqlpac/rpc-articles-indexing.php?" + urlencode(params), method="GET") if (headers.status==200) : …oracle-resultats-procedure-stockee-vers-ms-sql.html fr oracle-trigger-systeme-after-logon.html fr …
httplib2 - La méthode POST
En utilisant la méthode POST, la méthode est évidemment définie à POST, et 2 autres arguments sont donnés :
- headers: type de contenu, défini à- application/x-www-form-urlencodedpour un formulaire.
- body: valeurs des données à envoyer, à encoder avec- urlencode.
import httplib2 from urllib.parse import urlencode http = httplib2.Http() formdata = {'filename':'python-http-queries-with-packages-requests-httplib2.html', 'datets':'2020-04-16'} (headers, content) = http.request("https://www.sqlpac.com/sqlpac/rpc-update-article.php", method="POST", headers={'Content-type': 'application/x-www-form-urlencoded'}, body=urlencode(formdata) ) print(content)b'[{'returncode': 1, 'filename': 'python-http-queries-with-packages-requests-httplib2', 'datets':'2020-04-16'}]'
Comparé au package requests, plus de code est nécessaire pour gérer les uploads de fichiers avec la méthode POST.
httplib2 - Désactivation de la validation du certificat SSL
Définir la propriété disable_ssl_certificate_validation à True avant d’exécuter une requête
          si la validation du certificat SSL doit être désactivée pour une quelconque raison, aucun message d’avertissement n’est levé
          comparé au package requests :
import httplib2
http = httplib2.Http()
http.disable_ssl_certificate_validation=True
          
(headers, content) = http.request("url", method="GET")
…httplib2 et l’authentification basique (basic HTTP authentication)
Lorsqu’une authentification HTTP basique est mise en place, utiliser la méthode add_credentials(user, password) avant d’appeler la méthode request :
import httplib2 http = httplib2.Http() http.add_credentials('sqlpac','*********') (headers, content) = http.request("https://www.sqlpac.com/rpc/send-data.php", method="POST") print(headers.status)200
Avantages de httplib2 : usage d’un cache
Le package httplib2 est moins facile que le package requests,
          mais il a un gros avantage dans certaines circonstances : le cache.
Les résultats des requêtes peuvent être mis en cache dans un répertoire :
import httplib2 http = httplib2.Http("/tmp/.cache") (headers, content) = http.request("https://www.sqlpac.com/rpc/send-data.php", method="POST") print(headers.status)200
Dans l’exemple ci-dessus, les données sont mis en cache dans le répertoire /tmp/.cache, si le répertoire n’existe pas, le programme essaie de le créer.
L’expiration peut être gouvernée par l’entête Expires envoyé par le serveur Web. Avec Apache, pour définir une expiration dans un
          fichier .htaccess : 
.htaccess
<IfModule mod_expires.c>
	ExpiresActive on
	ExpiresDefault     "access plus 4 hours"
</IfModule>La propriété headers.fromcache (True | False) donne le statut "read from cache | lu depuis le cache" de la réponse.
import httplib2 http = httplib2.Http("/tmp/.cache") (headers, content) = http.request("https://www.sqlpac.com/rpc/1.html") print("Expires : %s" % (headers["expires"])) print(headers.fromcache) (headers, content) = http.request("https://www.sqlpac.com/rpc/1.html") print(headers.fromcache)Expires : Fri, 17 Apr 2020 14:50:13 GMT False True
Tous les appels suivants seront lus depuis le cache jusqu’à la date/heure de l’expiration, et ce sera valable également pour les futures autres exécutions du programme.
Ça peut être utile pour certains besoins, par exemple éviter les surcoûts d’accès réseau pour des données relativement statiques :
Expires : Fri, 17 Apr 2020 14:50:13 GMT
True
TruePour écraser et mettre à jour le cache pour un appel : utiliser l’entête cache-control et appliquer la valeur no-cache :
import httplib2 http = httplib2.Http("/tmp/.cache") (headers, content) = http.request("https://www.sqlpac.com/rpc/1.html") print("Expires : %s" % (headers["expires"])) print(headers.fromcache) (headers, content) = http.request("https://www.sqlpac.com/rpc/1.html", headers={'cache-control':'no-cache'}) print(headers.fromcache)Expires : Fri, 17 Apr 2020 14:50:13 GMT True False
Le package requests ne supporte pas en natif le mode cache, mais un package dérivé est disponible: requests-cache.
Conclusion
Selon les besoins, le package requests est le meilleur pour la gestion des requêtes HTTP si le format JSON est utilisé intensivement,
          ses syntaxes sont les plus faciles.
Pour le mécanisme de mise en cache (caching), httplib2 semble le plus approprié. La mise en cache avec le package requests nécessite 
          un package optionnel (requests-cache), non abordé ici.