MENU

JavaScript正規表現文字列操作

【JavaScript】 入れ子のhtmlタグを正規表現で取得する

更新日:2021/10/11

 

ブラウザなら入れ子となったタグをDOMで簡単に取得できます。
しかしNode.jsなどのブラウザ以外のJavaScriptや、わざわざDOMに展開するまでもない場合などは、正規表現などでテキストのまま処理したいケースがあります。

 

今回は、その方法を考えてみました。

 

入れ子を考えない場合

 

入れ子を考えずにhtmlのタグを取得する場合、次のような正規表現を使ったコードが考えられます。

 

タグを取得する単純な正規表現

 

IDで取得


const id = "abc";
const regex = new RegExp( `<div(?:\\s+?|\\s+?.+?\\s+?)id="${ id }".*?>.*?<\\/div.*?>`,"s");
console.log( regex.exec( htmlText ) );

 

classで取得


const className = "abc";
const regex = new RegExp( `<div(?:\\s+?|\\s+?.+?\\s+?)class=(?:"|".*?\\s+?)${ 
        className }(?:"|\\s+?.*?").*?>.*?<\\/div.*?>` , "sg");
let result;
while( (result = regex.exec( htmlText )) !== null ){
    console.log( result );
}

 

 

idはhtml中に一つ、classは複数存在するという前提で、二つのコードを作成しています。

 

両方に共通する最初の括弧、(?:\s+?|\s+?.+?\s+?)はスペースのみ、またはスペース+何らかの文字列+スペースに対応します。最初の?:は、内容をキャプチャしないという意味です。

 

クラス名のマッチングは、前後に他のクラス名が入ることを想定しています。
IDの前後にスペースが入る可能性がありますが、レアケースなのでここでは対応していません。

 

閉じタグの\s*?は、スペースが挿入されているケースを想定しています。

 

なお上のコードは、文字列をRegExpのコンストラクタに渡して、正規表現オブジェクトを作成しています。
そのためエスケープ文字(\)を二つ重ねています。

 

次のように正規表現リテラルで作成する場合は、重ねる必要がありません。

 

const regex = /<div(?:\s+?|\s+?.+?\s+?)id="abc".*?>.*?<\/div.*?>/s;

 

ただしリテラルではid名等を変更できないので、両方を適切に使い分ける必要があります。

 

ちなみに最近の僕は物忘れが激しいのか、3回に一回は間違えて、原因にたどり着くまで10分くらいかかります(汗

 

入れ子を考慮する

 

前述のコードは、次のような入れ子に対応できません。

 

<div id="abc">
  <div></div>
</div>

 

なぜなら、最初の閉じタグとマッチしてしまうからです。

 

<div id="abc">
  <div></div> ← これとマッチ
</div>

 

もしかしたら正規表現のみで実現できるかもしれませんが、僕には無理だったので、単純なコードで取得してみます。

 

入れ子タグを取得するコード

 


const getNestedTag =  (tagName,matchData) =>{

    if( matchData === null ) return null;
    const buff = [ matchData[0] ];

    let restData = matchData.input.substring( matchData.index + matchData[0].length );
    const regex = new RegExp(`<(\\/?)${tagName}.*?>`);
    let count=0;
    while(1){
        const match = restData.match( regex );
        if( match === null ) return null;
        const index = match.index + match[0].length;
        buff.push( restData.substring( 0 , index ) );

        if( match[1] === "/" ){
            if( count === 0 ) break;
            count --;
        }else{ count ++; }
        restData = restData.substring( index );
    }
    return  buff.join("");
};

 

考え方は単純です。

 

後に続くタグが <div>だったら、カウントを+1、</div>だったら-1します。
カウントが0のとき</div>が現れたら、それが閉じタグです。

 

この関数は、タグ名( "div"や"p" )と、スタートとなるタグの検索結果を引数として受け取ります。
次のように使用します。

 

使用例

 

IDで取得


const id = "abc";
const result = new RegExp( `<div(?:\\s+?|\\s+?.+?\\s+?)id="${ id }".*?>`,"s")
                .exec( htmlText );

console.log( getNestedTag("div" , result ))

 

classで取得


const className = "abc";
const regex = new RegExp( `<div(?:\\s+?|\\s+?.+?\\s+?)class=(?:"|".*?\\s+?)${
    className }(?:"|\\s+?.*?").*?>` , "sg");

let result;
while( (result = regex.exec( data )) !== null ){
    console.log( getNestedTag("div" , result ) );
}

けーちゃんおススメJavaScript入門書

  • スラスラ読める JavaScript ふりがなプログラミング
  • プログラム未経験者がJavaScript始めるならコレ!
    コードを掲載して自分で理解しろという投げっぱなしな入門書とは異なり、コードに一つ一つどんなことをやっているかをふりがなという形式で解説しています。
    それでいてJavaScriptの基礎と応用を学べる良書です。
  • これからWebをはじめる人のHTML&CSS、JavaScriptのきほんのきほん
  • JavaScriptの機能を実践で活かすにはHTMLやCSSの知識が不可欠です。
    しかしそれらの知識があることが前提として書かれている書籍が多い中、この本は総合的な知識を身に着けることができます。
    HTMLやCSSの知識も不安な方には、ぴったりの一冊です
  •  

    入門書の役割は、自分のやりたいことをネットで調べることができるようになるための、基礎的な知識の獲得です。
    まずはこれらの本でしっかりと基礎知識を身につけましょう。
    そしてもっと高度なことや専門的なことはネットで調べ、情報が足りないと感じたら書籍を購入してください。


    記事の内容について

     

    こんにちはけーちゃんです。
    説明するのって難しいですね。


    「なんか言ってることおかしくない?」
    たぶん、こんなご意見あると思います。

    裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。

    そんなときは、ご意見もらえたら嬉しいです。

    ご意見はこちら。
    https://note.affi-sapo-sv.com/info.php

    【お願い】

    お願い

    ■このページのURL


    ■このページのタイトル


    ■リンクタグ


    ※リンクして頂いた方でご希望者には貴サイトの紹介記事を作成してリンクを設置します。
    サイト上部の問い合わせよりご連絡ください。
    ただしサイトのジャンルによっては、お断りさせていただくことがあります。