Como usar re.findall() em Python para encontrar todas as substrings correspondentes

PythonBeginner
Pratique Agora

Introdução

Neste tutorial, exploraremos a função re.findall() do Python, uma ferramenta poderosa para extrair substrings correspondentes de um texto. Esta função faz parte do módulo de expressões regulares (regex) embutido no Python e é essencial para tarefas de processamento de texto.

Ao final deste laboratório, você será capaz de usar re.findall() para extrair vários padrões de texto, como endereços de e-mail, números de telefone e URLs. Essas habilidades são valiosas em análise de dados, web scraping e aplicações de processamento de texto.

Se você é novo no Python ou está procurando aprimorar suas capacidades de processamento de texto, este guia passo a passo o equipará com conhecimento prático para usar efetivamente expressões regulares em seus projetos Python.

Começando com re.findall()

Neste primeiro passo, aprenderemos sobre a função re.findall() e como usá-la para correspondência de padrões básica.

Entendendo Expressões Regulares

Expressões regulares (regex) são strings de texto especiais usadas para descrever padrões de busca. Elas são particularmente úteis quando você precisa:

  • Encontrar padrões de caracteres específicos em texto
  • Validar formato de texto (como endereços de e-mail)
  • Extrair informações de texto
  • Substituir texto

O Módulo re no Python

O Python fornece um módulo embutido chamado re para trabalhar com expressões regulares. Uma de suas funções mais úteis é re.findall().

Vamos começar criando um script Python simples para ver como re.findall() funciona.

  1. Primeiro, abra o terminal e navegue até o diretório do nosso projeto:
cd ~/project
  1. Crie um novo arquivo Python chamado basic_findall.py usando o editor de código. No VSCode, você pode clicar no ícone "Explorer" (geralmente o primeiro ícone na barra lateral), depois clicar no botão "New File" e nomeá-lo basic_findall.py.

  2. No arquivo basic_findall.py, escreva o seguinte código:

import re

## Sample text
text = "Python is amazing. Python is versatile. I love learning Python programming."

## Using re.findall() to find all occurrences of "Python"
matches = re.findall(r"Python", text)

## Print the results
print("Original text:")
print(text)
print("\nMatches found:", len(matches))
print("Matching substrings:", matches)
  1. Salve o arquivo e execute-o a partir do terminal:
python3 ~/project/basic_findall.py

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

Original text:
Python is amazing. Python is versatile. I love learning Python programming.

Matches found: 3
Matching substrings: ['Python', 'Python', 'Python']

Descompondo o Código

Vamos entender o que está acontecendo em nosso código:

  • Importamos o módulo re com import re
  • Definimos um texto de amostra com múltiplas ocorrências da palavra "Python"
  • Usamos re.findall(r"Python", text) para encontrar todas as ocorrências de "Python" no texto
  • O r antes da string denota uma string raw, o que é recomendado ao trabalhar com expressões regulares
  • A função retornou uma lista de todas as substrings correspondentes
  • Imprimimos os resultados, mostrando que "Python" apareceu 3 vezes em nosso texto

Encontrando Padrões Diferentes

Agora, vamos tentar encontrar um padrão diferente. Crie um novo arquivo chamado findall_words.py:

import re

text = "The rain in Spain falls mainly on the plain."

## Find all words ending with 'ain'
matches = re.findall(r"\w+ain\b", text)

print("Original text:")
print(text)
print("\nWords ending with 'ain':", matches)

Execute este script:

python3 ~/project/findall_words.py

A saída deve ser:

Original text:
The rain in Spain falls mainly on the plain.

Words ending with 'ain': ['rain', 'Spain', 'plain']

Neste exemplo:

  • \w+ corresponde a um ou mais caracteres de palavra (letras, dígitos ou sublinhados)
  • ain corresponde aos caracteres literais "ain"
  • \b representa uma fronteira de palavra (word boundary), garantindo que correspondemos a palavras completas que terminam com "ain"

Experimente com esses exemplos para ter uma ideia de como re.findall() funciona com padrões básicos.

Trabalhando com Padrões Mais Complexos

Nesta etapa, exploraremos padrões mais complexos com re.findall() e aprenderemos como usar classes de caracteres e quantificadores para criar padrões de busca flexíveis.

Encontrando Números em Texto

Primeiro, vamos escrever um script para extrair todos os números de um texto. Crie um novo arquivo chamado extract_numbers.py:

import re

text = "There are 42 apples, 15 oranges, and 123 bananas in the basket. The price is $9.99."

## Find all numbers (integers and decimals)
numbers = re.findall(r'\d+\.?\d*', text)

print("Original text:")
print(text)
print("\nNumbers found:", numbers)

## Finding only whole numbers
whole_numbers = re.findall(r'\b\d+\b', text)
print("Whole numbers only:", whole_numbers)

Execute o script:

python3 ~/project/extract_numbers.py

Você deve ver uma saída semelhante a:

Original text:
There are 42 apples, 15 oranges, and 123 bananas in the basket. The price is $9.99.

Numbers found: ['42', '15', '123', '9.99']
Whole numbers only: ['42', '15', '123', '9']

Vamos detalhar os padrões usados:

  • \d+\.?\d* corresponde a:

    • \d+: Um ou mais dígitos
    • \.?: Um ponto decimal opcional
    • \d*: Zero ou mais dígitos após o ponto decimal
  • \b\d+\b corresponde a:

    • \b: Fronteira de palavra (word boundary)
    • \d+: Um ou mais dígitos
    • \b: Outra fronteira de palavra (garantindo que correspondemos a números isolados)

Encontrando Palavras de um Comprimento Específico

Vamos criar um script para encontrar todas as palavras de quatro letras em um texto. Crie find_word_length.py:

import re

text = "The quick brown fox jumps over the lazy dog. A good day to code."

## Find all 4-letter words
four_letter_words = re.findall(r'\b\w{4}\b', text)

print("Original text:")
print(text)
print("\nFour-letter words:", four_letter_words)

## Find all words between 3 and 5 letters
words_3_to_5 = re.findall(r'\b\w{3,5}\b', text)
print("Words with 3 to 5 letters:", words_3_to_5)

Execute este script:

python3 ~/project/find_word_length.py

A saída deve ser:

Original text:
The quick brown fox jumps over the lazy dog. A good day to code.

Four-letter words: ['over', 'lazy', 'good', 'code']
Words with 3 to 5 letters: ['The', 'over', 'the', 'lazy', 'dog', 'good', 'day', 'code']

Nestes padrões:

  • \b\w{4}\b corresponde exatamente a 4 caracteres de palavra cercados por fronteiras de palavra
  • \b\w{3,5}\b corresponde a 3 a 5 caracteres de palavra cercados por fronteiras de palavra

Usando Classes de Caracteres

Classes de caracteres nos permitem corresponder a conjuntos específicos de caracteres. Vamos criar character_classes.py:

import re

text = "The temperature is 72°F or 22°C. Contact us at: info@example.com"

## Find words containing both letters and digits
mixed_words = re.findall(r'\b[a-z0-9]+\b', text.lower())

print("Original text:")
print(text)
print("\nWords with letters and digits:", mixed_words)

## Find all email addresses
emails = re.findall(r'\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b', text)
print("Email addresses:", emails)

Execute o script:

python3 ~/project/character_classes.py

A saída deve ser semelhante a:

Original text:
The temperature is 72°F or 22°C. Contact us at: info@example.com

Words with letters and digits: ['72°f', '22°c', 'info@example.com']
Email addresses: ['info@example.com']

Esses padrões demonstram:

  • \b[a-z0-9]+\b: Palavras contendo letras minúsculas e dígitos
  • O padrão de e-mail corresponde ao formato padrão para endereços de e-mail

Experimente com esses exemplos para entender como diferentes componentes de padrão trabalham juntos para criar padrões de busca poderosos.

Usando Flags e Grupos de Captura

Nesta etapa, aprenderemos como usar flags para modificar o comportamento das expressões regulares e como usar grupos de captura para extrair partes específicas de padrões correspondentes.

Entendendo Flags em Expressões Regulares

Flags modificam como o mecanismo de expressão regular realiza sua busca. O módulo re do Python fornece várias flags que podem ser passadas como um parâmetro opcional para re.findall(). Vamos explorar algumas flags comuns.

Crie um novo arquivo chamado regex_flags.py:

import re

text = """
Python is a great language.
PYTHON is versatile.
python is easy to learn.
"""

## Case-sensitive search (default)
matches_case_sensitive = re.findall(r"python", text)

## Case-insensitive search using re.IGNORECASE flag
matches_case_insensitive = re.findall(r"python", text, re.IGNORECASE)

print("Original text:")
print(text)
print("\nCase-sensitive matches:", matches_case_sensitive)
print("Case-insensitive matches:", matches_case_insensitive)

## Using the multiline flag
multiline_text = "First line\nSecond line\nThird line"
## Find lines starting with 'S'
starts_with_s = re.findall(r"^S.*", multiline_text, re.MULTILINE)
print("\nMultiline text:")
print(multiline_text)
print("\nLines starting with 'S':", starts_with_s)

Execute o script:

python3 ~/project/regex_flags.py

A saída deve ser semelhante a:

Original text:

Python is a great language.
PYTHON is versatile.
python is easy to learn.


Case-sensitive matches: ['python']
Case-insensitive matches: ['Python', 'PYTHON', 'python']

Multiline text:
First line
Second line
Third line

Lines starting with 'S': ['Second line']

Flags comuns incluem:

  • re.IGNORECASE (ou re.I): Torna o padrão insensível a maiúsculas e minúsculas
  • re.MULTILINE (ou re.M): Faz com que ^ e $ correspondam ao início/fim de cada linha
  • re.DOTALL (ou re.S): Faz com que . corresponda a qualquer caractere, incluindo quebras de linha

Usando Grupos de Captura

Grupos de captura permitem que você extraia partes específicas do texto correspondente. Eles são criados colocando parte da expressão regular dentro de parênteses.

Crie um arquivo chamado capturing_groups.py:

import re

## Sample text with dates in various formats
text = "Important dates: 2023-11-15, 12/25/2023, and Jan 1, 2024."

## Extract dates in YYYY-MM-DD format
iso_dates = re.findall(r'(\d{4})-(\d{1,2})-(\d{1,2})', text)

## Extract dates in MM/DD/YYYY format
us_dates = re.findall(r'(\d{1,2})/(\d{1,2})/(\d{4})', text)

print("Original text:")
print(text)
print("\nISO dates (Year, Month, Day):", iso_dates)
print("US dates (Month, Day, Year):", us_dates)

## Extract month names with capturing groups
month_dates = re.findall(r'(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+(\d{1,2}),\s+(\d{4})', text)
print("Month name dates (Month, Day, Year):", month_dates)

Execute o script:

python3 ~/project/capturing_groups.py

A saída deve ser:

Original text:
Important dates: 2023-11-15, 12/25/2023, and Jan 1, 2024.

ISO dates (Year, Month, Day): [('2023', '11', '15')]
US dates (Month, Day, Year): [('12', '25', '2023')]
Month name dates (Month, Day, Year): [('Jan', '1', '2024')]

Neste exemplo:

  • Cada conjunto de parênteses () cria um grupo de captura
  • A função retorna uma lista de tuplas, onde cada tupla contém os grupos capturados
  • Isso nos permite extrair e organizar dados estruturados do texto

Exemplo Prático: Analisando Arquivos de Log

Agora, vamos aplicar o que aprendemos a um exemplo prático. Imagine que temos um arquivo de log com entradas que queremos analisar. Crie um arquivo chamado log_parser.py:

import re

## Sample log entries
logs = """
[2023-11-15 08:30:45] INFO: System started
[2023-11-15 08:35:12] WARNING: High memory usage (85%)
[2023-11-15 08:42:11] ERROR: Connection timeout
[2023-11-15 09:15:27] INFO: Backup completed
"""

## Extract timestamp, level, and message from log entries
log_pattern = r'\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+): (.+)'
log_entries = re.findall(log_pattern, logs)

print("Original logs:")
print(logs)
print("\nParsed log entries (timestamp, level, message):")
for entry in log_entries:
    timestamp, level, message = entry
    print(f"Time: {timestamp} | Level: {level} | Message: {message}")

## Find all ERROR logs
error_logs = re.findall(r'\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] ERROR: (.+)', logs)
print("\nError messages:", error_logs)

Execute o script:

python3 ~/project/log_parser.py

A saída deve ser semelhante a:

Original logs:

[2023-11-15 08:30:45] INFO: System started
[2023-11-15 08:35:12] WARNING: High memory usage (85%)
[2023-11-15 08:42:11] ERROR: Connection timeout
[2023-11-15 09:15:27] INFO: Backup completed


Parsed log entries (timestamp, level, message):
Time: 2023-11-15 08:30:45 | Level: INFO | Message: System started
Time: 2023-11-15 08:35:12 | Level: WARNING | Message: High memory usage (85%)
Time: 2023-11-15 08:42:11 | Level: ERROR | Message: Connection timeout
Time: 2023-11-15 09:15:27 | Level: INFO | Message: Backup completed

Error messages: ['Connection timeout']

Este exemplo demonstra:

  • Usando grupos de captura para extrair informações estruturadas
  • Processando e exibindo as informações capturadas
  • Filtrando por tipos específicos de entradas de log

Flags e grupos de captura aprimoram o poder e a flexibilidade das expressões regulares, permitindo uma extração de dados mais precisa e estruturada.

Aplicações do Mundo Real de re.findall()

Nesta etapa final, exploraremos aplicações práticas e do mundo real de re.findall(). Escreveremos código para extrair e-mails, URLs e realizar tarefas de limpeza de dados.

Extraindo Endereços de E-mail

A extração de e-mails é uma tarefa comum em mineração de dados, web scraping e análise de texto. Crie um arquivo chamado email_extractor.py:

import re

## Sample text with email addresses
text = """
Contact information:
- Support: support@example.com
- Sales: sales@example.com, international.sales@example.co.uk
- Technical team: tech.team@subdomain.example.org
Personal email: john.doe123@gmail.com
"""

## Extract all email addresses
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
emails = re.findall(email_pattern, text)

print("Original text:")
print(text)
print("\nExtracted email addresses:")
for i, email in enumerate(emails, 1):
    print(f"{i}. {email}")

## Extract specific domain emails
gmail_emails = re.findall(r'\b[A-Za-z0-9._%+-]+@gmail\.com\b', text)
print("\nGmail addresses:", gmail_emails)

Execute o script:

python3 ~/project/email_extractor.py

A saída deve ser semelhante a:

Original text:

Contact information:
- Support: support@example.com
- Sales: sales@example.com, international.sales@example.co.uk
- Technical team: tech.team@subdomain.example.org
Personal email: john.doe123@gmail.com


Extracted email addresses:
1. support@example.com
2. sales@example.com
3. international.sales@example.co.uk
4. tech.team@subdomain.example.org
5. john.doe123@gmail.com

Gmail addresses: ['john.doe123@gmail.com']

Extraindo URLs

A extração de URLs é útil para web scraping, validação de links e análise de conteúdo. Crie um arquivo chamado url_extractor.py:

import re

## Sample text with various URLs
text = """
Visit our website at https://www.example.com
Documentation: http://docs.example.org/guide
Repository: https://github.com/user/project
Forum: https://community.example.net/forum
Image: https://images.example.com/logo.png
"""

## Extract all URLs
url_pattern = r'https?://[^\s]+'
urls = re.findall(url_pattern, text)

print("Original text:")
print(text)
print("\nExtracted URLs:")
for i, url in enumerate(urls, 1):
    print(f"{i}. {url}")

## Extract specific domain URLs
github_urls = re.findall(r'https?://github\.com/[^\s]+', text)
print("\nGitHub URLs:", github_urls)

## Extract image URLs
image_urls = re.findall(r'https?://[^\s]+\.(jpg|jpeg|png|gif)', text)
print("\nImage URLs:", image_urls)

Execute o script:

python3 ~/project/url_extractor.py

A saída deve ser semelhante a:

Original text:

Visit our website at https://www.example.com
Documentation: http://docs.example.org/guide
Repository: https://github.com/user/project
Forum: https://community.example.net/forum
Image: https://images.example.com/logo.png


Extracted URLs:
1. https://www.example.com
2. http://docs.example.org/guide
3. https://github.com/user/project
4. https://community.example.net/forum
5. https://images.example.com/logo.png

GitHub URLs: ['https://github.com/user/project']

Image URLs: ['https://images.example.com/logo.png']

Limpeza de Dados com re.findall()

Vamos criar um script para limpar e extrair informações de um conjunto de dados desorganizado. Crie um arquivo chamado data_cleaning.py:

import re

## Sample messy data
data = """
Product: Laptop X200, Price: $899.99, SKU: LP-X200-2023
Product: Smartphone S10+, Price: $699.50, SKU: SP-S10P-2023
Product: Tablet T7, Price: $299.99, SKU: TB-T7-2023
Product: Wireless Earbuds, Price: $129.95, SKU: WE-PRO-2023
"""

## Extract product information
product_pattern = r'Product: (.*?), Price: \$([\d.]+), SKU: ([A-Z0-9-]+)'
products = re.findall(product_pattern, data)

print("Original data:")
print(data)
print("\nExtracted and structured product information:")
print("Name\t\tPrice\t\tSKU")
print("-" * 50)
for product in products:
    name, price, sku = product
    print(f"{name}\t${price}\t{sku}")

## Calculate total price
total_price = sum(float(price) for _, price, _ in products)
print(f"\nTotal price of all products: ${total_price:.2f}")

## Extract only products above $500
expensive_products = [name for name, price, _ in products if float(price) > 500]
print("\nExpensive products (>$500):", expensive_products)

Execute o script:

python3 ~/project/data_cleaning.py

A saída deve ser semelhante a:

Original data:

Product: Laptop X200, Price: $899.99, SKU: LP-X200-2023
Product: Smartphone S10+, Price: $699.50, SKU: SP-S10P-2023
Product: Tablet T7, Price: $299.99, SKU: TB-T7-2023
Product: Wireless Earbuds, Price: $129.95, SKU: WE-PRO-2023


Extracted and structured product information:
Name		Price		SKU
--------------------------------------------------
Laptop X200	$899.99	LP-X200-2023
Smartphone S10+	$699.50	SP-S10P-2023
Tablet T7	$299.99	TB-T7-2023
Wireless Earbuds	$129.95	WE-PRO-2023

Total price of all products: $2029.43

Expensive products (>$500): ['Laptop X200', 'Smartphone S10+']

Combinando re.findall() com Outras Funções de String

Finalmente, vamos ver como podemos combinar re.findall() com outras funções de string para processamento de texto avançado. Crie um arquivo chamado combined_processing.py:

import re

## Sample text with mixed content
text = """
Temperature readings:
- New York: 72°F (22.2°C)
- London: 59°F (15.0°C)
- Tokyo: 80°F (26.7°C)
- Sydney: 68°F (20.0°C)
"""

## Extract all temperature readings in Fahrenheit
fahrenheit_pattern = r'(\d+)°F'
fahrenheit_temps = re.findall(fahrenheit_pattern, text)

## Convert to integers
fahrenheit_temps = [int(temp) for temp in fahrenheit_temps]

print("Original text:")
print(text)
print("\nFahrenheit temperatures:", fahrenheit_temps)

## Calculate average temperature
avg_temp = sum(fahrenheit_temps) / len(fahrenheit_temps)
print(f"Average temperature: {avg_temp:.1f}°F")

## Extract city and temperature pairs
city_temp_pattern = r'- ([A-Za-z\s]+): (\d+)°F'
city_temps = re.findall(city_temp_pattern, text)

print("\nCity and temperature pairs:")
for city, temp in city_temps:
    print(f"{city}: {temp}°F")

## Find the hottest and coldest cities
hottest_city = max(city_temps, key=lambda x: int(x[1]))
coldest_city = min(city_temps, key=lambda x: int(x[1]))

print(f"\nHottest city: {hottest_city[0]} ({hottest_city[1]}°F)")
print(f"Coldest city: {coldest_city[0]} ({coldest_city[1]}°F)")

Execute o script:

python3 ~/project/combined_processing.py

A saída deve ser semelhante a:

Original text:

Temperature readings:
- New York: 72°F (22.2°C)
- London: 59°F (15.0°C)
- Tokyo: 80°F (26.7°C)
- Sydney: 68°F (20.0°C)


Fahrenheit temperatures: [72, 59, 80, 68]
Average temperature: 69.8°F

City and temperature pairs:
New York: 72°F
London: 59°F
Tokyo: 80°F
Sydney: 68°F

Hottest city: Tokyo (80°F)
Coldest city: London (59°F)

Esses exemplos demonstram como re.findall() pode ser combinado com outras funcionalidades do Python para resolver problemas de processamento de texto do mundo real. A capacidade de extrair dados estruturados de texto não estruturado é uma habilidade essencial para análise de dados, web scraping e muitas outras tarefas de programação.

Resumo

Neste tutorial, você aprendeu como usar a poderosa função re.findall() em Python para correspondência e extração de padrões de texto. Você adquiriu conhecimento prático em várias áreas-chave:

  1. Correspondência de Padrões Básicos - Você aprendeu como encontrar substrings simples e usar padrões básicos de expressões regulares para corresponder a padrões de texto específicos.

  2. Padrões Complexos - Você explorou padrões mais complexos, incluindo classes de caracteres, limites de palavras e quantificadores para criar padrões de busca flexíveis.

  3. Flags e Grupos de Captura - Você descobriu como modificar o comportamento da busca usando flags como re.IGNORECASE e como extrair dados estruturados usando grupos de captura.

  4. Aplicações do Mundo Real - Você aplicou seu conhecimento a cenários práticos, como extrair endereços de e-mail e URLs, analisar arquivos de log e limpar dados.

As habilidades que você desenvolveu neste laboratório são valiosas para uma ampla gama de tarefas de processamento de texto, incluindo:

  • Extração e limpeza de dados
  • Análise de conteúdo
  • Web scraping
  • Análise de arquivos de log
  • Validação de dados

Com expressões regulares e a função re.findall(), você agora tem uma ferramenta poderosa para lidar com dados de texto em seus projetos Python. À medida que você continua a praticar e aplicar essas técnicas, você se tornará mais proficiente na criação de padrões eficientes para suas necessidades específicas de processamento de texto.