Regex для ссылок в HTML-текст - PullRequest
7 голосов
/ 10 января 2009

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

Адам

UPDATE: Я на самом деле ищу два разных ответа:

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

Ответы [ 8 ]

17 голосов
/ 10 января 2009

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

8 голосов
/ 10 января 2009

Как считают другие, если производительность в режиме реального времени не требуется, 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.

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

5 голосов
/ 10 января 2009

Нет, нет.

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

4 голосов
/ 10 января 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-супом из тегов, существует миллион искаженных возможностей.

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

3 голосов
/ 10 января 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 является контекстно-свободной грамматикой, а не обычным языком.

1 голос
/ 10 января 2009

Отвечаю на ваши два подвопроса.

  1. Иногда я делю SGMLParser на подклассы (включен в основной дистрибутив Python) и должен сказать, что он прямой.
  2. Я не думаю, что HTML поддается "четко определенным" регулярным выражениям, поскольку это не обычный язык.
1 голос
/ 10 января 2009

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

re.findall(r'''<link\s+.*?href=['"](.*?)['"].*?(?:</link|/)>''', html, re.I)
0 голосов
/ 10 января 2009

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

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

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

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

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

...