Cómo usar re.findall() en Python para encontrar todas las subcadenas coincidentes

PythonPythonBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este tutorial, exploraremos la función re.findall() de Python, una herramienta poderosa para extraer subcadenas coincidentes del texto. Esta función forma parte del módulo de expresiones regulares (regex) integrado en Python y es esencial para las tareas de procesamiento de texto.

Al final de este laboratorio (lab), podrás utilizar re.findall() para extraer varios patrones del texto, como direcciones de correo electrónico, números de teléfono y URLs. Estas habilidades son valiosas en el análisis de datos, la extracción de datos web (web scraping) y las aplicaciones de procesamiento de texto.

Ya sea que seas nuevo en Python o busques mejorar tus capacidades de procesamiento de texto, esta guía paso a paso te proporcionará el conocimiento práctico para utilizar eficazmente las expresiones regulares en tus proyectos de Python.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python(("Python")) -.-> python/DataScienceandMachineLearningGroup(["Data Science and Machine Learning"]) python/BasicConceptsGroup -.-> python/strings("Strings") python/FunctionsGroup -.-> python/build_in_functions("Build-in Functions") python/AdvancedTopicsGroup -.-> python/regular_expressions("Regular Expressions") python/DataScienceandMachineLearningGroup -.-> python/data_analysis("Data Analysis") subgraph Lab Skills python/strings -.-> lab-415132{{"Cómo usar re.findall() en Python para encontrar todas las subcadenas coincidentes"}} python/build_in_functions -.-> lab-415132{{"Cómo usar re.findall() en Python para encontrar todas las subcadenas coincidentes"}} python/regular_expressions -.-> lab-415132{{"Cómo usar re.findall() en Python para encontrar todas las subcadenas coincidentes"}} python/data_analysis -.-> lab-415132{{"Cómo usar re.findall() en Python para encontrar todas las subcadenas coincidentes"}} end

Empezando con re.findall()

En este primer paso, aprenderemos sobre la función re.findall() y cómo usarla para la coincidencia de patrones básicos.

Comprendiendo las Expresiones Regulares

Las expresiones regulares (regex) son cadenas de texto especiales utilizadas para describir patrones de búsqueda. Son especialmente útiles cuando necesitas:

  • Encontrar patrones de caracteres específicos en el texto
  • Validar el formato del texto (como direcciones de correo electrónico)
  • Extraer información del texto
  • Reemplazar texto

El Módulo re en Python

Python proporciona un módulo integrado llamado re para trabajar con expresiones regulares. Una de sus funciones más útiles es re.findall().

Comencemos creando un sencillo script de Python para ver cómo funciona re.findall().

  1. Primero, abre la terminal y navega hasta el directorio de nuestro proyecto:
cd ~/project
  1. Crea un nuevo archivo de Python llamado basic_findall.py utilizando el editor de código. En VSCode, puedes hacer clic en el icono "Explorador" (generalmente el primer icono en la barra lateral), luego hacer clic en el botón "Nuevo archivo" y nombrarlo basic_findall.py.

  2. En el archivo basic_findall.py, escribe el siguiente 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. Guarda el archivo y ejecútalo desde la terminal:
python3 ~/project/basic_findall.py

Deberías ver una salida similar a esta:

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

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

Desglosando el Código

Comprendamos qué está sucediendo en nuestro código:

  • Importamos el módulo re con import re
  • Definimos un texto de muestra con múltiples ocurrencias de la palabra "Python"
  • Usamos re.findall(r"Python", text) para encontrar todas las ocurrencias de "Python" en el texto
  • La r antes de la cadena denota una cadena sin formato (raw string), que se recomienda cuando se trabaja con expresiones regulares
  • La función devolvió una lista de todas las subcadenas coincidentes
  • Imprimimos los resultados, mostrando que "Python" apareció 3 veces en nuestro texto

Encontrando Diferentes Patrones

Ahora, intentemos encontrar un patrón diferente. Crea un nuevo archivo llamado 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)

Ejecuta este script:

python3 ~/project/findall_words.py

La salida debería ser:

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

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

En este ejemplo:

  • \w+ coincide con uno o más caracteres de palabra (letras, dígitos o guiones bajos)
  • ain coincide con los caracteres literales "ain"
  • \b representa un límite de palabra, asegurando que coincidamos con palabras completas que terminen con "ain"

Experimenta con estos ejemplos para entender cómo funciona re.findall() con patrones básicos.

Trabajando con Patrones Más Complejos

En este paso, exploraremos patrones más complejos con re.findall() y aprenderemos cómo usar clases de caracteres y cuantificadores para crear patrones de búsqueda flexibles.

Encontrando Números en el Texto

Primero, escribamos un script para extraer todos los números de un texto. Crea un nuevo archivo llamado 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)

Ejecuta el script:

python3 ~/project/extract_numbers.py

Deberías ver una salida similar 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']

Desglosemos los patrones utilizados:

  • \d+\.?\d* coincide con:

    • \d+: Uno o más dígitos
    • \.?: Un punto decimal opcional
    • \d*: Cero o más dígitos después del punto decimal
  • \b\d+\b coincide con:

    • \b: Límite de palabra
    • \d+: Uno o más dígitos
    • \b: Otro límite de palabra (asegurando que coincidamos con números independientes)

Encontrando Palabras de una Longitud Específica

Creemos un script para encontrar todas las palabras de cuatro letras en un texto. Crea 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)

Ejecuta este script:

python3 ~/project/find_word_length.py

La salida debería 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']

En estos patrones:

  • \b\w{4}\b coincide exactamente con 4 caracteres de palabra rodeados de límites de palabra
  • \b\w{3,5}\b coincide con 3 a 5 caracteres de palabra rodeados de límites de palabra

Usando Clases de Caracteres

Las clases de caracteres nos permiten coincidir con conjuntos específicos de caracteres. Creemos character_classes.py:

import re

text = "The temperature is 72°F or 22°C. Contact us at: [email protected]"

## 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)

Ejecuta el script:

python3 ~/project/character_classes.py

La salida debería ser similar a:

Original text:
The temperature is 72°F or 22°C. Contact us at: [email protected]

Words with letters and digits: ['72°f', '22°c', '[email protected]']
Email addresses: ['[email protected]']

Estos patrones demuestran:

  • \b[a-z0-9]+\b: Palabras que contienen letras minúsculas y dígitos
  • El patrón de correo electrónico coincide con el formato estándar de las direcciones de correo electrónico

Experimenta con estos ejemplos para entender cómo diferentes componentes de patrón trabajan juntos para crear patrones de búsqueda poderosos.

Usando Banderas y Grupos de Captura

En este paso, aprenderemos cómo usar banderas para modificar el comportamiento de las expresiones regulares y cómo usar grupos de captura para extraer partes específicas de los patrones coincidentes.

Comprendiendo las Banderas en las Expresiones Regulares

Las banderas modifican cómo el motor de expresiones regulares realiza su búsqueda. El módulo re de Python proporciona varias banderas que se pueden pasar como un parámetro opcional a re.findall(). Exploremos algunas banderas comunes.

Crea un nuevo archivo llamado 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)

Ejecuta el script:

python3 ~/project/regex_flags.py

La salida debería ser similar 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']

Las banderas comunes incluyen:

  • re.IGNORECASE (o re.I): Hace que el patrón sea insensible a mayúsculas y minúsculas
  • re.MULTILINE (o re.M): Hace que ^ y $ coincidan con el inicio/fin de cada línea
  • re.DOTALL (o re.S): Hace que . coincida con cualquier carácter, incluyendo saltos de línea

Usando Grupos de Captura

Los grupos de captura te permiten extraer partes específicas del texto coincidente. Se crean colocando parte de la expresión regular entre paréntesis.

Crea un archivo llamado 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)

Ejecuta el script:

python3 ~/project/capturing_groups.py

La salida debería 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')]

En este ejemplo:

  • Cada conjunto de paréntesis () crea un grupo de captura
  • La función devuelve una lista de tuplas, donde cada tupla contiene los grupos capturados
  • Esto nos permite extraer y organizar datos estructurados del texto

Ejemplo Práctico: Analizando Archivos de Registro

Ahora, apliquemos lo que hemos aprendido a un ejemplo práctico. Imagina que tenemos un archivo de registro con entradas que queremos analizar. Crea un archivo llamado 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)

Ejecuta el script:

python3 ~/project/log_parser.py

La salida debería ser similar 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 ejemplo demuestra:

  • El uso de grupos de captura para extraer información estructurada
  • El procesamiento y la visualización de la información capturada
  • El filtrado de entradas de registro de un tipo específico

Las banderas y los grupos de captura mejoran el poder y la flexibilidad de las expresiones regulares, permitiendo una extracción de datos más precisa y estructurada.

Aplicaciones del Mundo Real de re.findall()

En este último paso, exploraremos aplicaciones prácticas del mundo real de re.findall(). Escribiremos código para extraer correos electrónicos, URLs y realizar tareas de limpieza de datos.

Extrayendo Direcciones de Correo Electrónico

La extracción de correos electrónicos es una tarea común en la minería de datos, el web scraping y el análisis de texto. Crea un archivo llamado email_extractor.py:

import re

## Sample text with email addresses
text = """
Contact information:
- Support: [email protected]
- Sales: [email protected], [email protected]
- Technical team: [email protected]
Personal email: [email protected]
"""

## 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)

Ejecuta el script:

python3 ~/project/email_extractor.py

La salida debería ser similar a:

Original text:

Contact information:
- Support: [email protected]
- Sales: [email protected], [email protected]
- Technical team: [email protected]
Personal email: [email protected]


Extracted email addresses:
1. [email protected]
2. [email protected]
3. [email protected]
4. [email protected]
5. [email protected]

Gmail addresses: ['[email protected]']

Extrayendo URLs

La extracción de URLs es útil para el web scraping, la validación de enlaces y el análisis de contenido. Crea un archivo llamado 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)

Ejecuta el script:

python3 ~/project/url_extractor.py

La salida debería ser similar 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']

Limpieza de Datos con re.findall()

Creemos un script para limpiar y extraer información de un conjunto de datos desordenado. Crea un archivo llamado 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)

Ejecuta el script:

python3 ~/project/data_cleaning.py

La salida debería ser similar 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() con Otras Funciones de Cadenas

Finalmente, veamos cómo podemos combinar re.findall() con otras funciones de cadenas para un procesamiento de texto avanzado. Crea un archivo llamado 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)")

Ejecuta el script:

python3 ~/project/combined_processing.py

La salida debería ser similar 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)

Estos ejemplos demuestran cómo re.findall() se puede combinar con otras funcionalidades de Python para resolver problemas de procesamiento de texto del mundo real. La capacidad de extraer datos estructurados de texto no estructurado es una habilidad esencial para el análisis de datos, el web scraping y muchas otras tareas de programación.

Resumen

En este tutorial, has aprendido cómo utilizar la poderosa función re.findall() de Python para la coincidencia y extracción de patrones de texto. Has adquirido conocimientos prácticos en varias áreas clave:

  1. Coincidencia de Patrones Básicos - Aprendiste cómo encontrar subcadenas simples y utilizar patrones básicos de expresiones regulares para coincidir con patrones de texto específicos.

  2. Patrones Complejos - Exploraste patrones más complejos, incluyendo clases de caracteres, límites de palabras y cuantificadores, para crear patrones de búsqueda flexibles.

  3. Banderas y Grupos de Captura - Descubriste cómo modificar el comportamiento de la búsqueda utilizando banderas como re.IGNORECASE y cómo extraer datos estructurados utilizando grupos de captura.

  4. Aplicaciones del Mundo Real - Aplicaste tus conocimientos a escenarios prácticos, como la extracción de direcciones de correo electrónico y URLs, el análisis de archivos de registro y la limpieza de datos.

Las habilidades que has desarrollado en este laboratorio son valiosas para una amplia gama de tareas de procesamiento de texto, incluyendo:

  • Extracción y limpieza de datos
  • Análisis de contenido
  • Web scraping
  • Análisis de archivos de registro
  • Validación de datos

Con las expresiones regulares y la función re.findall(), ahora tienes una poderosa herramienta para manejar datos de texto en tus proyectos de Python. A medida que sigas practicando y aplicando estas técnicas, te volverás más hábil para crear patrones eficientes para tus necesidades específicas de procesamiento de texto.