Как использовать предшествующего брата для XML с xPath в Python? - PullRequest
0 голосов
/ 15 апреля 2020

У меня есть XML, структурированный так:

<?xml version="1.0" encoding="utf-8"?>
<pages>
    <page id="1" bbox="0.000,0.000,462.047,680.315" rotate="0">
        <textbox id="0" bbox="179.739,592.028,261.007,604.510">
            <textline bbox="179.739,592.028,261.007,604.510">
                <text font="NUMPTY+ImprintMTnum"  bbox="191.745,592.218,199.339,603.578" ncolour="0" size="12.482">C</text>
                <text font="NUMPTY+ImprintMTnum-it"  bbox="192.745,592.218,199.339,603.578" ncolour="0" size="12.333">A</text>
                <text font="NUMPTY+ImprintMTnum-it"  bbox="193.745,592.218,199.339,603.578" ncolour="0" size="12.333">P</text>
                <text font="NUMPTY+ImprintMTnum-it"  bbox="191.745,592.218,199.339,603.578" ncolour="0" size="12.333">I</text>
                <text font="NUMPTY+ImprintMTnum"  bbox="191.745,592.218,199.339,603.578" ncolour="0" size="12.482">T</text>
                <text font="NUMPTY+ImprintMTnum"  bbox="191.745,592.218,199.339,603.578" ncolour="0" size="12.482">O</text>
                <text font="NUMPTY+ImprintMTnum"  bbox="191.745,592.218,199.339,603.578" ncolour="0" size="12.482">L</text>
                <text font="NUMPTY+ImprintMTnum"  bbox="191.745,592.218,199.339,603.578" ncolour="0" size="12.482">O</text>
                <text></text>
                <text font="NUMPTY+ImprintMTnum"  bbox="191.745,592.218,199.339,603.578" ncolour="0" size="12.482">I</text>
                <text font="NUMPTY+ImprintMTnum"  bbox="191.745,592.218,199.339,603.578" ncolour="0" size="12.482">I</text>
                <text font="NUMPTY+ImprintMTnum"  bbox="191.745,592.218,199.339,603.578" ncolour="0" size="12.482">I</text>
                <text></text>
            </textline>
        </textbox>
    </page>
</pages>

Атрибут bbox в текстовом теге имеет четыре значения, и мне нужно иметь разность первого значения bbox элемента и его предыдущего , Другими словами, расстояние между первыми двумя bboxes равно 1. В следующем l oop мне нужно найти предшествующего брата значения атрибута bbox, которое я беру, чтобы вычислить расстояние между этими двумя.

   def wrap(line, idxList):
        if len(idxList) == 0:
            return    # No elements to wrap
        # Take the first element from the original location
        idx = idxList.pop(0)     # Index of the first element
        elem = removeByIdx(line, idx) # The indicated element
        # Create "newline" element with "elem" inside
        nElem = E.newline(elem)
        line.insert(idx, nElem)  # Put it in place of "elem"
        while len(idxList) > 0:  # Process the rest of index list
            # Value not used, but must be removed
            idxList.pop(0)
            # Remove the current element from the original location
            currElem = removeByIdx(line, idx + 1)
            nElem.append(currElem)  # Append it to "newline"

    for line in root.iter('textline'):
        idxList = []
        for elem in line:
            bbox = elem.attrib.get('bbox')
            if bbox is not None:
                tbl = bbox.split(',')
                distance = float(tbl[2]) - float(tbl[0])
            else:
                distance = 100  # "Too big" value
            if distance > 10:
                par = elem.getparent()
                idx = par.index(elem)
                idxList.append(idx)
            else:  # "Wrong" element, wrap elements "gathered" so far
                wrap(line, idxList)
                idxList = []
        # Process "good" elements without any "bad" after them, if any
        wrap(line, idxList)

    #print(etree.tostring(root, encoding='unicode', pretty_print=True))

Я пытался с xPath так:

for x in tree.xpath("//text[@bbox<preceding::text[1]/@bbox+11]"):
print(x)

Но он ничего не возвращает. Мой путь неверен и как я могу вставить его в l oop?

Ответы [ 2 ]

1 голос
/ 15 апреля 2020

Python использует очень старый стандарт XPath 1.0. В XPath 1.0 оператор «<» всегда преобразует свои операнды в числа. Поэтому, когда вы пишете </p>

//text[@bbox < preceding::text[1]/@bbox + 11]

, вы выполняете сложение чисел c и добавление чисел c к значениям @bbox.

Но @bbox - это не число, это разделенный запятыми список из четырех чисел:

179.739,592.028,261.007,604.510 

Преобразование этого числа в число дает NaN (не число), а NaN < NaN возвращает false.

Чтобы сделать что-нибудь полезное с таким структурированным значением атрибута вам действительно нужен XPath 2.0 или новее.

1 голос
/ 15 апреля 2020

Причина, по которой ваш код не удался, в том, что имя оси, относящееся к предыдущим братьям и сестрам, предшествующий брат (не предшествующий ).

Но здесь вам не нужно использовать XPath выражений, так как есть собственный l xml метод для получения (первого) предшествующего брата по имени getprevious .

To проверьте доступ к предыдущему узлу text , попробуйте следующий l oop:

for x in tree.xpath('//text'):
    bb = x.attrib.get('bbox')
    if bb is not None:
        bb = bb.split(',')
    print('This: ', bb)
    xPrev = x.getprevious()
    bb = None
    if xPrev is not None:
        bb = xPrev.attrib.get('bbox')
        if bb is not None:
            bb = bb.split(',')
    if bb is not None:
        print('  Previous: ', bb)
    else:
        print('  No previous bbox')

Он печатает bbox для текущего элемента text и для непосредственно предшествующего брата, если есть.

Редактировать

Если вы хотите, вы также можете напрямую получить доступ к атрибуту bbox в предыдущем элементе text, вызывая x.xpath ('previousing-sibling :: text [1] / @ bbox') .

Но помните, что эта функция возвращает список найденных узлы и, если ничего не найдено, этот список пуст (не Нет ).

Итак, прежде чем использовать этот результат, вы должны:

  • проверить длину возвращаемого списка (должно быть> 0),
  • извлечь первый элемент из этого списка (текстовое содержимое bbox атрибут, в этом случае этот список должен содержать только 1 элемент),
  • разделить его на , (получить список фрагментов),
  • проверить, не является ли первый элемент этого результата пусто,
  • преобразовать в float .

После этого вы можете использовать его, например, сравнить с соответствующим значением из текущего bbox .

...