2012-05-16 19 views
13

Objetivo: Extraer texto de un elemento en particular (por ejemplo, li), ignorando las diversas etiquetas mixtas, es decir, aplanar al hijo de primer nivel y simplemente devolver el texto concatenado de cada elemento secundario aplanado por separado.HTML XPath: ¿extracción de texto mezclado con varias etiquetas?

Ejemplo:

<div id="mw-content-text"><h2><span class="mw-headline" >CIA</span></h2> 
    <ol> 
    <li>Central <a href="/Intelligence_Agency.html">Intelligence Agency</a>.</li> 
    <li>Culinary <a href="/Institute.html">Institute</a> of <a href="/America.html">America</a>.</li> 
    </ol> 

    </Div> 

texto deseado:

  • Agencia Central de Inteligencia
  • Culinary Institute of America

Salvo que las etiquetas de anclaje que rodean impiden una recuperación sencilla.

Para volver cada etiqueta li separado, se utiliza el sencillo:

//div[contains(@id,"mw-content-text")]/ol/li 

sino que también incluye rodea las etiquetas de anclaje, etc. Y

//div[contains(@id,"mw-content-text")]/ol/li/text() 

sólo devuelve los elementos de texto que son hijos directos de li, es decir, 'Central', '.'...

Parecía lógico entonces buscar elementos de texto de uno mismo y descendientes

//div[contains(@id,"mw-content-text")]/ol/li[descendant-or-self::text] 

pero eso no devuelve nada en absoluto!

¿Alguna sugerencia? Estoy usando Python, por lo que estoy abierto a usar otros módulos para el postproceso.

(estoy usando el Scrapy HtmlXPathSelector que parece XPath 1.0 compatible)

+0

Puede ser útil: http://stackoverflow.com/questions/4378502/xpath-return-all-non-blank-text-nodes-not-descendant-of-a-style-or-script/6303276 – warvariuc

Respuesta

24

Usted estuvo casi allí.Hay un pequeño problema en:

//div[contains(@id,"mw-content-text")]/ol/li[descendant-or-self::text] 

La expresión corregida es:

//div[contains(@id,"mw-content-text")]/ol/li[descendant-or-self::text()] 

Sin embargo, hay una expresión más simple que produce exactamente la concatenación deseada de todo el texto nodos debajo de la especificada li:

string(//div[contains(@id,"mw-content-text")]/ol/li) 
+0

¿Existe una razón específica por la cual se usa 'contains' en lugar de' @ id = 'o es solo porque OP hizo la pregunta con' contains'? – Kiril

+0

@Lirik, con esta respuesta ayudo al OP a obtener su código haciendo lo que él quiere - no puedo adivinar si él quiere seleccionar un 'div' con exactamente ese atributo' id', o con un atributo 'id' que contiene una cadena dada Es probable que haya querido decir lo primero, pero un respondedor debe evitar hacer conjeturas, siempre que sea posible. –

2

La concatenación de cadenas es complicado. Aquí hay una solución rápida usando lxml:

>>> from lxml import etree 
>>> doc = etree.HTML("""<div id="mw-content-text"><h2><span class="mw-headline" >CIA</span></h2> 
...  <ol> 
...  <li>Central <a href="/Intelligence_Agency.html">Intelligence Agency</a>.</li> 
...  <li>Culinary <a href="/Institute.html">Institute</a> of <a href="/America.html">America</a>.</li> 
...  </ol> 
... 
...  </Div>""") 
>>> for element in doc.xpath('//div[@id="mw-content-text"]/ol/li'): 
... print "".join(element.xpath('descendant-or-self::text()')) 
... 
Central Intelligence Agency. 
Culinary Institute of America. 

Tenga en cuenta que // tiene potencialmente bajo rendimiento/ejecución involuntaria y debe evitarse siempre que sea posible, pero difícil hacerlo con el ejemplo de fragmento de HTML.

5

creo que el siguiente sería devolver el resultado correcto:

//div[contains(@id,"mw-content-text")]/ol/li//text() 

Nota de la doble barra antes de texto(). Esto significa que se deben devolver nodos de texto en cualquier nivel por debajo de li.

+0

Esto es una buena idea, pero devuelve todos los elementos de texto, sin ningún contexto del que provengan. Comprobando con 'XPath Checker' de Firefox obtengo: 1: Central 2: Agencia de inteligencia 3:. 4: Culinaria 5: Instituto 6: de 7: América 8:. No hay forma de saber qué texto provino de ... – ChaimKut

+0

Si cada línea termina con un punto (y no hay líneas con puntos intermedios (como Dr., Sr., etc.)), puede concatenar todo el texto hasta el período y simplemente asuma que cada período == al final de un li. – rishimaharaj

Cuestiones relacionadas