Думая о том, как работает следующая проблема с итерацией дерева, у меня в голове завязались узлы, которые, возможно, некоторые из вас помогут решить / развязать.
У меня есть (TEI) XML-тексты, и я хочу сегментировать их по <milestone>
s.Чтобы обозначить сегменты, я хочу использовать атрибут <milestone>
и одного из родительских элементов <div>
.Я думаю, это означает, что я не могу использовать метод itertext()
, потому что он не позволяет мне получить доступ к этим атрибутам при обработке текстовых узлов.Поэтому мой подход заключается в использовании метода iter()
, подачи всего текстового содержимого в буфер и записи его в выходные сегменты (словарь меток и текстовое содержимое сегмента), когда я сталкиваюсь с элементом вехи.Когда я не в элементе вехи в итерации, я записываю .text
существующего узла в мой буфер.
Трудная вещь в том, когда и как обрабатывать .tail
текст: Когда я просто проверяю текущий узел на .tail
и добавляю его в буфер, он появится перед .text
дочерних узлов текущего узла (хотя в действительности он должен следовать после), и если я не проверю наэто, у меня никогда не будет другого шанса поймать это, или я ошибся?
Могу ли я вообще использовать lxml iter()
или мне нужно создать свою собственную рекурсивную итерационную функцию?(Ранее я пытался сегментировать текст с помощью выражения XPath, но его производительность была недопустимой (> 24 ч).
Я извиняюсь за длинный и грязный фрагмент кода xml, но не могу понять, какие отрывки я мог быПриведите в порядок или пропустите этот вопрос. (И некоторые другие вопросы (например, this ) не применимы именно потому, что у меня более грязный xml.
Спасибо за любую помощь.
Код:
from typing import Dict
import lxml
from lxml import etree
def segment(chapter: lxml.etree._Element) -> Dict[str, str]:
segments = {} # this will be returned
t = [] # this is a buffer
chap_label = str(chapter.get("n"))
sect_label = "0"
for element in chapter.iter():
if element.get("unit")=="number":
# milestone: fill and close the previous segment:
label = chap_label + "_" + sect_label
segments[label] = " ".join(t)
# reset buffer
t = []
# if there is text after the milestone,
# add it as first content to the buffer
if element.tail:
t.append(" ".join(str.replace(element.tail, "\n", " ").strip().split()))
# prepare for next labelmaking
sect_label = str(element.get("n"))
else:
if element.text:
t.append(" ".join(str.replace(element.text, "\n", " ").strip().split()))
if element.tail:
t.append(" ".join(str.replace(element.tail, "\n", " ").strip().split()))
# all elements are processed,
# add text remainder/current text buffer content
label = chap_label + "_" + sect_label
segments[label] = " ".join(t)
return segments
nsmap = {"tei": "http://www.tei-c.org/ns/1.0"}
xp_divs = etree.XPath("(//tei:body/tei:div)", namespaces = nsmap)
segmented = {}
divs = xp_divs(document)
segments = (segment(div) for div in divs)
for d in segments:
print(d)
Входной документ (я оставил в нем много текста, чтобы было легче увидеть, где заканчиваются фразы в выходном документе):
document=etree.fromstring("""
<TEI xmlns="http://www.tei-c.org/ns/1.0">
<text>
<body>
<div n="1">
<p>
... <milestone unit="number" n="9"/>aun que el amor de Dios ha de ser
grandissimo ..., como despues de. S. Tho.
<ref target="#nm-0406">b</ref><note xml:id="nm-0406"><p>1. Sec. quaestio
109. ar. 3.</p></note>, poco ha lo tratamos
<ref target="#nm-0407">c</ref><note xml:id="nm-0407"><p>in addit. ca.
Quoniam. de consec. disti. 1. nu. 10.</p></note>. Anadimos, (virtual)
<milestone unit="number" n="10"/>porque aquella basta, ...
<ref target="#nm-0408">d</ref><note xml:id="nm-0408"><p>in 4. dis. 14.
q. 1. art. 3.</p></note>, que pone exemplo ..., que Gabriel sigue
<ref target="#nm-0409">e</ref><note xml:id="nm-0409"><p>in 4. dis. 14.
q. 1. col. 12. & 13. & in. 3. di. 27. q. 1. co. 15.</p></note>.
<milestone unit="other" rendition="#asterisk"/> Y aun, aquel doctissimo,
... <ref target="#nm-040a">f</ref><note xml:id="nm-040a"><p>In Codice de
poeni. q. 2.</p></note>, y con razon, ..., el martyrio atribuya esto
<ref target="#nm-040b">g</ref><note xml:id="nm-040b"><p>Lib. 2. c. 16.
de natu. & gra.</p></note>, porque mas haze para esto el amor, ...
que lo que se padece <ref target="#nm-040c">h</ref><note xml:id="nm-040c">
<p>Arg. c. 13. 1. ad Corinth.</p></note>. Y puede ser que mas ame, ...,
como lo prueua bien Medina
<ref target="#nm-040d">i</ref><note xml:id="nm-040d"><p>in predi.
q. 2.</p></note>. Por lo qual largamente paresce quan lexos esta esto
dela opinion de Luthero<milestone unit="other" rendition="#asterisk"/>.
De lo dicho se collige la razon, ..., segun Syluestro
<ref target="#nm-040e">k</ref><note xml:id="nm-040e"><p>verb. Contritio.
q. 1.</p></note>. Diximos <milestone unit="number" n="11"/> (auer
pecado,) porque el arrepentimiento ...
</p>
</div>
</body>
</text>
</TEI>""")
Результат (для удобочитаемости, обернуты и отображаются здесь как csv):
1_9,"aun que el amor de Dios ha de ser grandissimo ..., como despues
de. S. Tho. b , poco ha lo tratamos 1. Sec. quaestio 109. ar. 3.
c . Anadimos, (virtual) in addit. ca. Quoniam. de consec. disti.
1. nu. 10."
1_10,"porque aquella basta, ... d , que pone exemplo ..., que Gabriel
sigue in 4. dis. 14. q. 1. art. 3. e . in 4. dis. 14. q. 1. col.
12. & 13. & in. 3. di. 27. q. 1. co. 15. Y aun, aquel doctissimo,
... f, y con razon, ..., el martyrio atribuya esto In Codice de
poeni. q. 2. g , porque mas haze para esto el amor, ... que lo que
se padece Lib. 2. c. 16. de natu. & gra. h . Y puede ser que mas
ame, ..., como lo prueua bien Medina Arg. c. 13. 1. ad Corinth.
i . Por lo qual largamente paresce quan lexos esta esto dela
opinion de Luthero in predi. q. 2. . De lo dicho se collige la
razon, ..., segun Syluestro k . Diximos verb. Contritio. q. 1."
1_11,"(auer pecado,) porque el arrepentimiento ..."
(Вы можете видеть, например, что содержание примечания nm-0408
должно идти после "porque aquella basta, ... d", но это приходит только позже, после "Que Gabriel sigue" ...)