Введение
В этом руководстве мы рассмотрим функцию re.findall() в Python, мощный инструмент для извлечения совпадающих подстрок из текста. Эта функция является частью встроенного модуля регулярных выражений (regex) в Python и является важной для задач обработки текста.
К концу этого практического занятия (lab) вы сможете использовать re.findall() для извлечения различных шаблонов из текста, таких как адреса электронной почты, номера телефонов и URL-адреса. Эти навыки ценны в области анализа данных, веб-скрапинга и приложений для обработки текста.
Будь вы новичком в Python или желающим улучшить свои способности в обработке текста, это пошаговое руководство предоставит вам практические знания для эффективного использования регулярных выражений в своих Python-проектах.
Начало работы с re.findall()
На этом первом этапе мы узнаем о функции re.findall() и о том, как использовать ее для базового сопоставления шаблонов.
Понимание регулярных выражений
Регулярные выражения (regex) - это специальные текстовые строки, используемые для описания шаблонов поиска. Они особенно полезны, когда вам нужно:
- Найти определенные шаблоны символов в тексте
- Проверить формат текста (например, адреса электронной почты)
- Извлечь информацию из текста
- Заменить текст
Модуль re в Python
Python предоставляет встроенный модуль re для работы с регулярными выражениями. Одна из самых полезных функций этого модуля - re.findall().
Начнем с создания простого Python-скрипта, чтобы увидеть, как работает re.findall().
- Сначала откройте терминал и перейдите в наш проектный каталог:
cd ~/project
Создайте новый Python-файл с именем
basic_findall.pyс помощью редактора кода. В VSCode вы можете нажать на иконку "Explorer" (обычно это первая иконка в боковой панели), затем нажать кнопку "New File" и назвать файлbasic_findall.py.В файле
basic_findall.pyнапишите следующий код:
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)
- Сохраните файл и запустите его из терминала:
python3 ~/project/basic_findall.py
Вы должны увидеть вывод, похожий на следующий:
Original text:
Python is amazing. Python is versatile. I love learning Python programming.
Matches found: 3
Matching substrings: ['Python', 'Python', 'Python']
Анализ кода
Понять, что происходит в нашем коде:
- Мы импортировали модуль
reс помощьюimport re - Мы определили пример текста с несколькими вхождениями слова "Python"
- Мы использовали
re.findall(r"Python", text)для поиска всех вхождений "Python" в тексте - Символ
rперед строкой обозначает сырую строку, что рекомендуется при работе с регулярными выражениями - Функция вернула список всех совпадающих подстрок
- Мы вывели результаты, показывая, что "Python" встречается 3 раза в нашем тексте
Поиск различных шаблонов
Теперь давайте попробуем найти другой шаблон. Создайте новый файл с именем 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)
Запустите этот скрипт:
python3 ~/project/findall_words.py
Вывод должен быть следующим:
Original text:
The rain in Spain falls mainly on the plain.
Words ending with 'ain': ['rain', 'Spain', 'plain']
В этом примере:
\w+соответствует одному или нескольким символам слова (буквам, цифрам или подчеркиваниям)ainсоответствует буквальным символам "ain"\bпредставляет границу слова, обеспечивая, что мы ищем целые слова, оканчивающиеся на "ain"
Попробуйте эти примеры, чтобы понять, как работает re.findall() с базовыми шаблонами.
Работа с более сложными шаблонами
На этом этапе мы рассмотрим более сложные шаблоны с использованием re.findall() и узнаем, как использовать классы символов и квантификаторы для создания гибких шаблонов поиска.
Поиск чисел в тексте
Сначала напишем скрипт для извлечения всех чисел из текста. Создайте новый файл с именем 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)
Запустите скрипт:
python3 ~/project/extract_numbers.py
Вы должны увидеть вывод, похожий на следующий:
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']
Разберем использованные шаблоны:
\d+\.?\d*соответствует:\d+: Одному или нескольким цифрам\.?: Необязательной десятичной точке\d*: Нулям или более цифрам после десятичной точки
\b\d+\bсоответствует:\b: Границе слова\d+: Одному или нескольким цифрам\b: Еще одной границе слова (чтобы найти только отдельные числа)
Поиск слов определенной длины
Создадим скрипт для поиска всех четырехбуквенных слов в тексте. Создайте файл 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)
Запустите этот скрипт:
python3 ~/project/find_word_length.py
Вывод должен быть следующим:
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']
В этих шаблонах:
\b\w{4}\bсоответствует ровно 4 символам слова, окруженным границами слова\b\w{3,5}\bсоответствует от 3 до 5 символам слова, окруженным границами слова
Использование классов символов
Классы символов позволяют нам искать определенные наборы символов. Создайте файл 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)
Запустите скрипт:
python3 ~/project/character_classes.py
Вывод должен быть похожим на следующий:
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']
Эти шаблоны демонстрируют:
\b[a-z0-9]+\b: Слова, содержащие строчные буквы и цифры- Шаблон для поиска электронных адресов соответствует стандартному формату адресов электронной почты
Попробуйте эти примеры, чтобы понять, как различные компоненты шаблонов взаимодействуют для создания мощных шаблонов поиска.
Использование флагов и захватывающих групп
На этом этапе мы узнаем, как использовать флаги для изменения поведения регулярных выражений и как использовать захватывающие группы для извлечения определенных частей совпавших шаблонов.
Понимание флагов в регулярных выражениях
Флаги изменяют способ, которым движок регулярных выражений выполняет поиск. Модуль re в Python предоставляет несколько флагов, которые можно передать как необязательный параметр в функцию re.findall(). Исследуем некоторые распространенные флаги.
Создайте новый файл с именем 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)
Запустите скрипт:
python3 ~/project/regex_flags.py
Вывод должен быть похожим на следующий:
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']
Общие флаги включают:
re.IGNORECASE(илиre.I): делает шаблон нечувствительным к регистру буквre.MULTILINE(илиre.M): делает символы^и$соответствовать началу/концу каждой строкиre.DOTALL(илиre.S): делает символ.соответствовать любому символу, включая символы новой строки
Использование захватывающих групп
Захватывающие группы позволяют извлекать определенные части совпавшего текста. Они создаются путем помещения части регулярного выражения в круглые скобки.
Создайте файл с именем 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)
Запустите скрипт:
python3 ~/project/capturing_groups.py
Вывод должен быть следующим:
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')]
В этом примере:
- Каждая пара круглых скобок
()создает захватывающую группу - Функция возвращает список кортежей, где каждый кортеж содержит захваченные группы
- Это позволяет извлекать и структурировать данные из текста
Практический пример: разбор лог-файлов
Теперь применим наши знания на практическом примере. Предположим, у нас есть лог-файл с записями, которые мы хотим разобрать. Создайте файл с именем 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)
Запустите скрипт:
python3 ~/project/log_parser.py
Вывод должен быть похожим на следующий:
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']
Этот пример демонстрирует:
- Использование захватывающих групп для извлечения структурированной информации
- Обработку и отображение захваченной информации
- Фильтрацию определенных типов записей в логах
Флаги и захватывающие группы повышают мощность и гибкость регулярных выражений, позволяя более точно и структурированно извлекать данные.
Практические применения re.findall()
На этом последнем этапе мы рассмотрим практические, реальные применения функции re.findall(). Мы напишем код для извлечения адресов электронной почты, URL-адресов и выполнения задач по очистке данных.
Извлечение адресов электронной почты
Извлечение адресов электронной почты - это распространенная задача в области дата-майнинга, веб-скрапинга и текстового анализа. Создайте файл с именем 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)
Запустите скрипт:
python3 ~/project/email_extractor.py
Вывод должен быть похожим на следующий:
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']
Извлечение URL-адресов
Извлечение URL-адресов полезно для веб-скрапинга, проверки ссылок и анализа контента. Создайте файл с именем 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)
Запустите скрипт:
python3 ~/project/url_extractor.py
Вывод должен быть похожим на следующий:
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']
Очистка данных с использованием re.findall()
Создадим скрипт для очистки и извлечения информации из запутанного набора данных. Создайте файл с именем 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)
Запустите скрипт:
python3 ~/project/data_cleaning.py
Вывод должен быть похожим на следующий:
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+']
Комбинирование re.findall() с другими строковыми функциями
Наконец, посмотрим, как можно комбинировать функцию re.findall() с другими строковыми функциями для продвинутой обработки текста. Создайте файл с именем 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)")
Запустите скрипт:
python3 ~/project/combined_processing.py
Вывод должен быть похожим на следующий:
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)
Эти примеры демонстрируют, как функцию re.findall() можно комбинировать с другими возможностями Python для решения реальных задач по обработке текста. Возможность извлекать структурированные данные из неструктурированного текста - это важный навык для анализа данных, веб-скрапинга и многих других программистских задач.
Резюме
В этом руководстве вы узнали, как использовать мощную функцию re.findall() в Python для поиска и извлечения текстовых шаблонов. Вы приобрели практические знания в нескольких ключевых областях:
Базовое сопоставление шаблонов — вы научились находить простые подстроки и использовать базовые шаблоны регулярных выражений для сопоставления определенных текстовых шаблонов.
Сложные шаблоны — вы исследовали более сложные шаблоны, включая классы символов, границы слов и квантификаторы, чтобы создавать гибкие шаблоны поиска.
Флаги и захватывающие группы — вы узнали, как изменить поведение поиска с помощью флагов, таких как
re.IGNORECASE, и как извлекать структурированные данные с использованием захватывающих групп.Практические применения — вы применили свои знания в практических сценариях, таких как извлечение адресов электронной почты и URL-адресов, разбор лог-файлов и очистка данных.
Навыки, которые вы получили в этом LabEx, ценны для широкого спектра задач по обработке текста, включая:
- Извлечение и очистка данных
- Анализ контента
- Веб-скрапинг
- Разбор лог-файлов
- Валидация данных
С помощью регулярных выражений и функции re.findall() у вас теперь есть мощный инструмент для обработки текстовых данных в ваших Python-проектах. По мере того, как вы будете продолжать практиковаться и применять эти техники, вы станете более компетентными в создании эффективных шаблонов для своих конкретных задач по обработке текста.



