Самая простая вещь, о которой я могу подумать, это использовать parent
для построения пути элемента обратно к корню (то есть обратно к <html>
) и previous_element
в каждом узле, чтобы выяснить числовой индекс этого узла среди его братьев и сестер.Поскольку будет ровно один <body>
и <html>
(Нокогири добавит их для вас за вашей спиной, если необходимо), вы можете перестать ходить по родителям, как только вы нажмете на узел <body>
.
Алгоритмвыглядит следующим образом:
- Инициализация:
path = [ ]
, n
- это узел, который у вас уже есть. - Установите
s = n
и звоните s = s.previous_element
до s.nil?
и считайтесколько итераций вы сделали, это даст вам позицию n
среди его братьев и сестер.Поместите позицию в index
.Имейте в виду, что позиции XPath основаны на одном. - Сохраните новый компонент пути:
path.unshift('*[' + index.to_s + ']')
. - Установите
p = n.parent
, если p
не является <body>
, тогда n = p
и вернитесь к шагу 2. - Добавьте последние известные нам компоненты:
path.unshift('body').unshift('html')
. - Создайте выражение XPath:
xpath = '/' + path.join('/')
Итак, учитывая некоторый HTML-код вроде этого:
<ul><li>a</li><li><b>b<em>c</em></b></li></ul>
и начальный узел <em>c</em>
, вы получите XPath, подобный этому:
/html/body/*[1]/*[2]/*[1]/*[1]
Не совсем красивоно, по крайней мере, процесс довольно прост, и результирующий XPath будет уникальным.
Если вам нужны пути к большинству узлов в DOM, вы можете начать с корня и нумеровать все узлы на пути вниз,Таким образом, вы могли бы избежать прогулки с братьями и сестрами снова и снова.