Подтвердить что ты не робот

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

Я пытаюсь извлечь атрибуты тега привязки (<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>

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

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

4b9b3361

Ответ 1

Если у вас есть такой элемент, как

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

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

(\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"> не будет работать.

Ответ 2

Хотя рекомендация не анализировать 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 прямо перед именем атрибута и не беспокоиться о том, чтобы захватить все вещи, которые вы хотите пропустить.

Ответ 3

Ответ маркера Token: вам не следует настраивать/изменять/собирать/или иным образом создавать html/xml с использованием регулярного выражения.

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

Мне все равно, какой из них вы используете, пока его распознают, тестируют, и вы его используете.

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. 

Ответ 4

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

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

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

Ответ 5

Вы не можете использовать одно и то же имя для нескольких захватов. Таким образом, вы не можете использовать квантификатор в выражениях с именованными захватами.

Так что либо не используйте именованные записи:

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

Или не используйте квантификатор в этом выражении:

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

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

foo="bar=' baz='quux"

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

Ответ 6

PHP (PCRE) и Python

Простое извлечение атрибута (Посмотрите, как работает):

((?:(?!\s|=).)*)\s*?=\s*?["']?((?:(?<=")(?:(?<=\\)"|[^"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!"|')(?:(?!\/>|>|\s).)+))

Или с проверкой открытия/закрытия тегов, поиска имени тега и комментариями. Это выражение предусматривает кавычки без кавычек, одиночные/двойные кавычки, экранированные кавычки внутри атрибутов, пробелы вокруг знаков равенства, различное количество атрибутов, проверку только атрибутов внутри тегов и управление разными кавычками в значении атрибута. (Посмотрите, как работает):

(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)

(работает лучше с флагами "gisx".)


Javascript

Поскольку Javascript регулярные выражения не поддерживают look-behinds, он не будет поддерживать большинство функций предыдущих выражений, которые я предлагаю. Но в случае, если это может подойти кому-то, вы можете попробовать эту версию. (Посмотрите, как работает).

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

Ответ 7

Это мой лучший RegEx для извлечения свойств в теге HTML:

# Обрезать совпадение внутри кавычек (одинарное или двойное)

(\S+)\s*=\s*([']|["])\s*([\W\w]*?)\s*\2

# Без отделки

(\S+)\s*=\s*([']|["])([\W\w]*?)\2

Плюсы:

  • Вы можете обрезать содержимое внутри кавычек.
  • Совпадение всех специальных символов ASCII внутри кавычек.
  • Если у вас есть title= "Ты мой", RegEx не сломан

Минусы:

  • Возвращает 3 группы; сначала свойство, затем кавычка ("| ') и в конце свойство внутри кавычек, т.е.: <div title="You're"> результат - группа 1: заголовок, группа 2:", группа 3: вы "повторно.

Это онлайн пример RegEx: https://regex101.com/r/aVz4uG/13



Я обычно использую этот RegEx для извлечения тегов HTML:

Я рекомендую это, если вы не используете тип тега, такой как <div, <span и т.д.

<[^/]+?(?:\".*?\"|'.*?'|.*?)*?>

Например:

<div title="a>b=c<d" data-type='a>b=c<d'>Hello</div>
<span style="color: >=<red">Nothing</span>
# Returns 
# <div title="a>b=c<d" data-type='a>b=c<d'>
# <span style="color: >=<red">

Это онлайн пример RegEx: https://regex101.com/r/aVz4uG/15

Ошибка в этом RegEx:

<div[^/]+?(?:\".*?\"|'.*?'|.*?)*?>

В этом теге:

<article title="a>b=c<d" data-type='a>b=c<div '>Hello</article>

Возвращает <div '> но не должно совпадать:

Match:  <div '>

Чтобы "решить" это, удалите [^/]+? шаблон:

<div(?:\".*?\"|'.*?'|.*?)*?>


Ответ # 317081 хорош, но он не соответствует этим случаям:

<div id="a"> # It returns "a instead of a
<div style=""> # It doesn't match instead of return only an empty property
<div title = "c"> # It not recognize the space between the equal (=)

Это улучшение:

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

против

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

Избегайте пробелов между одинаковыми сигналами: (\ S+) \ s *= \ s * ((?:...

Поменяй последний + и. для: | [> "'])) ? [^"'] *) ["']?

Это онлайн пример RegEx: https://regex101.com/r/aVz4uG/8

Ответ 8

splattne,

Решение

@VonC частично работает, но есть некоторые проблемы, если в теге были смешанные некотируемые и цитируемые

Это работает со смешанными атрибутами

$pat_attributes = "(\S+)=(\"|'| |)(.*)(\"|'| |>)"

чтобы проверить его

<?php
$pat_attributes = "(\S+)=(\"|'| |)(.*)(\"|'| |>)"

$code = '    <IMG title=09.jpg alt=09.jpg src="http://example.com.jpg?v=185579" border=0 mce_src="example.com.jpg?v=185579"
    ';

preg_match_all( "@[email protected]", $code, $ms);
var_dump( $ms );

$code = '
<a href=test.html class=xyz>
<a href="test.html" class="xyz">
<a href=\'test.html\' class="xyz">
<img src="http://"/>      ';

preg_match_all( "@[email protected]", $code, $ms);

var_dump( $ms );

$ms будет содержать ключи и значения для второго и третьего элементов.

$keys = $ms[1];
$values = $ms[2];

Ответ 9

что-то вроде этого может быть полезно

'(\S+)\s*?=\s*([\'"])(.*?|)\2

Ответ 10

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

Ответ 11

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

Я бы предложил пойти для библиотеки для разбора html, в зависимости от языка, с которым вы работаете: например. как python Beautiful Soup.

Ответ 12

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

Затем вы можете использовать XPath.

Ответ 13

Теги и атрибуты в HTML имеют вид

<tag 
   attrnovalue 
   attrnoquote=bli 
   attrdoublequote="blah 'blah'"
   attrsinglequote='bloob "bloob"' >

Чтобы сопоставить атрибуты, вам нужно регулярное выражение attr которое находит одну из четырех форм. Затем вам нужно убедиться, что в HTML-тегах указываются только совпадения. Предполагая, что у вас есть правильное регулярное выражение, общее регулярное выражение будет:

attr(?=(attr)*\s*/?\s*>)

Предварительный просмотр гарантирует, что только другие атрибуты и закрывающий тег следуют за атрибутом. Я использую следующее регулярное выражение для attr:

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

Незначительные группы делаются без захвата. Первая подходящая группа $1 дает вам имя атрибута, значение равно одному из $2 или $3 или $4. Я использую $2$3$4 чтобы извлечь значение. Последнее регулярное выражение

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

Примечание. Я удалил все ненужные группы в окне просмотра и сделал все оставшиеся группы незаписанными.

Ответ 14

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

Ответ 15

Мне также понадобилось это и написал функцию для анализа атрибутов, вы можете получить ее здесь:

https://gist.github.com/4153580

(Примечание: он не использует регулярное выражение)

Ответ 16

Я создал функцию PHP, которая может извлекать атрибуты любых HTML-тегов. Он также может обрабатывать такие атрибуты, как disabled, который не имеет значения, а также может определить, является ли тег автономным тегом (не имеет закрывающего тега) или нет (имеет закрывающий тег), проверяя результат content:

/*! Based on <https://github.com/mecha-cms/cms/blob/master/system/kernel/converter.php> */
function extract_html_attributes($input) {
    if( ! preg_match('#^(<)([a-z0-9\-._:]+)((\s)+(.*?))?((>)([\s\S]*?)((<)\/\2(>))|(\s)*\/?(>))$#im', $input, $matches)) return false;
    $matches[5] = preg_replace('#(^|(\s)+)([a-z0-9\-]+)(=)(")(")#i', '$1$2$3$4$5<attr:value>$6', $matches[5]);
    $results = array(
        'element' => $matches[2],
        'attributes' => null,
        'content' => isset($matches[8]) && $matches[9] == '</' . $matches[2] . '>' ? $matches[8] : null
    );
    if(preg_match_all('#([a-z0-9\-]+)((=)(")(.*?)("))?(?:(\s)|$)#i', $matches[5], $attrs)) {
        $results['attributes'] = array();
        foreach($attrs[1] as $i => $attr) {
            $results['attributes'][$attr] = isset($attrs[5][$i]) && ! empty($attrs[5][$i]) ? ($attrs[5][$i] != '<attr:value>' ? $attrs[5][$i] : "") : $attr;
        }
    }
    return $results;
}

Тестовый код

$test = array(
    '<div class="foo" id="bar" data-test="1000">',
    '<div>',
    '<div class="foo" id="bar" data-test="1000">test content</div>',
    '<div>test content</div>',
    '<div>test content</span>',
    '<div>test content',
    '<div></div>',
    '<div class="foo" id="bar" data-test="1000"/>',
    '<div class="foo" id="bar" data-test="1000" />',
    '< div  class="foo"     id="bar"   data-test="1000"       />',
    '<div class id data-test>',
    '<id="foo" data-test="1000">',
    '<id data-test>',
    '<select name="foo" id="bar" empty-value-test="" selected disabled><option value="1">Option 1</option></select>'
);

foreach($test as $t) {
    var_dump($t, extract_html_attributes($t));
    echo '<hr>';
}

Ответ 17

Это работает для меня. Он также принимает во внимание некоторые конечные случаи, с которыми я столкнулся.

Я использую этот синтаксический анализатор Regex для XML

(?<=\s)[^><:\s]*=*(?=[>,\s])

Ответ 18

Извлеките элемент:

var buttonMatcherRegExp=/<a[\s\S]*?>[\s\S]*?<\/a>/;
htmlStr=string.match( buttonMatcherRegExp )[0]

Затем используйте jQuery для синтаксического анализа и извлечения нужного вам бита:

$(htmlStr).attr('style')