본문 바로가기

WEB&서버

[WEB] XPath axes

 DOM 상에서 엘리먼트를 찾을 때 단순히 해당 엘리먼트가 가진 속성이나 텍스트 값을 매칭시켜서 찾을 수도 있지만, parent~ / child~ 이름을 가지는 함수를 사용하여 DOM 트리 상의 관계를 기반으로 엘리먼트를 찾는 방법도 있다.

 XPath axes는 위 언급한 방식에 대응되는 기능이다.  XPath가 현재 가리키고 있는 컨텍스트 노드에 대한 상대적인 노드를 찾는 데 사용하는 방식으로, 특정 노드의 부모 - 자식 관계를 기반으로 노드를 식별할 수 있다.

XPath specification 상에는 총 13개의 axes가 존재한다고 한다. 일부만 살펴보면 다음과 같다.

  • ancestor: 상위 노드부터 루트 노드까지 컨텍스트의 모든 조상을 나타낸다. (루트노드는 포함 X)
  • descendant: 컨텍스트 노드의 모든 자식(자손)을 가리킨다.
  • parent: 컨텍스트 노드의 부모를 가리킨다. ( 바로 위 )
  • child: 컨텍스트 노드의 자식 노드들을 가리킨다. ( 바로 아래 )
  • preceding: 현재 노드 앞에 오는 ancestor, attribute, namespace 제외 노드들을 가리킨다.
  • preceding-sibling: 현재 노드 앞에 오는 형제 노드들을 가리킨다.
  • following: 현재 노드 뒤에 오는 ancestor, attribute, namespace 제외 노드들을 가리킨다.
  • following-sibling: 현재 노드 뒤에 오는 형제 노드들을 가리킨다.
  • attribute: @속성으로 표현하는 그것

ex) //span[text()='카페']/ancestor::ul/child::*

네이버에서 위 XPath을 검색한 모습

text 값이 '카페'인 span 노드를 찾는다. 해당 노드로부터 거슬러 올라가면서(ancestor), ul 엘리먼트를 탐색한다. 해당 엘리먼트는 자식으로 메뉴들을 포함하는데, 이는 child를 이용하여 찾는다. 결과적으로 '카페' 메뉴가 포함되어 있는 메뉴 리스트의 모든 메뉴를 가져올 수 있었다.

 한편, axes 없이도 부모를 탐색할 수 있는 방법이 있다. 대괄호 안에 자식 노드를 계층에 따라 하나씩 포함하는 구조로 표현하면 된다.

ex) //ul [li [a [span [normalize-space()='메일']]]]/li

위와 동일한 사진이지만, 다른 XPath 탐색문을 사용했다.
각 노드 사이의 관계

앞에서 보인 예시와 동일하게 text 값이 '카페'인 노드를 찾는다. 해당 표현은 부모 노드의 대괄호로 들어가며, 찾고 싶은 ul이 등장할 때까지 대괄호를 반복하면서 거슬러 올라간다. ul에 도달했다면/li을 이용하여 li 엘리먼트인 자식 노드들을 가리킨다. 이 문법의 탐색 결과는 앞에서 axes을 이용한 방법과 같다.

ex) //div [div [input [@id='id']]]//input

id input element 기반으로 id / password input 엘리먼트 가져오기
html 일부 발췌

  input 중 @id = 'id'인 엘리먼트를 찾는다. 이후 div를 2번 거슬러 올라간 후 상대 경로 지정 ( // )을 이용하여 input 엘리먼트들을 가져온다.

  만약 특정 요소로부터 부모 요소를 거슬러 탐색하는데, 부모 요소들이 id, class 등으로 구분되지 않는 상태로 연속된다면 구체적으로 경로를 지정할 필요가 있을 것 같으며, 이 경우 parent:: 구문을 명시해야 하는 axes 방식보다 axes을 사용하지 않는 방식이 더 좋을 수 있을 것 같다.

 예를 들어 위의 id / password input element를 가져오는 코드를 axes 기반으로 작성하면 다음과 같다.

ex) //input [@id='id']/parent::div/parent::div//input

 parent::div를 2번 반복하는 이유는 컨텍스트 노드인 input [@id = 'id']에서 부모로 거슬러 올라가는 과정에서 먼저 있는 div가 매칭되기 때문이다. ancestor::div/input도 동일하며, ancestor::div//input은 html 파일 내 모든 input과 매칭된다. 따라서 정확히 로그인 폼 내의 아이디 / 패스워드 요소만 매칭하려면 2번의 parent::div을 반복해야 한다.

 사실 대부분의 경우 html 내 엘리먼트를 꾸미거나 식별하기 위한 목적으로 class / id 속성을 사용하기 때문에 axes까지 사용해야 할 일은 많지 않을 것 같다. 다만 웹 사이트 제작자가 자신의 사이트를 어떻게 구성할지는 자유이므로, 주변 노드와의 관계를 통해 다른 노드를 식별할 수 있는 axes가 있다는 것을 알아둬야겠다.

'WEB&서버' 카테고리의 다른 글

[HTTP Status] 429 Too Many Requests  (0) 2023.09.30
[WEB] XPath로 SVG 요소 식별하기  (0) 2023.07.30
[WEB] XPath  (0) 2023.07.30
[부가정보] https 로컬에서 사용하기  (0) 2023.07.28
[WEB] MutationObserver API  (0) 2023.03.08