Регулярное выражение для извлечения атрибутов тега

Я пытаюсь извлечь атрибуты тега привязки (<a>). До сих пор у меня есть это выражение:

(?<name>\b\w+\b)\s*=\s*("(?<value>[^"]*)"|'(?<value>[^']*)'|(?<value>[^"'<> \s]+)\s*)+

который работает на строки как

<a href="test.html" class="xyz">

и (одинарные кавычки)

<a href='test.html' class="xyz">

но не для строки без кавычек:

<a href=test.html class=xyz>

Как я могу изменить свой regex то, чтобы заставлять это работать с атрибутами без кавычек? Или есть ли лучший способ сделать это?

Обновление: Спасибо за все хорошие комментарии и совет до сих пор. Существует одна вещь, которую я не упоминал: Я печально должен исправить/изменить код, не написанный мной. И нет никакого времени/денег для перезаписи этого материала с самого начала.

47
задан splattne 26 July 2019 в 07:03
поделиться

9 ответов

Если у Вас есть элемент как

<name attribute=value attribute="value" attribute='value'>

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

(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?

Прикладными на:

<a href=test.html class=xyz>
<a href="test.html" class="xyz">
<a href='test.html' class="xyz">

это уступило бы:

'href' => 'test.html'
'class' => 'xyz'

Примечание: Это не работает с числовыми значениями атрибута, например, <div id="1"> не будет работать.

86
ответ дан displayname 26 November 2019 в 19:11
поделиться

Я пересмотрел бы стратегию использовать только единственное регулярное выражение. Уверенный это - хорошая игра для предложения одного единственного регулярного выражения, которое делает все это. Но с точки зрения пригодности для обслуживания Вы собираетесь выстрелить себе в обе ноги.

1
ответ дан innaM 26 November 2019 в 19:11
поделиться

Если Вы находитесь в.NET, я рекомендую пакет гибкости HTML, очень устойчивый даже с уродливым HTML.

Тогда можно использовать XPath.

2
ответ дан splattne 26 November 2019 в 19:11
поделиться

Если Вы хотите быть общими, необходимо посмотреть на точную спецификацию тег, как здесь . Но даже с этим, если Вы делаете свой идеальный regexp, что, если у Вас есть уродливый HTML?

я предложил бы пойти для библиотеки для парсинга HTML, в зависимости от языка, с которым Вы работаете: например, как Красивый Суп Python.

2
ответ дан Piotr Lesnicki 26 November 2019 в 19:11
поделиться

Вы не можете использовать то же название нескольких получений. Таким образом Вы не можете использовать квантор по выражениям с именованными получениями.

Так любой использование don’t назвало получения:

(?:(\b\w+\b)\s*=\s*("[^"]*"|'[^']*'|[^"'<>\s]+)\s+)+

Или don’t используют квантор по этому выражению:

(?<name>\b\w+\b)\s*=\s*(?<value>"[^"]*"|'[^']*'|[^"'<>\s]+)

Это действительно также позволяет значения атрибута как bar=' baz='quux:

foo="bar=' baz='quux"

Хорошо недостаток будет состоять в том, что необходимо разделить продвижение и запаздывающие кавычки впоследствии.

11
ответ дан Gumbo 26 November 2019 в 19:11
поделиться

Только согласиться со всеми остальными: не анализируйте HTML с помощью regexp.

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

существуют существующие библиотеки, чтобы или считать поврежденный HTML или исправить его в допустимый XHTML, который можно тогда легко пожрать с синтаксическим анализатором XML. Используйте их.

11
ответ дан bobince 26 November 2019 в 19:11
поделиться

Хотя совет не проанализировать HTML через regexp допустим, вот выражение, которое делает в значительной степени, что Вы спросили:

/
   \G                     # start where the last match left off
   (?>                    # begin non-backtracking expression
       .*?                # *anything* until...
       <[Aa]\b            # an anchor tag
    )??                   # but look ahead to see that the rest of the expression
                          #    does not match.
    \s+                   # at least one space
    ( \p{Alpha}           # Our first capture, starting with one alpha
      \p{Alnum}*          # followed by any number of alphanumeric characters
    )                     # end capture #1
    (?: \s* = \s*         # a group starting with a '=', possibly surrounded by spaces.
        (?: (['"])        # capture a single quote character
            (.*?)         # anything else
             \2           # which ever quote character we captured before
        |   ( [^>\s'"]+ ) # any number of non-( '>', space, quote ) chars
        )                 # end group
     )?                   # attribute value was optional
/msx;

, "Но ожидают", Вы могли бы сказать. "Что относительно *комментарии?!?!" Хорошо, тогда можно заменить . в разделе неотслеживания в обратном порядке с: (Это также обрабатывает разделы CDATA.)

(?:[^<]|<[^!]|<![^-\[]|<!\[(?!CDATA)|<!\[CDATA\[.*?\]\]>|<!--(?:[^-]|-[^-])*-->)
  • Также, если Вы хотели выполнить замену под Perl 5.10 (и я думаю PCRE), можно поместить \K прямо перед названием атрибута и не иметь для волнения о получении всего материала, через который Вы хотите перескочить.
22
ответ дан Axeman 26 November 2019 в 19:11
поделиться

Я предлагаю, чтобы Вы использовали HTML, Опрятный , чтобы преобразовать HTML в XHTML, и затем использовать подходящее выражение XPath для извлечения атрибутов.

2
ответ дан activout.se 26 November 2019 в 19:11
поделиться

Маркерный ответ Молитвы: Вы не должны tweak/modify/harvest/or иначе производить html/xml, использующий регулярное выражение.

существуют, также может загнать условные выражения случая в угол такой как \' и \", который должен составляться. Вы - очень более обеспеченное использование надлежащего Синтаксического анализатора DOM, Синтаксического анализатора XML или одного из многих других десятков проверенных на практике инструментов для этого задания вместо того, чтобы изобрести Ваше собственное.

Я действительно не забочусь, какой Вы используете, пока его распознанное, протестированный, и Вы используете тот.

my $foo  = Someclass->parse( $xmlstring ); 
my @links = $foo->getChildrenByTagName("a"); 
my @srcs = map { $_->getAttribute("src") } @links; 
# @srcs now contains an array of src attributes extracted from the page. 
13
ответ дан Kent Fredric 26 November 2019 в 19:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: