Вопрос или проблема
Я наблюдаю странное поведение при парсинге текстов из HTML-файла с использованием регулярных выражений в Python. Буду очень благодарен за ваши предложения по регулярному выражению, которое мне следует использовать.
string = "<a href=\"https://academia/course/3743\">3743</a>, <a href=\"https://academia/course/3963\">3963</a>, <a href=\"https://academia/course/3850\">3850</a>,"
# Я хочу извлечь 3743, 3963, 3850 из приведенного выше текста
pattern = r".*?<a href=\"https://stackoverflow.com/questions/79128001/.*\">([0-9]+)</a>,.*"
result = re.findall(pattern, string)
print(result)
# Вывод
['3850']
Он выводит только последнее вхождение и игнорирует остальные. Я также пробовал следующее, но это не помогает:
python findall находит только последнее вхождение.
Может кто-нибудь помочь с регулярным выражением, которое мне следует использовать, чтобы получить все числа?
# ожидаемый вывод
[3743, 3963, 3850]
P.S. Я не могу использовать другие модули Python, такие как bs4. Мне нужно придерживаться встроенных модулей Python.
Вы можете использовать простое регулярное выражение для получения желаемого вывода.
import re
string = "<a href=\"https://academia/course/3743\">3743</a>, <a href=\"https://academia/course/3963\">3963</a>, <a href=\"https://academia/course/3850\">3850</a>,"
pattern = r"<a href=\"[^\"]*\">(\d+)</a>"
result = re.findall(pattern, string)
print(result)
Вывод:
['3743', '3963', '3850']
Когда вы ищете шаблон в строке с помощью регулярного выражения и findall, вы можете просто поместить искомый шаблон в регулярное выражение. Нет необходимости добавлять https://stackoverflow.com/questions/79128001/.*?
перед и после фактического шаблона.
Основная проблема вашего регулярного выражения – href="https://stackoverflow.com/questions/79128001/.*"
, что означает, что оно попытается сопоставить любой символ в значении href столько раз, сколько это возможно. В результате оно не остановится на первом '>
, который встретит, а на последнем, давая вам одно число в результате. Вы можете увидеть это поведение, если заключите значение в группу href="https://stackoverflow.com/questions/79128001/(.*)"
. Фактически захватить шаблон в href="https://stackoverflow.com/questions/79128001/(.*)"
это https://academia/course/3743'>3743</a>, <a href="https://academia/course/3963">3963</a>, <a href="https://academia/course/3850
.
Чтобы предотвратить это, вы должны указать регулярному выражению сопоставлять любой символ как можно реже с помощью href="https://stackoverflow.com/questions/79128001/.*?"
, знак вопроса является идентификатором для этого поведения. Затем оно остановится при первом возможности, т.е. на первом '
.
Финальный код, включая регулярное выражение будет:
string = "<a href=\"https://academia/course/3743\">3743</a>, <a href=\"https://academia/course/3963\">3963</a>, <a href=\"https://academia/course/3850\">3850</a>,"
pattern = r"<a href=\"https://stackoverflow.com/questions/79128001/.*?\">([0-9]+)</a>"
result = re.findall(pattern, string)
print(result)
# Вывод
['3743', '3963', '3850']
Ответ или решение
Когда вы работаете с регулярными выражениями в Python, особенно с функцией re.findall
, важно правильно формулировать шаблон, чтобы избежать нежелательного поведения, такого как «нахождение» только последнего вхождения. Рассмотрим вашу проблему более подробно.
Проблема: Вы пытаетесь извлечь номера курсов (3743, 3963, 3850) из строки, содержащей HTML-теги, но используемый вами шаблон возвращает только последнее совпадение.
Анализ проблемы
Ваш текущий шаблон:
pattern = r".*?<a href="https://stackoverflow.com/questions/79128001/.*">([0-9]+)</a>,.*"
Основная проблема заключается в том, что конструкция .*
(жадное соответствие) начинает захватывать как можно больше символов, включая символы, которые идут после последнего интересующего вас числа. Это приводит к тому, что в результате re.findall
возвращается только последнее соответствие, поскольку оно находит границы строки только в последний момент.
Оптимизированное решение
Чтобы решить вашу проблему, необходимо уточнить регулярное выражение. Вам не нужно использовать жадное соответствие, а вместо этого можно применить «ленивое» соответствие с помощью .*?
. Также потребуется удостовериться, что вы корректно обрабатываете пробелы и специальные HTML-коды. Попробуйте следующий код:
import re
# Исходная строка
string = "<a href=\"https://academia/course/3743\">3743</a>, <a href=\"https://academia/course/3963\">3963</a>, <a href=\"https://academia/course/3850\">3850</a>,"
# Оптимизированный шаблон
pattern = r"<a href=\"https://academia/course/\d+\">(\d+)</a>"
# Поиск всех совпадений
result = re.findall(pattern, string)
# Вывод результата
print(result)
Объяснение шаблона
-
<a href=\"https://academia/course/\d+\">
:<
и>
используются для экранирования символов<
и>
.\"https://academia/course/\d+\"
указывает на часть ссылки, где\d+
обозначает несколько цифр, что соответствует вашему ID курса.
-
(\d+)
:- Это обозначает захватывающую группу, которая захватывает само число курса.
-
</a>
:- Закрывающий тег ссылки.
Ожидаемый вывод
После выполнения кода вы получите следующий результат:
['3743', '3963', '3850']
Таким образом, вам не потребуются дополнительные модули, такие как BeautifulSoup
, так как регулярные выражения в Python достаточно мощные для выполнения вашей задачи.
Заключение
Регулярные выражения могут быть сложными, особенно когда речь идет о парсинге HTML-кода. Важно тщательно формулировать шаблоны и учитывать различные размеры соответствия. Проанализировав ваши потребности, мы смогли создать экономичный и эффективный подход, который позволит вам извлечь все нужные данные без лишних усилий.