質問
アンカータグ(<a>
)の属性を抽出しようとしています。これまでのところ、私はこの式を持っています:
(?<name>\b\w+\b)\s*=\s*("(?<value>[^"]*)"|'(?<value>[^']*)'|(?<value>[^"'<> \s]+)\s*)+
次のような文字列に対して機能します
<a href="test.html" class="xyz">
and(単一引用符)
<a href='test.html' class="xyz">
ただし、引用符のない文字列の場合:
<a href=test.html class=xyz>
引用符なしで属性を使用できるように正規表現を変更するにはどうすればよいですか?またはそれを行うためのより良い方法はありますか?
ありがとう!
更新: これまでのすべての良いコメントとアドバイスに感謝します。言及しなかったことが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">
は機能しません。
他のヒント
regexpを介して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;
<!> quot;しかし、待って、<!> quot;あなたは言うかもしれません。 <!> quot; *コメントはどうですか?!?!<!> quot;さて、その後、非バックトラッキングセクションの.
を次のように置き換えることができます(CDATAセクションも処理します)。
(?:[^<]|<[^!]|<![^-\[]|<!\[(?!CDATA)|<!\[CDATA\[.*?\]\]>|<!--(?:[^-]|-[^-])*-->)
- また、Perl 5.10(およびPCRE)で置換を実行したい場合、属性名の直前に
\K
を置くことができ、スキップしたいすべてのものをキャプチャすることを心配する必要はありません。
トークンマントラの応答:正規表現を使用してhtml / xmlを調整/変更/ハーベスト/または生成しないでください。
\ 'や\ <!> quotなどの大/小文字の条件があります。説明する必要があります。適切な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.
他のすべての人に同意するだけです:regexpを使用してHTMLを解析しないでください。
HTMLの正しい部分でさえ属性を選択する式を作成することはできません。考えられるすべての不正なバリアントを気にしないでください。あなたの正規表現は、引用符の無効な不足に対処しようとしても、すでにほとんど読めません。現実のHTMLの恐怖をさらに追いかけると、維持できない信頼できない表現の塊に夢中になります。
壊れたHTMLを読み込むか、有効なXHTMLに修正して、XMLパーサーで簡単に貪食できる既存のライブラリがあります。それらを使用します。
複数のキャプチャに同じ名前を使用することはできません。したがって、名前付きキャプチャを含む式では数量詞を使用できません。
したがって、ドン<!>#8217; t名前付きキャプチャを使用します:
(?:(\b\w+\b)\s*=\s*("[^"]*"|'[^']*'|[^"'<>\s]+)\s+)+
またはdon <!>#8217; tこの式で数量詞を使用します:
(?<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*)
(<!> quot; gisx <!> quot;フラグを使用すると効果的です。)
Javascript
Javascript
正規表現は後読みをサポートしていないため、私が提案する以前の表現のほとんどの機能をサポートしません。ただし、誰かのニーズに合う場合は、このバージョンを試すことができます。 (動作を確認)。
(\S+)=[\'"]?((?:(?!\/>|>|"|\'|\s).)+)
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];
これはHTMLタグのプロパティを抽出するための私の最高の正規表現です:
#引用符内の一致をトリムします(シングルまたはダブル)
(\S+)\s*=\s*([']|["])\s*([\W\w]*?)\s*\2
#トリムなし
(\S+)\s*=\s*([']|["])([\W\w]*?)\2
長所:
- 引用符内のコンテンツをトリミングできます。
- 引用符内のすべての特殊ASCII文字と一致します。
- title = <!> quot;を持っている場合、あなたは私の<!> quot;正規表現は壊れません
短所:
- 3つのグループを返します。最初にプロパティ、次に引用符(<!> quot; | ')、最後に引用符内のプロパティ、つまり:
<div title="You're">
結果はグループ1:タイトル、グループ2:<!> quot ;、グループ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 '>
<!> quot; solve <!> quot;これにより、[^/]+?
パターンが削除されます。
<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+)=|[>"']))?[^"']*)["']?
vs
(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?
等しい信号間のスペースを避けます。 (\ S +) \ s * = \ s * ((?:...
最後の+と。にとって: | [<!> gt; <!> quot; ']))?[^ <!> quot;'] * )[<!> quot; ']?
これはオンラインの正規表現の例です。 https://regex101.com/r/aVz4uG/8
このようなものが役立つかもしれません
'(\S+)\s*?=\s*([\'"])(.*?|)\2
HTML Tidy を使用してHTMLをXHTMLに変換し、適切なXPathを使用することをお勧めします属性を抽出する式。
一般的になりたい場合は、こちら。しかし、それでも、完璧な正規表現を行う場合、不正な形式のhtmlがある場合はどうなりますか?
使用する言語に応じて、htmlを解析するためにライブラリに行くことをお勧めします。 pythonのBeautiful Soupのように。
。
XPathを使用できます。
単一の正規表現のみを使用する戦略を再検討します。確かに、それをすべて行う単一の正規表現を考え出すのは素晴らしいゲームです。しかし、保守性に関しては、両足で自分を撃ちましょう。
HTMLのタグと属性の形式は
です<tag
attrnovalue
attrnoquote=bli
attrdoublequote="blah 'blah'"
attrsinglequote='bloob "bloob"' >
属性を一致させるには、4つの形式のいずれかを見つける正規表現attr
が必要です。次に、HTMLタグ内で一致のみが報告されるようにする必要があります。正しい正規表現があると仮定すると、正規表現の合計は次のようになります。
attr(?=(attr)*\s*/?\s*>)
lookaheadは、他の属性と終了タグのみが属性に続くようにします。 $1
には次の正規表現を使用します:
\s+(\w+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^><"'\s]+)))?
重要でないグループは非キャプチャになります。最初に一致するグループ$2
は属性の名前を示し、値は$3
または$4
または$2$3$4
のいずれかです。 <=>を使用して値を抽出します。
最終的な正規表現は
\s+(\w+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^><"'\s]+)))?(?=(?:\s+\w+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^><"'\s]+))?)*\s*/?\s*>)
注:先読みで不要なグループをすべて削除し、残りのすべてのグループをキャプチャしないようにしました。
HTMLタグの属性を抽出できる PHP関数を作成しました。また、値のないdisabled
などの属性を処理できます。また、content
の結果を確認することにより、タグがスタンドアロンタグ(終了タグがない)かどうか(終了タグがある)を判別できます。 / p>
/*! 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')
これを見てください 正規表現<!> amp; PHP-imgタグからsrc属性を分離します
おそらく、DOMをウォークスルーして必要な属性を取得できます。 body-tagから属性を取得して、私にとっては正常に動作します