Поиск по блогу

понедельник, 19 января 2015 г.

Самый краткий справочник по "простейшему" XPath и полсотни примеров

Сделан по двум книгам. "Самоучитель XML" и "XSLT сборник рецептов". Здесь я "адаптировал" почти все примеры для HTML и выполнил их, используя библиотеку lxml. Не хватило терпения на примеры фильтров, я просто скопировал их из книги...

Помним, что строки запросов строятся по принципу Ось::Тест узла[Предикат]

In []:
#Тест по имени узла (axis)
/                    #Узел документа (нет в списке осей)
self::               #
child::              #Прямой потомок
descendant::         #Все потомки
descendant-or-self::  #

following::          # ниже (по тексту)
following-sibling::        
preceding::          # выше (по тексту)
preceding-sibling::
        
parent::             #
ancestor::           #
ancestor-or-self::   #        
        
attribute::          #        
In []:
#Тест по виду узла
node()              # Узел любого вида
text()              # Текстовый узел
comment()           # Узлы-комментарии
element()           # =element(*) = element(*,*) 
element(name,type)  # Все узлы с именем name
attribute()         # =attribute(@*) = attribute(@*,*)
attribute(@name)
attribute(@name,type)
processing-instruction()
processing-instruction(name)  #Отбирает все инструкции по обработке с имененм обрабатывающей программы name
document-node()
document-node(element(args))
In []:
# Сокращения
//                  #= descendant-or-self::node()/
attribute::*        #= attribute::node()
namespace::*        #= namespace::node()
axis::*             #= axis::element() (axis - любая ось)
*                   #= child::element()
@                   #= attribute:: (@href, @*,)
.                   #= self::node()
..                  #= parent::node()
In []:
# Предикат - True / False
[1]                    #= [position()=1] Первый найденный узе[last()-1]        #= предпоследний узел
AAA/BBB/CCC[last()-1]  #Предпоследний из элементов ССС, вложенных в BBB
//*[count(CCC)=2]      #Все элементы документа с двумя вложенными CCC
/*[count(CCC)=2]       #Все элементы документа с двумя любыми вложенными элементами
//DDD[@name]           #Все DDD c атрибутом name
//DDD@id="3"]          # DDD c атрибутом id="3"
//DDD[not(@name)]      #Все DDD без атрибута name

Далее повторяем примеры (1.1. Применение осей) из книги XSLT сборник рецептов

Для работы с XPath c с кодом из In [5]

from lxml import html in1=html.fromstring(In[89])

Оси потомков

In [89]:
<div id="descendants">
 <div class="parent">
  <span class="x" id="1"/>
  <span class="x"  id="2"/>
  <p class="y"  id="3">
    <span class="x"  id="3-1"/>
    <p class="y" id="3-2"/>
    <span class="x"  id="3-3"/>
  </p>
  <span class="x"  id="4"/>
  <p class="y" id="5"/>
  <p class="z" id="6"/>
  <span class="x"  id="7"/>
  <span class="x"  id="8"/>
  <p class="y" id="9"/>
 </div>
</div>
  File "<ipython-input-89-557295199905>", line 1
    <div id="descendants">
    ^
SyntaxError: invalid syntax
In []:
#Дочерняя ось принимается в XPath по умолчанию. Иными словами, явно указывать ось
child::
#необязательно, но, если вы хотите быть педантом, то можете
# указать. Спуститься по XML дереву глубже, чем на один уровень, позволяют
#оси
descendant::

descendant or self::
#Первая не включает сам контекстный узел, вторая – включает.
In [10]:
in1.xpath('span') , in1.xpath('*/span/@id'), in1.xpath('//span/@id')
# // = descendant-or-self::node()/
#  * = child::element()
Out[10]:
([], ['4', '7', '8'], ['1', '2', '3-1', '3-3', '4', '7', '8'])
In [14]:
#Отображать второй дочерний элемент
in1.xpath('*/span[2]/@id'), in1.xpath('//span[2]/@id')
Out[14]:
(['7'], ['2', '7'])
In [19]:
in1.xpath('*/span[last()-2]/@id') # Последний элемент
Out[19]:
['4']
In [24]:
in1.xpath('//span[2][self::span]/@id'), in1.xpath('//span/@id') # [self::span] - При условии, что элемент называется span
Out[24]:
(['2', '7'], ['1', '2', '3-1', '3-3', '4', '7', '8'])
In [34]:
# Отображать всех потомков (и внуков)
in1.xpath('div/@id'), in1.xpath('div/div/@class')           
Out[34]:
(['descendants'], ['parent'])
In [35]:
in1.xpath('*/div/descendant::p/@id') # Все потомки с именем 'p' во вложеном теге div
Out[35]:
['3', '3-2']
In [37]:
in1.xpath('div/descendant::p/@id') # Все потомки с именем 'p' в корневом теге div
Out[37]:
['3', '3-2', '5', '6', '9']

Оси братьев

In [41]:
in2=html.fromstring(In[40])
In [40]:
<div id="preceding-siblings">
<span id="1"/>
<span id="2"/>
<p id="3"/>
<span id="4"/>
<p id="5"/>
<br id="6"/>
<span id="7"/>
<span id="8"/>
<p id="9"/>
</div>
  File "<ipython-input-40-3946c97984bd>", line 1
    <div id="preceding-siblings">
    ^
SyntaxError: invalid syntax
In [51]:
# Отображать всех братьев-предшественников с именем span
in2.xpath('//span[4]/@id'), in2.xpath('//span[4]/preceding-sibling::span/@id')
Out[51]:
(['7'], ['1', '2', '4'])
In [57]:
# Отображать всех братьев-предшественников с именем p
# и вместо номера по порядку условие с id
in2.xpath('//span[@id="7"]/@id'), in2.xpath('//span[@id="7"]/preceding-sibling::p/@id')
Out[57]:
(['7'], ['3', '5'])
In [59]:
# братья - последователи с именем span
in2.xpath('//span[@id="7"]/@id'), in2.xpath('//span[@id="7"]/following-sibling::span/@id')
Out[59]:
(['7'], ['8'])
In [60]:
# последователи с именем * (любым)
in2.xpath('//span[@id="7"]/@id'), in2.xpath('//span[@id="7"]/following-sibling::*/@id')
Out[60]:
(['7'], ['8', '9'])
In [61]:
# Отображать ближайшего (первого) братьев-предшественников с именем span 
# cмотри In [51] - там все братья
in2.xpath('//span[4]/@id'), in2.xpath('//span[4]/preceding-sibling::span[1]/@id')
Out[61]:
(['7'], ['4'])
In [63]:
# Отображать ближайшего (первого) братьев-предшественников 
# при условии, что его имя span
# cмотри In [51] - там все братья
in2.xpath('//span[4]/@id'), in2.xpath('//span[4]/preceding-sibling::*[1][self::span]/@id')
Out[63]:
(['7'], [])
In [65]:
# Отображать ближайшего (первого) братьев-предшественников 
# при условии, что его имя br
# cмотри In [51] - там все братья
in2.xpath('//span[4]/@id'), in2.xpath('//span[4]/preceding-sibling::*[1][self::br]/@id')
Out[65]:
(['7'], ['6'])
In [66]:
# Отображать  братьев-предшественников 
# при условии, что они не span
# cмотри In [51] - там все братья
in2.xpath('//span[4]/@id'), in2.xpath('//span[4]/preceding-sibling::*[not(self::span)]/@id')
Out[66]:
(['7'], ['3', '5', '6'])
In []:
Далее снова будем использовать пример
In [91]:
print In[89]
<div id="descendants">
 <div class="parent">
  <span class="x" id="1"/>
  <span class="x"  id="2"/>
  <p class="y"  id="3">
    <span class="x"  id="3-1"/>
    <p class="y" id="3-2"/>
    <span class="x"  id="3-3"/>
  </p>
  <span class="x"  id="4"/>
  <p class="y" id="5"/>
  <p class="z" id="6"/>
  <span class="x"  id="7"/>
  <span class="x"  id="8"/>
  <p class="y" id="9"/>
 </div>
</div>

In [110]:
# Первый брат сверху при условии, что у него есть дочка span 
in1.xpath('//span[@id="4"]/@id'), in2.xpath('//span[@id="4"]/preceding-sibling::*[1][span]/@id')
Out[110]:
(['4'], [])
In []:
preceding-sibling::*[1][A] # Пример выше не получился ...
In [114]:
# Первый брат сверху при условии, что его зовут span 
in1.xpath('//span[4]/@id'), in2.xpath('//span[4]/preceding-sibling::p[p][2]/@id')
Out[114]:
(['4'], [])
In []:
# Пример выше не получился ...

Родительская ось и ось предков

Родительская ось (parent::) относится к родителю контекстного узла.

Первое порождает последовательность, которая содержит в точности один элемент, если имя родителя контекстного узла – X, и пуста в противном случае. Второе – это сокращенная запись выражения parent::node()/X , которое отбирает всех братьев контекстного узла с именем X, в том числе и сам этот узел, если он называется X. С помощью осей ancestor:: ancestor or self:: можно перемещаться вверх по XML дереву (переходя к родителю, деду, прадеду и т.д.). В первом случае сам контекстный узел исключается, во втором – включается.

In []:
#Не путайте выражение
parent::X #îçâðàùàåò ðîäèòåëÿ êîíòåêñòíîãî óçëà, ïðè óñëîâèè, ÷òî îí íàçûâàåòñÿ
X, â ïðîòèâíîì ñëó÷àå  ïóñòóþ ïîñëåäîâàòåëüíîñò

../X
In [115]:
print In[89]
<div id="descendants">
 <div class="parent">
  <span class="x" id="1"/>
  <span class="x"  id="2"/>
  <p class="y"  id="3">
    <span class="x"  id="3-1"/>
    <p class="y" id="3-2"/>
    <span class="x"  id="3-3"/>
  </p>
  <span class="x"  id="4"/>
  <p class="y" id="5"/>
  <p class="z" id="6"/>
  <span class="x"  id="7"/>
  <span class="x"  id="8"/>
  <p class="y" id="9"/>
 </div>
</div>

In [126]:
in1.xpath('//span[@id="3-1"]/@id'), in1.xpath('//span[@id="3-1"]/parent::node()/@id') 
Out[126]:
(['3-1'], ['3'])
In [137]:
in1.xpath('//span[@id="3-1"]/@id'), in1.xpath('//span[@id="3-1"]/parent::p/@id'), in1.xpath('//span[@id="3-1"]/parent::*/@id')  
Out[137]:
(['3-1'], ['3'], ['3'])
In [144]:
in1.xpath('//span[@id="3-1"]/@id'), in1.xpath('//span[@id="3-1"]/ancestor::p/@id'), in1.xpath('//span[@id="3-1"]/ancestor::*/@id')  
Out[144]:
(['3-1'], ['3'], ['descendants', '3'])
In [146]:
in1.xpath('//p[@id="3"]/ancestor-or-self::*/@id'), in1.xpath('//p[@id="3"]/ancestor-or-self::*/span/@id'), 
Out[146]:
(['descendants', '3'], ['1', '2', '3-1', '3-3', '4', '7', '8'])

Оси предшественников и последователей

In [152]:
in1.xpath('//span[@id="4"]/preceding::*/@id'), in1.xpath('//span[@id="4"]/preceding::p/@id')
Out[152]:
(['1', '2', '3', '3-1', '3-2', '3-3'], ['3', '3-2'])
In [153]:
in1.xpath('//span[@id="4"]/preceding::p[2]/@id'), in1.xpath('//span[@id="4"]/preceding::p[last()-1]/@id')
Out[153]:
(['3'], ['3-2'])

Фильтрация узлов стр.27 XSLT сборник рецептов

In [157]:
# У всех кроме div, по два атрибута, поэтому фильтровать 
# по количесвтву атрибутов нечего, но можно
in1.xpath('//span[@*]/@id'), in1.xpath('//span[count(@*) >= 2]/@id'), in1.xpath('//div[count(@*) >= 2]/@id')
Out[157]:
(['1', '2', '3-1', '3-3', '4', '7', '8'],
 ['1', '2', '3-1', '3-3', '4', '7', '8'])
In []:
[not(@*)]
X[@a eq '10']
X[Z eq '10']
X[Z ne '10']
X[text()]
X[text()][normalize-space(.)]
X[node()]
X[comment()]
X[number(@a) < 10]
X[preceding-sibling::Z/@y ne '10']
X[. = ' ']
X[false()]
X[true()]
X[count(*) eq 5]
X[count(node()) eq 5]
X[count(@*) | node()) eq 5]
X[1][. eq 'some text']
X[. eq 'some text'][1]

Выше примеры фильтров описния к которым можно посмотреть по ссылке в заголовке раздела



Посты чуть ниже также могут вас заинтересовать

Комментариев нет:

Отправить комментарий