Xpath: все узлы до узла ( Wikiquote.org)

ДОКУМЕНТ: http://en.wikiquote.org/wiki/The_Matrix

Я хотел бы получить все кавычки (//ul/li) первого раздела (кавычки Нео).

Я не могу сделать //ul[1]/li потому что на некоторых страницах викицитат цитата представлена ​​в этой форме

<h2><span class="mw-headline" id="Neo">Neo</span></h2>  

<ul>
 <li> First quote </li>
</ul> 

<ul>
 <li> Second quote </li>
</ul> 

<h2><span class="mw-headline" id="dont wanna this">Useless</span></h2>  

Вместо

<ul>
     <li> First quote </li>
     <li> Second quote </li>
</ul>

Я пробовал это, чтобы получить первый раздел

(//*[@id='mw-content-text']/ul/preceding-sibling::h2/span[@class='mw-headline'])[1]

но у меня возникла проблема получить только цитаты из первого раздела. Вы можете мне помочь?

3 ответа

Решение

Используйте:

(//h2[span/@id='Neo'])[1]/following-sibling::ul
  [count(.
        |
         (//h2[span/@id='Neo'])[1]
            /following-sibling::h2[1]
              /preceding-sibling::ul
         )
  =
   count((//h2[span/@id='Neo'])[1]
            /following-sibling::h2[1]
              /preceding-sibling::ul
         )
  ]
   /li

Это выбирает все li которые сразу же следуют за первым h2 с span ребенок, который имеет id атрибут со значением "Нео".

Выбрать квотации для второго такого h2, просто замените в вышеприведенном выражении 1 с 2,

Сделайте это для всех номеров: 1,2, ..., count(//h2[span/@id='Neo'])

Проверка на основе XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:copy-of select=
   "(//h2[span/@id='Neo'])[1]/following-sibling::ul
      [count(.
            |
             (//h2[span/@id='Neo'])[1]
                /following-sibling::h2[1]
                  /preceding-sibling::ul
             )
      =
       count((//h2[span/@id='Neo'])[1]
                /following-sibling::h2[1]
                  /preceding-sibling::ul
             )
      ]
        /li

   "/>
 </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному документу XML:

<html>
 <h2><span class="mw-headline" id="Neo">Neo</span></h2>

 <ul>
  <li> First quote </li>
 </ul>

 <ul>
  <li> Second quote </li>
 </ul>

 <h2><span class="mw-headline" id="dont wanna this">Useless</span></h2>  >
</html>

выражение XPath вычисляется, и выбранные узлы копируются в вывод:

<li> First quote </li>
<li> Second quote </li>

Пояснение:

Это следует из формулы Кейсиана (доктора Майкла Кея) для пересечения двух наборов узлов:

$ns1[count(.|$ns2) = count($ns2)]

вышеизложенное выбирает ровно все узлы, которые принадлежат как к набору узлов $ns и набор узлов $ns2,

Итак, подставляем $ns1 с набором узлов, состоящим из всех следующих братьев и сестер ul из h2 представляет интерес. Мы заменяем $ns2 с набором узлов, состоящим из всех предшествующих братьев и сестер ul из h2 это непосредственный (1-й) после родного брата h2 представляет интерес.

Пересечение этих двух наборов узлов содержит ровно все ul элементы, которые нужны.


Обновление: в комментарии OP говорится, что он знает только то, что хочет получить результаты из первого раздела - строка "Neo" неизвестна.

Вот модифицированное решение:

(//h2[span/@id=$vSectionId])[1]
            /following-sibling::ul
  [count(.
        |
         (//h2[span/@id=$vSectionId])[1]
            /following-sibling::h2[1]
              /preceding-sibling::ul
         )
  =
   count((//h2[span/@id=$vSectionId])[1]
            /following-sibling::h2[1]
              /preceding-sibling::ul
         )
  ]
    /li

Переменная $vSectionId должен быть получен как строковое значение следующего выражения XPath:

  substring(//div[h2='Contents']
              /following-sibling::ul[1]
                 /li[1]/a/@href,
            2)

Здесь мы получаем желаемое id от href из a в первой записи оглавления и пропуская первый символ "#".

Вот снова проверка на основе XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vSectionId" select=
 "substring(//div[h2='Contents']
                      /following-sibling::ul[1]
                         /li[1]/a/@href,
                    2)
 "/>

 <xsl:template match="/">
  <xsl:copy-of select=
   "(//h2[span/@id=$vSectionId])[1]
                /following-sibling::ul
      [count(.
            |
             (//h2[span/@id=$vSectionId])[1]
                /following-sibling::h2[1]
                  /preceding-sibling::ul
             )
      =
       count((//h2[span/@id=$vSectionId])[1]
                /following-sibling::h2[1]
                  /preceding-sibling::ul
             )
      ]
        /li

   "/>
 </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется ко всему документу XML, который находится по адресу: http://en.wikiquote.org/wiki/The_Matrix, результат применения этих двух выражений XPath (подстановка результата первого во второй, затем оценка второе выражение) - искомый, правильный:

<li>I know you're out there. I can feel you now. I know that you're afraid. You're afraid of us. You're afraid of change. I don't know the future. I didn't come here to tell you how this is going to end. I came here to tell you how it's going to begin. I'm going to hang up this phone, and then I'm going to show these people what you don't want them to see. I'm going to show them a world … without you. A world without rules and controls, without borders or boundaries; a world where anything is possible. Where we go from there is a choice I leave to you.</li>
<li>Whoa.</li>
<li>I know kung-fu.</li>
<li>Yeah. Well, that sounds like a pretty good deal. But I think I may have a better one. How about, I give you the finger [He does] and you give me my phone call.</li>
<li>Guns.. lots of guns...</li>
<li>There is no spoon.</li>
<li>My name...is Neo!</li>

Использование API намного облегчит анализ. Вот запрос, который будет тянуть первый раздел:

http://en.wikiquote.org/w/api.php?action=parse&page=The_Matrix§ion=1&prop=wikitext

Выход:

<?xml version="1.0"?>
<api>
  <parse title="The Matrix">
    <wikitext xml:space="preserve">== Neo ==
[[File:The.Matrix.glmatrix.2.png|thumb|right|Unfortunately, no one can be ''told'' what The Matrix is. You have to see it for yourself.]]
[[Image:Arty spoon.jpg|thumb|right|Do not try to bend the spoon — that's impossible. Instead, only try to realize the truth: there is no spoon.]]

* I know you're out there. I can feel you now. I know that you're afraid. You're afraid of us. You're afraid of change. I don't know the future. I didn't come here to tell you how this is going to end. I came here to tell you how it's going to begin. I'm going to hang up this phone, and then I'm going to show these people what you don't want them to see. I'm going to show them a world … without you. A world without rules and controls, without borders or boundaries; a world where anything is possible. Where we go from there is a choice I leave to you.

* Whoa.
* I know kung-fu.

* Yeah. Well, that sounds like a pretty good deal. But I think I may have a better one. How about, I give you the finger [He does] and you give me my phone call.

* Guns.. lots of guns...

* There is no spoon. 

* My name...is Neo!</wikitext>
  </parse>
</api>

Вот один из способов разобрать это (используя HTTParty):

require 'httparty'

class Wikiquote
  include HTTParty
  base_uri 'en.wikiquote.org/w/'

  def self.get_quotes(page)
    url = "/api.php?action=parse&page=#{page}&section=1&prop=wikitext&format=xml"
    headers = {"User-Agent" => "Wikiquote scraper 1.0"}
    content = get(url, headers: headers)['api']['parse']['wikitext']['__content__']
    return content.scan(/^\* (.*)$/).flatten
  end
end

Использование:

Wikiquote.get_quotes("The_Matrix")

Выход:

["I know you're out there. I can feel you now. I know that you're afraid. You're afraid of us. You're afraid of change. I don't know the future. I didn't come here to tell you how this is going to end. I came here to tell you how it's going to begin. I'm going to hang up this phone, and then I'm going to show these people what you don't want them to see. I'm going to show them a world … without you. A world without rules and controls, without borders or boundaries; a world where anything is possible. Where we go from there is a choice I leave to you.",
 "Whoa.",
 "I know kung-fu.",
 "Yeah. Well, that sounds like a pretty good deal. But I think I may have a better one. How about, I give you the finger [He does] and you give me my phone call.",
 "Guns.. lots of guns...",
 "There is no spoon. ",
 "My name...is Neo!"]

Я предлагаю //ul[preceding-sibling::h2[1][span/@id = 'Neo']]/li, Или если id Атрибут тоже не присутствует, соответственно не релевантен для поиска, тогда на основе ответа в комментарии я думаю, что вы хотите

(//h2[span[contains(@class, 'mw-headline')]])[1]/following-sibling::ul
   [1 = count(preceding-sibling::h2[1] | (//h2[span[contains(@class, 'mw-headline')]])[1])]/li

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

Другие вопросы по тегам