Регулярное выражение для ссылок в тексте HTML

Надеюсь, это не вопрос RTFM. Я пытаюсь написать сценарий Python, который извлекает ссылки со стандартной веб-страницы HTML (теги <link href...). Я искал в Интернете подходящие регулярные выражения и нашел много разных шаблонов. Есть ли какое-либо согласованное стандартное регулярное выражение для сопоставления ссылок?

Адам

ОБНОВЛЕНИЕ: на самом деле я ищу два разных ответа:

  1. Какое библиотечное решение для разбора HTML-ссылок. Beautiful Soup кажется хорошим решением (спасибо, Igal Serban и cletus!)
  2. Можно ли определить ссылку с помощью регулярного выражения?

person Adam Matan    schedule 10.01.2009    source источник


Ответы (8)


Как предлагали другие, если производительность в реальном времени не требуется, BeautifulSoup - хорошее решение:

import urllib2
from BeautifulSoup import BeautifulSoup

html = urllib2.urlopen("http://www.google.com").read()
soup = BeautifulSoup(html)
all_links = soup.findAll("a")

Что касается второго вопроса, да, HTML-ссылки должны быть четко определены, но HTML, с которым вы действительно сталкиваетесь, вряд ли будет стандартным. Прелесть BeautifulSoup заключается в том, что он использует эвристику браузера, чтобы попытаться проанализировать нестандартный, искаженный HTML, с которым вы, вероятно, действительно столкнетесь.

Если вы уверены, что работаете со стандартным XHTML, вы можете использовать (гораздо) более быстрые парсеры XML, такие как expat.

Регулярное выражение по указанным выше причинам (синтаксический анализатор должен поддерживать состояние, а регулярное выражение не может этого сделать) никогда не будет общим решением.

person Triptych    schedule 10.01.2009

Регулярные выражения с HTML запутываются. Просто используйте парсер DOM, например Beautiful Soup.

person cletus    schedule 10.01.2009
comment
+1: Нет, HTML нельзя описать регулярными выражениями. Это более сложно. И, что еще хуже, браузеру разрешено принимать недопустимый HTML, поэтому веб-сайты отправляют недопустимый HTML. - person S.Lott; 10.01.2009
comment
Я клянусь, что этот вопрос возникает достаточно, чтобы оставить ответ в часто задаваемых вопросах. - person annakata; 10.01.2009

Нет, нет.

Вы можете рассмотреть возможность использования Beautiful Soup. Вы можете назвать это стандартом для разбора файлов html.

person Igal Serban    schedule 10.01.2009

Разве ссылка не должна быть четко определенным регулярным выражением?

Нет, [X] HTML в общем случае не поддается синтаксическому анализу с помощью регулярных выражений. Рассмотрим такие примеры, как:

<link title='hello">world' href="x">link</link>
<!-- <link href="x">not a link</link> -->
<![CDATA[ ><link href="x">not a link</link> ]]>
<script>document.write('<link href="x">not a link</link>')</script>

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

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

person bobince    schedule 10.01.2009
comment
Все ваши примеры на самом деле анализируются с помощью регулярного выражения (чтобы не сказать, что последний недействителен). Синтаксический анализатор XML SAX (который нужен OP) - это не что иное, как лексер языка, определенного RE. искаженные возможности ничего в этом не меняют. - person jpalecek; 06.03.2009

Разве ссылка не должна быть четко определенным регулярным выражением? Это скорее теоретический вопрос,

Второй ответ PEZ:

Я не думаю, что HTML поддается «четко определенным» регулярным выражениям, поскольку это не обычный язык.

Насколько мне известно, любой HTML-тег может содержать любое количество вложенных тегов. Например:

<a href="http://stackoverflow.com">stackoverflow</a>
<a href="http://stackoverflow.com"><i>stackoverflow</i></a>
<a href="http://stackoverflow.com"><b><i>stackoverflow</i></b></a>
...

Таким образом, в принципе, чтобы правильно сопоставить тег, вы должны уметь сопоставить хотя бы строки вида:

BE
BBEE
BBBEEE
...
BBBBBBBBBBEEEEEEEEEE
...

где B означает начало тега, а E означает конец. То есть вы должны иметь возможность сопоставлять строки, образованные любым количеством букв B, за которыми следует такое же количество символов E. Для этого ваш сопоставитель должен уметь «подсчитывать», а регулярные выражения (то есть конечные автоматы) просто не могут этого делать (для подсчета автомату нужен как минимум стек). Что касается ответа PEZ, HTML - это контекстно-свободная грамматика, а не обычный язык.

person Federico A. Ramponi    schedule 10.01.2009
comment
Нет, на самом деле тебе это не нужно. В HTML теги A не могут быть вложенными, и то, что внутри них, выходит за рамки того, что вам нужно для получения ссылок. - person jpalecek; 06.03.2009

Это немного зависит от того, как создается HTML. Если это несколько контролируемо, вам сойдет с рук:

re.findall(r'''<link\s+.*?href=['"](.*?)['"].*?(?:</link|/)>''', html, re.I)
person PEZ    schedule 10.01.2009

Отвечая на два ваших подвопроса там.

  1. Я иногда создавал подклассы SGMLParser (включенный в основной дистрибутив Python) и должен сказать, что это прямолинейно.
  2. Я не думаю, что HTML поддается «четко определенным» регулярным выражениям, поскольку это не обычный язык.
person PEZ    schedule 10.01.2009
comment
Это может быть. Там, где я работаю, не всегда все идет на передовой. знак равно - person PEZ; 10.01.2009
comment
:-) Любые рекомендации по правильной замене py3? - person Adam Matan; 10.01.2009
comment
Не совсем. Возможно, в этой статье можно найти некоторые зацепки: boddie.org.uk/python/HTML.html < / а> - person PEZ; 10.01.2009

В ответ на вопрос № 2 (не должна ли ссылка быть четко определенным регулярным выражением) ответ будет ... нет.

Структура ссылок HTML является рекурсивной, как скобки и скобки в языках программирования. Должно быть равное количество начальных и конечных конструкций, а выражение «ссылка» может быть вложено внутри себя.

Чтобы правильно сопоставить выражение «ссылка», потребуется регулярное выражение для подсчета начального и конечного тегов. Регулярные выражения - это класс конечных автоматов. По определению Конечный Автомат не может «подсчитывать» конструкции в шаблоне. Грамматика необходима для описания такой рекурсивной структуры данных. Неспособность регулярного выражения «подсчитывать» - вот почему вы видите языки программирования, описанные с помощью грамматик, в отличие от регулярных выражений.

Таким образом, невозможно создать регулярное выражение, которое будет положительно соответствовать 100% всех выражений «ссылки». Конечно, существуют регулярные выражения, которые будут соответствовать большому количеству «ссылок» с высокой степенью точности, но они никогда не будут идеальными.

Я недавно написал в блоге статью об этой проблеме. Ограничения регулярных выражений

person JaredPar    schedule 10.01.2009
comment
И интересно, и полезно - спасибо. Кстати, эта проблема решается автоматом стека с опусканием вниз, который имеет большую вычислительную мощность, чем регулярное выражение - и это легко доказать с помощью леммы о накачке (en.wikipedia.org/wiki/Pumping_lemma) - person Adam Matan; 10.01.2009
comment
Не правда. Рекурсивные структуры в HTML (такие как таблицы в таблицах и многие другие), безусловно, не поддаются синтаксическому анализу с помощью RE, но LINK и As рекурсивны в HTML, поэтому вам просто не нужно заботиться о рекурсивных структурах для получения ссылок. - person jpalecek; 07.03.2009
comment
@jpalecek, вы ошиблись. Тег A, безусловно, рекурсивен, потому что содержимое тега A может содержать другой тег. Это может показаться странным, но это, безусловно, HTML-код, который можно анализировать. - person JaredPar; 07.03.2009
comment
Нет, тег не может содержать теги A. Из HTML 4.01 DTD: ‹! ELEMENT A - - (% inline;) * - (A), - (A) означает, что не может быть тега A, вложенного в другой тег A. XML DTD не может выразить это, но w3.org/TR/xhtml1/#prohibitions запрещает это. - person jpalecek; 07.03.2009
comment
@jpalecek, интересно. Я обычно подхожу к этим вопросам гораздо больше, исходя из того, поддается ли он синтаксическому анализу, чем из того, является ли это законным HTML, потому что веб-сайты, как правило, находятся на стороне первого. Даже за исключением того, что вы все еще можете иметь ‹a› буквально внутри него, вставив в CDATA или буквальную строку. - person JaredPar; 07.03.2009
comment
Да, но это на самом деле не поддается синтаксическому анализу, потому что браузеры не анализируют его :-) Это свойство, которое упрощает язык, разработчики браузеров используют его, так что зачем беспокоиться. Что касается CDATA и литералов - все они являются обычными языками, поэтому не являются препятствием для RE. - person jpalecek; 07.03.2009