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.
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().
- Primero, abre la terminal y navega hasta el directorio de nuestro proyecto:
cd ~/project
Crea un nuevo archivo de Python llamado
basic_findall.pyutilizando 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 nombrarlobasic_findall.py.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)
- 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
reconimport 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
rantes 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)aincoincide con los caracteres literales "ain"\brepresenta 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+\bcoincide 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}\bcoincide exactamente con 4 caracteres de palabra rodeados de límites de palabra\b\w{3,5}\bcoincide 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: 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)
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: info@example.com
Words with letters and digits: ['72°f', '22°c', 'info@example.com']
Email addresses: ['info@example.com']
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(ore.I): Hace que el patrón sea insensible a mayúsculas y minúsculasre.MULTILINE(ore.M): Hace que^y$coincidan con el inicio/fin de cada líneare.DOTALL(ore.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: 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)
Ejecuta el script:
python3 ~/project/email_extractor.py
La salida debería ser similar 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']
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:
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.
Patrones Complejos - Exploraste patrones más complejos, incluyendo clases de caracteres, límites de palabras y cuantificadores, para crear patrones de búsqueda flexibles.
Banderas y Grupos de Captura - Descubriste cómo modificar el comportamiento de la búsqueda utilizando banderas como
re.IGNORECASEy cómo extraer datos estructurados utilizando grupos de captura.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.



