Bibliothèque de demandes de Python (Guide)

Bibliothèque de demandes de Python (Guide)

La bibliothèquerequests est le standard de facto pour faire des requêtes HTTP en Python. Il résume la complexité de faire des demandes derrière une belle et simple API afin que vous puissiez vous concentrer sur l'interaction avec les services et la consommation de données dans votre application.

Tout au long de cet article, vous découvrirez certaines des fonctionnalités les plus utiles derequests, ainsi que la manière de personnaliser et d'optimiser ces fonctionnalités pour différentes situations que vous pourriez rencontrer. Vous apprendrez également à utiliserrequests de manière efficace et à empêcher les demandes adressées à des services externes de ralentir votre application.

Dans ce didacticiel, vous apprendrez à:

  • Make requests en utilisant les méthodes HTTP les plus courantes

  • Customize les en-têtes et les données de vos demandes, en utilisant la chaîne de requête et le corps du message

  • Inspect données de vos demandes et réponses

  • Faire des requêtesauthenticated

  • Configure vos demandes pour empêcher votre application de sauvegarder ou de ralentir

Bien que j'aie essayé d'inclure autant d'informations que nécessaire pour comprendre les fonctionnalités et les exemples inclus dans cet article, je suppose queverybasic general knowledge of HTTP. Cela dit, vous pourrez peut-être toujours bien suivre de toute façon.

Maintenant que ce n'est plus le cas, plongeons-nous et voyons comment vous pouvez utiliserrequests dans votre application!

Premiers pas avecrequests

Commençons par installer la bibliothèquerequests. Pour ce faire, exécutez la commande suivante:

$ pip install requests

Si vous préférez utiliserPipenv pour gérer les packages Python, vous pouvez exécuter ce qui suit:

$ pipenv install requests

Une fois querequests est installé, vous pouvez l'utiliser dans votre application. L'importation derequests ressemble à ceci:

import requests

Maintenant que vous êtes prêt, il est temps de commencer votre voyage à traversrequests. Votre premier objectif sera d'apprendre à faire une requêteGET.

La demande GET

HTTP methods tels queGET etPOST, déterminent l'action que vous essayez d'effectuer lors d'une requête HTTP. OutreGET etPOST, il existe plusieurs autres méthodes courantes que vous utiliserez plus tard dans ce didacticiel.

L'une des méthodes HTTP les plus courantes estGET. La méthodeGET indique que vous essayez d'obtenir ou de récupérer des données à partir d'une ressource spécifiée. Pour faire une requêteGET, appelezrequests.get().

Pour tester cela, vous pouvez envoyer une requêteGET auxRoot REST API de GitHub en appelantget() avec l'URL suivante:

>>>

>>> requests.get('https://api.github.com')

Toutes nos félicitations! Vous avez fait votre première demande. Approfondissons un peu la réponse à cette demande.

La réponse

UnResponse est un objet puissant pour inspecter les résultats de la requête. Faisons à nouveau la même demande, mais cette fois stockons la valeur de retour dans une variable afin que vous puissiez voir de plus près ses attributs et comportements:

>>>

>>> response = requests.get('https://api.github.com')

Dans cet exemple, vous avez capturé la valeur de retour deget(), qui est une instance deResponse, et vous l'avez stockée dans une variable appeléeresponse. Vous pouvez maintenant utiliserresponse pour voir beaucoup d'informations sur les résultats de votre requêteGET.

Codes d'état

Le premier bit d'information que vous pouvez collecter à partir deResponse est le code d'état. Un code d'état vous informe de l'état de la demande.

Par exemple, un statut200 OK signifie que votre demande a réussi, tandis qu'un statut404 NOT FOUND signifie que la ressource que vous recherchiez n'a pas été trouvée. Il existe également desmany other possible status codes pour vous donner des informations spécifiques sur ce qui s'est passé avec votre demande.

En accédant à.status_code, vous pouvez voir le code d'état que le serveur a renvoyé:

>>>

>>> response.status_code
200

.status_code a renvoyé un200, ce qui signifie que votre demande a réussi et que le serveur a répondu avec les données que vous demandiez.

Parfois, vous souhaiterez peut-être utiliser ces informations pour prendre des décisions dans votre code:

if response.status_code == 200:
    print('Success!')
elif response.status_code == 404:
    print('Not Found.')

Avec cette logique, si le serveur renvoie un code d'état200, votre programme imprimeraSuccess!. Si le résultat est un404, votre programme imprimeraNot Found.

requests va encore plus loin en simplifiant ce processus pour vous. Si vous utilisez une instanceResponse dans une expression conditionnelle, elle évaluera àTrue si le code d'état était entre200 et400, etFalse dans le cas contraire.

Par conséquent, vous pouvez simplifier le dernier exemple en réécrivant l'instructionif:

if response:
    print('Success!')
else:
    print('An error has occurred.')

Technical Detail: CeTruth Value Test est rendu possible car__bool__() is an overloaded method surResponse.

Cela signifie que le comportement par défaut deResponse a été redéfini pour prendre en compte le code d'état lors de la détermination de la valeur de vérité de l'objet.

Gardez à l'esprit que cette méthode estnot vérifiant que le code d'état est égal à200. La raison en est que d'autres codes d'état dans la plage200 à400, tels que204 NO CONTENT et304 NOT MODIFIED, sont également considérés comme réussis dans le sens où ils fournissent réponse.

Par exemple, le204 vous indique que la réponse a réussi, mais qu'il n'y a aucun contenu à renvoyer dans le corps du message.

Donc, assurez-vous que vous n'utilisez ce raccourci pratique que si vous voulez savoir si la demande a généralement réussi et ensuite, si nécessaire, gérez la réponse de manière appropriée en fonction du code d'état.

Supposons que vous ne souhaitiez pas vérifier le code d’état de la réponse dans une instructionif. Au lieu de cela, vous souhaitez déclencher une exception si la demande a échoué. Vous pouvez le faire en utilisant.raise_for_status():

import requests
from requests.exceptions import HTTPError

for url in ['https://api.github.com', 'https://api.github.com/invalid']:
    try:
        response = requests.get(url)

        # If the response was successful, no Exception will be raised
        response.raise_for_status()
    except HTTPError as http_err:
        print(f'HTTP error occurred: {http_err}')  # Python 3.6
    except Exception as err:
        print(f'Other error occurred: {err}')  # Python 3.6
    else:
        print('Success!')

Si vous appelez.raise_for_status(), unHTTPError sera déclenché pour certains codes d'état. Si le code d'état indique une demande réussie, le programme se poursuivra sans que cette exception ne soit levée.

Further Reading: Si vous n'êtes pas familier avec lesf-strings de Python 3.6, je vous encourage à en profiter car ils sont un excellent moyen de simplifier vos chaînes formatées.

Vous savez maintenant comment gérer le code d'état de la réponse que vous avez reçue du serveur. Cependant, lorsque vous effectuez une requêteGET, vous ne vous souciez rarement que du code d'état de la réponse. Habituellement, vous voulez en voir plus. Ensuite, vous verrez comment afficher les données réelles que le serveur a renvoyées dans le corps de la réponse.

Contenu

La réponse à une requêteGET contient souvent des informations précieuses, appelées charge utile, dans le corps du message. En utilisant les attributs et les méthodes deResponse, vous pouvez afficher la charge utile dans une variété de formats différents.

Pour voir le contenu de la réponse enbytes, vous utilisez.content:

>>>

>>> response = requests.get('https://api.github.com')
>>> response.content
b'{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}","commit_search_url":"https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}","emails_url":"https://api.github.com/user/emails","emojis_url":"https://api.github.com/emojis","events_url":"https://api.github.com/events","feeds_url":"https://api.github.com/feeds","followers_url":"https://api.github.com/user/followers","following_url":"https://api.github.com/user/following{/target}","gists_url":"https://api.github.com/gists{/gist_id}","hub_url":"https://api.github.com/hub","issue_search_url":"https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}","issues_url":"https://api.github.com/issues","keys_url":"https://api.github.com/user/keys","notifications_url":"https://api.github.com/notifications","organization_repositories_url":"https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}","organization_url":"https://api.github.com/orgs/{org}","public_gists_url":"https://api.github.com/gists/public","rate_limit_url":"https://api.github.com/rate_limit","repository_url":"https://api.github.com/repos/{owner}/{repo}","repository_search_url":"https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}","current_user_repositories_url":"https://api.github.com/user/repos{?type,page,per_page,sort}","starred_url":"https://api.github.com/user/starred{/owner}{/repo}","starred_gists_url":"https://api.github.com/gists/starred","team_url":"https://api.github.com/teams","user_url":"https://api.github.com/users/{user}","user_organizations_url":"https://api.github.com/user/orgs","user_repositories_url":"https://api.github.com/users/{user}/repos{?type,page,per_page,sort}","user_search_url":"https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"}'

Alors que.content vous donne accès aux octets bruts de la charge utile de réponse, vous souhaiterez souvent les convertir enstring en utilisant un encodage de caractères tel queUTF-8. response le fera pour vous lorsque vous accéderez à.text:

>>>

>>> response.text
'{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}","commit_search_url":"https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}","emails_url":"https://api.github.com/user/emails","emojis_url":"https://api.github.com/emojis","events_url":"https://api.github.com/events","feeds_url":"https://api.github.com/feeds","followers_url":"https://api.github.com/user/followers","following_url":"https://api.github.com/user/following{/target}","gists_url":"https://api.github.com/gists{/gist_id}","hub_url":"https://api.github.com/hub","issue_search_url":"https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}","issues_url":"https://api.github.com/issues","keys_url":"https://api.github.com/user/keys","notifications_url":"https://api.github.com/notifications","organization_repositories_url":"https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}","organization_url":"https://api.github.com/orgs/{org}","public_gists_url":"https://api.github.com/gists/public","rate_limit_url":"https://api.github.com/rate_limit","repository_url":"https://api.github.com/repos/{owner}/{repo}","repository_search_url":"https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}","current_user_repositories_url":"https://api.github.com/user/repos{?type,page,per_page,sort}","starred_url":"https://api.github.com/user/starred{/owner}{/repo}","starred_gists_url":"https://api.github.com/gists/starred","team_url":"https://api.github.com/teams","user_url":"https://api.github.com/users/{user}","user_organizations_url":"https://api.github.com/user/orgs","user_repositories_url":"https://api.github.com/users/{user}/repos{?type,page,per_page,sort}","user_search_url":"https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"}'

Comme le décodage debytes enstr nécessite un schéma de codage,requests essaiera de deviner lesencoding en fonction desheaders de la réponse si vous ne spécifiez pas un. Vous pouvez fournir un encodage explicite en définissant.encoding avant d'accéder à.text:

>>>

>>> response.encoding = 'utf-8' # Optional: requests infers this internally
>>> response.text
'{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}","commit_search_url":"https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}","emails_url":"https://api.github.com/user/emails","emojis_url":"https://api.github.com/emojis","events_url":"https://api.github.com/events","feeds_url":"https://api.github.com/feeds","followers_url":"https://api.github.com/user/followers","following_url":"https://api.github.com/user/following{/target}","gists_url":"https://api.github.com/gists{/gist_id}","hub_url":"https://api.github.com/hub","issue_search_url":"https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}","issues_url":"https://api.github.com/issues","keys_url":"https://api.github.com/user/keys","notifications_url":"https://api.github.com/notifications","organization_repositories_url":"https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}","organization_url":"https://api.github.com/orgs/{org}","public_gists_url":"https://api.github.com/gists/public","rate_limit_url":"https://api.github.com/rate_limit","repository_url":"https://api.github.com/repos/{owner}/{repo}","repository_search_url":"https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}","current_user_repositories_url":"https://api.github.com/user/repos{?type,page,per_page,sort}","starred_url":"https://api.github.com/user/starred{/owner}{/repo}","starred_gists_url":"https://api.github.com/gists/starred","team_url":"https://api.github.com/teams","user_url":"https://api.github.com/users/{user}","user_organizations_url":"https://api.github.com/user/orgs","user_repositories_url":"https://api.github.com/users/{user}/repos{?type,page,per_page,sort}","user_search_url":"https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"}'

Si vous regardez la réponse, vous verrez qu'il s'agit en fait d'un contenu JSON sérialisé. Pour obtenir un dictionnaire, vous pouvez prendre lesstr que vous avez récupérés à partir de.text et les désérialiser en utilisantjson.loads(). Cependant, un moyen plus simple d'accomplir cette tâche consiste à utiliser.json():

>>>

>>> response.json()
{'current_user_url': 'https://api.github.com/user', 'current_user_authorizations_html_url': 'https://github.com/settings/connections/applications{/client_id}', 'authorizations_url': 'https://api.github.com/authorizations', 'code_search_url': 'https://api.github.com/search/code?q={query}{&page,per_page,sort,order}', 'commit_search_url': 'https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}', 'emails_url': 'https://api.github.com/user/emails', 'emojis_url': 'https://api.github.com/emojis', 'events_url': 'https://api.github.com/events', 'feeds_url': 'https://api.github.com/feeds', 'followers_url': 'https://api.github.com/user/followers', 'following_url': 'https://api.github.com/user/following{/target}', 'gists_url': 'https://api.github.com/gists{/gist_id}', 'hub_url': 'https://api.github.com/hub', 'issue_search_url': 'https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}', 'issues_url': 'https://api.github.com/issues', 'keys_url': 'https://api.github.com/user/keys', 'notifications_url': 'https://api.github.com/notifications', 'organization_repositories_url': 'https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}', 'organization_url': 'https://api.github.com/orgs/{org}', 'public_gists_url': 'https://api.github.com/gists/public', 'rate_limit_url': 'https://api.github.com/rate_limit', 'repository_url': 'https://api.github.com/repos/{owner}/{repo}', 'repository_search_url': 'https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}', 'current_user_repositories_url': 'https://api.github.com/user/repos{?type,page,per_page,sort}', 'starred_url': 'https://api.github.com/user/starred{/owner}{/repo}', 'starred_gists_url': 'https://api.github.com/gists/starred', 'team_url': 'https://api.github.com/teams', 'user_url': 'https://api.github.com/users/{user}', 'user_organizations_url': 'https://api.github.com/user/orgs', 'user_repositories_url': 'https://api.github.com/users/{user}/repos{?type,page,per_page,sort}', 'user_search_url': 'https://api.github.com/search/users?q={query}{&page,per_page,sort,order}'}

Letype de la valeur de retour de.json() est un dictionnaire, vous pouvez donc accéder aux valeurs de l'objet par clé.

Vous pouvez faire beaucoup avec les codes d'état et les corps de message. Mais, si vous avez besoin de plus d'informations, telles que des métadonnées sur la réponse elle-même, vous devrez consulter les en-têtes de la réponse.

En-têtes

Les en-têtes de réponse peuvent vous fournir des informations utiles, telles que le type de contenu de la charge utile de réponse et une limite de temps pour mettre en cache la réponse. Pour afficher ces en-têtes, accédez à.headers:

>>>

>>> response.headers
{'Server': 'GitHub.com', 'Date': 'Mon, 10 Dec 2018 17:49:54 GMT', 'Content-Type': 'application/json; charset=utf-8', 'Transfer-Encoding': 'chunked', 'Status': '200 OK', 'X-RateLimit-Limit': '60', 'X-RateLimit-Remaining': '59', 'X-RateLimit-Reset': '1544467794', 'Cache-Control': 'public, max-age=60, s-maxage=60', 'Vary': 'Accept', 'ETag': 'W/"7dc470913f1fe9bb6c7355b50a0737bc"', 'X-GitHub-Media-Type': 'github.v3; format=json', 'Access-Control-Expose-Headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type', 'Access-Control-Allow-Origin': '*', 'Strict-Transport-Security': 'max-age=31536000; includeSubdomains; preload', 'X-Frame-Options': 'deny', 'X-Content-Type-Options': 'nosniff', 'X-XSS-Protection': '1; mode=block', 'Referrer-Policy': 'origin-when-cross-origin, strict-origin-when-cross-origin', 'Content-Security-Policy': "default-src 'none'", 'Content-Encoding': 'gzip', 'X-GitHub-Request-Id': 'E439:4581:CF2351:1CA3E06:5C0EA741'}

.headers renvoie un objet de type dictionnaire, vous permettant d'accéder aux valeurs d'en-tête par clé. Par exemple, pour voir le type de contenu de la charge utile de réponse, vous pouvez accéder àContent-Type:

>>>

>>> response.headers['Content-Type']
'application/json; charset=utf-8'

Il y a cependant quelque chose de spécial à propos de cet objet d'en-tête de type dictionnaire. La spécification HTTP définit les en-têtes comme étant insensibles à la casse, ce qui signifie que nous pouvons accéder à ces en-têtes sans se soucier de leur capitalisation:

>>>

>>> response.headers['content-type']
'application/json; charset=utf-8'

Que vous utilisiez la clé'content-type' ou'Content-Type', vous obtiendrez la même valeur.

Maintenant, vous avez appris les bases deResponse. Vous avez vu ses attributs et méthodes les plus utiles en action. Prenons un peu de recul et voyons comment vos réponses changent lorsque vous personnalisez vos demandesGET.

Paramètres de chaîne de requête

Une manière courante de personnaliser une requêteGET consiste à transmettre des valeurs via les paramètresquery string dans l'URL. Pour ce faire en utilisantget(), vous transmettez les données àparams. Par exemple, vous pouvez utiliser l'APISearch de GitHub pour rechercher la bibliothèquerequests:

import requests

# Search GitHub's repositories for requests
response = requests.get(
    'https://api.github.com/search/repositories',
    params={'q': 'requests+language:python'},
)

# Inspect some attributes of the `requests` repository
json_response = response.json()
repository = json_response['items'][0]
print(f'Repository name: {repository["name"]}')  # Python 3.6+
print(f'Repository description: {repository["description"]}')  # Python 3.6+

En passant le dictionnaire{'q': 'requests+language:python'} au paramètreparams de.get(), vous pouvez modifier les résultats qui reviennent de l'API de recherche.

Vous pouvez passerparams àget() sous forme de dictionnaire, comme vous venez de le faire, ou sous forme de liste de tuples:

>>>

>>> requests.get(
...     'https://api.github.com/search/repositories',
...     params=[('q', 'requests+language:python')],
... )

Vous pouvez même passer les valeurs enbytes:

>>>

>>> requests.get(
...     'https://api.github.com/search/repositories',
...     params=b'q=requests+language:python',
... )

Les chaînes de requête sont utiles pour paramétrer les requêtesGET. Vous pouvez également personnaliser vos demandes en ajoutant ou en modifiant les en-têtes que vous envoyez.

En-têtes de demande

Pour personnaliser les en-têtes, vous transmettez un dictionnaire d'en-têtes HTTP àget() à l'aide du paramètreheaders. Par exemple, vous pouvez modifier votre demande de recherche précédente pour mettre en évidence les termes de recherche correspondants dans les résultats en spécifiant le type de médiatext-match dans l'en-têteAccept:

import requests

response = requests.get(
    'https://api.github.com/search/repositories',
    params={'q': 'requests+language:python'},
    headers={'Accept': 'application/vnd.github.v3.text-match+json'},
)

# View the new `text-matches` array which provides information
# about your search term within the results
json_response = response.json()
repository = json_response['items'][0]
print(f'Text matches: {repository["text_matches"]}')

L'en-têteAccept indique au serveur les types de contenu que votre application peut gérer. Dans ce cas, puisque vous vous attendez à ce que les termes de recherche correspondants soient mis en surbrillance, vous utilisez la valeur d'en-têteapplication/vnd.github.v3.text-match+json, qui est un en-tête propriétaire de GitHubAcceptdont le contenu est un format JSON spécial.

Avant d'en savoir plus sur la personnalisation des demandes, élargissons l'horizon en explorant d'autres méthodes HTTP.

Autres méthodes HTTP

En plus deGET, d'autres méthodes HTTP populaires incluentPOST,PUT,DELETE,HEAD,PATCH etOPTIONS. requests fournit une méthode, avec une signature similaire àget(), pour chacune de ces méthodes HTTP:

>>>

>>> requests.post('https://httpbin.org/post', data={'key':'value'})
>>> requests.put('https://httpbin.org/put', data={'key':'value'})
>>> requests.delete('https://httpbin.org/delete')
>>> requests.head('https://httpbin.org/get')
>>> requests.patch('https://httpbin.org/patch', data={'key':'value'})
>>> requests.options('https://httpbin.org/get')

Chaque appel de fonction fait une demande au servicehttpbin en utilisant la méthode HTTP correspondante. Pour chaque méthode, vous pouvez inspecter leurs réponses de la même manière qu'auparavant:

>>>

>>> response = requests.head('https://httpbin.org/get')
>>> response.headers['Content-Type']
'application/json'

>>> response = requests.delete('https://httpbin.org/delete')
>>> json_response = response.json()
>>> json_response['args']
{}

Les en-têtes, les corps de réponse, les codes d'état, etc. sont renvoyés dans lesResponse pour chaque méthode. Ensuite, vous examinerez de plus près les méthodesPOST,PUT etPATCH et découvrirez en quoi elles diffèrent des autres types de requêtes.

Le corps du message

Selon la spécification HTTP, les requêtesPOST,PUT et les requêtesPATCH moins courantes transmettent leurs données via le corps du message plutôt que via les paramètres de la chaîne de requête. À l'aide derequests, vous passerez la charge utile au paramètredata de la fonction correspondante.

data prend un dictionnaire, une liste de tuples, d'octets ou un objet de type fichier. Vous souhaiterez adapter les données que vous envoyez dans le corps de votre demande aux besoins spécifiques du service avec lequel vous interagissez.

Par exemple, si le type de contenu de votre demande estapplication/x-www-form-urlencoded, vous pouvez envoyer les données du formulaire sous forme de dictionnaire:

>>>

>>> requests.post('https://httpbin.org/post', data={'key':'value'})

Vous pouvez également envoyer ces mêmes données sous forme de liste de tuples:

>>>

>>> requests.post('https://httpbin.org/post', data=[('key', 'value')])

Si, cependant, vous devez envoyer des données JSON, vous pouvez utiliser le paramètrejson. Lorsque vous transmettez des données JSON viajson,requests sérialisera vos données et ajoutera l'en-têteContent-Type correct pour vous.

httpbin.org est une excellente ressource créée par l'auteur derequests,Kenneth Reitz. Il s'agit d'un service qui accepte les demandes de test et répond avec des données sur les demandes. Par exemple, vous pouvez l'utiliser pour inspecter une requête de basePOST:

>>>

>>> response = requests.post('https://httpbin.org/post', json={'key':'value'})
>>> json_response = response.json()
>>> json_response['data']
'{"key": "value"}'
>>> json_response['headers']['Content-Type']
'application/json'

Vous pouvez voir dans la réponse que le serveur a reçu vos données de demande et vos en-têtes au fur et à mesure que vous les avez envoyés. requests vous fournit également ces informations sous la forme d'unPreparedRequest.

Inspecter votre demande

Lorsque vous effectuez une requête, la bibliothèquerequests prépare la requête avant de l'envoyer réellement au serveur de destination. La préparation des demandes comprend des éléments tels que la validation des en-têtes et la sérialisation du contenu JSON.

Vous pouvez afficher lesPreparedRequest en accédant à.request:

>>>

>>> response = requests.post('https://httpbin.org/post', json={'key':'value'})
>>> response.request.headers['Content-Type']
'application/json'
>>> response.request.url
'https://httpbin.org/post'
>>> response.request.body
b'{"key": "value"}'

L'inspection desPreparedRequest vous donne accès à toutes sortes d'informations sur la demande en cours, telles que la charge utile, l'URL, les en-têtes, l'authentification, etc.

Jusqu'à présent, vous avez fait de nombreux types de demandes, mais ils ont tous une chose en commun: ce sont des demandes non authentifiées adressées à des API publiques. De nombreux services que vous pourriez rencontrer voudront que vous vous authentifiiez d'une manière ou d'une autre.

Authentification

L'authentification aide un service à comprendre qui vous êtes. En règle générale, vous fournissez vos informations d'identification à un serveur en passant des données via l'en-têteAuthorization ou un en-tête personnalisé défini par le service. Toutes les fonctions de requête que vous avez vues jusqu'à présent fournissent un paramètre appeléauth, qui vous permet de transmettre vos informations d'identification.

L'APIAuthenticated User de GitHub est un exemple d'API nécessitant une authentification. Ce point de terminaison fournit des informations sur le profil de l'utilisateur authentifié. Pour faire une demande à l'API utilisateur authentifié, vous pouvez transmettre votre nom d'utilisateur et votre mot de passe GitHub dans un tuple àget():

>>>

>>> from getpass import getpass
>>> requests.get('https://api.github.com/user', auth=('username', getpass()))

La demande a réussi si les informations d'identification que vous avez transmises dans le tuple àauth sont valides. Si vous essayez de faire cette demande sans informations d'identification, vous verrez que le code d'état est401 Unauthorized:

>>>

>>> requests.get('https://api.github.com/user')

Lorsque vous transmettez votre nom d'utilisateur et votre mot de passe dans un tuple au paramètreauth,requests applique les informations d'identification en utilisant lesBasic access authentication scheme de HTTP sous le capot.

Par conséquent, vous pouvez effectuer la même demande en transmettant des informations d'authentification de base explicites à l'aide deHTTPBasicAuth:

>>>

>>> from requests.auth import HTTPBasicAuth
>>> from getpass import getpass
>>> requests.get(
...     'https://api.github.com/user',
...     auth=HTTPBasicAuth('username', getpass())
... )

Bien que vous n'ayez pas besoin d'être explicite pour l'authentification de base, vous souhaiterez peut-être vous authentifier à l'aide d'une autre méthode. requests fournit d'autres méthodes d'authentification prêtes à l'emploi telles queHTTPDigestAuth etHTTPProxyAuth.

Vous pouvez même fournir votre propre mécanisme d'authentification. Pour ce faire, vous devez d'abord créer une sous-classe deAuthBase. Ensuite, vous implémentez__call__():

import requests
from requests.auth import AuthBase

class TokenAuth(AuthBase):
    """Implements a custom authentication scheme."""

    def __init__(self, token):
        self.token = token

    def __call__(self, r):
        """Attach an API token to a custom auth header."""
        r.headers['X-TokenAuth'] = f'{self.token}'  # Python 3.6+
        return r


requests.get('https://httpbin.org/get', auth=TokenAuth('12345abcde-token'))

Ici, votre mécanismeTokenAuth personnalisé reçoit un jeton, puis inclut ce jeton dans l'en-têteX-TokenAuth de votre requête.

De mauvais mécanismes d'authentification peuvent entraîner des vulnérabilités de sécurité. Par conséquent, à moins qu'un service ne nécessite un mécanisme d'authentification personnalisé pour une raison quelconque, vous souhaiterez toujours utiliser un schéma d'authentification éprouvé comme Basic ou OAuth.

Pendant que vous pensez à la sécurité, envisagez de traiter les certificats SSL en utilisantrequests.

Vérification du certificat SSL

Chaque fois que les données que vous essayez d'envoyer ou de recevoir sont sensibles, la sécurité est importante. La façon dont vous communiquez avec des sites sécurisés via HTTP consiste à établir une connexion chiffrée à l'aide de SSL, ce qui signifie que la vérification du certificat SSL du serveur cible est essentielle.

La bonne nouvelle est querequests fait cela pour vous par défaut. Cependant, dans certains cas, vous souhaiterez peut-être modifier ce comportement.

Si vous souhaitez désactiver la vérification du certificat SSL, vous passezFalse au paramètreverify de la fonction de requête:

>>>

>>> requests.get('https://api.github.com', verify=False)
InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)

requests vous avertit même lorsque vous faites une demande non sécurisée pour vous aider à protéger vos données!

Note:requests uses a package called certifi pour fournir les autorités de certification. Cela permet àrequests de savoir à quelles autorités il peut faire confiance. Par conséquent, vous devez mettre à jourcertifi fréquemment pour que vos connexions soient aussi sécurisées que possible.

Performance

Lorsque vous utilisezrequests, en particulier dans un environnement d'application de production, il est important de prendre en compte les implications en termes de performances. Des fonctionnalités telles que le contrôle du délai d'expiration, les sessions et les limites de nouvelle tentative peuvent vous aider à assurer le bon fonctionnement de votre application.

Délais

Lorsque vous faites une demande en ligne à un service externe, votre système devra attendre la réponse avant de continuer. Si votre application attend trop longtemps cette réponse, les demandes adressées à votre service pourraient être sauvegardées, votre expérience utilisateur pourrait en souffrir ou vos tâches d'arrière-plan pourraient se bloquer.

Par défaut,requests attendra indéfiniment la réponse, vous devriez donc presque toujours spécifier une durée d'expiration pour éviter que ces choses ne se produisent. Pour définir le délai d'expiration de la demande, utilisez le paramètretimeout. timeout peut être un entier ou un flottant représentant le nombre de secondes à attendre une réponse avant l'expiration:

>>>

>>> requests.get('https://api.github.com', timeout=1)

>>> requests.get('https://api.github.com', timeout=3.05)

Dans la première demande, la demande expirera après 1 seconde. Dans la deuxième demande, la demande expirera après 3,05 secondes.

You can also pass a tuple àtimeout avec le premier élément étant un timeout de connexion (le temps qu'il laisse au client pour établir une connexion au serveur), et le second étant un timeout de lecture (le temps qu'il attendra une réponse une fois que votre client a établi une connexion):

>>>

>>> requests.get('https://api.github.com', timeout=(2, 5))

Si la demande établit une connexion dans les 2 secondes et reçoit des données dans les 5 secondes suivant l'établissement de la connexion, la réponse sera retournée comme avant. Si la demande expire, la fonction lèvera une exceptionTimeout:

import requests
from requests.exceptions import Timeout

try:
    response = requests.get('https://api.github.com', timeout=1)
except Timeout:
    print('The request timed out')
else:
    print('The request did not time out')

Votre programme peut intercepter l'exceptionTimeout et répondre en conséquence.

L'objet Session

Jusqu'à présent, vous avez utilisé des APIrequests de haut niveau telles queget() etpost(). Ces fonctions sont des abstractions de ce qui se passe lorsque vous faites vos demandes. Ils masquent les détails d'implémentation tels que la gestion des connexions afin que vous n'ayez pas à vous en préoccuper.

Sous ces abstractions se trouve une classe appeléeSession. Si vous avez besoin d'affiner votre contrôle sur la façon dont les demandes sont effectuées ou d'améliorer les performances de vos demandes, vous devrez peut-être utiliser directement une instanceSession.

Les sessions sont utilisées pour conserver les paramètres entre les demandes. Par exemple, si vous souhaitez utiliser la même authentification sur plusieurs demandes, vous pouvez utiliser une session:

import requests
from getpass import getpass

# By using a context manager, you can ensure the resources used by
# the session will be released after use
with requests.Session() as session:
    session.auth = ('username', getpass())

    # Instead of requests.get(), you'll use session.get()
    response = session.get('https://api.github.com/user')

# You can inspect the response just like you did before
print(response.headers)
print(response.json())

Chaque fois que vous faites une demande avecsession, une fois qu'elle a été initialisée avec les informations d'authentification, les informations d'identification seront conservées.

L'optimisation principale des performances des sessions se présente sous la forme de connexions persistantes. Lorsque votre application établit une connexion à un serveur à l'aide d'unSession, elle conserve cette connexion dans un pool de connexions. Lorsque votre application souhaite se connecter à nouveau au même serveur, elle réutilisera une connexion à partir du pool plutôt que d'en établir une nouvelle.

Max tentatives

Lorsqu'une demande échoue, vous souhaiterez peut-être que votre application réessaye la même demande. Cependant,requests ne le fera pas pour vous par défaut. Pour appliquer cette fonctionnalité, vous devez implémenter unTransport Adapter personnalisé.

Les adaptateurs de transport vous permettent de définir un ensemble de configurations par service avec lequel vous interagissez. Par exemple, supposons que vous souhaitiez que toutes les requêtes adressées àhttps://api.github.com réessayent trois fois avant de lever finalement unConnectionError. Vous devez construire un adaptateur de transport, définir son paramètremax_retries et le monter sur unSession existant:

import requests
from requests.adapters import HTTPAdapter
from requests.exceptions import ConnectionError

github_adapter = HTTPAdapter(max_retries=3)

session = requests.Session()

# Use `github_adapter` for all requests to endpoints that start with this URL
session.mount('https://api.github.com', github_adapter)

try:
    session.get('https://api.github.com')
except ConnectionError as ce:
    print(ce)

Lorsque vous montez lesHTTPAdapter,github_adapter, sursession,session adhère à sa configuration pour chaque requête surhttps://api.github.com.

Les délais d'expiration, les adaptateurs de transport et les sessions permettent de garder votre code efficace et votre application résiliente.

Conclusion

Vous avez parcouru un long chemin pour découvrir la puissante bibliothèquerequests de Python.

Vous pouvez désormais:

  • Effectuez des requêtes à l'aide de différentes méthodes HTTP telles queGET,POST etPUT

  • Personnalisez vos demandes en modifiant les en-têtes, l'authentification, les chaînes de requête et les corps de message

  • Inspectez les données que vous envoyez au serveur et les données que le serveur vous renvoie

  • Travailler avec la vérification du certificat SSL

  • Utilisez efficacementrequests en utilisantmax_retries,timeout, les sessions et les adaptateurs de transport

Parce que vous avez appris à utiliserrequests, vous êtes équipé pour explorer le vaste monde des services Web et créer des applications impressionnantes en utilisant les données fascinantes qu'ils fournissent.