Żądania Pythona

Pytania do rozmowy kwalifikacyjnej na temat projektowania systemu może być tak nieograniczona, że ​​trudno jest określić właściwy sposób przygotowania. Teraz jestem w stanie złamać rundy projektowe Amazon, Microsoft i Adobe po zakupie ta książka. Codziennie poprawiaj jeden pytanie projektowe i obiecuję, że możesz złamać cały projekt.

W Pythonie wywołań to wbudowana biblioteka służąca do wykonywania żądań HTTP. Ukrywa wszystkie zawiłości związane z wysyłaniem żądań za prostym interfejsem API, dzięki czemu możemy skupić się na interakcji z usługami i zbieraniu danych.

Biblioteka żądań ma do zaoferowania tak wiele przydatnych funkcji. W tym samouczku zobaczmy, jak korzystać z tych funkcji w sposób dostosowany i zoptymalizowany w różnych sytuacjach. Dowiemy się również, jak efektywnie korzystać z żądań i zapobiegać żądaniom do usług zewnętrznych, co spowalnia działanie aplikacji.

Oto kluczowe rzeczy, których nauczysz się z tego samouczka:

  • Wysyłaj żądania przy użyciu najpopularniejszych metod HTTP
  • Dostosuj nagłówki i dane swoich żądań, używając ciągu zapytania i treści wiadomości
  • Sprawdź dane ze swoich próśb i odpowiedzi
  • Wykonywanie uwierzytelnionych żądań
  • Skonfiguruj żądania, aby uniemożliwić tworzenie kopii zapasowych lub spowolnienie aplikacji

Jeśli masz bardzo podstawową wiedzę na temat HTTP, ten kurs jest dla Ciebie. Jeśli tego nie zrobisz, nie martw się, nadal możesz iść dalej. Zacznijmy.

Prośby o instalację:

Zanim zagłębimy się w szczegóły, zainstalujmy bibliotekę żądań.

Wpisz poniższe polecenie w swoim terminalu, aby zainstalować żądania.

pip install requests

Jeśli wolisz pipenv, użyj poniższego polecenia:

pipenv install requests

Po zainstalowaniu zaimportuj do swojej aplikacji, aby z niej korzystać.

import requests

Teraz mamy wszystko, aby przejść do samouczka, zacznijmy od składania żądań GET.

Żądanie GET:

GET i POST to najpopularniejsze metody HTTP. Te metody określają, jaką akcję zamierzamy wykonać podczas składania żądania. Na przykład, aby pobrać dane ze strony internetowej, używamy GET, a do publikowania danych na stronie internetowej, używamy POST. W dalszej części tego kursu omówimy inne metody HTTP.

Aby złożyć żądanie GET, użyj

requests.get()

Możemy to przetestować, wysyłając żądanie GET do głównego interfejsu API GitHub:

import requests
print(requests.get('https://api.github.com'))
<Response [200]>

Pomyślnie złożyłeś swoje pierwsze żądania GET. Teraz musimy zrozumieć odpowiedź zwróconą przez żądanie.

Odpowiedź:

Za każdym razem, gdy wysyłamy żądanie HTTP, w rezultacie otrzymujemy odpowiedź. Zawiera informacje o tym, czy żądanie zakończyło się powodzeniem, czy nie. Aby zrozumieć obiekt odpowiedzi, musimy przyjrzeć się jego atrybutom i zachowaniom.

Aby to zrobić, wykonaj żądanie GET, ale zapisz wynik w zmiennej.

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

Wartość zwracana przez get() jest instancją Response, którą przechowujemy w zmiennej zwanej odpowiedzią. Teraz możemy zebrać wynik żądania HTTP za pomocą zmiennej odpowiedzi.

Kod statusu:

Jeśli chcesz wiedzieć, czy Twoje żądanie HTTP zakończyło się powodzeniem, czy nie, kod stanu jest tym, na który należy spojrzeć. Kod statusu zawiera informacje o statusie żądania.

Na przykład kod stanu 200 wskazuje, że żądanie zakończyło się powodzeniem, a 404 wskazuje, że żądany zasób nie został znaleziony. Spójrz na lista kodów statusu aby zrozumieć wynik Twojej prośby. C

Sprawdź status żądania GET, które złożyliśmy wcześniej. Możesz uzyskać kod statusu przez

>>> response.status_code
200

Kod statusu to 200, co oznacza, że ​​nasze żądanie GET zakończyło się powodzeniem, a obiekt odpowiedzi zawiera żądane informacje.

Możesz znaleźć się w sytuacji, w której będziesz musiał podjąć decyzję na podstawie odpowiedzi.

if response.status_code==200:
    print('Request is Successful!')
elif response.status_code==404:
    print('Resource not found')

Powyższy kod wyświetli odpowiedni komunikat na konsoli w oparciu o kod statusu odpowiedzi.

Biblioteka żądań dodatkowo upraszcza powyższy kod. Jeśli oceniasz odpowiedź w dowolnym wyrażeniu warunkowym, zwraca True, jeśli kod stanu zawiera się między 200 a 400, w przeciwnym razie zwraca False.

Przepiszmy powyższy kod za pomocą wyrażenia warunkowego:

if response:
    print('Request is Successful!')
else:
    print('There is an error')

Tutaj sprawdzamy kod stanu między 200 a 400. Tak więc kody stanu 204 NO CONTENT (Żądanie zakończyło się powodzeniem, ale treść wiadomości nie zawiera żadnej treści) i 304 NOT MODIFIED również uznano za udane.

Dlatego używaj tego skróconego kodu tylko w celu sprawdzenia, czy żądanie zakończyło się powodzeniem. A następnie obsłuż żądanie na podstawie kodu stanu, jeśli to konieczne.

Zgłaszasz również wyjątek dla nieudanego żądania za pomocą .raise_for_status(). W przypadku pomyślnej odpowiedzi nie zostanie zgłoszony żaden wyjątek.

import requests
from requests.exceptions import HTTPError

urls=[
    'https://api.github.com',
    'https://api.github.com/valid_api'
]
for url in urls:
    try:
        response=requests.get(url)
        response.raise_for_status()
    except HTTPError as http_error:
        print(f'There is an HTTP Error : {http_error}')
    except Exception as error:
        print(f'Some other error : {error}')
    else:
        print('Request is Successful!')

Metoda .raise_for_status() zwraca komunikat „Żądanie powiodło się!” wiadomość dla pomyślnych żądań. W przypadku niektórych kodów stanu możemy otrzymać HTTPError.

Treść:

Teraz wiemy, jak poznać stan żądania za pomocą kodu stanu. Ale w odpowiedzi jest o wiele więcej. W rzeczywistości możesz zobaczyć dane odesłane przez serwer w treści odpowiedzi. Zobaczmy, jak to zrobić.

Odpowiedź zawiera cenne informacje zwane ładunkiem w treści wiadomości. Możemy wyświetlić ładunek w różnych formatach, korzystając z atrybutów i zachowania odpowiedzi.

Na przykład użyj .content, aby zobaczyć zawartość odpowiedzi w bajtach.

response=requests.get('https://api.github.com')
print(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}"}'

Jeśli chcesz przekonwertować surowe bajty ładunku odpowiedzi na ciągi znaków przy użyciu kodowania znaków, takiego jak UTF-8, to mamy .text, który wykonuje zadanie za nas.

>>> 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}"}'

Konwersja z bajtów na ciąg wymaga schematu kodowania. Jeśli nie określimy schematu kodowania, żądania odgadną jeden na podstawie nagłówka odpowiedzi. Możesz ustawić .encoding jawnie przed użyciem .text.

>>> response.encoding='utf-8'
>>> 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}"}'

Treść odpowiedzi wygląda jak obiekt JSON. Możesz przekonwertuj ciąg uzyskane z .text do JSON przy użyciu json.loads() lub .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}'}

Ponieważ wartość zwracana jest słownikiem, możemy dostęp do wartości za pomocą jego klawisza. Możesz zrobić więcej z kodami stanu i treścią wiadomości. Jeśli jednak chcesz uzyskać więcej informacji, takich jak metadane odpowiedzi, musisz spojrzeć na nagłówek odpowiedzi.

Nagłówek odpowiedzi:

Z nagłówka odpowiedzi możemy uzyskać wiele przydatnych informacji, takich jak typ ładunku odpowiedzi i limit czasu na buforowanie odpowiedzi.

>>> response.headers
{'date': 'Fri, 12 Jun 2020 09:03:08 GMT', 'content-type': 'application/json; charset=utf-8', 'server': 'GitHub.com', 'status': '200 OK', 'cache-control': 'public, max-age=60, s-maxage=60', 'vary': 'Accept, Accept-Encoding, Accept, X-Requested-With, Accept-Encoding', 'etag': 'W/"c6bac8870a7f94b08b440c3d5873c9ca"'}

Zwrócona wartość jest obiektem podobnym do słownika, dzięki czemu można uzyskać dostęp do wartości za pomocą klucza. Na przykład, aby sprawdzić typ ładunku odpowiedzi, możesz uzyskać wartość klucza „content-type”.

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

Specyfikacja HTTP definiuje nagłówki bez uwzględniania wielkości liter. Dlatego nie musimy się martwić o sprawę podczas dostępu do nagłówków odpowiedzi.

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

Niezależnie od przypadku, zwracana wartość pozostaje taka sama.

Omówiliśmy przydatne atrybuty i zachowania obiektów odpowiedzi. Zobaczmy teraz, jak dostosować żądanie GET i jak odpowiednio zmienić odpowiedź.

Ciąg zapytania:

Przekazywanie wartości do parametrów ciągu zapytania w adresie URL jest jednym z typowych sposobów dostosowywania żądania GET. Możemy przekazać wartości do params w metodzie get(). Zobaczmy przykład przeszukiwania biblioteki żądań w Repozytoria GitHub.

import requests

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

json_response=response.json()
respository_details=json_response['items'][0]
print(f'Name of the respository : {respository_details["name"]}')
print(f'Description of the repository : {respository_details["description"]}')

Zmieniając wartość przekazaną do params, możesz zmodyfikować wynik odpowiedzi.

Przekazaliśmy dane do params w typie słownikowym. Możesz również przekazać wartości w

  • Lista krotek:
response = requests.get(
    'https://api.github.com/search/repositories',
    params=[('q','requests+language:python')],
)
  • Bajty:
response = requests.get(
    'https://api.github.com/search/repositories',
    params=b'q=requests+language:python',
)

Nagłówki żądań:

Dodając lub modyfikując nagłówki możemy dostosować żądanie HTTP. W metodzie get() przekaż wartości do parametru headers w formacie słownikowym. W nagłówku Accept, określając typ nośnika jako dopasowanie tekstu, możemy podświetlić pasujący termin wyszukiwania z poprzedniego żądania.

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'},
)

json_response=response.json()
respository_details=json_response['items'][0]
print(f'Matching items : {respository_details["text_matches"]}')

Nagłówek Accept informuje serwer o typie treści, którą obsługuje nasza aplikacja. Wartość „application/vnd.github.v3.text-match+json” to zastrzeżony nagłówek GitHub Accept, a treść jest w specjalnym formacie JSON.

Inne metody HTTP:

Jak wspomniano wcześniej, oprócz GET, istnieją inne popularne metody HTTP: POST, PUT, PATCH, DELETE, HEAD i OPTIONS.

>>> 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')

Żądania do serwisu httpbin dokonujemy za pomocą w/w metod HTTP. Możesz sprawdzić odpowiedź dla każdej z metod podobnie jak zrobiliśmy to dla metody GET. Odpowiedź dla każdej z tych metod zawiera nagłówki, kod stanu i nie tylko.

GŁÓWKA i USUŃ:

Metoda HEAD żąda nagłówków z serwera.

>>> response = requests.head('https://httpbin.org/get')
>>> response.headers
{'Date': 'Sat, 13 Jun 2020 11:13:05 GMT', 'Content-Type': 'application/json', 'Content-Length': '308', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true'}

Metoda DELETE usuwa określony zasób.

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

POST, PUT i PATCH:

Metody POST, PUT i PATCH wysyłają dane na serwer za pośrednictwem treści wiadomości. Ładunek przekazujemy w parametrze danych funkcji.

Metoda POST służy do tworzenia nowego zasobu. PUT i PATCH są używane odpowiednio do pełnej i częściowej aktualizacji.

Możemy przekazać dane ze słownika, listy krotek, bajtów lub obiektu plikopodobnego. ten rodzaj danych zależy od konkretnych potrzeb usługi, której potrzebujemy. Na przykład, jeśli typ treści żądania to application/x-www-form-urlencoded, musisz przesłać dane w formacie słownikowym.

Użyj parametru json zamiast danych, aby wysłać dane json. Biblioteka żądań doda dla nas odpowiedni typ zawartości podczas korzystania z parametru json. Możesz to sprawdzić na podstawie odpowiedzi, którą otrzymaliśmy na zgłoszenie prośby.

import requests

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

Przygotowane żądanie:

Biblioteka żądań sprawdza poprawność nagłówka i serializuje zawartość JSON przed wysłaniem żądania do serwera docelowego. Dzięki .request możemy zobaczyć to PreparedRequest. PreparedRequest zawiera informacje o żądaniu, takie jak ładunek, adres URL, nagłówki i inne.

import requests

response=requests.post('https://httpbin.org/post', json={'key':'value'})
print(response.request.url)
print(response.request.headers)
print(response.request.body)
https://httpbin.org/post
{'User-Agent': 'python-requests/2.23.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '16', 'Content-Type': 'application/json'}
b'{"key": "value"}'

Poświadczenie:

Do tej pory widzieliśmy, jak wysyłać nieuwierzytelnione żądania do publicznych interfejsów API. Ale usługi mogą wymagać uwierzytelnienia. Wysyłamy nasze dane uwierzytelniające przez nagłówek autoryzacji lub niestandardowy nagłówek zdefiniowany przez serwer. Funkcja request udostępnia parametr auth do przekazywania poświadczeń.

Rzućmy okiem na GitHub Uwierzytelniony użytkownik API dostarczające informacji o profilu uwierzytelnionego użytkownika. Przekaż nazwę użytkownika i hasło w krotce w get(), aby wysłać żądanie do interfejsu API uwierzytelnionego użytkownika.

import requests
from getpass import getpass

print(requests.get('https://api.github.com/user', auth=('username', getpass())))
<Response [200]>

Aby żądanie zakończyło się powodzeniem, musisz przekazać prawidłowe poświadczenia. W przeciwnym razie otrzymasz nieautoryzowany błąd 401.

>>> requests.get('https://api.github.com/user')
<Response [401]>

Za każdym razem, gdy przekażesz żądanie nazwy użytkownika i hasła, zastosujesz poświadczenia przy użyciu protokołu HTTP Podstawowy schemat uwierzytelniania dostępu. Dlatego możesz zmodyfikować powyższe żądanie, jawnie przekazując poświadczenia przy użyciu HTTPBasicAuth.

import requests
from getpass import getpass
from requests.auth import HTTPBasicAuth

print(requests.get('https://api.github.com/user', auth=HTTPBasicAuth('[email protected]', getpass())))
<Response [200]>

Biblioteka żądań udostępnia również inne metody uwierzytelniania, takie jak HTTPDigestAuth i HTTPProxyAuth.

Możesz również mieć swój niestandardowy mechanizm uwierzytelniania, tworząc podklasę AuthBase i implementując metodę __call__().

import requests
from requests.auth import AuthBase

class CustomTokenAuth(AuthBase):

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

    def __call__(self, request):
        request.headers['X-CustomTokenAuth']=f'{self.token}'
        return request

print(requests.get('https://httpbin.org/get', auth=CustomTokenAuth('sample_token123')))
<Response [200]>

Tutaj otrzymujemy token i dodajemy go do nagłówka X-CustomTokenAuth żądania. Ale jeśli mechanizm uwierzytelniania będzie zły, pojawią się luki w zabezpieczeniach. Dlatego zawsze lepiej jest trzymać się sprawdzonych schematów uwierzytelniania, takich jak Basic lub OAuth.

Weryfikacja certyfikatu SSL:

Bezpieczeństwo jest najważniejsze, gdy wysyłamy i odbieramy dane ze strony internetowej. Komunikacja z bezpiecznymi witrynami przez HTTP jest nawiązywana przez szyfrowane połączenie z wykorzystaniem SSL, poprzez weryfikację certyfikatu SSL serwera docelowego.

Biblioteka żądań robi to za nas domyślnie. Możesz zmienić to zachowanie, przekazując parametr weryfikacji jako False. Żądanie zawiera również ostrzeżenie, że składasz niezabezpieczone żądanie.

>>> requests.get('https://api.github.com', verify=False)
C:\Users\Gopi\AppData\Local\Programs\Python\Python36-32\lib\site-packages\urllib3\connectionpool.py:858: InsecureRequest
Warning: 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)
<Response [200]>

Performance:

Musimy zadbać o wydajność podczas korzystania z żądań w środowisku produkcyjnym. Aby aplikacja działała bez żadnych problemów, musimy zadbać o kontrolę limitów czasu, sesji i limitów ponownych prób.

Koniec czasu:

Gdy wysyłamy żądanie do usługi zewnętrznej, system czeka na odpowiedź. Jeśli uzyskanie odpowiedzi zajmie zbyt dużo czasu, może to spowodować złe wrażenia użytkownika lub zawieszenie się aplikacji zaplecza.

Biblioteka żądań będzie domyślnie czekać na odpowiedź w nieskończoność. Możesz jednak temu zapobiec, określając limit czasu za pomocą parametru timeout. Limit czasu akceptuje wartości całkowite lub zmiennoprzecinkowe reprezentujące czas w sekundach oczekiwania na odpowiedź przed upływem limitu czasu.

>>> requests.get('https://api.github.com', timeout=3)
<Response [200]>
>>> requests.get('https://api.github.com', timeout=4.5)
<Response [200]>

Pierwsze żądanie wygaśnie po 3 sekundach, a drugie po 4.5 sekundy.

Limit czasu akceptuje również krotkę. Pierwsza wartość to czas, w którym klient może nawiązać połączenie, a druga wartość to czas oczekiwania na odpowiedź po ustanowieniu połączenia.

>>> requests.get('https://api.github.com', timeout=(3,4.5))
<Response [200]>

Otrzymasz 200 Response, jeśli połączenie zostanie nawiązane w ciągu 3 sekund, a odpowiedź zostanie odebrana w ciągu 4.5 sekundy po nawiązaniu połączenia. Możesz również zgłosić wyjątek Timeout dla przekroczenia limitu czasu żądania.

import requests
from requests.exceptions import Timeout

try:
    response = requests.get('https://api.github.com', timeout=0.01)

except Timeout:
    print('Request timed out')
else:
    print('Request is successful')
Request timed out

Obiekt sesji:

Do tej pory nie martwiliśmy się o to, jak ustanawiane są połączenia, gdy tworzymy API żądań wysokiego poziomu, takie jak get() i post(). Te funkcje to tylko abstrakcje tego, co się dzieje, gdy wysyłamy żądanie. Pod tymi abstrakcjami znajduje się klasa o nazwie sesja. Aby poprawić wydajność żądań lub mieć kontrolę nad swoimi żądaniami, musisz bezpośrednio skorzystać z instancji sesji.

We wszystkich żądaniach do utrwalania parametrów używane są sesje. Na przykład możesz użyć sesji, w której musisz użyć tych samych poświadczeń autoryzacji w wielu żądaniach.

import requests
from getpass import getpass

with requests.Session() as session:
    session.auth = ('username', getpass())

    response = session.get('https://api.github.com/user')

print(response.headers)
print(response.json())

Po zainicjowaniu obiektu sesji przy użyciu poświadczeń uwierzytelniających, poświadczenia zostaną zachowane za każdym razem, gdy wyślemy żądanie z sesją.

Za każdym razem, gdy sesja jest używana do nawiązania połączenia, utrzymuje to połączenie w puli połączeń. Gdy zajdzie potrzeba ponownego nawiązania tego samego połączenia, zamiast ustanawiania nowego, użyje ponownie połączenia z puli połączeń. Dlatego wydajność jest zoptymalizowana dzięki trwałemu połączeniu.

Maksymalna liczba prób:

Otrzymasz ConnectionError, gdy żądanie się nie powiedzie, ale chcesz, aby aplikacja ponowiła próbę. Biblioteka żądań nie robi tego za nas. Rozwiązaniem jest wdrożenie niestandardowego adaptera transportowego.

Dzięki adapterom transportowym możesz zdefiniować zestaw konfiguracji dla każdej usługi, z którą wchodzimy w interakcję. Na przykład chcesz, aby wszystkie żądania do https://api.github.com spróbowały 5 razy przed podniesieniem ConnectionError. Zbuduj własny adapter transportowy, ustaw jego parametr max_retries na 5 razy i zamontuj go w istniejącej sesji.

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

transport_adapter = HTTPAdapter(max_retries=5)

session = requests.Session()

session.mount('https://api.github.com', transport_adapter)

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

Montując transport_adaptor do instancji sesji, instancja sesji będzie przestrzegać swojej konfiguracji dla każdego żądania wysłanego do https://api.github.com.

 

Wywiady dotyczące projektowania systemu pękania
Translate »