@lala preguntado en ¿Cómo se puede usar la sustitución de expresiones regulares <*>? sobre agarrar URL de HTML . Aquí hay un poco más de detalles sobre tres respuestas propuestas. Primero, el descargo de responsabilidad: ninguno de estos ejemplos hace un trabajo adecuado al analizar HTML, por lo que todos ellos pueden encontrar URL que realmente no están allí y perder otras URL que realmente están allí.
/*
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.
*/
Los problemas al analizar HTML se relacionan principalmente con la búsqueda de etiquetas de inicio y final coincidentes y la observación de los datos entre ellas. Todo el JSL presentado aquí se puede tropezar con las características de HTML. Pero, podría ser lo suficientemente bueno; haz tus propias pruebas. Ver también hermosa sopa que podría ser una mejor respuesta.
Aquí hay un fragmento de una página web HTML que muestra un enlace (la etiqueta ) con una URL (la parte href=) y un poco de texto (la parte Discusiones) que será visible en la página.
< a > tag with URL and Text< a > etiqueta con URL y texto
El objetivo es hacer una tabla de datos de las URL y el texto del enlace, uno por fila. Una página web típica puede tener alrededor de 100 enlaces y tener medio megabyte de texto. Una lento La forma de atacar el problema (que no se muestra) es escribir una expresión regular simple que encuentre un enlace a la vez, eliminar el enlace del texto y repetir hasta que no se pueda encontrar más. Si solo hay uno o dos, genial. Pero si pudiera haber 100, entonces se manipulan y buscan unos pocos cientos de megabytes de datos.
Una forma más rápida es buscar a través de los datos, siempre avanzando, construyendo la tabla de datos a medida que se encuentran los enlaces. El primer ejemplo usa la función contains() que tiene un tercer argumento para especificar una posición inicial. Contiene () también devuelve una posición encontrada. Al actualizar la posición de inicio un poco más allá de la posición encontrada, contains() puede examinar el texto de manera eficiente sin necesidad de realizar una copia de seguridad o modificar el texto.
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>
El corazón de ese código es la línea.
while( (p1 = Contains( txt, "<A></A>
que está haciendo un montón de trabajos: usa pos en el tercer argumento para omitir cualquier trabajo anterior, encuentra el siguiente Nada Encontrado , por lo que el ciclo se detiene. la siguiente linea
p4 = Contains( txt, ">", p1 + 3 );
busca la coincidencia > que finaliza la etiqueta
p5 = Contains( txt, "", p4 + 1 );
p5 apuntará a la etiqueta que finaliza el texto del enlace. Muchas, pero no todas, las etiquetas HTML funcionan así, una etiqueta de inicio sin / y una etiqueta de fin con la barra inclinada. A veces, la etiqueta inicial y final son las mismas, usando , pero este JSL ignora esa posibilidad.
La declaración if determina si se encontró algo y, de ser así, recoge la descripción de esta manera
desc = Substr( txt, p4 + 1, p5 - p4 - 1 );
p4 y p5 apuntan a > y <. entonces p4+1 es el primer carácter de descripción y p5-p4-1 es la longitud de la descripción. La siguiente línea limpia la descripción eliminando las etiquetas incrustadas
desc = Regex( desc, "<[^>]*>", "", globalreplace );
GlobalReplace significa que la expresión regular encontrará el patrón, reemplazará ese texto con nada, tantas veces como sea posible. El patrón coincide con <, seguido de cero o más caracteres que no son >, seguidos de >, que podría ser una etiqueta o un montón de etiquetas de estilo de texto. El resultado final es el texto de la etiqueta sin ninguna imagen/color/fuente/etc.
Las siguientes dos líneas son similares, para obtener la URL en lugar de la descripción.
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 y p4 (ver comentario) son las posiciones completas de la etiqueta de enlace; linktext es la etiqueta del enlace que incluye una serie de bits no deseados. hreftext usa regex() para extraer solo la URL entre las comillas href o apóstrofes. El patrón de expresiones regulares utiliza el backref \1 para asegurarse de que el apóstrofo final o las comillas coincidan con el inicial. El primer paréntesis abierto en el patrón de expresiones regulares crea un grupo de captura (para \1) que coincide con " o '. El segundo paréntesis abierto es el grupo de captura (para \2) que coincide, avanzando a regañadientes tanto como sea necesario para coincidir entre los delimitadores. El tercer paréntesis abierto es el grupo de captura 3; los paréntesis y la agrupación no son realmente necesarios. El resultado, \2, es solo la URL.
Todo menos hecho: si hay una descripción, agregue una fila a la tabla y luego, muy importante, ¡avance la variable pos!
pos = Max( pos, p5 + 3 );
Max se asegura de que no retroceda al final y se atasque en un bucle.
Una forma más rápida
El código anterior es bastante rápido, pero este es aún mejor.
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 0Esta vez, el ciclo está dentro del comparador de patrones (usando patrepeat() para empujar el patrón a través de la cadena). Los dos patrones
linkpat = "<A>" ) >> linktext + ">" + Pat Arb() >> desctext + "</A>";
scriptpat = "<SCRIPT>" ) + "&amp;amp;amp;gt;" + Pat Arb() + "</SCRIPT>";
están haciendo dos trabajos diferentes: linkpat coincide con algún texto y almacena partes de la coincidencia en linktext y desctext. Scriptpat coincide con algún texto entre y lo ignora. La etiqueta en HTML define una sección de código JavaScript que debe ignorarse. En este caso de ejemplo, JavaScript contiene muchas secuencias &amp;amp;amp;lt;a que generan falsos positivos, y omitir JavaScript las elimina. Más sobre esto en un momento. El bucle ahora lo realiza patmatch() que comienza&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;Al final, show(rc) verifica que el patmatch se haya realizado correctamente. Pero primero, el segundo argumento para patmatch es el patrón&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;que se puede simplificar en tres partes&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;lo que significa que cada paso en el camino uno de esos tres patrones coincidirá con algún texto. Primero vea si el linkpat coincide; eso hará parte del trabajo que se simplificó. Si no, vea si el scriptpat coincide; eso omitirá algo de JavaScript. De lo contrario, omita un carácter (generalmente un &amp;amp;amp;lt;), además de suficientes caracteres para llegar al siguiente &amp;amp;amp;lt;. Luego haz un bucle para obtener más.&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;La parte que se simplificó es el JSL que se ejecuta cuando linkpat tiene éxito (recuerde que linkpat guarda algo de texto en linktext y desctext a medida que los empareja)&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;Eso debería parecerse mucho al JSL en el primer ejemplo que escribe filas en la tabla de datos. pattest() es una función especial de coincidencia de patrones que devuelve 1 si la coincidencia tiene éxito y 0 si falla. Aquí, pattest se usa para ejecutar JSL y siempre devuelve 1 para que el comparador pueda avanzar. El JSL que ejecuta no es realmente&amp;amp;amp;lt;EM&amp;amp;amp;gt; pruebas&amp;amp;amp;lt;/EM&amp;amp;amp;gt; si el comparador necesita hacer una copia de seguridad y volver a intentarlo; el JSL solo está actualizando la tabla.&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;Creo que esto es más rápido que el ejemplo anterior porque el ejemplo anterior llama a contains() con una posición inicial, y todavía tiene tiempo de configuración para hacerlo. Aquí, hay una sola llamada a patmatch, con solo una sobrecarga de configuración.&amp;amp;amp;lt;/P&amp;amp;amp;gt;&amp;amp;amp;lt;P&amp;amp;amp;gt;Ambos ejemplos usan expresiones regulares para trabajos más pequeños dentro de la imagen más grande: extraer el texto del enlace y la URL de una cadena corta.&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;En la respuesta, Text Explorer también es una posible solución. Tiene una biblioteca de expresiones regulares preescritas que incluyen un capturador de enlaces, pero no un capturador de texto descriptivo. Puede ser más fácil para algunos casos, pero probablemente no será más rápido.&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="EnlaceConTextoYURL.png" alt="URL en la que se puede hacer clic en una etiqueta de enlace &amp;amp;amp;lt; a &amp;amp;amp;gt;" /&amp;amp;amp;gt;&amp;amp;amp;lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&amp;amp;amp;gt;URL en la que se puede hacer clic en una etiqueta de enlace &amp;amp;amp;lt; a &amp;amp;amp;gt;&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;Usar Regex o Explorador de texto&amp;amp;amp;lt;SPAN style="font-family: inherit;"&amp;amp;amp;gt; para encontrar URL en HTML&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;