Python Регулярные выражения ищут только последнее совпадение при использовании re.findall

Вопрос или проблема

Я наблюдаю странное поведение при парсинге текстов из 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)

Объяснение шаблона

  1. <a href=\"https://academia/course/\d+\">:

    • &lt; и &gt; используются для экранирования символов < и >.
    • \"https://academia/course/\d+\" указывает на часть ссылки, где \d+ обозначает несколько цифр, что соответствует вашему ID курса.
  2. (\d+):

    • Это обозначает захватывающую группу, которая захватывает само число курса.
  3. &lt;/a&gt;:

    • Закрывающий тег ссылки.

Ожидаемый вывод

После выполнения кода вы получите следующий результат:

['3743', '3963', '3850']

Таким образом, вам не потребуются дополнительные модули, такие как BeautifulSoup, так как регулярные выражения в Python достаточно мощные для выполнения вашей задачи.

Заключение

Регулярные выражения могут быть сложными, особенно когда речь идет о парсинге HTML-кода. Важно тщательно формулировать шаблоны и учитывать различные размеры соответствия. Проанализировав ваши потребности, мы смогли создать экономичный и эффективный подход, который позволит вам извлечь все нужные данные без лишних усилий.

Оцените материал
Добавить комментарий

Капча загружается...