web-dev-qa-db-ja.com

RegExはXHTML自己完結型タグを除くオープンタグと一致します

これらすべての開始タグを一致させる必要があります。

<p>
<a href="foo">

しかし、これらではありません:

<br />
<hr class="foo" />

私はこれを思いついたし、それが正しいことを確認したかった。私はa-zをキャプチャしているだけです。

<([a-z]+) *[^/]*?>

私はそれが言うと信じています:

  • 小なりを見つけ、
  • A-zを1回以上見つけて(そしてキャプチャして)、
  • ゼロ個以上のスペースを見つけて
  • /を除いて、欲張りな文字を0回以上見つけます
  • より大きいを探す

その権利はありますか?そしてもっと重要なことに、あなたはどう思いますか?

1324
Jeff

[X] HTMLを正規表現で解析することはできません。 HTMLは正規表現では解析できないからです。正規表現は、HTMLを正しく解析するために使用できるツールではありません。ここでは何度もHTMLと正規表現に関する質問に答えましたが、正規表現を使用してもHTMLを消費することはできません。正規表現は、HTMLで採用されている構成要素を理解するのに不十分なほど洗練されていません。 HTMLは正規言語ではないため、正規表現では解析できません。正規表現クエリは、HTMLをその意味のある部分に分解することはできません。何度もそうだけど、それは私には届いていない。 Perlで使用されているような拡張された不規則な正規表現でさえ、HTMLを解析するというタスクには及ばない。あなたは私を亀裂させることは決してないだろう。 HTMLは、正規表現では解析できないほど複雑な言語です。 Jon Skeetでも正規表現を使ってHTMLを解析することはできません。 HTMLを正規表現で解析しようとするたびに、不幸な子供が処女の血を流し、ロシアのハッカーがあなたのWebアプリケーションを攻撃します。正規表現を使用してHTMLを解析すると、汚染された魂が生活の領域に入ります。 HTMLと正規表現は、愛、結婚、そして儀式の幼児殺害のように一緒になります。 <センター>はそれを保持することができません遅すぎます。同じ概念的な空間で正規表現とHTMLを一緒に使用すると、水っぽいPuTTYのように頭が痛くなります。 HTMLを正規表現でパースすると、基本多言語平面で名前を表現できないものに対して、私たち全員が非人道的な苦労を強いられることになります。 HTML-plus-regexpはあなたが観察している間に感傷者の気持ちを液化させ、あなたの精神は恐怖の猛攻撃で枯れていくでしょう。正規表現ベースのHTMLパーサーはStackOverflowを殺している癌です 遅すぎます遅すぎます私たちは救えません 子供の不思議さは正規表現がすべての生きている組織を消費することを確実にします(以前に予言されたようにそれができないHTMLを除いて) 親愛なる主よ、私たちを助けてください。 HTMLをパースするために正規表現を使うことは、人類を恐怖の拷問とセキュリティホールの永遠へと運命づけました。 regeを使うhTMLを処理するためのツールとしてのxは間違いをはっきりさせるこの世界のあいだ そしてc͒ͪo͛ͫrruptエンティティの恐ろしい領域(SGMLエンティティのように、しかし もっと腐敗している)regの世界へhTML用のexパーサは役に立つでしょうpをしっかりと輸送するrogrammerの意識私nto a wオルル絶え間ない叫び声のd、彼は来ます、疫病sl正規表現感染ウィルあなたのHTを貪るlMLパーサー、アプリケーション、そしてVisual Basicのような常に存在 彼は彼が来ますes しないでくださいええとecom̡es、̕h̵isの非常に簡単なradiańcédeすべての列挙型HTMLタグの列挙 ͠fŗ̧͘frfrǫǫfr͢͢͢͢͢͢͢͢͢͢͢li li li li li liuid pain、regegular exp reの歌ssionの構文解析 消滅するmorの声をいじるspからタルマンここで私はそれを見ることができますあなたはそれを見ることができますそれは美しいです彼はfinal snuffing oうそをつくs of ManすべてIS LOŚ͖̩͇̗̪̏̈́T ALL I S LOST th彼がやってくる彼は - そうですes彼cos tまたは浸透する特に私のFACE MY FACE godh god nnONOO̼O O NΘ停止t彼は*───────e̠¼s͎a̧͈͖r̽̾̈́͒͑e n他の人に聞くああË̸̸̶̡̡̡̨̨̯͍̭̯͍̭̯͍̭̪̯̪̯̱̹̭̯̱̹̭̯́̉̉̐ͨ͊̽ͨ͊̽ͧ̾ͬͧ̾ͬ̚͠͠ ̷̨̨̨̨̨̱̹̭̯̱̹̭̯̱̹̭̯̱̹̭̯̙̲̝͖̱̹̭̯̲̖ͧ̾ͬͧ̾ͬͧ̾ͬͧ̾ͬͭ̏ͥͮͧ̾ͬ͊̒ͪͩͬ̚̚͟͜Ȇ̴̟̟͙̞ͩ͌͝S̨̥̫͎̭ͯ̿̔̀ͅ


代わりにXMLパーサーを使ってみましたか?


議長ノート

この投稿は、そのコンテンツへの不適切な編集を防ぐためにロックされています。投稿は見た目どおりに表示されます - コンテンツに問題はありません。私たちの注意のためにそれにフラグを立てないでください。

4421
bobince

任意 HTMLを解析するように正規表現を要求することは、オペレーティングシステムを書くように初心者に求めることに似ていますが、 限定された既知の HTMLのセットを解析することが適切な場合があります。

データを収集してデータベースに格納するHTMLページのセットが少ない場合は、正規表現を使用しても問題ありません。例えば、私は最近、私が議会のウェブサイトを離れたところで、オーストラリア連邦代表の名前、政党、および地区を取得したいと思いました。これは限られた一度限りの仕事でした。

正規表現は私のためにうまく働いた、そしてセットアップするのが非常に速かった。

3091

ここでの欠点は、HTMLは チョムスキータイプ2文法(コンテキストフリー文法) そしてRegExは チョムスキータイプ3文法(通常の文法) であることだと思います。 Type 2の文法はType 3の文法よりも基本的に複雑です( Chomsky階層 を参照)ので、RegExでXMLを解析することは 数学的に不可能です です。

しかし多くの人が試してみるでしょう、ある人は成功を主張するでしょう - しかし他の人がその失敗を見つけてあなたをめちゃくちゃにするまで。

1926
NealB

これらの人たちに耳を傾けないでください。タスクを細かく分割すると、完全に can コンテキストフリーの文法を正規表現で解析できます。これらのそれぞれを順番に実行するスクリプトを使用して正しいパターンを生成できます。

  1. 停止問題を解く.
  2. 円を四角にします。
  3. 巡回セールスマン問題をO(log n)以下で解く。それ以上の場合は、RAMを使い果たし、エンジンがハングします。
  4. パターンはかなり大きくなるので、無作為にランダムデータを圧縮するアルゴリズムがあることを確認してください。
  5. ほとんどの場合 - 全体をゼロで除算するだけです。簡単です。

私は自分自身で最後の部分をまだ終えていない、しかし私は私が親密になっていることを知っている。何らかの理由でCthulhuRlyehWgahnaglFhtagnExceptionsがスローされ続けるので、VB 6に移植してOn Error Resume Nextを使用します。壁に開いたばかりのこの奇妙なドアを調べたら、コードを更新します。うーん。

P.S Pierre de Fermatもその方法を考え出しましたが、彼が書いていたマージンはコードにとって十分な大きさではありませんでした。

1249
Justin Morgan

免責事項 :オプションがある場合はパーサーを使用してください。それは言った...

これは私が(!)を使ってHTMLタグを一致させるための正規表現です。

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

それは完璧ではないかもしれませんが、私はこのコードをHTMLのlotを通して実行しました。それは<a name="badgenerator"">のような奇妙なことさえ捕まえます、そしてそれはウェブ上に現れます。

私はそれが自己完結型のタグと一致しないようにすると思います、あなたはどちらかを使用したいと思うでしょう Kobi の否定的な振り返り:

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

あるいはそうでなければ単に組み合わせる。

ダウン投票者の方へ: これは実際の商品の実動コードです。このページを読んでいる人なら誰でも、HTML上で正規表現を使用することが社会的に許容されるという印象を得るでしょう。

警告 :この正規表現は、CDATAブロック、コメント、およびスクリプト要素とスタイル要素が存在する場合にはまだ機能しないことに注意してください。良いニュースです、あなたは正規表現を使っているものを取り除くことができます...

1044
itsadok

地球が円形であることを伝える人々がいます(あるいは、彼らが奇妙な言葉を使いたいのであれば、地球は扁長回転楕円体です)。彼らは嘘をついています。

正規表現は再帰的であってはいけないと言う人もいます。彼らはあなたを制限しています。彼らはあなたを征服する必要があります、そして彼らはあなたを無知に保つことによってそれをします。

あなたは彼らの現実の中で生きるか、または赤い丸薬を飲むことができます。

Lord Marshal(彼はMarshal .NETクラスの親戚ですか?)のように、私は 裏返し スタックベースのRegex-Verseを返し、 権力 あなたが想像できない知識。はい、私はそれらを保護するOld Oneまたは2つがあったと思いますが、彼らはテレビでサッカーを見ていたので、それは難しくありませんでした。

XMLのケースは非常に単純だと思います。微妙な気持ちで理解しやすくするためにbase64でデフレートおよびコード化された(.NET構文の)RegExは、次のようになります。

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
+r/vD34mUADO1P4/AQAA//8=

設定するオプションはRegexOptions.ExplicitCaptureです。探しているキャプチャグループはELEMENTNAMEです。キャプチャグループERRORが空ではない場合、構文解析エラーが発生し、正規表現は停止しました。

人間が判読できる正規表現に変換するのに問題がある場合は、これが役立ちます。

static string FromBase64(string str)
{
    byte[] byteArray = Convert.FromBase64String(str);

    using (var msIn = new MemoryStream(byteArray))
    using (var msOut = new MemoryStream()) {
        using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {
            ds.CopyTo(msOut);
        }

        return Encoding.UTF8.GetString(msOut.ToArray());
    }
}

あなたが自信がないならば、いいえ、私は冗談ではありません(しかし、おそらく私は嘘をついています)。それが動作します。私はそれをテストするためにたくさんの単体テストを作成しました、そして 適合テスト(の一部)を使用しました 。これはトークナイザーであり、本格的なパーサーではないため、XMLをコンポーネントトークンに分割するだけです。 DTDを解析/統合することはありません。

ああ...あなたがいくつかの補助的な方法で、正規表現のソースコードが欲しいなら:

xmlをトークン化するための正規表現 または 完全な正規表現

476
xanatos

シェルでは、 sed を使用して _ html _ を解析できます。

  1. Turing.sed
  2. HTMLパーサーを書く(宿題)
  3. ???
  4. 利益!

関連する(なぜあなたは正規表現のマッチを使うべきでない):

290
dubiousjim

私は、XMLと特にHTMLを解析するための適切なツールはパーサーであり、正規表現エンジンではないことに同意します。しかし、他の人が指摘したように、正規表現を使用する方が速くて簡単であることがあり、データ形式がわかっていれば仕事を終わらせることができます。

Microsoftは実際に .NET Frameworkの正規表現のためのベストプラクティスのセクションを持っています そして特に/について話しています - 入力ソースについて考えること

正規表現には制限がありますが、次のことを考慮しましたか?

.NETフレームワークは正規表現に関しては Balancing Group Definitions をサポートするという点でユニークです。

そのため、正規表現を使用してXMLを解析することができます。ただし、 は有効なXMLでなければならないことに注意してください ブラウザはHTMLを非常に寛容にし、HTML内で不正なXML構文を許可する)。これは、 "Balancing Group Definition"によって正規表現エンジンがPDAとして機能するようになるためです。

上記の記事1からの引用:

.NET正規表現エンジン

上記のように、適切にバランスのとれた構成は正規表現では記述できません。ただし、.NET正規表現エンジンには、バランスの取れた構成要素を認識できるようにする構成要素がいくつかあります。

  • (?<group>) - キャプチャした結果を名前がgroupのキャプチャスタックにプッシュします。
  • (?<-group>) - 一番上のキャプチャを名前スタックと共にキャプチャスタックからポップします。
  • (?(group)yes|no) - groupという名前のグループが存在する場合はyes部分に一致し、それ以外の場合はどの部分にも一致しません。

これらの構造体は、基本的に単純なバージョンのスタック操作(Push、pop、およびempty)を許可することによって、.NETの正規表現が制限されたPDAをエミュレートすることを可能にします。単純な操作は、それぞれ増分、減分、およびゼロとの比較とほぼ同じです。これにより、.NET正規表現エンジンは文脈自由言語のサブセット、特に単純なカウンターしか必要としない言語を認識することができます。これにより、非伝統的な.NET正規表現が個々の適切にバランスの取れた構成を認識することが可能になります。

次の正規表現を考えてください。

(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
   <!-- .*? -->                  |
   <[^>]*/>                      |
   (?<opentag><(?!/)[^>]*[^/]>)  |
   (?<-opentag></[^>]*[^/]>)     |
   [^<>]*
)*
(?(opentag)(?!))

フラグを使う:

  • 単線
  • IgnorePatternWhitespace(正規表現を折りたたみ、すべての空白を削除する場合は不要です)
  • 無視ケース(必須ではありません)

正規表現の説明(インライン)

(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...
(?>                                        # atomic group / don't backtrack (faster)
   <!-- .*? -->                 |          # match xml / html comment
   <[^>]*/>                     |          # self closing tag
   (?<opentag><(?!/)[^>]*[^/]>) |          # Push opening xml tag
   (?<-opentag></[^>]*[^/]>)    |          # pop closing xml tag
   [^<>]*                                  # something between tags
)*                                         # match as many xml tags as possible
(?(opentag)(?!))                           # ensure no 'opentag' groups are on stack

あなたはこれを試すことができます より良い.NET正規表現テスター

私は以下のサンプルソースを使用しました。

<html>
<body>
<div>
   <br />
   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>
</div>
</body>
</html>

これが一致を見つけました:

   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>

それは実際にはこのように出てきましたが:

<ul id="matchMe" type="square">           <li>stuff...</li>           <li>more stuff</li>           <li>               <div>                    <span>still more</span>                    <ul>                         <li>Another &gt;ul&lt;, oh my!</li>                         <li>...</li>                    </ul>               </div>           </li>        </ul>

最後に、私は本当にJeff Atwoodの記事を楽しんだ: Htmlの解析Cthulhu Way 。十分に面白い、それは現在4k以上の票を持っているこの質問に対する答えを引用しています。

269
Sam

PHPでXMLとHTMLを解析するために QueryPath を使用することをお勧めします。これは基本的にjQueryとほぼ同じ構文ですが、サーバーサイドのみです。

256
John Fiala

HTMLを正規表現で解析できないという答えは正しいのですが、ここでは当てはまりません。 OPは正規表現を使用して1つのHTMLタグを解析したいだけなので、これは正規表現を使用して実行できます。

ただし、推奨されている正規表現は正しくありません。

<([a-z]+) *[^/]*?>

正規表現に何かを追加した場合、それを遡って<a >>のような愚かなものと一致させることを余儀なくされる可能性があります、[^/]は寛容すぎます。 <space>*[^/]*はスペースにも一致する可能性があるため、[^/]*は冗長です。

私の提案は

<([a-z]+)[^>]*(?<!/)>

(?<! ... )は(Perlの正規表現では)否定的な見方です。 「<、次にWord、それから>ではないもの、最後のものは/ではないもの、そしてそれに続く>」と読みます。

これは<a/ >のようなことを可能にすることに注意してください(オリジナルの正規表現と同じように)、もっと制限的なものが欲しい場合は、スペースで区切られた属性ペアにマッチする正規表現を構築する必要があります。

214
moritz

試してください:

<([^\s]+)(\s[^>]*?)?(?<!/)>

それはあなたのものに似ていますが、最後の>はスラッシュの後にあってはいけません、そしてまたh1を受け入れます。

178
Kobi

中国の古代ストラテジスト、将軍、そして哲学者であるSun Tzuは次のように述べています。

あなたがあなたの敵を知っていて、あなた自身を知っていれば、一度も負けずに百戦に勝つことができると言われています。あなたがあなた自身を知っているだけであなたの対戦相手を知らないならば、あなたは勝つか負けるかもしれません。あなた自身も敵も知らないのであれば、あなたはいつも自分自身を危険にさらすでしょう。

この場合、あなたの敵はHTMLであり、あなたはあなた自身か正規表現のどちらかです。あなたは不規則な正規表現を持ったPerlかもしれません。 HTMLを知っている自分を知っています。

HTMLの性質を説明する俳句を作成しました。

HTML has
complexity exceeding
regular language.

私はPerlで正規表現の性質を説明する俳句も作成しました。

The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
169
cytinus
<?php
$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed');

$html = '
<p><a href="#">foo</a></p>
<hr/>
<br/>
<div>name</div>';

$dom = new DOMDocument();
$dom->loadHTML($html);
$els = $dom->getElementsByTagName('*');
foreach ( $els as $el ) {
    $nodeName = strtolower($el->nodeName);
    if ( !in_array( $nodeName, $selfClosing ) ) {
        var_dump( $nodeName );
    }
}

出力:

string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"

基本的には、自己閉鎖的な要素ノード名を定義し、html文字列全体をDOMライブラリにロードし、すべての要素を取得し、自己閉鎖的でないものをループスルーして除外し、それらを操作します。

私はあなたがすでにこの目的のために正規表現を使うべきでないことをすでに知っていると確信しています。

154
meder omuraliev

あなたがこれを正確に必要としているかどうかはわかりませんが、.NETも使用しているのであれば、 Html Agility Pack を使用できませんでしたか。

抜粋:

"out of the web" HTMLファイルを解析できる.NETコードライブラリです。このパーサーは、「実世界の」不正なHTMLに対して非常に寛容です。

148
GONeale

最初の>の前に/を付けないでください。その方法についての詳細は here を見てください。これはネガティブルックビハインドと呼ばれています。

ただし、この単純な実装では、このサンプルドキュメントの<bar/></foo>と一致します。

<foo><bar/></foo>

解決しようとしている問題についてもう少し詳しく教えてください。プログラム的にタグを反復処理していますか?

135
Jherico

W3Cは疑似正規表現形式で構文解析を説明します。
W3Cリンク

より明確な図を得るために、QNameS、およびAttributeのvarリンクをたどってください。
それに基づいて、タグを削除するようなことを処理するためにかなり良い正規表現を作成することができます。

122

あなたがPHPのためにこれを必要とするならば:

PHP DOMfunctions は、適切にフォーマットされたXMLでないと正しく動作しません。それらの使用が他の人類にとってどれほど優れていても関係ありません。

simplehtmldom は良いのですが、ちょっとバグが多いことに気付きました、そしてそれはかなりのメモリを消費しています。

querypath を使ったことがないので、その有用性についてコメントすることはできません。

もう一つ試してみるのが私の DOMParser です。これはリソースが非常に少なく、私はしばらくの間楽しく使用しています。学ぶのが簡単&パワフル。

PythonとJavaでは、同様のリンクが投稿されています。

悪意のある人のために - 私はXMLパーサーが実際の使用に耐えることができないことが証明されたときに私のクラスを書いただけです。宗教的な投票は、有用な回答が投稿されるのを防ぎます - 質問の観点から物事を守ってください。

104
SamGoody

これが解決策です。

<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/';

// a string to parse:
$string = 'Hello, try clicking <a href="#paragraph">here</a>
    <br/>and check out.<hr />
    <h2>title</h2>
    <a name ="paragraph" rel= "I\'m an anchor"></a>
    Fine, <span title=\'highlight the "punch"\'>thanks<span>.
    <div class = "clear"></div>
    <br>';

// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);

// print the result:
print_r($matches[0]);
?>

それを深くテストするために、私は次のように自動クローズタグの文字列を入力しました。

  1. <hr />
  2. <br/> <br/>
  3. <br>

私もタグを入力しました:

  1. 1つの属性
  2. 複数の属性
  3. 値が 一重引用符 または 二重引用符 にバインドされている属性
  4. 区切り文字が二重引用符の場合は一重引用符を含む属性、その逆の場合も同様です。
  5. "unpretty"属性は "="記号の前、後と前後の両方に空白を含みます。

上記の概念実証で機能しないものを見つけた場合は、コードを分析してスキルを向上させることができます。

<EDIT> ユーザーからの質問が自己終了タグの解析を避けることであることを忘れていました。この場合、パターンはより単純になり、次のようになります。

$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';

ユーザー@ridgerunnerは、パターンが 引用符で囲まれていない属性 または値を持たない 属性を許可しないことに気付きました 。この場合、微調整すると次のようなパターンになります。

$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';

</EDIT>

パターンを理解する

誰かがパターンについてもっと知りたいのであれば、私はいくつかの行を提供します:

  1. 最初の部分式(\ w +)はタグ名と一致します
  2. 2番目の部分式は属性のパターンを含みます。それは構成されています:
    1. 1つ以上の空白文字
    2. 属性の名前(\ w +)
    3. ゼロ個以上の空白\ s *(可能かどうかは、ここに空白を残して)
    4. "="記号
    5. 繰り返しますが、ゼロ個以上の空白
    6. 属性値の区切り文字、単一引用符または二重引用符( '| ")。パターン内では、単一引用符はPHPストリング区切り文字と一致するためエスケープされます。括弧で囲まれているため、属性のクロージャを解析するために再度参照することができます。そのため、これが非常に重要です。
    7. にほぼ一致する属性の値。ほぼ 何でも:(。*?);この特定の構文では、 greedy match (アスタリスクの後の疑問符)を使用して、RegExpエンジンは "先読み"のような演算子を有効にします。
    8. \ 4の部分は 後方参照演算子 で、パターンの前に定義されている部分式を参照します。この場合、最初の属性である4番目の部分式を参照しています。区切り文字が見つかりました
    9. 0個以上の空白文字
    10. 属性部分式は、アスタリスクで指定された、0個以上の出現可能性を指定してここで終わります。
  3. その場合、タグは ">"記号の前の空白で終わる可能性があるため、0個以上の空白が\ s *サブパターンと一致します。
  4. 一致させるタグは、単純な ">"記号、またはその前にスラッシュを使用する可能性のあるXHTMLクロージャー(/> |>)で終わることがあります。スラッシュは、正規表現の区切り文字と一致するため、もちろんエスケープされます。

ちょっとしたコツ:このコードをよりよく分析するには、エスケープするHTML特殊文字を提供していないので、生成されたソースコードを調べる必要があります。

91

HTML文書から何かを素早く抽出する必要があるときはいつでも、Tidyを使用してそれをXMLに変換し、次にXPathまたはXSLTを使用して必要なものを入手します。あなたの場合は、次のようになります。

//p/a[@href='foo']
89
Sembiance

以前は HTMLParser というオープンソースのツールを使用していました。 HTMLをさまざまな方法で解析するように設計されており、その目的には非常に役立ちます。 HTMLを別のツリーノードとして解析することができ、そのAPIを使用してノードから属性を取得することができます。それをチェックして、これがあなたを助けることができるかどうか確かめてください。

89
wen

私は正規表現でHTMLをパースするのが好きです。私は故意に壊れたばかげたHTMLを解析しようとはしません。このコードは私のメインパーサです(Perl版):

$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g;
s/^ ?\n//s; s/ $//s; print

これはhtmlsplitと呼ばれ、HTMLを行に分割します。各行に1つのタグまたはテキストの塊があります。その行は、 grepsed 、Perlなどの他のテキストツールやスクリプトでさらに処理できます。冗談ではありません。

巨大なWebページを処理したいのであれば、私のSlurp-everything-first PerlスクリプトをNiceストリーミングのものに変更するのは簡単です。しかし、それは必ずしも必要ではありません。

私はこれについては控えめになるでしょう。

HTML分割


私の期待に反してこれはいくつかの支持を得たので、私はいくつかのより良い正規表現を提案します:

/(<.*?>|[^<]+)\s*/g    # get tags and text
/(\w+)="(.*?)"/g       # get attibutes

それらはXML/XHTMLに適しています。

多少のバリエーションがありますが、面倒なHTMLに対処することも、HTMLからXHTMLに変換することもできます。


正規表現を書くための最善の方法は、 Lex / Yacc スタイルで、不透明な一行またはコメント付きの複数行の怪物ではありません。私はまだここでそれをしませんでした。これらのものはほとんどそれを必要としません。

83
Sam Watkins

これは PHPベースのパーサ です。このプロジェクトの作者として、正規表現を使用してHTMLを解析することは可能ですが、効率的ではありません。サーバーサイドのソリューションが必要な場合(私の wp-Typography WordPressプラグインの場合 )、これは機能します。

73
kingjeffrey

HTMLをBBCodeに置き換えるためのいくつかのNice正規表現があります ここ 。ご存知のとおり、彼はHTMLを完全に解析するのではなく、単にサニタイズすることを試みていないことに注意してください。彼はおそらく、彼の単純な「パーサー」が理解できないタグを削除する余裕があります。

例えば:

$store =~ s/http:/http:\/\//gi;
$store =~ s/https:/https:\/\//gi;
$baseurl = $store;

if (!$query->param("ascii")) {
    $html =~ s/\s\s+/\n/gi;
    $html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;
}

$html =~ s/\n//gi;
$html =~ s/\r\r//gi;
$html =~ s/$baseurl//gi;
$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi;
$html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi;
$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi;
$html =~ s/<ul(.*?)>/\[list]/gi;
$html =~ s/<\/ul>/\[\/list]/gi;
$html =~ s/<div>/\n/gi;
$html =~ s/<\/div>/\n/gi;
$html =~ s/<td(.*?)>/ /gi;
$html =~ s/<tr(.*?)>/\n/gi;

$html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi;

$html =~ s/<head>(.*?)<\/head>//sgmi;
$html =~ s/<object>(.*?)<\/object>//sgmi;
$html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;
$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi;
$html =~ s/<title>(.*?)<\/title>//sgmi;
$html =~ s/<!--(.*?)-->/\n/sgmi;

$html =~ s/\/\//\//gi;
$html =~ s/http:\//http:\/\//gi;
$html =~ s/https:\//https:\/\//gi;

$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi;
$html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi;
$html =~ s/\[url=\//\[url=/gi;
69
sblom

(x)HTMLを解析するRegExpメソッドの質問について、いくつかの制限について話したすべての人への答えは、この強力な武器の力を支配するのに十分な訓練を受けていないからですNOBODYここでrecursionについて話しました。

RegExpにとらわれない同僚がこの議論を私に通知しましたが、これは確かにこの古くてホットなトピックに関するウェブ上での最初のものではありません。

いくつかの投稿を読んだ後、私が最初にしたことは、このスレッドで「?R」文字列を探すことでした。 2番目は、「再帰」について検索することでした。
いいえ、聖なる牛、一致が見つかりません。
パーサーが組み込まれている主要なメカニズムについて誰も言及していないので、私はすぐに誰もポイントを獲得していないことに気付きました。

(x)HTMLパーサーが再帰を必要とする場合、再帰のないRegExpパーサーでは目的には不十分です。シンプルな構造です。

RegExpのブラックアートを習得するのは難しいので、ウェブ全体をキャプチャするための個人的なソリューションを試してテストする際に、除外した可能性があるかもしれません片手...まあ、私はそれについて確信しています:)

魔法のパターンは次のとおりです。

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

やってみなよ。
PHP文字列として記述されているため、 "s"修飾子はクラスに改行を含めます。
これは、1月に書いたPHPマニュアルのサンプルノートですReference

(注意してください、私は誤って "m"修飾子を使用しました; ^または$アンカーが使用されていないため、RegExpエンジンによって破棄されますが、削除する必要があります)。

ここで、より情報に基づいた観点からこのメソッドの制限について話すことができます。

  1. regExpエンジンの特定の実装によると、再帰では、解析されるネストされたパターンの数の数に制限がある場合がありますが、使用する言語によって異なります
  2. 破損した(x)HTMLは重大なエラーにはなりませんが、sanitizedではありません。

とにかくRegExpパターンにすぎませんが、多くの強力な実装の開発の可能性を明らかにしています。
フレームワークで作成したテンプレートエンジンの再帰降下パーサーを強化するためにこのパターンを作成しました。実行時間またはメモリの両方でパフォーマンスが非常に優れています使用法(同じ構文を使用する他のテンプレートエンジンとは関係ありません)。

67

すでに指摘したように、HTMLは通常の言語ではないため解析が非常に困難です。これに対する私の解決策は、きちんとしたプログラムを使用してそれを通常の言語に変換し、次にXMLパーサーを使用して結果を消費することです。これには良い選択肢がたくさんあります。私のプログラムは、 jtidy ライブラリを使ってJavaを使ってHTMLをXMLに変換し、次にJaxenを使って結果をxpathに変換します。

61
Corey Sanders
<\s*(\w+)[^/>]*>

部品は説明した:

<:開始文字

\s*:タグ名の前に空白があるかもしれません(醜いが可能です)。

(\w+):タグには文字と数字を含めることができます(h1)。 \wも '_'と一致しますが、それは私が推測していることを傷つけません。興味があれば代わりに([a-zA-Z0-9] +)を使ってください。

[^/>]*>を閉じるまでの/>以外のもの

>:閉じる>

無関係

そして、彼らが通常の言語と同じくらい強力であると言って正規表現を過小評価する仲間に:

あるnbanban これは規則的ではなく、文脈自由でさえもなく、^(a+)b\1b\1$とマッチさせることができます

逆参照 _ ftw _

59
daghan

最後に "/"を付けずにタグを一致させようとしているようです。これを試して:

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
52
manixrock

あなたが単にそれらのタグを(構文解析の野心なしに)見つけようとしているなら、この正規表現を試してください:

/<[^/]*?>/g

私は30秒でそれを書いて、そしてここでテストしました: http://gskinner.com/RegExr/ /

無視したいと言ったタイプを無視しながら、それはあなたが言及したタグのタイプと一致します。

51
Lonnie Best

特に正確性が最優先される場合(例えば、処理がセキュリティに影響を与える可能性がある場合)は、プログラミング時にHTMLを扱うときに正規表現の代わりに専用のパーサーとAPIを使用するのが最善です。ただし、XMLスタイルのマークアップは正規表現で処理しないでくださいという教義的な見方をしていません。テキストエディタで一度だけ編集を行ったり、壊れたXMLファイルを修正したり、まったくXMLではないように見えるファイル形式を扱う場合など、正規表現がその作業に最適なツールとなる場合があります。注意すべき点がいくつかありますが、それは克服できないわけでもなく、必ずしも関連性があるわけでもありません。

<([^>"']|"[^"]*"|'[^']*')*>のような単純な正規表現は、私が今言及したような場合には、通常は十分です。これは単純な解決策であり、すべて考慮しますが、属性値にエンコードされていない>シンボルを正しく使用できます。例えばtableタグを探しているなら、</?table\b([^>"']|"[^"]*"|'[^']*')*>としてそれを適応させることができます。

より高度なHTML正規表現がどのように見えるかを理解するために、実際のブラウザの動作とHTML 5の解析アルゴリズムをエミュレートするというかなり立派な仕事をします。

</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)

以下は、XMLタグのかなり厳密な定義に一致します(ただし、XML名で許可されているUnicode文字のフルセットは考慮されていません)。

<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>

確かに、これらは周囲の状況やいくつかのEdgeのケースを考慮に入れていませんが、あなたが本当に望んでいればそのようなことでさえ対処することができます(例えば、他の正規表現のマッチ間を検索することによって)。

そのツールが正規表現になっている場合でも、1日の終わりには、そのジョブに最も適したツールを使用してください。

50
slevithan

そのために正規表現を使用するのは適切で効果的ではありませんが、正規表現を使用すると簡単な一致の問題を簡単に解決できる場合があります。

Steven Levithanによって書かれた最も内側のHTML要素のマッチングについての 決定的なブログ投稿 があります。

48
Emre Yazici

タグ名だけが欲しいのであれば、regexを使ってこれを行うことが可能です。

<([a-zA-Z]+)(?:[^>]*[^/] *)?> 

あなたが必要とすることをするべきです。しかし、私は "moritz"の解決策はすでにうまくいっていると思います。初めは見えなかった。

すべての有権者にとって:正規表現を使うのが理にかなっている場合もあるでしょう。なぜならそれは最も簡単で最速の解決策であるからです。私は一般的にあなたが正規表現でHTMLを解析するべきではないことに同意します。しかし、正規表現は、フォーマットがわかっていて、いくつかの値を抽出したいだけのHTMLのサブセットがある場合には非常に強力なツールになります。私は何百回もそれをやりました、そして、ほとんどいつも私が欲しかったものを達成しました。

41
morja

OPは彼がタグをどうする必要があるかを言っているようには見えません。たとえば、彼は内部のテキストを抽出する必要があるか、単にタグを調べる必要がありますか?

私は、RegExが万能のテキストパーサーではないと言っていることをしっかりと理解しています。 HTMLタグを解析するためのこのコード を含む、大量のテキスト解析コードを書きました。

私がRegExを使ってもそれほど素晴らしいことではないというのは事実ですが、正規表現は厳密すぎて、この種の構文解析を維持するのは難しいと思います。

39
Jonathan Wood

これはするかもしれません:

<.*?[^/]>

または終了タグなしで:

<[^/].*?[^/]>

HTMLパーサの炎上戦争はどうなっていますか? HTMLパーサーはそれがあなたの検索を分類することができる前に文書全体を解析(そして再構築!)しなければなりません。特定の状況では、正規表現の方が速くてエレガントです。私の2セント...

34
Paul

これでうまくいくと思います

<[a-z][^<>]*(?:(?:[^/]\s*)|(?:\s*[^/]))>

そしてそれはテストすることができます ここ


のように w3schools ...

XML命名規則

XML要素は、次の命名規則に従う必要があります。

  • 名前には文字、数字、その他の文字を含めることができます
  • 名前を数字または句読文字で始めることはできません
  • 名前は文字xml(またはXML、またはXmlなど)で始めることはできません。
  • 名前にスペースを含めることはできません
  • 任意の名前を使用でき、単語は予約されていません。

そして私が使ったパターンはこれらの規則を守ることです。

20
Cylian