Como analisar o conteúdo da resposta de uma chamada Python requests

PythonBeginner
Pratique Agora

Introdução

A biblioteca Python requests é uma ferramenta poderosa para interagir com serviços web e APIs. Neste tutorial, você aprenderá como enviar requisições HTTP e analisar dados de resposta usando Python. Ao final deste laboratório, você será capaz de extrair informações valiosas de diferentes tipos de respostas de API, permitindo que você construa aplicações orientadas a dados e automatize interações web.

Instalando a Biblioteca Requests e Fazendo uma Requisição Básica

Neste primeiro passo, instalaremos a biblioteca Python requests e faremos nossa primeira requisição HTTP para recuperar dados de uma API pública.

Instalando Requests

A biblioteca requests é um pacote de terceiros que precisa ser instalado usando o pip, o instalador de pacotes do Python. Vamos começar instalando-o:

pip install requests

Você deve ver uma saída confirmando que o requests foi instalado com sucesso.

Fazendo Sua Primeira Requisição HTTP

Agora, vamos criar um arquivo Python para fazer uma requisição HTTP simples. No WebIDE, crie um novo arquivo chamado basic_request.py no diretório /home/labex/project.

Adicione o seguinte código ao arquivo:

import requests

## Make a GET request to a public API
response = requests.get("https://jsonplaceholder.typicode.com/todos/1")

## Print the status code
print(f"Status code: {response.status_code}")

## Print the raw response content
print("\nRaw response content:")
print(response.text)

## Print the response headers
print("\nResponse headers:")
for header, value in response.headers.items():
    print(f"{header}: {value}")

Este código faz uma requisição GET para um endpoint de API de exemplo e imprime informações sobre a resposta.

Entendendo o Objeto de Resposta

Vamos executar o código para ver quais informações recebemos de volta. No terminal, execute:

python basic_request.py

Você deve ver uma saída semelhante a esta:

Status code: 200

Raw response content:
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

Response headers:
Date: Mon, 01 Jan 2023 12:00:00 GMT
Content-Type: application/json; charset=utf-8
...

O objeto de resposta contém vários atributos importantes:

  • status_code: Código de status HTTP (200 significa sucesso)
  • text: O conteúdo da resposta como uma string
  • headers: Um dicionário de cabeçalhos de resposta

Ao trabalhar com requisições web, esses atributos ajudam você a entender a resposta do servidor e tratá-la adequadamente.

Códigos de Status HTTP

Os códigos de status HTTP indicam se uma requisição foi bem-sucedida ou falhou:

  • 2xx (como 200): Sucesso
  • 3xx (como 301): Redirecionamento
  • 4xx (como 404): Erros do cliente
  • 5xx (como 500): Erros do servidor

Vamos modificar nosso código para verificar uma resposta bem-sucedida. Crie um novo arquivo chamado check_status.py com este conteúdo:

import requests

try:
    ## Make a GET request to a valid URL
    response = requests.get("https://jsonplaceholder.typicode.com/todos/1")

    ## Check if the request was successful
    if response.status_code == 200:
        print("Request successful!")
    else:
        print(f"Request failed with status code: {response.status_code}")

    ## Try an invalid URL
    invalid_response = requests.get("https://jsonplaceholder.typicode.com/invalid")
    print(f"Invalid URL status code: {invalid_response.status_code}")

except requests.exceptions.RequestException as e:
    print(f"An error occurred: {e}")

Execute este código para ver como diferentes URLs retornam diferentes códigos de status:

python check_status.py

Você deve ver que a URL válida retorna o código de status 200, enquanto a URL inválida retorna o código de status 404.

Analisando Dados de Resposta JSON

Muitas APIs modernas retornam dados no formato JSON (JavaScript Object Notation). Nesta etapa, você aprenderá como analisar respostas JSON e trabalhar com os dados em Python.

Entendendo JSON

JSON é um formato leve de intercâmbio de dados que é fácil para humanos lerem e escreverem, e fácil para máquinas analisarem e gerarem. Ele é baseado em pares chave-valor, semelhante aos dicionários Python.

Aqui está um exemplo de um objeto JSON:

{
  "name": "John Doe",
  "age": 30,
  "email": "john@example.com",
  "is_active": true,
  "hobbies": ["reading", "swimming", "cycling"]
}

Analisando Respostas JSON

A biblioteca requests facilita a análise de respostas JSON usando o método .json(). Vamos criar um novo arquivo chamado parse_json.py e adicionar o seguinte código:

import requests

## Make a request to a GitHub API endpoint that returns JSON data
response = requests.get("https://api.github.com/users/python")

## Check if the request was successful
if response.status_code == 200:
    ## Parse the JSON response
    data = response.json()

    ## Print the parsed data
    print("Parsed JSON data:")
    print(f"Username: {data['login']}")
    print(f"Name: {data.get('name', 'Not provided')}")
    print(f"Followers: {data['followers']}")
    print(f"Public repositories: {data['public_repos']}")

    ## Print the type to verify it's a Python dictionary
    print(f"\nType of parsed data: {type(data)}")

    ## Access nested data
    print("\nAccessing specific elements:")
    print(f"Avatar URL: {data['avatar_url']}")
else:
    print(f"Request failed with status code: {response.status_code}")

Execute este script para ver como os dados JSON são analisados em um dicionário Python:

python parse_json.py

Você deve ver uma saída que exibe informações sobre o usuário do GitHub, incluindo seu nome de usuário, contagem de seguidores e contagem de repositórios.

Trabalhando com Listas de Dados

Muitas APIs retornam listas de objetos. Vamos ver como lidar com esse tipo de resposta. Crie um arquivo chamado json_list.py com este conteúdo:

import requests

## Make a request to an API that returns a list of posts
response = requests.get("https://jsonplaceholder.typicode.com/posts")

## Check if the request was successful
if response.status_code == 200:
    ## Parse the JSON response (this will be a list of posts)
    posts = response.json()

    ## Print the total number of posts
    print(f"Total posts: {len(posts)}")

    ## Print details of the first 3 posts
    print("\nFirst 3 posts:")
    for i, post in enumerate(posts[:3], 1):
        print(f"\nPost #{i}")
        print(f"User ID: {post['userId']}")
        print(f"Post ID: {post['id']}")
        print(f"Title: {post['title']}")
        print(f"Body: {post['body'][:50]}...")  ## Print just the beginning of the body
else:
    print(f"Request failed with status code: {response.status_code}")

Execute este script para ver como processar uma lista de objetos JSON:

python json_list.py

Você deve ver informações sobre as três primeiras postagens, incluindo seus títulos e o início de seu conteúdo.

Tratamento de Erros com Análise JSON

Às vezes, uma resposta pode não conter dados JSON válidos. Vamos ver como lidar com isso de forma adequada. Crie um arquivo chamado json_error.py com este código:

import requests
import json

def get_and_parse_json(url):
    try:
        ## Make the request
        response = requests.get(url)

        ## Check if the request was successful
        response.raise_for_status()

        ## Try to parse the JSON
        try:
            data = response.json()
            return data
        except json.JSONDecodeError:
            print(f"Response from {url} is not valid JSON")
            print(f"Raw response: {response.text[:100]}...")  ## Print part of the raw response
            return None

    except requests.exceptions.HTTPError as e:
        print(f"HTTP error: {e}")
    except requests.exceptions.RequestException as e:
        print(f"Request error: {e}")

    return None

## Test with a valid JSON endpoint
json_data = get_and_parse_json("https://jsonplaceholder.typicode.com/posts/1")
if json_data:
    print("\nValid JSON response:")
    print(f"Title: {json_data['title']}")

## Test with a non-JSON endpoint
html_data = get_and_parse_json("https://www.example.com")
if html_data:
    print("\nThis should not print as example.com returns HTML, not JSON")
else:
    print("\nAs expected, could not parse HTML as JSON")

Execute este script para ver como lidar com diferentes tipos de respostas:

python json_error.py

Você deve ver que o código lida com sucesso com respostas JSON válidas e respostas não JSON.

Analisando Conteúdo HTML com BeautifulSoup

Ao trabalhar com dados da web, você frequentemente encontrará respostas HTML. Para analisar HTML, a biblioteca BeautifulSoup do Python é uma excelente ferramenta. Nesta etapa, aprenderemos como extrair informações de respostas HTML.

Instalando BeautifulSoup

Primeiro, vamos instalar BeautifulSoup e seu analisador HTML:

pip install beautifulsoup4

Análise HTML Básica

Vamos criar um arquivo chamado parse_html.py para buscar e analisar uma página da web:

import requests
from bs4 import BeautifulSoup

## Make a request to a webpage
url = "https://www.example.com"
response = requests.get(url)

## Check if the request was successful
if response.status_code == 200:
    ## Parse the HTML content
    soup = BeautifulSoup(response.text, 'html.parser')

    ## Extract the page title
    title = soup.title.text
    print(f"Page title: {title}")

    ## Extract all paragraphs
    paragraphs = soup.find_all('p')
    print(f"\nNumber of paragraphs: {len(paragraphs)}")

    ## Print the text of the first paragraph
    if paragraphs:
        print(f"\nFirst paragraph text: {paragraphs[0].text.strip()}")

    ## Extract all links
    links = soup.find_all('a')
    print(f"\nNumber of links: {len(links)}")

    ## Print the href attribute of the first link
    if links:
        print(f"First link href: {links[0].get('href')}")

else:
    print(f"Request failed with status code: {response.status_code}")

Execute este script para ver como extrair informações básicas de uma página HTML:

python parse_html.py

Você deve ver a saída mostrando o título da página, o número de parágrafos, o texto do primeiro parágrafo, o número de links e a URL do primeiro link.

Encontrando Elementos Específicos

Agora, vamos ver como encontrar elementos específicos usando seletores CSS. Crie um arquivo chamado html_selectors.py:

import requests
from bs4 import BeautifulSoup

## Make a request to a webpage with more complex structure
url = "https://quotes.toscrape.com/"
response = requests.get(url)

## Check if the request was successful
if response.status_code == 200:
    ## Parse the HTML content
    soup = BeautifulSoup(response.text, 'html.parser')

    ## Find all quote elements
    quote_elements = soup.select('.quote')
    print(f"Number of quotes found: {len(quote_elements)}")

    ## Process the first 3 quotes
    print("\nFirst 3 quotes:")
    for i, quote_element in enumerate(quote_elements[:3], 1):
        ## Extract the quote text
        text = quote_element.select_one('.text').text

        ## Extract the author
        author = quote_element.select_one('.author').text

        ## Extract the tags
        tags = [tag.text for tag in quote_element.select('.tag')]

        ## Print the information
        print(f"\nQuote #{i}")
        print(f"Text: {text}")
        print(f"Author: {author}")
        print(f"Tags: {', '.join(tags)}")

else:
    print(f"Request failed with status code: {response.status_code}")

Execute este script para ver como usar seletores CSS para extrair elementos específicos:

python html_selectors.py

Você deve ver a saída mostrando informações sobre as três primeiras citações, incluindo o texto da citação, o autor e as tags.

Construindo um Web Scraper Simples

Vamos juntar tudo para construir um web scraper simples que extrai dados estruturados de uma página da web. Crie um arquivo chamado quotes_scraper.py:

import requests
from bs4 import BeautifulSoup
import json
import os

def scrape_quotes_page(url):
    ## Make a request to the webpage
    response = requests.get(url)

    ## Check if the request was successful
    if response.status_code != 200:
        print(f"Request failed with status code: {response.status_code}")
        return None

    ## Parse the HTML content
    soup = BeautifulSoup(response.text, 'html.parser')

    ## Extract all quotes
    quotes = []
    for quote_element in soup.select('.quote'):
        ## Extract the quote text
        text = quote_element.select_one('.text').text.strip('"')

        ## Extract the author
        author = quote_element.select_one('.author').text

        ## Extract the tags
        tags = [tag.text for tag in quote_element.select('.tag')]

        ## Add the quote to our list
        quotes.append({
            'text': text,
            'author': author,
            'tags': tags
        })

    ## Check if there's a next page
    next_page = soup.select_one('.next a')
    next_page_url = None
    if next_page:
        next_page_url = 'https://quotes.toscrape.com' + next_page['href']

    return {
        'quotes': quotes,
        'next_page': next_page_url
    }

## Scrape the first page
result = scrape_quotes_page('https://quotes.toscrape.com/')

if result:
    ## Print information about the quotes found
    quotes = result['quotes']
    print(f"Found {len(quotes)} quotes on the first page")

    ## Print the first 2 quotes
    print("\nFirst 2 quotes:")
    for i, quote in enumerate(quotes[:2], 1):
        print(f"\nQuote #{i}")
        print(f"Text: {quote['text']}")
        print(f"Author: {quote['author']}")
        print(f"Tags: {', '.join(quote['tags'])}")

    ## Save the quotes to a JSON file
    output_dir = '/home/labex/project'
    with open(os.path.join(output_dir, 'quotes.json'), 'w') as f:
        json.dump(quotes, f, indent=2)

    print(f"\nSaved {len(quotes)} quotes to {output_dir}/quotes.json")

    ## Print information about the next page
    if result['next_page']:
        print(f"\nNext page URL: {result['next_page']}")
    else:
        print("\nNo next page available")

Execute este script para extrair citações de um site:

python quotes_scraper.py

Você deve ver a saída mostrando informações sobre as citações encontradas na primeira página, e as citações serão salvas em um arquivo JSON chamado quotes.json.

Verifique o arquivo JSON para ver os dados estruturados:

cat quotes.json

O arquivo deve conter uma matriz JSON de objetos de citação, cada um com propriedades de texto, autor e tags.

Trabalhando com Conteúdo de Resposta Binária

Até agora, nos concentramos em respostas baseadas em texto, como JSON e HTML. No entanto, a biblioteca requests também pode lidar com conteúdo binário, como imagens, PDFs e outros arquivos. Nesta etapa, aprenderemos como baixar e processar conteúdo binário.

Baixando uma Imagem

Vamos começar baixando uma imagem. Crie um arquivo chamado download_image.py:

import requests
import os

## URL of an image to download
image_url = "https://httpbin.org/image/jpeg"

## Make a request to get the image
response = requests.get(image_url)

## Check if the request was successful
if response.status_code == 200:
    ## Get the content type
    content_type = response.headers.get('Content-Type', '')
    print(f"Content-Type: {content_type}")

    ## Check if the content is an image
    if 'image' in content_type:
        ## Create a directory to save the image if it doesn't exist
        output_dir = '/home/labex/project/downloads'
        os.makedirs(output_dir, exist_ok=True)

        ## Save the image to a file
        image_path = os.path.join(output_dir, 'sample_image.jpg')
        with open(image_path, 'wb') as f:
            f.write(response.content)

        ## Print information about the saved image
        print(f"Image saved to: {image_path}")
        print(f"Image size: {len(response.content)} bytes")
    else:
        print("The response does not contain an image")
else:
    print(f"Request failed with status code: {response.status_code}")

Execute este script para baixar uma imagem:

python download_image.py

Você deve ver a saída confirmando que a imagem foi baixada e salva em /home/labex/project/downloads/sample_image.jpg.

Baixando um Arquivo com Progresso

Ao baixar arquivos grandes, pode ser útil exibir um indicador de progresso. Vamos criar um script que mostra o progresso do download. Crie um arquivo chamado download_with_progress.py:

import requests
import os
import sys

def download_file(url, filename):
    ## Make a request to get the file
    ## Stream the response to handle large files efficiently
    response = requests.get(url, stream=True)

    ## Check if the request was successful
    if response.status_code != 200:
        print(f"Request failed with status code: {response.status_code}")
        return False

    ## Get the total file size if available
    total_size = int(response.headers.get('Content-Length', 0))
    if total_size:
        print(f"Total file size: {total_size/1024:.2f} KB")
    else:
        print("Content-Length header not found. Unable to determine file size.")

    ## Create a directory to save the file if it doesn't exist
    os.makedirs(os.path.dirname(filename), exist_ok=True)

    ## Download the file in chunks and show progress
    print(f"Downloading {url} to {filename}...")

    ## Initialize variables for progress tracking
    downloaded = 0
    chunk_size = 8192  ## 8 KB chunks

    ## Open the file for writing
    with open(filename, 'wb') as f:
        ## Iterate through the response chunks
        for chunk in response.iter_content(chunk_size=chunk_size):
            if chunk:  ## Filter out keep-alive chunks
                f.write(chunk)
                downloaded += len(chunk)

                ## Calculate and display progress
                if total_size:
                    percent = downloaded * 100 / total_size
                    sys.stdout.write(f"\rProgress: {percent:.1f}% ({downloaded/1024:.1f} KB)")
                    sys.stdout.flush()
                else:
                    sys.stdout.write(f"\rDownloaded: {downloaded/1024:.1f} KB")
                    sys.stdout.flush()

    ## Print a newline to ensure the next output starts on a new line
    print()

    return True

## URL of a file to download
file_url = "https://speed.hetzner.de/100MB.bin"

## Path where the file will be saved
output_path = '/home/labex/project/downloads/test_file.bin'

## Download the file
success = download_file(file_url, output_path)

if success:
    ## Get file stats
    file_size = os.path.getsize(output_path)
    print(f"\nDownload complete!")
    print(f"File saved to: {output_path}")
    print(f"File size: {file_size/1024/1024:.2f} MB")
else:
    print("\nDownload failed.")

Execute este script para baixar um arquivo com acompanhamento de progresso:

python download_with_progress.py

Você verá uma barra de progresso sendo atualizada conforme o arquivo é baixado. Observe que isso baixa um arquivo de 100MB, o que pode levar algum tempo dependendo da velocidade da sua conexão.

Para cancelar o download, você pode pressionar Ctrl+C.

Trabalhando com Cabeçalhos de Resposta e Metadados

Ao baixar arquivos, os cabeçalhos de resposta geralmente contêm metadados úteis. Vamos criar um script que examina os cabeçalhos de resposta em detalhes. Crie um arquivo chamado response_headers.py:

import requests

def check_url(url):
    print(f"\nChecking URL: {url}")

    try:
        ## Make a HEAD request first to get headers without downloading the full content
        head_response = requests.head(url)

        print(f"HEAD request status code: {head_response.status_code}")

        if head_response.status_code == 200:
            ## Print all headers
            print("\nResponse headers:")
            for header, value in head_response.headers.items():
                print(f"  {header}: {value}")

            ## Extract content type and size
            content_type = head_response.headers.get('Content-Type', 'Unknown')
            content_length = head_response.headers.get('Content-Length', 'Unknown')

            print(f"\nContent Type: {content_type}")

            if content_length != 'Unknown':
                size_kb = int(content_length) / 1024
                size_mb = size_kb / 1024

                if size_mb >= 1:
                    print(f"Content Size: {size_mb:.2f} MB")
                else:
                    print(f"Content Size: {size_kb:.2f} KB")
            else:
                print("Content Size: Unknown")

            ## Check if the server supports range requests
            accept_ranges = head_response.headers.get('Accept-Ranges', 'none')
            print(f"Supports range requests: {'Yes' if accept_ranges != 'none' else 'No'}")

        else:
            print(f"HEAD request failed with status code: {head_response.status_code}")

    except requests.exceptions.RequestException as e:
        print(f"Error: {e}")

## Check a few different URLs
check_url("https://httpbin.org/image/jpeg")
check_url("https://speed.hetzner.de/100MB.bin")
check_url("https://example.com")

Execute este script para ver informações detalhadas sobre os cabeçalhos de resposta:

python response_headers.py

Você verá a saída mostrando os cabeçalhos para diferentes tipos de conteúdo, incluindo imagens, arquivos binários e páginas HTML.

Compreender os cabeçalhos de resposta é crucial para muitas tarefas de desenvolvimento web, como:

  • Determinar tipos e tamanhos de arquivos antes de baixar
  • Implementar downloads retomáveis com solicitações de intervalo (range requests)
  • Verificar políticas de cache e datas de expiração
  • Lidar com redirecionamentos e autenticação

Resumo

Neste laboratório, você aprendeu a trabalhar com a biblioteca requests do Python para interagir com serviços web e APIs. Agora você tem as habilidades para:

  1. Fazer requisições HTTP e lidar com códigos de status e erros de resposta
  2. Analisar dados JSON de respostas de API
  3. Extrair informações de conteúdo HTML usando BeautifulSoup
  4. Baixar e processar conteúdo binário, como imagens e arquivos
  5. Trabalhar com cabeçalhos de resposta e metadados

Essas habilidades formam a base para muitas aplicações Python, incluindo web scraping, integração de API, coleta de dados e automação. Agora você pode construir aplicações que interagem com serviços web, extraem informações úteis de sites e processam vários tipos de conteúdo web.

Para continuar aprendendo, você pode querer explorar:

  • Métodos de autenticação para acessar APIs protegidas
  • Trabalhar com APIs mais complexas que exigem cabeçalhos ou formatos de requisição específicos
  • Construir um projeto completo de web scraping que coleta e analisa dados
  • Criar uma aplicação Python que se integra a múltiplas APIs

Lembre-se que, ao fazer web scraping ou usar APIs, é importante verificar os termos de serviço e respeitar os limites de taxa para evitar ser bloqueado.