Регулярное выражение для извлечения атрибутов тега - PullRequest
46 голосов
/ 25 ноября 2008

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

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

Спасибо!

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

Ответы [ 18 ]

84 голосов
/ 25 ноября 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'

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

22 голосов
/ 26 ноября 2008

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

Ответ токен-мантры: вы не должны настраивать / модифицировать / собирать / иным образом создавать 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. 
10 голосов
/ 25 ноября 2008

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

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

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

10 голосов
/ 22 февраля 2009

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

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

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

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

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

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

foo="bar=' baz='quux"

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

7 голосов
/ 11 июля 2016

PHP (PCRE) и Python

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

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

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

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

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


Javascript

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

(\S+)=[\'"]?((?:(?!\/>|>|"|\'|\s).)+)
4 голосов
/ 29 ноября 2012

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( "@$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];
4 голосов
/ 08 октября 2017

Это мой лучший 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 +) \ с * = \ с * ((?: ...

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

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

3 голосов
/ 22 июля 2010

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

'(\S+)\s*?=\s*([\'"])(.*?|)\2
2 голосов
/ 25 ноября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...