Obtener todo el texto dentro de una etiqueta en lxml

Me gustaría escribir un fragmento de código que capturaría todo el texto dentro de la etiqueta , en lxml, en las tres instancias a continuación, incluidas las tags de código. He intentado tostring(getchildren()) pero eso faltaría el texto entre las tags. No tuve mucha suerte al buscar en la API una función relevante. ¿Podrías ayudarme?

   
Text inside tag
#should return "
Text inside tag
Text with no tag #should return "Text with no tag" Text outside tag
Text inside tag
#should return "Text outside tag
Text inside tag
"

Tratar:

 def stringify_children(node): from lxml.etree import tostring from itertools import chain parts = ([node.text] + list(chain(*([c.text, tostring(c), c.tail] for c in node.getchildren()))) + [node.tail]) # filter removes possible Nones in texts and tails return ''.join(filter(None, parts)) 

Ejemplo:

 from lxml import etree node = etree.fromstring(""" Text outside tag 
Text inside tag
""") stringify_children(node)

Produce: '\nText outside tag

Text inside tag

\n'

¿ Text_content () hace lo que necesitas?

Solo usa el método node.itertext() , como en:

  ''.join(node.itertext()) 

El siguiente fragmento de código que utiliza generadores de python funciona perfectamente y es muy eficiente.

''.join(node.itertext()).strip()

Una versión del contenido stringify de albertov que resuelve los errores reportados por hoju:

 def stringify_children(node): from lxml.etree import tostring from itertools import chain return ''.join( chunk for chunk in chain( (node.text,), chain(*((tostring(child, with_tail=False), child.tail) for child in node.getchildren())), (node.tail,)) if chunk) 
 import urllib2 from lxml import etree url = 'some_url' 

obteniendo url

 test = urllib2.urlopen(url) page = test.read() 

obteniendo todo el código html dentro de la etiqueta de tabla incluida

 tree = etree.HTML(page) 

selector de ruta

 table = tree.xpath("xpath_here") res = etree.tostring(table) 

res es el código html de la tabla que estaba haciendo mi trabajo.

para que pueda extraer el contenido de las tags con xpath_text () y las tags, incluido su contenido, utilizando tostring ()

 div = tree.xpath("//div") div_res = etree.tostring(div) 
 text = tree.xpath_text("//content") 

o text = tree.xpath (“// content / text ()”)

 div_3 = tree.xpath("//content") div_3_res = etree.tostring(div_3).strip('').rstrip(' 

Esta última línea con método de tira no es agradable, pero simplemente funciona

Definir stringify_children esta manera puede ser menos complicado:

 from lxml import etree def stringify_children(node): s = node.text if s is None: s = '' for child in node: s += etree.tostring(child, encoding='unicode') return s 

o en una linea

 return (node.text if node.text is not None else '') + ''.join((etree.tostring(child, encoding='unicode') for child in node)) 

La lógica es la misma que en esta respuesta : deje la serialización de nodos secundarios a lxml. La parte final del node en este caso no es interesante, ya que está “detrás” de la etiqueta final. Tenga en cuenta que el argumento de encoding se puede cambiar de acuerdo con las necesidades de cada uno.

Otra solución posible es serializar el nodo en sí mismo y, luego, quitar la etiqueta de inicio y final:

 def stringify_children(node): s = etree.tostring(node, encoding='unicode', with_tail=False) return s[s.index(node.tag) + 1 + len(node.tag): s.rindex(node.tag) - 2] 

que es algo horrible. Este código es correcto solo si el node no tiene atributos, y no creo que nadie quiera usarlo incluso en ese momento.

En respuesta al comentario de @ Richard anterior, si parcheas stringify_children para leer:

  parts = ([node.text] + -- list(chain(*([c.text, tostring(c), c.tail] for c in node.getchildren()))) + ++ list(chain(*([tostring(c)] for c in node.getchildren()))) + [node.tail]) 

Parece evitar la duplicación a la que se refiere.

Uno de los fragmentos de código más simples, que realmente funcionó para mí y según la documentación en http://lxml.de/tutorial.html#using-xpath-to-find-text es

 etree.tostring(html, method="text") 

donde etree es un nodo / etiqueta cuyo texto completo está intentando leer. Sin embargo, tenga en cuenta que no se deshace de las tags de estilo y script.

Sé que esta es una pregunta antigua, pero este es un problema común y tengo una solución que parece más simple que las sugeridas hasta ahora:

 def stringify_children(node): """Given a LXML tag, return contents as a string >>> html = "

Sample sentence with tags.

" >>> node = lxml.html.fragment_fromstring(html) >>> extract_html_content(node) "Sample sentence with tags." """ if node is None or (len(node) == 0 and not getattr(node, 'text', None)): return "" node.attrib.clear() opening_tag = len(node.tag) + 2 closing_tag = -(len(node.tag) + 3) return lxml.html.tostring(node)[opening_tag:closing_tag]

A diferencia de algunas de las otras respuestas a esta pregunta, esta solución conserva todas las tags que contiene y ataca el problema desde un ángulo diferente al de las otras soluciones de trabajo.

Aquí hay una solución de trabajo. Podemos obtener contenido con una etiqueta principal y luego cortar la etiqueta principal de la salida.

 import re from lxml import etree def _tostr_with_tags(parent_element, html_entities=False): RE_CUT = r'^<([\w-]+)>(.*)$' content_with_parent = etree.tostring(parent_element) def _replace_html_entities(s): RE_ENTITY = r'&#(\d+);' def repl(m): return unichr(int(m.group(1))) replaced = re.sub(RE_ENTITY, repl, s, flags=re.MULTILINE|re.UNICODE) return replaced if not html_entities: content_with_parent = _replace_html_entities(content_with_parent) content_with_parent = content_with_parent.strip() # remove 'white' characters on margins start_tag, content_without_parent, end_tag = re.findall(RE_CUT, content_with_parent, flags=re.UNICODE|re.MULTILINE|re.DOTALL)[0] if start_tag != end_tag: raise Exception('Start tag does not match to end tag while getting content with tags.') return content_without_parent 

parent_element debe tener tipo Element .

Tenga en cuenta que si desea contenido de texto (no entidades html en texto), deje el parámetro html_entities como Falso.

lxml tiene un método para eso:

 node.text_content() 

Si esta es una etiqueta, puedes probar:

 node.values() 
 import re from lxml import etree node = etree.fromstring(""" Text before inner tag 
Text inside tag
Text after inner tag
""") print re.search("\A<[^<>]*>(.*)]*>\Z", etree.tostring(node), re.DOTALL).group(1)