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
\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-]+)>(.*)([\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)