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

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

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

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

Для C# я выбрал AngleSharp и его класс HtmlParser.

Uwe Keim 25.07.2019 15:04
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Введение в CSS
Введение в CSS
CSS является неотъемлемой частью трех основных составляющих front-end веб-разработки.
Как выровнять Div по центру?
Как выровнять Div по центру?
Чтобы выровнять элемент <div>по горизонтали и вертикали с помощью CSS, можно использовать комбинацию свойств и значений CSS. Вот несколько методов,...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
59
1
104 642
19
Перейти к ответу Данный вопрос помечен как решенный

Ответы 19

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

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

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

Ответ 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. Единственный способ включить их - это закодировать их как объект & quot;

nickf 25.11.2008 14:55

Да, в спецификации HTML указано, что вы должны кодировать их, но, тем не менее, из-за того, что люди, использующие обратную косую черту с использованием, адаптируются, чтобы заставить его работать, и все больше людей используют его, поэтому ваш синтаксический анализатор должен уметь обрабатывать его, когда они это делают :)

Kent Fredric 17.01.2009 05:28

Это не так с 2020 года в Chrome 79; обратная косая черта-кавычка - это нет, распознаваемая как экранированная кавычка, скорее, косая черта удаляется, а кавычка либо распознается как разделитель значения, либо как часть значения, в зависимости от ее положения / окружения.

John Frazer 20.02.2020 09:30

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

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

Ответ принят как подходящий

Обновление (2020), Гюм Фокс предлагает https://regex101.com/r/U9Yqqg/2 (обратите внимание, что regex101.com не существовал, когда я изначально написал этот ответ)

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

Применительно к:

<a href=test.html class=xyz>
<a href = "test.html" class = "xyz">
<a href='test.html' class = "xyz">
<script type = "text/javascript" defer async id = "something" onload = "alert('hello');"></script>
<img src = "test.png">
<img src = "a test.png">
<img src=test.png />
<img src=a test.png />
<img src=test.png >
<img src=a test.png >
<img src=test.png alt=crap >
<img src=a test.png alt=crap >

Оригинальный ответ (2008 г.): Если у вас есть такой элемент, как

<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'

Note: This does not work with numeric attribute values e.g. <div id = "1"> won't work.

Edited: Improved regex for getting attributes with no value and values with " ' " inside.

([^\r\n\t\f\v= '"]+)(?:=(["'])?((?:.(?!\2?\s+(?:\S+)=|\2))+.)\2?)?

Применяется на:

<script type = "text/javascript" defer async id = "something" onload = "alert('hello');"></script>

это даст:

'type' => 'text/javascript'
'defer' => ''
'async' => ''
'id' => 'something'
'onload' => 'alert(\'hello\');'

А как насчет «foo = " bar 'bar =' bla "»?

Gumbo 22.02.2009 16:52

@Gumbo: это регулярное выражение должно учитывать одинарные или двойные кавычки, поскольку оно использует класс символов ['"]

VonC 22.02.2009 17:52

Конечно, он не мог управлять кавычками в пределах значения атрибута.

VonC 22.02.2009 17:54

Я знаю. Но значение foo будет «bar 'bar =' bla», а не просто «bar».

Gumbo 22.02.2009 18:04

Фон, это частично работает, но когда в одном теге есть сочетание нецитированных и цитируемых слов, это больше не работает. См. Мой ответ для другой версии регулярного выражения

fedmich 29.11.2012 07:46

Было бы здорово, если бы он мог ловить пустые атрибуты, такие как «выбранный» и «проверен».

Andrea Silvestri 26.06.2014 11:43

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

Andrea Silvestri 26.06.2014 12:28

@AndreaSilvestri, можете ли вы отредактировать этот ответ с фиксированным регулярным выражением, которое бы этого избежало?

VonC 26.06.2014 12:29

@PauloCosta, вы можете увидеть регулярное выражение в regex101.com/r/bY3kM1/1: оно распакует регулярное выражение для вас.

VonC 17.08.2015 17:54

@VonC, похоже, не работает, если у вас есть атрибут типа data-test = "1", выбрано значение "1, а не просто 1.

Jed Grant 31.10.2015 00:08

@SlavikMeltser Да, 4 года назад я выделил более надежное решение: stackoverflow.com/a/13618472/6309

VonC 02.10.2016 09:15

Я добавил решение, которое позволяет использовать кавычки без кавычек / кавычек, одинарные / двойные кавычки, экранированные кавычки внутри атрибутов, пробелы вокруг знаков равенства, различное количество атрибутов, проверять только атрибуты внутри тегов и управлять разными кавычками внутри значения атрибута: stackoverflow.com/a/38305337/1204332

Ivan Chaer 07.10.2016 16:57

@IvanChaer Отлично сделано. Но я проголосовал за вашу первую итерацию еще в июле;)

VonC 07.10.2016 17:15

Да, VonC, но совсем недавно @SlavikMeltser пролил свет на ретроспективы, которые позволили мне улучшить свой ответ. Спасибо! :)

Ivan Chaer 07.10.2016 18:11

А как насчет <glyph unicode = "« "horizon-adv-x = " 450 "d = " M45,532C45,478,71 />?

user1932634 23.01.2018 22:05

@ user1932634 Это точно не совпадет с моим регулярным выражением 9-летней давности ...

VonC 24.01.2018 00:08

Возникла проблема с этим образцом: <img src=test.png />. Это дает src => test.png /> со вторым регулярным выражением и src => test.png / с первым.

Gyum Fox 20.08.2020 11:14

@GyumFox Действительно. Я обновил регулярное выражение в regex101.com/r/k5qzMI/2, но оно не работает для значений, которые не заключены в кавычки (одинарные или двойные)

VonC 20.08.2020 11:42

У меня лучший результат при изменении вашего первого регулярного выражения (хотя я тестировал только те случаи, которые меня интересовали ...): regex101.com/r/U9Yqqg/1

Gyum Fox 20.08.2020 11:56

@GyumFox Спасибо. Я включил ваш комментарий в ответ для большей наглядности. Как проиллюстрировано regex101.com/r/U9Yqqg/2, это не охватывает случаи все, но близко.

VonC 20.08.2020 15:00

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

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

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

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

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

Значение без кавычек, "([^> \ s '"] +) ", не будет выполнено для <tag attr = value />, включая' / 'в значении. Вероятно, это должно быть что-то вроде (непроверено)" ( . *?) (?: \ s |> | /> | '| ") # символы до первого пробела,>, />, цитата"

mheyman 04.08.2012 21:11

Именно то, что я искал - мне нужно было удалить кучу атрибутов (один и тот же атрибут, разные значения "и attribute = ". *? "Помогло в моем случае ... очень удобная ссылка, спасибо @mheyman

Jayx 04.03.2015 16:46

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

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

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

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

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

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

foo = "bar=' baz='quux"

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

Гораздо точнее, чем мое регулярное выражение. +1. Обратите внимание, почему [^ \ s], а [^ \ s] будет достаточно?

VonC 22.02.2009 18:00

Я просто скопировал регулярное выражение из вопроса. :)

Gumbo 22.02.2009 18:07

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

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

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

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

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

$(htmlStr).attr('style') 

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

https://gist.github.com/4153580

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

Привет, Фуркан, решение с регулярным выражением может быть лучшим для этой ситуации, так как оно быстрее :) см. Мой ответ

fedmich 29.11.2012 07:47

Я также думаю, что регулярное выражение должно быть лучше, но я не хотел иметь дело с деталями с регулярным выражением, например с атрибутом вроде this value = "Tester's Device", этот единственный qoute будет путать вещи с простыми шаблонами регулярных выражений, или даже иногда нет цитаты вокруг ценностей. Я сделал это надежным способом. Если бы это было на C, это было бы быстрее, чем регулярное выражение, но я не могу сказать то же самое для php.

Furkan Mustafa 03.02.2013 17:01

Сплаттне

Решение @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];

Это действительно кажется более надежным решением. +1

VonC 29.11.2012 08:48

Хороший. Однако я попытался присоединиться следующим образом: array_combine ($ ms [1], $ ms [3]) (3 вместо 2)

magallanes 11.06.2016 04:58

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

Не определяет тег doctype

Roni 08.11.2016 07:34

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

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

PHP (PCRE) и Python

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

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

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

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

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


Javascript

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

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

Ваше извлечение RegEx не является точным. См. Обновленный пример из форка вашего тестового примера: regex101.com/r/y3DOf5/1

Slavik Meltser 02.10.2016 09:06

Да, невозможно выполнить условный поиск, основанный на проверке наличия предыдущего символа (в данном случае одинарные / двойные кавычки). Не с одним регулярным выражением. Вот почему парсер - лучший вариант. К сожалению, это регулярное выражение является приблизительным.

Ivan Chaer 03.10.2016 16:56

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

Slavik Meltser 05.10.2016 09:55

Спасибо за подсказки, @SlavikMeltser! Я обновил свой ответ, оглянувшись назад.

Ivan Chaer 06.10.2016 17:52

К сожалению, это не работает с javascript, поскольку он не поддерживает ретроспективный просмотр.

choise 15.12.2016 20:38

@choise Верно! Я добавил упрощенное выражение для JS.

Ivan Chaer 27.12.2016 13:47

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

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

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

Это мой лучший 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

Теги и атрибуты в 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*>)

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

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