Python

O que Todo Dev Deve Saber sobre Web Scraping em Python: requests, BeautifulSoup e Selenium

13 min de leitura

O que Todo Dev Deve Saber sobre Web Scraping em Python: requests, BeautifulSoup e Selenium

O que é Web Scraping e Por Que Aprender Web scraping é a técnica de extrair dados estruturados de páginas da web de forma automatizada. Em essência, você está programando um "robô" que acessa URLs, interpreta o HTML/CSS e extrai as informações que precisam. É uma habilidade crítica quando você precisa coletar dados em massa para análise, monitoramento de preços, pesquisa de mercado ou agregação de conteúdo. A diferença entre web scraping e usar uma API é simples: nem todo site oferece uma API pública. Quando você enfrenta essa situação e precisa dos dados, scraping é a solução. Porém, sempre respeite o arquivo do site e os termos de serviço — há uma linha ética que não deve ser ultrapassada. Neste artigo, você dominará as três ferramentas mais poderosas do Python para isso: para fazer requisições HTTP, para parsear HTML e para automação de navegadores. Fundamentos: Requests e BeautifulSoup para Sites Estáticos Como Funciona o Requests O é a biblioteca

<h2>O que é Web Scraping e Por Que Aprender</h2>

<p>Web scraping é a técnica de extrair dados estruturados de páginas da web de forma automatizada. Em essência, você está programando um &quot;robô&quot; que acessa URLs, interpreta o HTML/CSS e extrai as informações que precisam. É uma habilidade crítica quando você precisa coletar dados em massa para análise, monitoramento de preços, pesquisa de mercado ou agregação de conteúdo.</p>

<p>A diferença entre web scraping e usar uma API é simples: nem todo site oferece uma API pública. Quando você enfrenta essa situação e precisa dos dados, scraping é a solução. Porém, sempre respeite o arquivo <code>robots.txt</code> do site e os termos de serviço — há uma linha ética que não deve ser ultrapassada. Neste artigo, você dominará as três ferramentas mais poderosas do Python para isso: <code>requests</code> para fazer requisições HTTP, <code>BeautifulSoup</code> para parsear HTML e <code>Selenium</code> para automação de navegadores.</p>

<h2>Fundamentos: Requests e BeautifulSoup para Sites Estáticos</h2>

<h3>Como Funciona o Requests</h3>

<p>O <code>requests</code> é a biblioteca padrão para fazer requisições HTTP em Python. Ele abstrai a complexidade do protocolo HTTP e oferece uma interface simples e intuitiva. Quando você acessa um site no navegador, basicamente está enviando uma requisição GET para um servidor. O <code>requests</code> faz exatamente isso.</p>

<pre><code class="language-python">import requests

from bs4 import BeautifulSoup

Fazer uma requisição GET simples

url = &quot;https://quotes.toscrape.com/&quot;

response = requests.get(url)

Verificar se a requisição foi bem-sucedida

if response.status_code == 200:

print(&quot;Requisição bem-sucedida!&quot;)

print(f&quot;Tamanho do conteúdo: {len(response.text)} caracteres&quot;)

else:

print(f&quot;Erro: {response.status_code}&quot;)</code></pre>

<p>O <code>status_code</code> 200 significa sucesso. Códigos 4xx indicam erros no cliente (URL inválida, acesso negado), e 5xx indicam problemas no servidor. O <code>response.text</code> contém o HTML completo da página em forma de string.</p>

<h3>Parseando HTML com BeautifulSoup</h3>

<p>BeautifulSoup transforma HTML em bruto em uma árvore de objetos Python que você pode navegar e consultar facilmente. Não é suficiente apenas baixar o HTML — você precisa extrair os dados relevantes de forma estruturada.</p>

<pre><code class="language-python">import requests

from bs4 import BeautifulSoup

url = &quot;https://quotes.toscrape.com/&quot;

response = requests.get(url)

soup = BeautifulSoup(response.text, &#039;html.parser&#039;)

Encontrar todos os elementos com classe &#039;quote&#039;

quotes = soup.find_all(&#039;div&#039;, class_=&#039;quote&#039;)

for quote in quotes:

Extrair o texto da citação

text = quote.find(&#039;span&#039;, class_=&#039;text&#039;).get_text()

Extrair o autor

author = quote.find(&#039;small&#039;, class_=&#039;author&#039;).get_text()

print(f&quot;{text}\n— {author}\n&quot;)</code></pre>

<p>Neste exemplo, usamos <code>find_all()</code> para obter todos os elementos que correspondem ao seletor CSS, depois <code>find()</code> para localizar elementos específicos dentro deles. O método <code>get_text()</code> extrai apenas o texto, removendo tags HTML. Este é o padrão: navegar a árvore DOM, localizar elementos e extrair dados.</p>

<h3>Seletores CSS vs. Métodos Find</h3>

<p>Você tem duas abordagens com BeautifulSoup. A primeira é usar <code>find()</code> e <code>find_all()</code> com parâmetros específicos (tag, class, id). A segunda é usar seletores CSS com <code>select()</code>, que é mais poderosa quando você tem seletores complexos.</p>

<pre><code class="language-python">from bs4 import BeautifulSoup

html = &quot;&quot;&quot;

&lt;div class=&quot;container&quot;&gt;

&lt;article class=&quot;post featured&quot;&gt;

&lt;h2&gt;Título 1&lt;/h2&gt;

&lt;/article&gt;

&lt;article class=&quot;post&quot;&gt;

&lt;h2&gt;Título 2&lt;/h2&gt;

&lt;/article&gt;

&lt;/div&gt;

&quot;&quot;&quot;

soup = BeautifulSoup(html, &#039;html.parser&#039;)

Abordagem 1: find_all com parâmetros

posts = soup.find_all(&#039;article&#039;, class_=&#039;post&#039;)

Abordagem 2: select com CSS (mais poderosa)

featured = soup.select(&#039;article.post.featured h2&#039;)

print(featured[0].get_text()) # &quot;Título 1&quot;

Seletores CSS complexos

titles = soup.select(&#039;div.container article.post &gt; h2&#039;)

for title in titles:

print(title.get_text())</code></pre>

<p>Use <code>select()</code> quando seus seletores são complexos ou quando você já está familiarizado com CSS. Use <code>find()</code> e <code>find_all()</code> para casos simples e quando a legibilidade é prioritária.</p>

<h2>Automação com Selenium para Sites Dinâmicos</h2>

<h3>Por Que Selenium é Necessário</h3>

<p>Nem todo conteúdo de um site é servido no HTML inicial. Muitos sites modernos carregam dados via JavaScript após o carregamento da página. Quando você usa <code>requests</code>, recebe apenas o HTML bruto — sem executar JavaScript. O Selenium resolve isso controlando um navegador real (Chrome, Firefox) que executa JavaScript e renderiza a página completamente.</p>

<p>A desvantagem é que Selenium é muito mais lento que requests + BeautifulSoup. Use-o apenas quando necessário, quando o conteúdo é carregado dinamicamente via JavaScript.</p>

<pre><code class="language-python">from selenium import webdriver

from selenium.webdriver.common.by import By

from selenium.webdriver.support.ui import WebDriverWait

from selenium.webdriver.support import expected_conditions as EC

from bs4 import BeautifulSoup

import time

Inicializar o driver (certifique-se de ter o chromedriver instalado)

driver = webdriver.Chrome()

try:

Navegar para a URL

driver.get(&quot;https://quotes.toscrape.com/js/&quot;)

Esperar até 10 segundos por um elemento aparecer

element = WebDriverWait(driver, 10).until(

EC.presence_of_all_elements_located((By.CLASS_NAME, &quot;quote&quot;))

)

Agora o JavaScript foi executado, parse com BeautifulSoup

soup = BeautifulSoup(driver.page_source, &#039;html.parser&#039;)

quotes = soup.find_all(&#039;div&#039;, class_=&#039;quote&#039;)

for quote in quotes:

text = quote.find(&#039;span&#039;, class_=&#039;text&#039;).get_text()

print(text)

finally:

driver.quit() # Sempre fechar o driver</code></pre>

<p>A combinação Selenium + BeautifulSoup é poderosa: o Selenium espera e executa tudo, depois você usa BeautifulSoup para fazer o parsing. O <code>WebDriverWait</code> é crucial — ele espera o elemento aparecer antes de prosseguir, evitando erros de &quot;elemento não encontrado&quot;.</p>

<h3>Interações Avançadas com Selenium</h3>

<p>Selenium não apenas carrega páginas — você pode clicar em botões, preencher formulários, scrollar, enviar tecladas. Isso é essencial quando o site requer autenticação, paginação via cliques, ou quando o conteúdo é carregado sob demanda.</p>

<pre><code class="language-python">from selenium import webdriver

from selenium.webdriver.common.by import By

from selenium.webdriver.common.keys import Keys

from selenium.webdriver.support.ui import WebDriverWait

from selenium.webdriver.support import expected_conditions as EC

import time

driver = webdriver.Chrome()

try:

driver.get(&quot;https://quotes.toscrape.com/&quot;)

Scrollar para baixo

driver.execute_script(&quot;window.scrollTo(0, document.body.scrollHeight);&quot;)

time.sleep(1)

Clicar em um elemento

next_button = driver.find_element(By.CSS_SELECTOR, &quot;li.next a&quot;)

next_button.click()

time.sleep(2)

Preencher um campo de formulário (exemplo hipotético)

search_box = driver.find_element(By.NAME, &quot;search&quot;)

search_box.send_keys(&quot;Python&quot;)

search_box.send_keys(Keys.RETURN)

Esperar elemento e extrair texto

wait = WebDriverWait(driver, 10)

quote = wait.until(EC.presence_of_element_located((By.CLASS_NAME, &quot;quote&quot;)))

print(quote.text)

finally:

driver.quit()</code></pre>

<p>Note que usamos <code>time.sleep()</code> para aguardar elementos carregarem. Embora funcione, <code>WebDriverWait</code> é mais robusto porque espera até que a condição seja atendida, com timeout automático. Sempre use <code>WebDriverWait</code> em produção; <code>sleep()</code> é frágil e lento.</p>

<h2>Boas Práticas e Tratamento de Erros</h2>

<h3>Gerenciando Requisições Responsavelmente</h3>

<p>Ao fazer scraping em larga escala, você pode ser bloqueado pelo servidor se fazer muitas requisições muito rápido. Headers customizados (especialmente User-Agent) fazem seu scraper parecer um navegador real, e adicionar delays entre requisições é educado e eficaz.</p>

<pre><code class="language-python">import requests

import time

from bs4 import BeautifulSoup

Headers para parecer um navegador real

headers = {

&#039;User-Agent&#039;: &#039;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36&#039;

}

urls = [

&quot;https://quotes.toscrape.com/page/1/&quot;,

&quot;https://quotes.toscrape.com/page/2/&quot;,

&quot;https://quotes.toscrape.com/page/3/&quot;

]

for url in urls:

try:

response = requests.get(url, headers=headers, timeout=10)

response.raise_for_status() # Lança exceção se status_code &gt;= 400

soup = BeautifulSoup(response.text, &#039;html.parser&#039;)

quotes = soup.find_all(&#039;div&#039;, class_=&#039;quote&#039;)

print(f&quot;Encontradas {len(quotes)} citações em {url}&quot;)

Delay entre requisições (1 segundo)

time.sleep(1)

except requests.exceptions.Timeout:

print(f&quot;Timeout ao acessar {url}&quot;)

except requests.exceptions.HTTPError as e:

print(f&quot;Erro HTTP {response.status_code}: {url}&quot;)

except Exception as e:

print(f&quot;Erro inesperado: {e}&quot;)</code></pre>

<p>Este código implementa tratamento de erros robusto: timeout, erros HTTP, e exceções genéricas. O <code>headers</code> com User-Agent é simples mas eficaz. O delay <code>time.sleep(1)</code> respeita o servidor. Em produção, você pode aumentar o delay ou usar <code>requests.Session()</code> para reutilizar conexões e ser mais eficiente.</p>

<h3>Salvando Dados Estruturados</h3>

<p>Não há ponto em raspar dados se você não os salvar estruturadamente. CSV e JSON são formatos comuns e facilmente importáveis em ferramentas de análise.</p>

<pre><code class="language-python">import requests

from bs4 import BeautifulSoup

import json

import csv

url = &quot;https://quotes.toscrape.com/&quot;

response = requests.get(url)

soup = BeautifulSoup(response.text, &#039;html.parser&#039;)

quotes = soup.find_all(&#039;div&#039;, class_=&#039;quote&#039;)

Estruturar dados como lista de dicionários

data = []

for quote in quotes:

text = quote.find(&#039;span&#039;, class_=&#039;text&#039;).get_text()

author = quote.find(&#039;small&#039;, class_=&#039;author&#039;).get_text()

tags = [tag.get_text() for tag in quote.find_all(&#039;a&#039;, class_=&#039;tag&#039;)]

data.append({

&#039;text&#039;: text,

&#039;author&#039;: author,

&#039;tags&#039;: tags

})

Salvar como JSON

with open(&#039;quotes.json&#039;, &#039;w&#039;, encoding=&#039;utf-8&#039;) as f:

json.dump(data, f, ensure_ascii=False, indent=2)

Salvar como CSV

with open(&#039;quotes.csv&#039;, &#039;w&#039;, newline=&#039;&#039;, encoding=&#039;utf-8&#039;) as f:

writer = csv.DictWriter(f, fieldnames=[&#039;text&#039;, &#039;author&#039;, &#039;tags&#039;])

writer.writeheader()

for item in data:

item[&#039;tags&#039;] = &#039;, &#039;.join(item[&#039;tags&#039;]) # CSV não suporta listas nativamente

writer.writerow(item)

print(f&quot;Salvos {len(data)} registros&quot;)</code></pre>

<p>Estruturar dados como dicionários Python torna trivial exportar para qualquer formato. JSON é ótimo para dados hierárquicos; CSV é melhor para importação em Excel/Pandas. Escolha baseado em seus casos de uso.</p>

<h2>Conclusão</h2>

<p>Você aprendeu as três ferramentas fundamentais para web scraping em Python: <strong>requests</strong> para fazer requisições HTTP de forma simples e eficiente, <strong>BeautifulSoup</strong> para parsear e navegar HTML/XML, e <strong>Selenium</strong> para automação quando JavaScript está envolvido. A chave é saber qual ferramenta usar para cada situação — requests é rápido para conteúdo estático, Selenium é necessário para conteúdo dinâmico. Igualmente importante é usar esses poderes responsavelmente: respeitar <code>robots.txt</code>, adicionar delays entre requisições, e sempre verificar os termos de serviço antes de raspar um site. Com essas três habilidades, você está equipado para extrair dados de praticamente qualquer site.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://requests.readthedocs.io/" target="_blank" rel="noopener noreferrer">Documentação Oficial - Requests</a></li>

<li><a href="https://www.crummy.com/software/BeautifulSoup/bs4/doc/" target="_blank" rel="noopener noreferrer">Documentação Oficial - BeautifulSoup</a></li>

<li><a href="https://selenium-python.readthedocs.io/" target="_blank" rel="noopener noreferrer">Documentação Oficial - Selenium Python</a></li>

<li><a href="https://quotes.toscrape.com/" target="_blank" rel="noopener noreferrer">Quotes to Scrape - Site de Prática</a></li>

<li><a href="https://realpython.com/web-scraping-python/" target="_blank" rel="noopener noreferrer">Real Python - Web Scraping Best Practices</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Python

Poetry em Python: Gerenciamento Moderno de Dependências e Build: Do Básico ao Avançado
Poetry em Python: Gerenciamento Moderno de Dependências e Build: Do Básico ao Avançado

Introdução ao Poetry: Por Que Abandonar pip e requirements.txt Durante anos,...

O que Todo Dev Deve Saber sobre Programação Funcional em Python: map, filter, functools e itertools
O que Todo Dev Deve Saber sobre Programação Funcional em Python: map, filter, functools e itertools

Fundamentos da Programação Funcional em Python A programação funcional é um p...

Dominando Classes Abstratas em Python: ABC, abstractmethod e Interfaces em Projetos Reais
Dominando Classes Abstratas em Python: ABC, abstractmethod e Interfaces em Projetos Reais

Classes Abstratas em Python: O Fundamento do Design Orientado a Objetos Class...