У меня была та же проблема. В моем случае у меня было 2 вложенных Relative Layouts. RelativeLayout всегда должен выполнять два прохода. Если вы вложите RelativeLayouts, вы получите экспоненциальный алгоритм измерения.
Попробуйте:
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))
Пример:
from lxml import etree
node = etree.fromstring("""<content>
Text outside tag <div>Text <em>inside</em> tag</div>
</content>""")
stringify_children(node)
Производит: '\nText outside tag <div>Text <em>inside</em> tag</div>\n'
В ответ на комментарий Ричарда выше, если вы исправляете stringify_children для чтения:
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])
, похоже, чтобы избежать дублирования, на которое он ссылается.
import urllib2
from lxml import etree
url = 'some_url'
получение url
test = urllib2.urlopen(url)
page = test.read()
получение всего html-кода внутри тега table
tree = etree.HTML(page)
селектор xpath
table = tree.xpath("xpath_here")
res = etree.tostring(table)
res is html-код таблицы, это выполняло для меня работу.
, чтобы вы могли извлекать содержимое тегов с помощью xpath_text () и тегов, включая их содержимое, используя tostring ()
div = tree.xpath("//div")
div_res = etree.tostring(div)
text = tree.xpath_text("//content")
или text = tree.xpath ("// content / text ()")
div_3 = tree.xpath("//content")
div_3_res = etree.tostring(div_3).strip('<content>').rstrip('</')
эта последняя строка с использованием метода strip не очень приятна, но она просто работает
xpath_text
из lxml? В нем говорится AttributeError: 'lxml.etree._Element' object has no attribute 'xpath_text'
– roger
10 April 2015 в 07:40
Один из простейших фрагментов кода, который действительно работал для меня и в соответствии с документацией в http://lxml.de/tutorial.html#using-xpath-to-find-text , - это
etree.tostring(html, method="text")
, где etree - это узел / тег, полный текст которого вы пытаетесь прочитать. Вот почему он не избавляется от сценариев и стилей.
lxml имеют метод для этого:
node.text_content()
Выполняет ли text_content () то, что вам нужно?
text_content
, он говорит AttributeError: 'lxml.etree._Element' object has no attribute 'text_content'
– roger
10 April 2015 в 07:38
text_content()
доступен, только если ваше дерево является HTML (т. е. если оно было проанализировано с помощью методов в lxml.html
).
– Louis
15 July 2015 в 19:14
<p>
. Мне не хватало текста (например, вложенные ссылки) при использовании text()
в XPath, но ваш метод работал для меня !.
– Sam Chats
6 July 2017 в 09:21
Я знаю, что это старый вопрос, но это общая проблема, и у меня есть решение, которое кажется более простым, чем предлагаемые до сих пор:
def stringify_children(node):
"""Given a LXML tag, return contents as a string
>>> html = "<p><strong>Sample sentence</strong> with tags.</p>"
>>> node = lxml.html.fragment_fromstring(html)
>>> extract_html_content(node)
"<strong>Sample sentence</strong> 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]
В отличие от некоторых других ответов на этот вопрос это решение сохраняет все теги, содержащиеся в нем, и атакует проблему под другим углом, чем другие рабочие решения.
import re
from lxml import etree
node = etree.fromstring("""
<content>Text before inner tag
<div>Text
<em>inside</em>
tag
</div>
Text after inner tag
</content>""")
print re.search("\A<[^<>]*>(.*)</[^<>]*>\Z", etree.tostring(node), re.DOTALL).group(1)
Определение stringify_children
таким образом может быть менее сложным:
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
или в одной строке
return (node.text if node.text is not None else '') + ''.join((etree.tostring(child, encoding='unicode') for child in node))
Обоснование такое же, как в , этот ответ : оставьте сериализацию дочерних узлов в lxml. tail
часть node
в этом случае не интересна, поскольку она «отстает» от конечного тега. Обратите внимание, что аргумент encoding
может быть изменен в соответствии с потребностями.
Еще одно возможное решение - сериализовать сам узел, а затем отделить начальный и конечный теги:
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]
, что несколько ужасно. Этот код правильный, только если node
не имеет атрибутов, и я не думаю, что кто-то захочет его использовать даже тогда.
node.text if node.text is not None else ''
может быть просто node.txt or ''
– yprez
11 March 2016 в 20:41
''.join([node.text or ''] + [etree.tostring(e) for e in node])
– Tim Alexander
3 July 2017 в 18:11
Версия stringify-content Альбертова, которая решает баги , сообщенные 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)
Следующий фрагмент, который использует генераторы python, отлично работает и очень эффективен.
''.join(node.itertext()).strip()
' '.join(node.itertext('span', 'b'))
- использовать текст только с тегов <span>
и <b>
, отбрасывая теги с помощью «\n»; от отступа.
– Zoltan K.
8 April 2018 в 10:28
Это рабочее решение. Мы можем получить контент с родительским тегом, а затем вырезать родительский тег из вывода.
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
должен иметь тип Element
.
Обратите внимание, что если вы хотите текстовый контент (а не html-объекты в тексте), пожалуйста, оставьте параметр html_entities
как False.
Если это тег, вы можете попробовать:
node.values()
Просто используйте метод node.itertext()
, как в:
''.join(node.itertext())
<word><pre>con</pre>gregate</word>
, чтобы указать префикс в слове. Предположим, я хочу извлечь слово без разметки. Если я использую .join
с пробелом, то я бы получил "con gregate"
, тогда как без пробела я получаю "congregate"
.
– Louis
1 September 2015 в 20:18
tostring
уже обрабатывает рекурсивный случай. Вы заставили меня сомневаться, поэтому я попробовал это на реальном коде и обновил ответ на примере. Спасибо, что указали это. – albertov 7 January 2011 в 14:36with_tail=False
в качестве параметра вtostring()
. Итак,tostring(c, with_tail=False)
. Это устранит проблему с хвостовым текстом (C
). Для исправления проблемы с префиксным текстом (A
) это, кажется, ошибка вtostring()
, которая добавляет тег<p>
, поэтому это не ошибка в коде OP. – anana 27 January 2015 в 15:38c.text
из спискаparts
. Я отправил новый ответ с исправленными ошибками. – anana 27 January 2015 в 16:20tostring(c, encoding=str)
для запуска на Python 3. – Antoine Dusséaux 9 January 2017 в 23:12