Понимание шаблона регулярного выражения, используемого для поиска строки между строками в html

У меня есть следующий html-файл:

<!-- <div class="_5ay5"><table class="uiGrid _51mz" cellspacing="0" cellpadding="0"><tbody><tr class="_51mx"><td class="_51m-"><div class="_u3y"><div class="_5asl"><a class="_47hq _5asm" href="/Dev/videos/1610110089242029/" aria-label="Who said it?" ajaxify="/Dev/videos/1610110089242029/" rel="theater">

Чтобы вытащить строку чисел между videos/ и /", я использую следующий метод, который я нашел:

import re 

Source_file = open('source.html').read()
result = re.compile('videos/(.*?)/"').search(Source_file)
print result

Я попытался найти в Google объяснение того, как именно работает (.*?) в этой конкретной реализации, но я все еще не понимаю. Может ли кто-нибудь объяснить это мне? Это так называемое «нежадное» совпадение? Если да, то что это значит?


person SeanJarp    schedule 10.09.2015    source источник
comment
Сопоставьте что угодно (.), любое количество раз (*), как можно меньшее количество раз (?) (не жадный).   -  person Sebastian Simon    schedule 10.09.2015
comment
Кроме того, вы пометили свой вопрос как «нежадный». Вы можете заглянуть в его вики тегов. Информация о нем уже есть.   -  person Sebastian Simon    schedule 10.09.2015


Ответы (3)


? в этом контексте является специальным оператором для операторов повторения (+, * и ?). В движках, где это доступно, это делает повторение ленивым или нежадным или неохотным или другими подобными терминами. Обычно повторение является жадным, что означает, что оно должно совпадать как можно больше. Таким образом, в большинстве современных perl-совместимых движков у вас есть три типа повторения:

.*  # Match any character zero or more times
.*? # Match any character zero or more times until the next match (reluctant)
.*+ # Match any character zero or more times and don't stop matching! (possessive)

Дополнительную информацию можно найти здесь: http://www.regular-expressions.info/repeat.html#lazy для неохотных/ленивых и здесь: http://www.regular-expressions.info/possessive.html для притяжательного (которое я пропущу в этом ответе).

Предположим, у нас есть строка aaaa. Мы можем сопоставить все a с /(a+)a/. Буквально это

соответствует одному или нескольким a, за которыми следует a.

Это будет соответствовать aaaa. Регулярное выражение является жадным и будет соответствовать как можно большему количеству a. Первое подсовпадение aaa.

Если мы используем регулярное выражение /(a+?)a, это

неохотно совпадать с одним или несколькими a, за которыми следует a
или
совпадать с одним или несколькими a, пока не будет найдено другое a

То есть соответствовать только тому, что нам нужно. Таким образом, в этом случае совпадение равно aa, а первое подсовпадение — a. Нам нужно только сопоставить одно a, чтобы выполнить повторение, а затем за ним следует a.

Это часто возникает при использовании регулярных выражений для сопоставления внутри тегов html, кавычек и тому подобного - обычно зарезервировано для быстрых и грязных операций. То есть использование регулярного выражения для извлечения из очень больших и сложных строк html или строк в кавычках с escape-последовательностью может вызвать много проблем, но это прекрасно подходит для конкретных случаев использования. Итак, в вашем случае имеем:

/Dev/videos/1610110089242029/

Выражение должно соответствовать videos/, за которым следует ноль или более символов, за которыми следует /". Если там есть только один URL-адрес видео, это нормально, без нежелания.

Однако у нас есть

/videos/1610110089242029/" ... ajaxify="/Dev/videos/1610110089242029/"

Без нежелания регулярное выражение будет соответствовать:

1610110089242029/" ... ajaxify="/Dev/videos/1610110089242029

Он пытается максимально соответствовать друг другу, и / и " прекрасно удовлетворяют .. С неохотой сопоставление останавливается на первых /" (на самом деле происходит откат, но об этом можно прочитать отдельно). Таким образом, вы получаете только ту часть URL, которая вам нужна.

person Explosion Pills    schedule 10.09.2015
comment
Потрясающий ответ, спасибо! Если я хочу получить более одной числовой строки из html (т.е. есть несколько чисел между несколькими /Dev/videos/..../), должен ли я искать в другом месте, чем подход с регулярным выражением? Вместо этого что-то вроде парсера HTML? - person SeanJarp; 10.09.2015

Это можно объяснить просто:

  • .: соответствует чему угодно (любому символу),
  • *: любое количество раз (не менее нуля раз),
  • ?: как можно меньше раз (отсюда не жадный).
videos/(.*?)/"

как регулярное выражение соответствует (например)

videos/1610110089242029/"

а первая захватывающая группа возвращает 1610110089242029, потому что любая из цифр является частью «любого символа» и в ней не менее нуля символов.

? вызывает что-то вроде этого:

videos/1610110089242029/" something else … "videos/2387423470237509/"

для правильного сопоставления как 1610110089242029 и 2387423470237509 вместо 1610110089242029/" something else … "videos/2387423470237509, следовательно, «как можно меньше раз», следовательно, «не жадный».

person Sebastian Simon    schedule 10.09.2015

. означает любой символ. * означает любое количество раз, включая ноль. ? действительно означает нежадный; это означает, что он попытается захватить как можно меньше символов, т. е. если регулярное выражение встретит /, оно может сопоставить его с ., но не будет, потому что . не является жадным, и поскольку следующий символ в регулярное выражение счастливо соответствовать /, . не обязательно. Если бы у вас не было ?, этот . съел бы всю оставшуюся часть файла, потому что он бы грыз бит, чтобы сопоставить как можно больше вещей, и, поскольку он соответствует всем, это продолжалось бы вечно.

person ddsnowboard    schedule 10.09.2015