从 HTML 中获取 URL@拉拉问进来如何使用正则表达式替换<*>?关于抢网址从HTML . 以下是关于三个建议答案的更多细节。 首先,免责声明:这些示例都不能正确解析 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 特性所困扰。 但是,它可能已经足够好了;做你自己的测试。 另见美丽的汤这可能是一个更好的答案。
这是一个 HTML 网页的片段,其中显示了一个链接( 标记),其中包含一个 URL(href= 部分)和一些将在页面上可见的文本(讨论部分)。
< a > tag with URL and Text 带有 URL 和文本的标签
目标是制作一个包含 URL 和链接文本的数据表,每行一个。 一个典型的网页可能有 100 个左右的链接,并且是半兆字节的文本。 一减缓解决问题的方法(未显示)是编写一个简单的正则表达式,一次找到一个链接,从文本中删除链接,然后重复直到找不到更多链接。 如果只有一两个,那就太好了。 但是,如果可能有 100 个,那么就有数百兆字节的数据被操纵和搜索。
更快的方法是搜索数据,始终向前,在找到链接时构建数据表。 第一个示例使用 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>
它正在做一堆工作:它在第三个参数中使用 pos 来跳过任何以前的工作,它找到下一个 什么都没找到,所以循环停止。 下一行
p4 = Contains( txt, ">", p1 + 3 );
搜索以 。 p4(见评论)指向那里。 通过从 p1+3 开始, contains() 在 p1 之后找到一个,而不是重新找到更早的事件。 下一行类似,寻找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 意味着正则表达式将找到模式,尽可能多次替换该文本。 该模式匹配 <,后跟零个或多个不是 > 的字符,然后是 >,它可能是一个 
标签或一堆文本样式标签。 最终结果是没有任何图片/颜色/字体/等的标签文本。
接下来的两行类似,用于获取 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),不情愿地向前移动,直到需要在分隔符之间匹配。 第三个开放括号是捕获组 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() 将模式推入字符串。)这两个模式
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;a 序列,跳过 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 的第二个参数是模式&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;可以简化为三个部分&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;这意味着这三种模式之一的每一步都将匹配一些文本。 先看linkpat是否匹配;这将完成一些被简化的工作。 如果没有,看看 scriptpat 是否匹配;这将跳过一些 JavaScript。 否则,跳过一个字符(通常是 &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 的调用,只有一次设置开销。&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;