Разбор XML с разбитым текстом

Я хочу проанализировать XML-документ Word, чтобы получить информацию нижнего колонтитула для каждого абзаца, предложения или фразы.

Эта команда получает весь текст без пробелов между ними.

pry(main)> doc.header_and_footers_xml[1].text()
=> " PAGE 1FirstGoogle.comSomething privacy Saturday, February 23, 2019"

Эта команда немного лучше, но странным образом разбивает текст:

pry(main)> doc.header_and_footers_xml[1].search('//text()')
=> [#<Nokogiri::XML::Text:0x3fdf0eb0c3a4 " PAGE ">, #<Nokogiri::XML::Text:0x3fdf10c41b78 "1">, #<Nokogiri::XML::Text:0x3fdf0eaa427c "F">, #<Nokogiri::XML::Text:0x3fdf0ea60bbc "irst">, #<Nokogiri::XML::Text:0x3fdf0e9f9bc4 "Google.com">, #<Nokogiri::XML::Text:0x3fdf0f6b636c "Something privacy">, #<Nokogiri::XML::Text:0x3fdf0b9ded90 " Saturday, February 23, 2019">]

pry(main)> doc.header_and_footers_xml[1].search('//text()')[2]
=> #(Text "F")

pry(main)> doc.header_and_footers_xml[1].search('//text()')[3]
=> #(Text "irst")

Я хотел бы получить итерацию по списку/массиву с элементами: «СТРАНИЦА», «1», «Первая», «Google.com», «Что-то конфиденциальное», «Суббота, 23 февраля 2019 г.»

Ниже приведен весь XML. Можно ли просто перебирать name = "p" элементов?

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:hdr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:sdt>
        <w:sdtPr>
            <w:rPr>
                <w:rStyle w:val="PageNumber" />
            </w:rPr>
            <w:id w:val="-157074914" />
            <w:docPartObj>
            <w:docPartGallery w:val="Page Numbers (Top of Page)" />
            <w:docPartUnique />
            </w:docPartObj>
        </w:sdtPr>
        <w:sdtEndPr>
            <w:rPr>
                <w:rStyle w:val="PageNumber" />
            </w:rPr>
        </w:sdtEndPr>
        <w:sdtContent>
            <w:p w:rsidR="00140C14" w:rsidRDefault="00140C14" w:rsidP="00AD16D8">
                <w:pPr>
                    <w:pStyle w:val="Header" />
                    <w:framePr w:wrap="none" w:vAnchor="text" w:hAnchor="margin" w:xAlign="right" w:y="1" />
                    <w:rPr>
                        <w:rStyle w:val="PageNumber" />
                    </w:rPr>
                </w:pPr>
                <w:r>
                    <w:rPr>
                        <w:rStyle w:val="PageNumber" />
                    </w:rPr>
                    <w:fldChar w:fldCharType="begin" />
                </w:r>
                <w:r>
                    <w:rPr>
                        <w:rStyle w:val="PageNumber" />
                    </w:rPr>
                    <w:instrText xml:space="preserve"> PAGE </w:instrText>
                </w:r>
                <w:r>
                    <w:rPr>
                        <w:rStyle w:val="PageNumber" />
                    </w:rPr>
                    <w:fldChar w:fldCharType="separate" />
                </w:r>
                <w:r>
                    <w:rPr>
                            <w:rStyle w:val="PageNumber" />
                        <w:noProof />
                    </w:rPr>
                    <w:t>1</w:t>
                </w:r>
                <w:r>
                    <w:rPr>
                        <w:rStyle w:val="PageNumber" />
                    </w:rPr>
                    <w:fldChar w:fldCharType="end" />
                </w:r>
            </w:p>
        </w:sdtContent>
    </w:sdt>
    <w:p w:rsidR="002132D5" w:rsidRDefault="00140C14" w:rsidP="00140C14">
        <w:pPr>
            <w:pStyle w:val="Header" />
            <w:ind w:right="360" />
        </w:pPr>
        <w:r>
            <w:t>F</w:t>
        </w:r>
        <w:r w:rsidR="002132D5">
            <w:t>irst</w:t>
        </w:r>
    </w:p>
    <w:p w:rsidR="00140C14" w:rsidRDefault="00140C14" w:rsidP="00140C14">
        <w:pPr>
            <w:pStyle w:val="Header" />
            <w:ind w:right="360" />
        </w:pPr>
        <w:r>
        <w:t>Google.com</w:t>
        </w:r>
    </w:p>
    <w:p w:rsidR="00140C14" w:rsidRDefault="00140C14" w:rsidP="00140C14">
        <w:pPr>
            <w:pStyle w:val="Header" />
            <w:ind w:right="360" />
        </w:pPr>
        <w:r>
            <w:t>Something privacy</w:t>
        </w:r>
        <w:r w:rsidR="00710468">
            <w:t xml:space="preserve"> Saturday, February 23, 2019</w:t>
        </w:r>
        <w:bookmarkStart w:id="0" w:name="_GoBack" />
        <w:bookmarkEnd w:id="0" />
    </w:p>
</w:hdr>

Вот слово документ:

фактический макет в документе Word


person echan00    schedule 23.02.2019    source источник
comment
Вы не определили желаемый результат. Пожалуйста, опубликуйте скриншот текста, как он появляется в текстовом документе, а также точный открытый текст, который вы хотите извлечь из него.   -  person Tomalak    schedule 23.02.2019
comment
Включите метки форматирования ( ¶ ) в Word, чтобы было легче видеть. Кроме того, отображение отформатированного образца фактического XML может быть проще для чтения, чем дамп дерева Nokogiri.   -  person Tomalak    schedule 23.02.2019
comment
В результате я хочу получить массив с элементами: СТРАНИЦА 1, Первая, Google.com, Что-то конфиденциальное, Суббота, 23 февраля 2019 года.   -  person echan00    schedule 23.02.2019
comment
отличная идея: посмотреть на реальный XML!   -  person echan00    schedule 23.02.2019
comment
фактический XML также не кажется простым для анализа, это только я?   -  person echan00    schedule 23.02.2019
comment
Когда вы публикуете все это, а не только соответствующую часть, и когда вы публикуете это без какого-либо формата, да, тогда это сложнее анализировать. Хотя это не совсем то, что я имел в виду. Кроме того, запрос на скриншот не был полностью случайным. WordML сложен, и делать то, что вы хотите, может быть сложнее, чем вы думаете. Например, если часть текста выровнена по правому краю, между ними не будет пробелов (это то, что вы видите в своем первом примере кода). Возможность увидеть, какие части XML соответствуют каким частям на экране, помогает понять это.   -  person Tomalak    schedule 23.02.2019
comment
Скриншот @Tomalak и проанализированный xml добавлены!   -  person echan00    schedule 24.02.2019
comment
Так намного лучше, спасибо. Это дало мне возможность повозиться.   -  person Tomalak    schedule 24.02.2019


Ответы (1)


Принцип такой:

  1. Определите URI пространства имен, чтобы мы могли правильно искать элементы <w:p> с помощью XPath. В этом случае имеет значение только пространство имен w:.
  2. Повторить <w:p> узлов
  3. Соедините непустые текстовые узлы в каждом из них

Это, вероятно, ужасно неидиоматический Ruby, но это должно помочь вам начать:

require 'nokogiri'

header_and_footers_xml = Nokogiri::XML(open("footer.xml"))  

namespaces = {
  "w" => "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
}

paras = header_and_footers_xml.search('//w:p', namespaces)
paras.each do |p|
  nodes = p.xpath('.//text()[normalize-space()]')
  texts = nodes.map { |n| n.text }
  puts(texts.join)
end

Это печатает (проверено на Ruby 2.5):

 PAGE 1
First
Google.com
Something privacy Saturday, February 23, 2019

Выражение XPath .//text()[normalize-space()] собирает все текстовые узлы (text()), являющиеся потомками текущего узла (.), и отфильтровывает пустые, вызывая normalize-space() для каждого из них, что обрезает пробелы — возвращаются только те узлы, где непустой веревка остается после обрезки.

person Tomalak    schedule 24.02.2019