@lalaで尋ねた正規表現置換<*>はどのように使用できますか?つかむことについてURLからHTML 。 提案された3つの回答についてもう少し詳しく説明します。 まず、免責事項:これらの例はいずれもHTMLを適切に解析しないため、実際には存在しないURLを見つけて、実際に存在する他のURLを見逃す可能性があります。
/*
THIS IS NOT AN HTML PARSER
HTML parsers are hard to write. There are better ways.
Neither of these JSL ideas can properly skip comments or
many other HTML tags that should be skipped. They will
find things they shouldn't and miss other things they
should find. They've only been minimally tested on one site.
This code is presented as "how can I use contains() vs patmatch()
to efficiently work through a large string looking for something?"
The second example is FIVE TIMES FASTER and a LITTLE MORE ACCURATE.
The third example uses text explorer to grab links. I don't think
there is a pre-existing regex for the link text descriptions. It works
very similar to this code; it is NOT an HTML parser either.
If you can use python, you might want to investigate "beautiful soup".
I've not used it, but believe it addresses the "hard to write" issue.
*/
HTMLを解析する際の問題は、主に、一致する開始タグと終了タグを見つけて、それらの間のデータを調べることです。 ここに示されているすべてのJSLは、HTML機能によってトリップされる可能性があります。 しかし、それで十分かもしれません。独自のテストを行います。 も参照してください美しいスープこれはより良い答えかもしれません。
これは、ページに表示されるURL(href =部分)と少しのテキスト(ディスカッション部分)を含むリンク(タグ)を示すHTMLWebページからの抜粋です。
< a > tag with URL and Text<>>URLとテキストのタグ
目標は、URLとリンクテキストのデータテーブルを1行に1つずつ作成することです。 一般的なWebページには、100程度のリンクがあり、0.5メガバイトのテキストである場合があります。 1スロー問題を攻撃する方法(図には示されていません)は、一度に1つのリンクを見つける単純な正規表現を作成し、テキストからリンクを削除して、それ以上見つからなくなるまで繰り返すことです。 1つか2つしかない場合は、すばらしいです。 しかし、100が存在する可能性がある場合は、数百メガバイトのデータが操作および検索されます。
より高速な方法は、データを検索し、常に前進し、リンクが見つかったときにデータテーブルを作成することです。 最初の例では、開始位置を指定するための3番目の引数を持つcontains()関数を使用しています。 contains()は、見つかった位置も返します。 開始位置を検出された位置のすぐ先に更新することにより、contains()は、テキストをバックアップまたは変更することなく、テキストを効率的に調べることができます。
u = "https://community.jmp.com/t5/Discussions/bd-p/discussions";
txt = Load Text File( u );
// a link on a page has at least two parts:
// the URL and some descriptive text.
// <A ...="" href="https://community.jmp.com/url" ...="" target="_blank" rel="nofollow noopener noreferrer"> descripton </A>
// p1 p4 p5
// using contains and regex. "<A>", p1 + 3 ); // find the end of the opening tag
p5 = Contains( txt, "</A>", p4 + 1 ); // find the ending tag
if( p5 > p4, // as long as the end is not zero, we found one, see break() below
desc = Substr( txt, p4 + 1, p5 - p4 - 1 ); // the visible link description. Images can be here too.
desc = Regex( desc, "<[^>]*>", "", globalreplace ); // remove span, image, etc tags
linktext = Substr( txt, p1, p4 - p1 + 1 ); // <A href="https://community.jmp.com/" target="_blank">
hreftext = Regex( linktext, "href\s*=\s*(\!"|')(.*?)(\1)", "\2" );// use regex to get the href value
if( !Is Missing( hreftext ), // sometimes there isn't one
dt << addrows( 1 );
irow = N Rows( dt );
dt:link[irow] = hreftext;
dt:description[irow] = desc;
);
, // else
Break() // no more end tags. there is maybe a mess of javascript.
);
pos = Max( pos, p5 + 3 ); // advance past the one just found
);
stoptime = Tick Seconds();
dt << endDataUpdate;
Show( stoptime - starttime ); </A>
そのコードの中心は行です
while( (p1 = Contains( txt, "<A></A>
これは一連のジョブを実行しています。3番目の引数でposを使用して前の作業をスキップし、リンクタグを開始する次の何も見つかりません、ループが停止します。 次の行
p4 = Contains( txt, ">", p1 + 3 );
を検索します。 p4(コメントを参照)はそこを指しています。 p1 + 3から開始することにより、contains()はp1の後に1つを検索し、以前のオカレンスを再検索しません。 次の行も同様で、p4の後に終了タグを探します
p5 = Contains( txt, "", p4 + 1 );
p5は、リンクテキストを終了するタグを指します。 すべてではありませんが、多くのHTMLタグはそのように機能し、/のない開始タグとスラッシュのある終了タグがあります。 を使用して、開始タグと終了タグが同じ場合もありますが、このJSLはその可能性を無視しています。
ifステートメントは、何かが見つかったかどうかを判断し、見つかった場合は、次のような説明を取得します
desc = Substr( txt, p4 + 1, p5 - p4 - 1 );
p4とp5は>と<を指しています。したがって、p4 + 1は最初の説明文字であり、p5-p4-1は説明の長さです。 次の行は、埋め込まれたタグを削除して説明をクリーンアップします
desc = Regex( desc, "<[^>]*>", "", globalreplace );
GlobalReplaceは、正規表現がパターンを検出し、そのテキストを可能な限り何も置き換えないことを意味します。 パターンは<に一致し、その後に>以外の0個以上の文字が続き、その後にタグまたは一連のテキストスタイリングタグである可能性のある>が続きます。 最終結果は、picture / color / font/etcを含まないタグのテキストです。
次の2行は、説明ではなくURLを取得するために類似しています。
linktext = Substr( txt, p1, p4 - p1 + 1 ); // <A href="https://community.jmp.com/" target="_blank">
hreftext = Regex( linktext, "href\s*=\s*(\!"|')(.*?)(\1)", "\2" );// use regex to get the href value</A>
p1とp4(コメントを参照)は、リンクタグ全体の位置です。 linktextは、いくつかの不要なビットを含むリンクタグです。 hreftextは、regex()を使用して、href引用符またはアポストロフィの間のURLのみを抽出します。 正規表現パターンは、backref \ 1を使用して、末尾のアポストロフィまたは引用符が先頭のものと一致することを確認します。 正規表現パターンの最初の開いた親は、「または」に一致するキャプチャグループ(\ 1の場合)を作成します。 2番目のオープンパレンは、一致するキャプチャグループ(\ 2の場合)であり、区切り文字間で一致するために必要な限り、しぶしぶ前進します。 3番目のオープンパレンはキャプチャグループ3です。パレンとグループ化は実際には必要ありません。 結果の\2は単なるURLです。
完了以外のすべて:説明がある場合は、テーブルに行を追加してから、非常に重要なこととして、pos変数を進めます。
pos = Max( pos, p5 + 3 );
Maxは、最後に逆方向に移動してループに陥らないようにします。
より速い方法
上記のコードはかなり高速ですが、これはさらに優れています。
u = "https://community.jmp.com/t5/Discussions/bd-p/discussions";
txt = Load Text File( u );
// using pattern matching and regex
// this is about 5X faster and skips over the garbage in the <SCRIPT> sections
// two patterns, one for &amp;amp;amp;lt;a&amp;amp;amp;gt; links and one for &amp;amp;amp;lt;script&amp;amp;amp;gt; to skip over
linkpat = "&amp;amp;amp;lt;a " + Pat Break( "&amp;amp;amp;gt;" ) &amp;amp;amp;gt;&amp;amp;amp;gt; linktext + "&amp;amp;amp;gt;" + Pat Arb() &amp;amp;amp;gt;&amp;amp;amp;gt; desctext + "&amp;amp;amp;lt;/a&amp;amp;amp;gt;";
scriptpat = "&amp;amp;amp;lt;script" + Pat Break( "&amp;amp;amp;gt;" ) + "&amp;amp;amp;gt;" + Pat Arb() + "</SCRIPT>";
dt = New Table( "patmatch", New Column( "description", Character, "Nominal" ), New Column( "link", Character, "Nominal" ) );
Wait( .1 );
dt << beginDataUpdate;
startTime = Tick Seconds();
rc = patmatch( txt,
patrepeat( // repeat the pattern until no more
(linkpat + pattest(// run some JSL for the linkpat...
desc = Regex( desctext, "<[^>]*>", "", globalreplace ); // remove span, etc tags
hreftext = Regex( linktext, "href\s*=\s*(\!"|')(.*?)(\1)", "\2" );
if( !Is Missing( hreftext ),
dt << addrows( 1 );
irow = N Rows( dt );
dt:link[irow] = hreftext;
dt:description[irow] = desc;
);
1; // pattest needs this to succeed
)) | scriptpat /* maybe skip a script */ | /* maybe skip to the next tag */ (Pat Len( 1 ) + Pat Break( "<" ))
),
NULL,IGNORECASE
);
stoptime = Tick Seconds();
dt << endDataUpdate;
Show( stoptime - starttime );
Show( rc ); // should be 1, not 0今回は、ループはパターンマッチャーの内側にあります(patrepeat()を使用してパターンを文字列にプッシュします)。2つのパターン
linkpat = "<A>" ) >> linktext + ">" + Pat Arb() >> desctext + "</A>";
scriptpat = "<SCRIPT>" ) + "&amp;amp;amp;gt;" + Pat Arb() + "</SCRIPT>";
linkpatは一部のテキストと一致し、一致の一部をlinktextとdesctextに格納します。 Scriptpatは、の間のテキストと一致し、それを無視します。 HTMLのタグは、無視する必要のあるJavaScriptコードセクションを定義します。 この例の場合、JavaScriptには誤検知を行う&amp;amp;amp;lt;シーケンスが多数含まれており、JavaScriptをスキップするとそれらが削除されます。 これについては、すぐに詳しく説明します。 ループは、開始するpatmatch()によって実行されます。&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;PRE&amp;amp;amp;gt;&amp;amp;amp;lt;CODE class=" language-jsl"&amp;amp;amp;gt;rc = patmatch( txt,&amp;amp;amp;lt;/CODE&amp;amp;amp;gt;&amp;amp;amp;lt;/PRE&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;最後に、show(rc)はpatmatchが成功したことを確認します。 しかし、最初に、patmatchの2番目の引数はパターンです&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;PRE&amp;amp;amp;gt;&amp;amp;amp;lt;CODE class=" language-jsl"&amp;amp;amp;gt; patrepeat( // repeat the pattern until no more
(linkpat + pattest(// run some JSL for the linkpat...
desc = Regex( desctext, "&amp;amp;amp;lt;[^&amp;amp;amp;gt;]*&amp;amp;amp;gt;", "", globalreplace ); // remove span, etc tags
hreftext = Regex( linktext, "href\s*=\s*(\!"|')(.*?)(\1)", "\2" );//
if( !Is Missing( hreftext ), //
dt &amp;amp;amp;lt;&amp;amp;amp;lt; addrows( 1 );//
irow = N Rows( dt );//
dt:link[irow] = hreftext;//
dt:description[irow] = desc; //
);//
1; // pattest needs this to succeed
)) | scriptpat /* maybe skip a script */ | /* maybe skip to the next tag */ (Pat Len( 1 ) + Pat Break( "&amp;amp;amp;lt;" ))
),&amp;amp;amp;lt;/CODE&amp;amp;amp;gt;&amp;amp;amp;lt;/PRE&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;これは3つの部分に簡略化できます&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;PRE&amp;amp;amp;gt;&amp;amp;amp;lt;CODE class=" language-jsl"&amp;amp;amp;gt;patrepeat(
linkpat // part 1
|
scriptpat // part 2
|
(Pat Len( 1 ) + Pat Break( "&amp;amp;amp;lt;" )) // part 3
)&amp;amp;amp;lt;/CODE&amp;amp;amp;gt;&amp;amp;amp;lt;/PRE&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;これは、これら3つのパターンのいずれかが途中の各ステップでテキストと一致することを意味します。 まず、linkpatが一致するかどうかを確認します。これにより、簡略化された作業の一部が実行されます。 そうでない場合は、scriptpatが一致するかどうかを確認してください。一部のJavaScriptをスキップします。 それ以外の場合は、1文字(通常は&amp;amp;amp;lt;)に加えて、次の&amp;amp;amp;lt;に到達するのに十分な文字をスキップします。 次に、さらにループします。&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;簡略化された部分は、linkpatが成功したときに実行されるJSLです(linkpatは、一部のテキストをlinktextとdesctextに一致するため、それらに保存することを忘れないでください)&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;PRE&amp;amp;amp;gt;&amp;amp;amp;lt;CODE class=" language-jsl"&amp;amp;amp;gt;linkpat + pattest(
desc = Regex( desctext, "&amp;amp;amp;lt;[^&amp;amp;amp;gt;]*&amp;amp;amp;gt;", "", globalreplace );
hreftext = Regex( linktext, "href\s*=\s*(\!"|')(.*?)(\1)", "\2" );
if( !Is Missing( hreftext ),
dt &amp;amp;amp;lt;&amp;amp;amp;lt; addrows( 1 );
irow = N Rows( dt );
dt:link[irow] = hreftext;
dt:description[irow] = desc;
);
1; // pattest needs this to succeed
)&amp;amp;amp;lt;/CODE&amp;amp;amp;gt;&amp;amp;amp;lt;/PRE&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;これは、データテーブルに行を書き込む最初の例のJSLと非常によく似ているはずです。 pattest()は、一致が成功した場合は1を返し、失敗した場合は0を返す特別なパターンマッチング関数です。 ここで、pattestはいくつかのJSLを実行するために使用され、マッチャーが前進できるように常に1を返します。 それが実行するJSLは実際にはそうではありません&amp;amp;amp;lt;EM&amp;amp;amp;gt;テスト&amp;amp;amp;lt;/EM&amp;amp;amp;gt;マッチャーがバックアップして再試行する必要がある場合。 JSLはテーブルを更新しているだけです。&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;前の例では開始位置を指定してcontains()を呼び出しているため、これは前の例よりも高速であると思いますが、それを実行するためのセットアップ時間がまだあります。 ここでは、patmatchの呼び出しが1回あり、セットアップのオーバーヘッドは1回だけです。&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;どちらの例も、全体像内の小さなジョブに正規表現を使用しています。つまり、短い文字列からリンクテキストとURLを抽出します。&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;&amp;amp;amp;nbsp;&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;答えでは、テキストエクスプローラーも可能な解決策です。 リンクグラバーを含む事前に作成された正規表現のライブラリがありますが、説明的なテキストグラバーは含まれていません。 場合によっては簡単かもしれませんが、おそらく速くはなりません。&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;&amp;amp;amp;nbsp;&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;/lingo-body&amp;amp;amp;gt;&amp;amp;amp;lt;lingo-teaser id="lingo-teaser-367121" slang="en-US"&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;&amp;amp;amp;lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Clickable URL in a &amp;amp;amp;lt; a &amp;amp;amp;gt; link tag" style="width: 456px;"&amp;amp;amp;gt;&amp;amp;amp;lt;img src="https://us.v-cdn.net/6038925/uploads/images/31168i4AA911021B7FAF36/LinkWithTextAndURL.png" role="button" title="LinkWithTextAndURL.png" alt="&amp;amp;amp;lt;a&amp;amp;amp;gt;リンクタグ内のクリック可能なURL" /&amp;amp;amp;gt;&amp;amp;amp;lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&amp;amp;amp;gt;&amp;amp;amp;lt;a&amp;amp;amp;gt;リンクタグ内のクリック可能なURL&amp;amp;amp;lt;/span&amp;amp;amp;gt;&amp;amp;amp;lt;/span&amp;amp;amp;gt;&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;正規表現またはテキストエクスプローラーを使用する&amp;amp;amp;lt;SPAN style="font-family: inherit;"&amp;amp;amp;gt;HTMLでURLを検索するには&amp;amp;amp;lt;/SPAN&amp;amp;amp;gt;&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;/lingo-teaser&amp;amp;amp;gt;