[Tutorial] Fazendo requisição a um Webservice (Python)

[Tutorial] Fazendo requisição a um Webservice (Python)

Overview

Neste post prático e divertido, vamos explorar como fazer requisições a um Webservice usando Python para buscar endereços com base no CEP. Vamos mergulhar na importação de módulos, na organização de variáveis, e como não poderia faltar, no tratamento de erros e na interação dinâmica com o usuário. Preparado para transformar linhas de código em soluções eficientes e amigáveis? Então, acompanhe cada passo, junte-se à aventura de codificação e descubra como lidar com cada desafio na rota dos dados até o resultado final.

Uma das coisas mais básicas hoje em dia é ter que fazer alguma requisição a um Webservice. Neste post vou mostrar a forma que considero razoavelmente simples. O exemplo que utilizarei será recuperar um endereço com base no CEP.

A primeira coisa é importar o modulo que vamos utilizar:

import requests

Depois, para ficar mais organizado, vamos definir variáveis com o que vamos utilizar:

base_url = "http://api.postmon.com.br/v1/cep/%s" 
cep_num = "30170010" #CEP do Escritório da Google em Belo Horizonte.

Requisição básica

Agora que temos tudo resolvido, vamos para a parte difícil: Fazer a requisição para o Webservice.

response = requests.get(base_url % cep_num)
data = response.json()

E agora é só mostrar o resultado:

print(data)
{'complemento': 'até 279/280', 'bairro': 'Lourdes', 'cidade': 'Belo Horizonte', 'logradouro': 'Avenida Bias Fortes', 'estado_info': {'area_km2': '586.521,235', 'codigo_ibge': '31', 'nome': 'Minas Gerais'}, 'cep': '30170010', 'cidade_info': {'area_km2': '331,401', 'codigo_ibge': '3106200'}, 'estado': 'MG'}

Fonte do exemplo até este ponto disponível no meu Github.

Neste ponto, temos o resultado (json) da requisição na variável data, mas não fizemos qualquer tratamento de erro. Nem para verificar se a requisição foi bem sucedida.

Incluindo tratamento de erro

O primeiro tratamento lógico que faremos é o de verificar se a requisição foi bem sucedida. Isso é feito através da verificação do status code: Se ele for igual a 200, tudo correu bem.

O código base permanecerá o mesmo:

import requests

base_url = "http://api.postmon.com.br/v1/cep/%s" 
cep_num = "30170010" 

response = requests.get(base_url % cep_num)

A diferença aparece no tratamento da resposta:

if response.status_code == 200:
    data = response.json()
    print(data)
else:
    print("Something went wrong.\r\nStatus code: %s" % response.status_code)
    print("Find more info about this error at: https://www.google.com.br/search?q=http+status+code+%s" % response.status_code)

No código acima, verifico o status code que veio na resposta da requisição (response.status_code) e verifico se ele é igual a 200, então associo o json da resposta na variável data e faço o print do resultado (igual no exemplo anterior).

Se o status code for qualquer outra coisa, meu tratamento de erro foi contar para o usuário que deu erro e mandar ele pesquisar no Google. (Mas você vai fazer um tratamento melhor, tenho certeza!)

Inclui um exemplo com este tratamento no meu github.

Ainda existem erros que podem ocorrer e que não estão sendo tratados. Estou falando das exceptions, mas antes de entrar nesta área, que tal deixar as coisas mais interativas?

Interagindo com o usuário

Nesta interação, vamos pedir ao usuário que digite o CEP, fazemos a requisição, exibimos o resultado e solicitamos outro CEP para ele. Este loop continua até que o usuário informe o valor -1. Se ele não digitar nada, fazemos a requisição para pegar o endereço do escritório do Google em Belo Horizonte.

Para não ficar confuso, vamos por partes. O inicio do fonte vai ser bem parecido:

import requests

base_url = "http://api.postmon.com.br/v1/cep/%s" 
google_cep_num = "30170010" #Google's office in Belo Horizonte, MG. Brazil.


cep_num = ""

A variável com CEP do Google em Belo Horizonte (google_cep_num) mudou de nome e agora a variável cep_num está vazia.

Agora vamos declarar o loop que vai ser feito, pois vamos solicitar CEPs para o usuário até que ele canse de canse de brincar com a aplicação.

print("Input your CEP number or press Enter to use our default test value.")
print("To exit, type -1 and press Enter.")
while cep_num != "-1":

No fonte acima, informo ao usuário que ele deve digitar o CEP, apertar enter sem digitar nada ou digitar -1 para sair. O loop irá acontecer enquanto a variável cep_num for diferente de -1.

O próximo passo é fazer a interação com o usuário:

    cep_num = input("Please, enter your CEP (just numbers): ")
    should_exit = cep_num == "-1"
    if cep_num.strip() == "":
        print("Using default test value...")
        cep_num = google_cep_num
    elif not cep_num.isdigit() and not should_exit:
        print("Please, enter CEP number... just the numbers.")
        continue

Então, o que está acontecendo neste pedaço do código?

  1. Através da utilização da função input, estamos solicitando ao usuário que digite o CEP;
  2. Definimos uma variavel chamada should_exit (deve sair) com True se o valor informado for igual a -1 ou False se for qualquer outra coisa;
  3. Verificamos se ele digitou alguma coisa. Nesta verificação, removemos espaços em excesso (cep_num.strip()), para evitar que o engraçadinho tenha digitado só " " e apertado enter. Se nada foi informado, o cep do google é utilizado;
  4. Se o valor informado não for vazio, testamos para ver se ele é composto apenas por números (cep_num.isdigit()). A função isdigit verifica se a string é composta apenas por números. Além do cep ser composto apenas por números, ele não pode ser igual a -1. Se não atender ambas condições, pula esta iteração e solicita outro cep ao usuário.

A próxima parte é 90% igual ao exemplo anterior;

    if not should_exit:
        response = requests.get(base_url % cep_num)
        if response.status_code == 200:
            data = response.json()
            print(data)
        else:
            print("Something went wrong.\r\nStatus code: %s" % response.status_code)
            print("Find more info about this error at: https://www.google.com.br/search?q=http+status+code+%s" % response.status_code)
    
    else:
        print("Cancelling operation.")

A diferença entre este fonte e o do exemplo anterior é que verificamos se a variável should_exit está definida como True ou False. Se for False, realiza a requisição, se for true, mostra a mensagem “Cancelling operation.”

Abaixo está um exemplo de utilização deste script:

Input your CEP number or press Enter to use our default test value.
To exit, type -1 and press Enter.
Please, enter your CEP (just numbers): 
Using default test value...
{'cep': '30170010', 'complemento': 'até 279/280', 'logradouro': 'Avenida Bias Fortes', 'cidade': 'Belo Horizonte', 'estado_info': {'codigo_ibge': '31', 'area_km2': '586.521,235', 'nome': 'Minas Gerais'}, 'cidade_info': {'codigo_ibge': '3106200', 'area_km2': '331,401'}, 'bairro': 'Lourdes', 'estado': 'MG'}
Please, enter your CEP (just numbers): 30112021
{'cep': '30112021', 'complemento': 'de 551/552 a 1219/1220', 'logradouro': 'Avenida Getúlio Vargas', 'cidade': 'Belo Horizonte', 'estado_info': {'codigo_ibge': '31', 'area_km2': '586.521,235', 'nome': 'Minas Gerais'}, 'cidade_info': {'codigo_ibge': '3106200', 'area_km2': '331,401'}, 'bairro': 'Savassi', 'estado': 'MG'}
Please, enter your CEP (just numbers): -1
Cancelling operation.

O fonte deste terceiro exemplo também está disponível no meu github.

Agora que já estamos interagindo com o usuário e tratando o status_code, falta uma coisa: Cuidar das exceptions que podem ocorrer, porque nem só de caminho feliz vive um desenvolvedor.

Tratando as exceptions, parte I

Vamos um tratamento simples de Exception no exemplo anterior.

Para este exemplo, a parte que vamos alterar do código é apenas a seção onde é feita a requisição para o webservice, então não vou duplicar o resto do código.

    if not should_exit:
        response = requests.get(base_url % cep_num)
        if response.status_code == 200:
            try:
                data = response.json()
                print(data)
            except RequestException as ree:
                print("[ERROR!]")
                print("\tSomething went wrong, but i don't really care... ¯\_(ツ)_/¯")
                print("\tHere's the technical details:\r\n\t%s" % ree)
        else:
            print("Something went wrong.\r\nStatus code: %s" % response.status_code)
            print("Find more info about this error at: https://www.google.com.br/search?q=http+status+code+%s" % response.status_code)

No fonte acima, colocamos a requisição dentro de um try/catch que está de olho em exceptions do tipo RequestException. Se ocorrer qualquer exception deste tipo ou derivada dele, será capturado pelo catch (except) e a mensagem será exibida para o usuário.

Isso já resolve, mas acaba deixando um tratamento único para uma série de coisas que podem dar errado. Dizem que cada caso é um caso, então vamos tratar cada exception de uma forma diferente.

(Fonte completo deste exemplo também está disponível no meu GitHub)

Tratando as exceptions, parte II

Nesta parte do tutorial, vamos adicionar tratamentos para as seguintes exceptions: ConnectionError, Timeout, HTTPError, TooManyRedirects e Exception. A alteração no código vai ser muito simples, vamos apenas adicionar outras clausulas catch.

Antes de alterar o try/catch, precisamos realizar mais uma importação.

import requests #Já existia
from requests.exceptions import * #Novo

No código acima, adicionamos as classes de Exception que existem no modulo Requests. Agora podemos alterar o try/catch.

Vamos as alterações:

        if response.status_code == 200:
            try:
                data = response.json()
                print(data)
            except ConnectionError as coe:
                print("[ERROR!]")
                print("\tConnection error. Are you connected to the internet?")
                print("\tTechnical details:\r\n\t%s" % coe)

            except Timeout as toe:
                print("[ERROR!]")
                print("\tError! Request timedout.")
                print("\tTechnical details:\r\n\t%s" % toe)

            except HTTPError as hpe:
                print("[ERROR!]")
                print("\tSomething went wrong.\r\n\tStatus code: %s" % response.status_code)
                print("\tFind more info about this error at: https://www.google.com.br/search?q=http+status+code+%s" % response.status_code)
                print("\tTechnical details:\r\n\t%s" % hpe)

            except TooManyRedirects as tre:
                print("[ERROR!]")
                print("\tError! Too Many Redirects...")
                print("\tTechnical details:\r\n\t%s" % tre)
                
            except RequestException as ree:
                print("[ERROR!]")
                print("\tSomething went wrong with the request...")
                print("\tHere's the technical details:\r\n\t%s" % ree)          

            except Exception as why:
                print("[ERROR!]")
                print("\tWell... i was not expecting that. Sorry...")
                print("\tTechnical details:\r\n\t%s" % why)                
                

        else:
            print("Something went wrong.\r\nStatus code: %s" % response.status_code)
            print("Find more info about this error at: https://www.google.com.br/search?q=http+status+code+%s" % response.status_code)
    
    else:
        print("Cancelling operation.")

No fonte acima, ao invés de já capturar as exceptions do tipo RequestException (except RequestException as ree), tento capturar outros tipos de erros relacionados a requisição. No caso deste exemplo, estou apenas exibindo mensagens diferentes para cada tipo de erro, mas você poderia tratar cada um de forma diferente.

No final do try/catch, tento capturar erros genéricos (except Exception), pois se algo realmente não esperado acontecer, temos como tratar estes problemas sem que o problema exploda na cara do usuário.

O fonte completo deste exemplo está disponível no meu Github.

Existe outras coisas que podemos fazer, otimizações, deixar o código mais aderente ao PEP, etc., mas acredito que ele é um exemplo funcional de como realizar as requisições e como tratar os resultados.

Espero ter ajudado!