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

StackOverflow https://stackoverflow.com/questions/317053

  •  11-07-2019
  •  | 
  •  

Вопрос

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

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

Спасибо!

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

Это было полезно?

Решение

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

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

Другие советы

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

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

Ответ Token Mantra: вы не должны настраивать / модифицировать / собирать / иным образом создавать 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. 

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

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

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

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

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

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

Или не используйте & # 8217; не используйте квантификатор для этого выражения:

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

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

foo="bar=' baz='quux"

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

PHP (PCRE) и Python

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

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

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

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

(лучше работает с " gisx " flags.)

<Ч>

Javascript

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

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

сплаттне,

Решение @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( "@$pat_attributes@isU", $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( "@$pat_attributes@isU", $code, $ms);

var_dump( $ms );

Тогда $ ms содержал бы ключи и значения для 2-го и 3-го элемента.

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

Это мое лучшее регулярное выражение для извлечения свойств из HTML-тега:

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

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

# Без отделки

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

Плюсы:

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

Минусы:

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

Это пример регулярного выражения в режиме онлайн:https://regex101.com/r/aVz4uG/13



Обычно я использую это регулярное выражение для извлечения 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">

Это пример регулярного выражения в режиме онлайн:https://regex101.com/r/aVz4uG/15

Ошибка в этом регулярном выражении заключается в следующем:

<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*((?:...

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

Это пример регулярного выражения в режиме онлайн:https://regex101.com/r/aVz4uG/8

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

https://gist.github.com/4153580

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

Я создал функцию 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>';
}

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

Я использую это регулярное выражение для парсера XML

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

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

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

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

$(htmlStr).attr('style') 

посмотрите на это Regex & amp; PHP - изолировать атрибут src от тега img

возможно, вы сможете пройти через DOM и получить нужные атрибуты. Он отлично работает для меня, получая атрибуты из тега body

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top