html - Nokogiri y Xpath: encuentran todo el texto entre dos etiquetas
ruby (2)
Encuentre todos los elementos que preceden a la primera <h3>
en la celda, luego de recuperar todos los hermanos precedentes que no tienen una etiqueta <h2>
como hermano anterior. Reemplace //td
por la expresión XPath para recuperar exactamente esta celda de la tabla.
//td/h3[1]/preceding-sibling::*[preceding-sibling::h2]
No estoy seguro de si es una cuestión de sintaxis o diferencias en las versiones, pero parece que no puedo resolverlo. Quiero tomar datos que están dentro de un td
(sin cierre) de la etiqueta h2
a la etiqueta h3
. Aquí es cómo se vería el HTML.
<td valign="top" width="350">
<br><h2>NameIWant</h2><br>
<br>Town<br>
PhoneNumber<br>
<a href="mailto:[email protected]" class="links">[email protected]</a>
<br>
<a href="http://websiteIwant.com" class="links">websiteIwant.com</a>
<br><br>
<br><img src="images/spacer.gif"/><br>
<h3><b>I want to stop before this!</b></h3>
Lorem Ipsum Yadda Yadda<br>
<img src="images/spacer.gif" border="0" width="20" height="11" alt=""/><br>
<td width="25">
<img src="images/spacer.gif" border="0" width="20" height="8" alt=""/>
<td valign="top" width="200"><img src="images/spacer.gif"/>
<br>
<br>
<table cellspacing="0" cellpadding="0" border="0"/>205"><tr><td>
<a href="http://dontneedthis.com">
</a></td></tr><br>
<table border="0" cellpadding="3" cellspacing="0" width="200">
...
El <td valign>
no se cierra hasta la parte inferior de la página, lo que creo que podría ser el motivo por el que estoy teniendo problemas.
Mi código de Ruby se ve así:
require ''open-uri''
require ''nokogiri''
@doc = Nokogiri::XML(open("http://www.url.com"))
content = @doc.css(''//td[valign="top"] [width="350"]'')
name = content.xpath(''//h2'').text
puts name // Returns NameIwant
townNumberLinks = content.search(''//following::h2'')
puts content // Returns <h2> NameIWant </h2>
Según entiendo, la siguiente sintaxis debería "Seleccionar todo en el documento después de la etiqueta de cierre del nodo actual". Si trato de usar el preceding
como:
townNumberLinks = content.search(''//preceding::h3'')
// I get: <h3><b>I want to stop before this!</b></h3>
Espero haber dejado en claro lo que estoy tratando de hacer. ¡Gracias!
No es trivial. En el contexto de los nodos que seleccionó (el td
), para obtener todo entre dos elementos, debe realizar una intersección de estos dos conjuntos:
- Conjunto A : Todos los nodos que preceden al primer
h3
://h3[1]/preceding::node()
- Conjunto B : Todos los nodos que siguen a la primera
h2
://h2[1]/following::node()
Para realizar una intersección, puede usar el método Kaysian (después de Michael Kay , quien lo propuso). La fórmula básica es:
A[count(.|B) = count(B)]
Al aplicarlo a sus conjuntos, como se definió anteriormente, donde A = //h3[1]/preceding::node()
, y B = //h2[1]/following::node()
, tenemos:
//h3[1]/preceding::node()[ count( . | //h2[1]/following::node()) = count(//h2[1]/following::node()) ]
que seleccionará todos los elementos y nodos de texto comenzando con la primera <br>
después de la etiqueta </h2>
, en el nodo de texto en blanco después de la última <br>
, justo antes de la próxima etiqueta <h3>
.
Puede seleccionar fácilmente solo los nodos de texto entre h2
y h3
reemplazando el node()
por text()
en la expresión. Éste devolverá todos los nodos de texto (incluidos espacios en blanco y saltos de línea) entre los dos encabezados:
//h3[1]/preceding::text()[ count( . | //h2[1]/following::text()) = count(//h2[1]/following::text()) ]