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 requests
requests (2.23.0) - Python HTTP for Humans.
pip3 install requests
Installing 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 httplib2
httplib2 (0.17.2) - A comprehensive HTTP client library.
pip3 install httplib2
Installing 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-urlencoded
pour un formulaire.body
: valeurs des données à envoyer, à encoder avecurlencode
.
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
True
Pour é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.