Como executar solicitações HTTP com Python - Parte 1 A Biblioteca Padrão

Como executar solicitações HTTP com Python - Parte 1 A Biblioteca Padrão

HTTP é o protocolo usado pela World Wide Web, é por isso que ser capaz de interagir com ele programaticamente é essencial: raspar uma página da web, se comunicar com uma APIs de serviço ou mesmo simplesmente baixar um arquivo, são todas as tarefas com base nessa interação. O Python facilita muito essas operações: algumas funções úteis já são fornecidas na biblioteca padrão e, para tarefas mais complexas, é possível (e até recomendado) usar o externo solicitações de módulo. Neste primeiro artigo da série, focaremos nos módulos internos. Usaremos o Python3 e principalmente trabalharemos dentro da concha interativa do Python: as bibliotecas necessárias serão importadas apenas uma vez para evitar repetições.

Neste tutorial, você aprenderá:

  • Como executar solicitações HTTP com Python3 e o urllib.Solicitar biblioteca
  • Como trabalhar com respostas ao servidor
  • Como baixar um arquivo usando o URLOPEN ou as funções URLrevieve


Solicitação http com python - pt. I: a biblioteca padrão

Requisitos de software e convenções usadas

Requisitos de software e convenções de linha de comando Linux
Categoria Requisitos, convenções ou versão de software usada
Sistema Independente do OS
Programas Python3
Outro
  • Conhecimento dos conceitos básicos de programação orientada a objetos e da linguagem de programação Python
  • Conhecimento básico do protocolo HTTP e verbos HTTP
Convenções # - requer que os comandos Linux sejam executados com privilégios root diretamente como usuário root ou por uso de sudo comando
$ - Requer que os comandos do Linux sejam executados como um usuário não privilegiado regular

Executando solicitações com a biblioteca padrão

Vamos começar com um muito fácil PEGAR solicitar. O verbo get http é usado para recuperar dados de um recurso. Ao executar esse tipo de solicitação, é possível especificar alguns parâmetros nas variáveis ​​de formulário: essas variáveis, expressas como pares de valor-chave, formam um string de consulta que é "anexado" ao Url do recurso. Uma solicitação de get deve sempre ser idempotent (Isso significa que o resultado da solicitação deve ser independente do número de vezes que é realizado) e nunca deve ser usado para mudar um estado. Executar solicitações de get com python é realmente fácil. Para o bem deste tutorial, aproveitaremos a chamada aberta da API da NASA, que nos vamos recuperar a chamada "foto do dia":



>>> de urllib.Solicite Urlopen de importação >>> com Urlopen ("https: // API.NASA.Gov/planetário/apod?api_key = Demo_key ") como resposta: ... resposta_content = resposta.ler() 
cópia de

A primeira coisa que fizemos foi importar o urlopen função do urllib.solicitar Biblioteca: esta função retorna um http.cliente.Httpro -resposta objeto que tem alguns métodos muito úteis. Usamos a função dentro de um com declaração porque o Httpro -resposta Objeto suporta o gerenciamento de contexto Protocolo: os recursos são imediatamente fechados após a declaração "com" é executada, mesmo que um exceção é levantado.

O ler Método que usamos no exemplo acima retorna o corpo do objeto de resposta como um bytes e opcionalmente, leva um argumento que representa a quantidade de bytes para ler (veremos mais adiante como isso é importante em alguns casos, especialmente ao baixar arquivos grandes). Se esse argumento for omitido, o corpo da resposta é lido na íntegra.

Neste ponto, temos o corpo da resposta como um objeto de bytes, referenciado pelo Response_content variável. Podemos querer transformá -lo em outra coisa. Para transformá -lo em uma corda, por exemplo, usamos o decodificar Método, fornecendo o tipo de codificação como argumento, normalmente:

>>> Response_Content.Decode ('UTF-8')

No exemplo acima, usamos o UTF-8 codificação. A chamada da API que usamos no exemplo, no entanto, retorna uma resposta em JSON formato, portanto, queremos processá -lo com a ajuda do JSON módulo:

>>> Importar json json_ropnse = json.Cargas (Response_Content) 
cópia de

O JSON.Cargas Método desaperializa a corda, a bytes ou a bytearray instância contendo um documento JSON em um objeto Python. O resultado de chamar a função, neste caso, é um dicionário:

>>> De Pprint Importar Pprint >>> Pprint (JSON_RESPONIFICADO) 'DATE': '2019-04-14', 'Explicação': 'Sente-se e assista a dois buracos negros mesclados mesclar. Inspirado na 'Primeira detecção direta de ondas gravitacionais em 2015, este vídeo de simulação é reproduzido em câmera lenta, mas aceitaria' 'um terço de segundo se for executado em tempo real. Situado em um palco cósmico, os buracos negros são colocados na frente de estrelas, gás e '' poeira. Sua extrema gravidade lentes a luz por trás deles '' em Einstein toca enquanto eles se aproximam e finalmente se fundem '' em um. As ondas gravitacionais invisíveis '' geradas à medida que os objetos maciços se fundem rapidamente fazem com que a imagem visível '' ondule e slosh, dentro e fora dos anéis de 'Einstein, mesmo depois que os buracos negros se fundiram. Apelidado de '' GW150914, as ondas gravitacionais detectadas pelo LIGO são '' consistentes com a fusão de 36 e 31 orifícios de preto de massa solar 'a uma distância de 1.3 bilhões de anos-luz. O final, '' Hole Black Hole tem 63 vezes a massa do sol, com as 3 massas solares restantes convertidas em energia em '' ondas gravitacionais. Desde então, os observatórios de ondas gravitacionais do Ligo e Virgem relataram várias outras detecções de fusão de sistemas maciços, enquanto na semana passada o telescópio '' do evento Horizon relatou a primeira imagem da escala de horizonte de um buraco negro de um buraco negro.',' Media_type ':' Video ',' Service_version ':' V1 ',' Title ':' Simulation: Two Black Hole Merge ',' Url ':' https: // www.YouTube.COM/INCED/I_88S8DWBCU?rel = 0 ' 
cópia de

Como alternativa, também poderíamos usar o json_load função (observe a falta de "s" que faltava). A função aceita um como um arquivo objeto como argumento: isso significa que podemos usá -lo diretamente no Httpro -resposta objeto:

>>> com urlopen ("https: // API.NASA.Gov/planetário/apod?api_key = Demo_key ") como resposta: ... JSON_RESPONSENTE = JSON.Carga (resposta) 
cópia de

Lendo os cabeçalhos de resposta

Outro método muito útil utilizável no Httpro -resposta objeto é Getheaders. Este método retorna o cabeçalhos da resposta como uma variedade de tuplas. Cada tupla contém um parâmetro de cabeçalho e seu valor correspondente:



>>> Pprint (resposta.GETHEADERS ()) [('servidor', 'OpenResty'), ('Date', 'Sun, 14 de abril de 2019 10:08:48 GMT'), ('Content-Type', 'Application/json'), ( 'Length-comprimento', '1370'), ('Connection', 'Close'), ('Vary', 'Acep-Encoding'), ('X-Ratelimit-Limit', '40'), ('X x -Ratelimit-Remaining ',' 37 '), (' via ',' 1.1 VEGUR, HTTP/1.1 API-UBRELLA (APACHETRAFFICSERVER [CMSSF]) '), (' Age ',' 1 '), (' X-cache ',' Miss '), (' Access-Control-Alow-Origin ','*') , ('Strict-Transport-Security', 'Max-Iage = 31536000; Preload')]] 

Você pode notar, entre os outros, o Tipo de conteúdo parâmetro, que, como dissemos acima, é Aplicação/JSON. Se quisermos recuperar apenas um parâmetro específico, podemos usar o GETHEADER Método, em vez disso, passando o nome do parâmetro como argumento:

>>> resposta.Getheader ('Content-Type') 'Aplicativo/JSON' 
cópia de

Obtendo o status da resposta

Obtendo o código de status e Phrase da razão devolvido pelo servidor após uma solicitação HTTP também é muito fácil: tudo o que precisamos fazer é acessar o status e razão propriedades do Httpro -resposta objeto:

>>> resposta.Status 200 >>> Resposta.razão 'ok' 
cópia de

Incluindo variáveis ​​no pedido de get

O URL da solicitação que enviamos acima continha apenas uma variável: Chave API, e seu valor era "Demo_key". Se quisermos passar várias variáveis, em vez de conectá-las ao URL manualmente, podemos fornecer a elas e seus valores associados como pares de valor-chave de um dicionário Python (ou como uma sequência de tuplas de dois elementos); Este dicionário será passado para o urllib.analisar.urlencode método, que construirá e retornará o string de consulta. A chamada da API que usamos acima, permita -nos especificar uma variável opcional de "data", para recuperar a imagem associada a um dia específico. Aqui está como poderíamos proceder:

>>> de urllib.Parse importar urlencode >>> query_params =  ..."API_KEY": "Demo_Key", ..."Date": "2019-04-11" >>> Query_string = urlencode (query_params) >>> query_string 'api_key = Demo_key & date = 2019-04-11' 
cópia de

Primeiro, definimos cada variável e seu valor correspondente como pares de valor-chave de um dicionário, do que passamos o referido dicionário como um argumento para o urlencode função, que retornou uma sequência de consulta formatada. Agora, ao enviar o pedido, tudo o que precisamos fazer é anexá -lo ao URL:

>>> url = "?".Junte -se (["https: // API.NASA.Gov/planetário/apod ", query_string])

Se enviarmos a solicitação usando o URL acima, obtemos uma resposta diferente e uma imagem diferente:

'Date': '2019-04-11', 'Explicação': 'Como é um buraco negro? Para descobrir, o rádio "Telescópios de todo o mundo coordenou observações de" buracos negros com os maiores horizontes de eventos conhecidos no "céu. Sozinho, os buracos negros são apenas negros, mas esses atratores de monstro são conhecidos por estarem cercados por gás brilhante. A "primeira imagem foi lançada ontem e resolveu a área" ao redor do buraco negro no centro do Galaxy M87 em uma escala "abaixo da esperada para o seu evento no evento. Na foto, a "região central escura não é o horizonte de eventos, mas a sombra do buraco negro - a região central da emissão de gás" "escurecida pela gravidade do buraco negro central. O tamanho e "'' Shap of the Shadow são determinados por gás brilhante perto do" Evento Horizon, por fortes deflexões de lentes gravitacionais "e pelo spin do buraco negro. Ao resolver o "Shadow, o telescópio de horizonte de eventos (EHT) reforçou as evidências" de que a gravidade de Einstein funciona mesmo em regiões extremas, e "" deu evidências claras de que o M87 tem um buraco de giro central "de cerca de 6 bilhões de massas solares. O EHT não está pronto -"Observações futuras serão voltadas para uma resolução ainda mais alta", melhor rastreamento da variabilidade e explorando a "vizinhança imediata do buraco negro no centro de nossa" galáxia da Via Láctea.',' hdurl ':' https: // apod.NASA.Gov/apod/imagem/1904/m87bh_eht_2629.JPG ',' Media_type ':' Image ',' Service_version ':' V1 ',' Title ':' Primeira imagem em escala de horizonte de um buraco negro ',' url ':' https: // apod.NASA.gov/apod/imagem/1904/m87bh_eht_960.jpg ' 


Caso você não tenha notado, o URL da imagem devolvido aponta para a primeira foto recentemente revelada de um buraco negro:



A imagem devolvida pela chamada da API - a primeira imagem de um buraco negro

Enviando um pedido de postagem

Enviando uma solicitação de postagem, com variáveis ​​'contidas' dentro do corpo da solicitação, usando a biblioteca padrão, requer etapas adicionais. Primeiro de tudo, como fizemos antes, construímos os dados post na forma de um dicionário:

>>> dados =  ... "variável1": "value1", ... "variável2": "value2" ... 
cópia de

Depois que construímos nosso dicionário, queremos usar o urlencode funcionar como antes e codificar adicionalmente a string resultante em ASCII:

>>> post_data = urlencode (dados).Encodes ('ASCII')

Finalmente, podemos enviar nossa solicitação, passando os dados como o segundo argumento do urlopen função. Nesse caso, usaremos https: // httpbin.org/post como URL de destino (httpbin.Org é um serviço de solicitação e resposta):

>>> com urlopen ("https: // httpbin.org/post ", post_data) como resposta: ... JSON_RESPONSENTE = JSON.carregamento (resposta) >>> pprint (json_Response) 'args': , 'dados': ", 'arquivos': , 'form': 'variable1': 'value1', 'variable2': ' Value2 ',' cabeçalhos ': ' aceit-e-codificação ':' identidade ',' content-comprimento ':' 33 ',' content-type ':' aplicativo/x-www-corm-urlencoded ',' host ' : 'httpbin.org ',' user-agent ':' python-urllib/3.7 ',' json ': nenhum,' origem ':' xx.xx.xx.xx, xx.xx.xx.xx ',' url ':' https: // httpbin.org/post ' 
cópia de

O pedido foi bem -sucedido e o servidor retornou uma resposta JSON, que inclui informações sobre a solicitação que fizemos. Como você pode ver, as variáveis ​​que passamos no corpo da solicitação são relatadas como o valor do 'forma' chave no corpo de resposta. Lendo o valor do cabeçalhos chave, também podemos ver que o tipo de conteúdo da solicitação era APLICAÇÃO/X-WWW-FORM-URLENCODED e o agente do usuário 'Python-urllib/3.7 '.

Enviando dados JSON no pedido

E se quisermos enviar uma representação JSON de dados com nosso pedido? Primeiro, definimos a estrutura dos dados, do que convertemos para JSON:

>>> pessoa =  ... "FirstName": "Luke", ... "LastName": "Skywalker", ... "Título": "Jedi Knight" ...  
cópia de

Também queremos usar um dicionário para definir cabeçalhos personalizados. Nesse caso, por exemplo, queremos especificar que nosso conteúdo de solicitação é Aplicação/JSON:

>>> Custom_headers =  ... "Tipo de Conteúdo": "Aplicativo/JSON" ... 
cópia de

Finalmente, em vez de enviar a solicitação diretamente, criamos um Solicitar Objeto e passamos, em ordem: o URL de destino, os dados de solicitação e os cabeçalhos de solicitação como argumentos de seu construtor:

>>> de urllib.solicitar solicitação de importação >>> req = solicitação ( ... "https: // httpbin.org/post ", ... JSON.Lumps (pessoa).Encodes ('ASCII'), ... Custom_Headers ...) 
cópia de

Uma coisa importante a perceber é que usamos o JSON.lixões função que passa o dicionário que contém os dados que queremos ser incluídos na solicitação como seu argumento: esta função é usada para serializar um objeto em uma corda formatada JSON, que codificamos usando o codificar método.



Neste ponto, podemos enviar nosso Solicitar, passando como o primeiro argumento do urlopen função:

>>> com urlopen (req) como resposta: ... JSON_RESPONSENTE = JSON.Carga (resposta) 
cópia de

Vamos verificar o conteúdo da resposta:

'args': , 'dados': '"FirstName": "Luke", "LastName": "Skywalker", "Title": "Jedi" Knight "', 'arquivos': , ' formulário ': ,' cabeçalhos ': ' aceitar-codificação ':' identidade ',' content-length ':' 70 ',' content-type ':' Application/json ',' host ':' httpbin.org ',' user-agent ':' python-urllib/3.7 ',' JSON ': ' FirstName ':' Luke ',' LastName ':' Skywalker ',' Title ':' Jedi Knight ',' Origin ':' xx.xx.xx.xx, xx.xx.xx.xx ',' url ':' https: // httpbin.org/post ' 

Desta vez, podemos ver que o dicionário associado à chave "forma" no corpo de resposta está vazio, e o associado à chave "json" representa os dados que enviamos como json. Como você pode observar, mesmo o parâmetro de cabeçalho personalizado que enviamos foi recebido corretamente.

Enviando uma solicitação com um verbo http diferente de obter ou postar

Ao interagir com as APIs, podemos precisar usar Verbos http Além de apenas obter ou postar. Para realizar esta tarefa, devemos usar o último parâmetro do Solicitar construtor de classe e especifique o verbo que queremos usar. O verbo padrão é Get se o dados parâmetro é Nenhum, Caso contrário, a postagem é usada. Suponha que queremos enviar um COLOCAR solicitar:

>>> req = request ( ... "https: // httpbin.org/put ", ... JSON.Lumps (pessoa).Encodes ('ASCII'), ... Custom_Headers, ... método = 'put' ...) 
cópia de

Baixando um arquivo

Outra operação muito comum que podemos querer executar é baixar algum tipo de arquivo da web. Usando a biblioteca padrão, existem duas maneiras de fazê -lo: usando o urlopen função, lendo a resposta em pedaços (especialmente se o arquivo para download for grande) e escrevendo -os em um arquivo local "manualmente" ou usando o urlretrieve A função, que, como declarada na documentação oficial, é considerada parte de uma interface antiga e pode ser preterida no futuro. Vamos ver um exemplo de ambas as estratégias.

Download de um arquivo usando Urlopen

Digamos que queremos baixar o tarball que contém a versão mais recente do código -fonte do kernel Linux. Usando o primeiro método que mencionamos acima, escrevemos:

>>> Último.núcleo.org/pub/linux/kernel/v5.X/Linux-5.0.7.alcatrão.xz ">>> com urlopen (mais last_kernel_tarball) como resposta: ... com open ('mais recente-kernel.alcatrão.xz ',' wb ') como tarball: ... enquanto é verdade: ... Chunk = resposta.Leia (16384) ... Se Chunk: ... Tarball.Escreva (Chunk) ... outro: ... quebrar 
cópia de

No exemplo acima, usamos primeiro os dois urlopen função e o abrir Um dentro com declarações e, portanto, usando o protocolo de gerenciamento de contexto para garantir que os recursos sejam limpos imediatamente após o bloco de código onde são usados. Dentro de a enquanto loop, em cada iteração, o pedaço Variável Referências Os bytes lidos da resposta (16384 neste caso - 16 Kibibytes). Se pedaço não está vazio, escrevemos o conteúdo para o objeto de arquivo ("Tarball"); Se estiver vazio, significa que consumimos todo o conteúdo do corpo de resposta, portanto quebramos o loop.

Uma solução mais concisa envolve o uso do Shutil biblioteca e o copyfileobj função, que copia os dados de um objeto semelhante a um arquivo (neste caso "resposta") para outro objeto semelhante a um arquivo (neste caso, "tarball"). O tamanho do buffer pode ser especificado usando o terceiro argumento da função, que, por padrão, é definido como 16384 bytes):

>>> Importar fechado ... com urlopen (mais last_kernel_tarball) como resposta: ... com open ('mais recente-kernel.alcatrão.xz ',' wb ') como tarball: ... Shutil.copyfileobj (resposta, tarball) 
cópia de

Download de um arquivo usando a função de urlrete

O método alternativo e ainda mais conciso para baixar um arquivo usando a biblioteca padrão é pelo uso do urllib.solicitar.urlretrieve função. A função leva quatro argumentos, mas apenas os dois primeiros se interessam agora: a primeira é obrigatória e é o URL do recurso a ser baixado; O segundo é o nome usado para armazenar o recurso localmente. Se não for fornecido, o recurso será armazenado como um arquivo temporário em /tmp. O código se torna:

>>> de urllib.Solicitar importação de importação >>> urlretrieve ("https: // cdn.núcleo.org/pub/linux/kernel/v5.X/Linux-5.0.7.alcatrão.xz ") ('mais recente-kernel.alcatrão.xz ', ) 
cópia de

Muito simples, não é? A função retorna uma tupla que contém o nome usado para armazenar o arquivo (isso é útil quando o recurso é armazenado como arquivo temporário, e o nome é gerado aleatório) e o HttpMessage objeto que mantém os cabeçalhos da resposta HTTP.

Conclusões

Nesta primeira parte da série de artigos dedicados às solicitações Python e HTTP, vimos como enviar vários tipos de solicitações usando apenas funções de biblioteca padrão e como trabalhar com respostas. Se você tiver dúvidas ou quiser explorar mais as coisas em profundidade, consulte o urllib oficial oficial.Solicite documentação. A próxima parte da série se concentrará na biblioteca de solicitações HTTP do Python.

Tutoriais do Linux relacionados:

  • Uma introdução à automação, ferramentas e técnicas do Linux
  • Mastering Bash Script Loops
  • Loops aninhados em scripts de basquete
  • Mint 20: Melhor que o Ubuntu e o Microsoft Windows?
  • Coisas para instalar no Ubuntu 20.04
  • Como trabalhar com a API de Rest WooCommerce com Python
  • Como lançar processos externos com Python e…
  • Manipulando a entrada do usuário em scripts bash
  • Como carregar, descarregar e listar os módulos do kernel Linux
  • Sistema Linux Hung? Como escapar para a linha de comando e…