IT/WEB

XPATH 사용법·작성법

개발자 두더지 2021. 1. 13. 22:50
728x90

일본의 블로그 글을 번역한 포스팅입니다. 오역 및 직역이 있을 수 있으므로 내용 지적 언제나 환영합니다.

 

초급편


XPath란?

 XPath는 XML문장 속의 요소, 속성 등을 지정하기 위한 언어이다. XPath에는 XML문장을 트리로서 다루기 때문에, 요소나 속성의 위치를 지정하는 것이 가능하다. HTML도 XML의 일종으로 간주 될 수 있으므로, XPath를 사용하여 HTML문장의 요소를 지정하는 것도 가능하다.

 예를 들어

<html>
...
  <body>
    <h1>원피스</h1>
    <div class="item">
      <span class="brand">iQON</span>
      <span class="regular_price">12,000원</span>
      <span class="sale_price">10,000원</span>
    </div>
  </body>
</html>

 위와 같은 HTML의 경우라면, 아래와 같이 트리 구조로 나타낼 수 있다.

 XPath는 이러한 트리 구성으로 부터 요소를 얻어낸다.

 

XPath의 기초

 XPath는 로케이션 패스에의해 표현된다. 로케이션 패스란 트리구조로부터 특정 요소를 지정하기 위한 식과 같은 것이다. 로케이션 패스는 URL과 같이 "/"를 이용해 요소를 연결하여 작성한다.

 아까봤던 코드를 다시 한 번 살펴보자. 이 HTML 코드에서 "h1요소"를 얻어내는 XPath는 트리 구조상의 순서로 보자면 "html 요소 → body요소 → h1요소"로 지정하는 것이다. 

<html>
...
  <body>
    <h1>원피스</h1>
    <div class="item">
      <span class="brand">iQON</span>
      <span class="regular_price">12,000원</span>
      <span class="sale_price">10,000원</span>
    </div>
  </body>
</html>

 로케이션 패스로 표현하자면, 다음과 같다.

/html/body/h1

 즉, 아까의 그림을 다시 살펴보자면 다음과 같이 된다.

 

중급편


속성에 대해서

 class와 같이 요소를 결부하는 속성을 XPath에서는 "@"로 표현한다. "12,000원"이라는 요소를 취득하고 싶은 경우에는 XPath에는 아래와 같이 쓸 수 있다.

/html/body/div/span[@class='regular_price']

 

//를 사용하여 중간의 패스를 생략

/html/body/div/span[@class='regular_price']

 XPath는 "//"를 이용하여 노드 패스를 생략할 수 있다. "//"는 descendant-or-self의 생략형이다. 즉 기점이 되는 노드의 모든 자식들의 집합을 일컫는다. 

 예를 들어,

/html/body/div/span[@class='regular_price']

 위의 XPath를 "//"를 이용하여 생략하면 아래와 같이 쓸 수 있다.

//span[@class='regular_price']

 

지정하는 문자열이 포함되어 있는 요소를 취득하는 contains

 contains는 지정하는 문자열이 포함되어 있는 요소를 얻을 수 있다.

<img class="large_image">
<img class="small_image">
<img class="thumbnail">

 위의 HTML로부터 class에 image가 붙어 있는 것을 모두 얻고 싶은 경우, "contains"를 사용하면 된다. contains함수는 제1요소 문자열 내에, 제2인수 문자열이 포함되어 있는가를 조사하는 함수이다.  class에 image가 붙은 요소를 모두 취득하는 조건을 contains를 이용해 표현하자면 다음과 같이 쓸 수 있다.

//img[contains(@class, 'image')]

 이 XPath는 class에 image가 포함된 img요소를 취득한다는 의미가 된다.

 또한 텍스트 중에 포함되어 있는 문자를 지정하고 싶은 경우에는 text()와 contains를 합쳐서 사용한다.

<div class="item">
  <h1>원피스</h1>
  <div class="price">12,000원</div>
    <div class="description">
       겨울에 이쁘게 입을 수 있는 니트 원피스입니다.
       상품번호:100000000
    </div>
  </div>
</div>

 위의 HTML부터 "상품번호"라는 문자가 포함되어 있는 요소를 지정하고 싶은 경우에는, 아래와 같이 쓸 수 있다.

//div[contains(text(), '상품번호')]

 더욱이 "지정하는 문자가 포함된 JavaScript'를 얻고 싶은 경우에는 아래와 같이 쓸 수 있다.

//script[contains(text(), 'stock')]

 

요소의 위치 지정:position

 요소의 위치를 지정하고 싶은 경우에는 position을 사용한다. position은 지정한 노드로부터 몇 번째의 노드를 취득할 것인지 지정할 수 있다.

<ul>
  <li>색상 선택</li>
  <li>화이트</li>
  <li>레드</li>
  <li>블루</li>
</ul>

 위의 HTML에서 position()을 사용해보자. 

position()= 

 위의 HTML에서 "레드"는 li요소 중 세 번째이므로 position을 이용하면 아래와 같이 표현할 수 있다.

//li[position()=3]

또한 position()=3을 생략하여 아래와 같이 쓸 수 있다.

//li[3]

position()>

 "색상 선택"이외의 li요소를 얻고 싶은 경우에는 position을 이용하자면 아래와 같이 쓴다.

//li[position()>1]

 "색상 선택"은 li요소의 첫 번째이므로, position()>1은 "색상 선택" 외의 li요소를 의미한다.

 

텍스트 노드의 취득

 요소 내의 텍스트를 취득하고 싶은 경우에는 "text()"라는 텍스트 노드를 이용한다.

<p>S사이즈<span>레드</span></p>

 이 HTML로 부터 "S사이즈"라는 문자열만을 얻고 싶은 경우, text()를 사용하면 아래와 같이 쓸 수 있다.

//p/text()

 

not

not은 술어에서 부정을 표현한다.

<img src="http://sample.ne.jp/sample_main_image.jpg">
<img src="http://sample.ne.jp/sample_sub_image.jpg">
<img src="http://sample.ne.jp/sample_thumbnail.jpg">

 위 HTML에서 http://sample.ne.jp/sample_main_image.jpg 이외의 @src를 취득하고 싶은 경우 not을 사용하자면 다음과 같이 쓸 수 있다.

//img[not(contains(@src, 'main'))]/@src

 

or

or조건문을 XPath에서도 사용할 수 있다.

<img src="http://sample.ne.jp/sample_100.jpg">
<img src="http://sample.ne.jp/sample_200.jpg">
<img src="http://sample.ne.jp/sample_300.jpg">
<img src="http://sample.ne.jp/sample_400.jpg">
<img src="http://sample.ne.jp/sample_500.jpg">
<img src="http://sample.ne.jp/sample_600.jpg">

 위의 HTML에서 100 또는 300을 포함하는 src를 취득하고 싶은 경우 or를 사용하자면 아래와 같이 쓸 수 있다.

//td[contains(@src,'100') or contains(@src, '300')]

 또한, 100 혹은 300이외의 src를 얻고 싶은 경우에는 not과 or를 합쳐 다음과 쓸 수도 있다.

//td[not(contains(@src,'100') or contains(@src, '300'))]

 

and

 and 조건도 마찬가지로 XPath에서 사용할 수 있다.

<img src="http://sample.ne.jp/main_100.jpg">
<img src="http://sample.ne.jp/main_300.jpg">
<img src="http://sample.ne.jp/sub_100.jpg">
<img src="http://sample.ne.jp/sub_300.jpg">
<img src="http://sample.ne.jp/thumbnail_100.jpg">
<img src="http://sample.ne.jp/thumbnail_300.jpg">

 이 HTML에서 "main"과 "300"을 포함하는 src를 취득하고 싶은 경우에, and를 사용하여 어래와 같이 작성할 수 있다.

//img[contains(@src, 'main') and contains(@src, '300')]

 

 

고급편


축·노드 테스트·술어 부분

로케이션 패스에서 요소를 표현할 때, "축·노드 테스트·술어 부분"이라고 불리는 것을 사용하여 표현한다.

이름 설명
트리 상의 위치관계를 지정한다.
노드 테스트 선택하는 노드의 형과 이름을 지정한다.
술어 부분 선택하는 노드의 집합을 임의의 식을 사용해 더욱 자세히 지정한다.
/html/body/h1

 이라는 XPath는 노드 테스트만으로 요소를 표현했다. 노드 테스트만으로는 원하는 요소를 취득할 수 없는 경우는 축이나 술어 부분을 사용하여 좀 더 상세히 요소를 지정할 수 있다.

 

술어 부분에 대해서

트리 구조도에 class 등의 속성 정보를 추가하자면 다음과 같이 된다.

 

축은 트리상의 위치관계를 일컫는 것이다. 축의 대표적인 종류로는 다음과 같은 것들이 있다.

이름 설명
self 노드 자신을 의미
child 노드의 자식 노드의 집합
parent 노드의 부모 노드의 집합
ancestor 노드로부터의 조상 노드의 집합(부모를 포함한)
descendant 노드로부터의  자손 노드의 집합
following 노드의 뒤에 따라오는 노드의 집합
preceding 노드의 앞에 나오는 노드의 집합
following-sibling 노드와 같은 계층며, 그 뒤에 따라오는 형제 노드의 집합
preceding-sibling 노와 같은 계층이며, 그 앞에 나오는 형제 노드의 집합

 축을 앞에서 본 그림에 추가하자면 다음같다. 어디를 기점으로 생각하느냐에 따라 위치 관계가 달라지지만, 이번에는 "div"를 중심으로 축을 생각해보자.

 앞서 말했듯 이번에는 div를 중심으로 생각하기로 했으므로, div자체는 기점이 되는 노드 즉 "self"가 된다. body는 dib로부터 봤을 때 한 단계 위의 계층, 즉 부모가 되므로 "parent", 3개의 span은 한 계층 아래에 위치하므로 자식인 "child"가 된다. 또한 h1요소는 div와 같은 계층이지만 div보다 앞에 위치하므로 축 중 "preceding-sibling"이 된다. 

 

child의 생략에 대해

 명시적으로 축을 지정하지 않는 경우에는 축을 child로 본다. 그러므로 기본적으로 child는 생략할 수 있다.

 

//를 이용해서 중간의 패스를 생략

 앞서 보았듯, "//"는 descendant-or-self의 생략형이다. 즉 기점이 되는 노드의 자손 집합을 의미한다. 이 "//"를 이용해 패스를 생략할 수 있다. 예를 들어,

/html/body/div/span[@class='regular_price']

 XPath를 "//"를 이용해 생략하면, 아래와 같이 쓸 수 있다.

//span[@class='regular_price']

 

축::노드 테스트[술어]

축·노드 테스트·술어부분을 사용하여 XPath를 작성하는 경우, "축::노드테스트[술어]"와 같이 작성하여 요소를 지정한다.

<html>
...
  <body>
    <h1>원피스</h1>
    <div class="item">
      <span class="brand">iQON</span>
      <span class="regular_price">12,000원</span>
      <span class="sale_price">10,000원</span>
    </div>
  </body>
</html>

 이 HTML로부터 "12,000원"의 요소를 얻는 XPath는 아래와 같이 쓸 수 있다.

/html/body/div/span[@class='regular_price']/self::text()

 또한, 생략형을 쓰자면, 다음과 같다.

//span[@class='regular_price']

 여기까지 축·노드 테스트·술어 부분에 대해 설명했다. 축·노드 테스트·술어 부분에대해 더욱 자세히 알고 싶은 경우 링크(일본어 자료)를 참고해보길 바란다.

 

지정한 요소보다 뒤에 있는 형제 요소를 갖고 오는: following-sibling::

 축에 관련된 부분에서 설명했지만, "following-sibling::"은 기점이 되는 노드와 같은 꼐층에 있으며, 그 기점이 되는 노드보다 "뒤'에 따라오는 형제 노드의 집합을 표현하는 축이다. 이 "following-sibling::"은 테이블 요소를 지정할 때 활약한다.

<table>
  <tr>
    <td>생산 국가</td>
    <td>한국</td>
  </tr>
  <tr>
    <td>소재</td>
    <td>면</td>
  </tr>
</table> 

 위와 같은 테이블이 준비되어 있는 경우, "면"을 어떻게 취득할까?

//td[4]

 이와 같이 쓸 수 있지만, td요소가 늘거나 줄어드는 변화가 있는 경우, //td[4]의 내용도 그에 따라 변하므로 변화에 대응할 수 있다. 그러므로 이러한 때에 "following-sibling::"을 사용한다. "면"을 얻고 싶은 경우에는 아래와 같이 쓸 수 있다.

//td[contains(*, '소재')]/following-sibling::td[1]

 

지정된 요소보다 앞의 형제 요소를 갖고 오는: precedng-sibling::

"precedng-sibling::"은 following-sibling::에 대응하는 축으로 기점이 되는 노드와 같은 계층이며, 기점이 되는 노드보다 앞에 나오는 형제 노드의 집합을 일컫는 축이다. 이것도 테이블 요소를 지정할 때에 유용하다.

<table>
  <tr>
    <td class="title">22cm</td>
    <td class="title">23cm</td>
    <td class="title">24cm</td>
  </tr>
  <tr>
    <td class="title">레드</td>
    <td class="inventory">재고 있음</td>
    <td class="inventory">재고 있음</td>
  </tr>
  <tr>
    <td class="title">블루</td>
    <td class="inventory">재고 있음</td>
    <td class="inventory">재고 없음</td>
  </tr>
  <tr>
    <td class='title'>그린</td>
    <td class='inventory'>재고 있음</td>
    <td class='inventory'>재고 있음</td>
  </tr>
</table>

 위의 HTML에서 색(레드, 블루, 그린)을 모두 취득하고 싶은 경우, 어떻게 XPath를 작성할까. 단순히 아래와 같이 작성하면 색 뿐만 아니라 사이즈까지 얻어진다. 

//td[@class='title']

 따라서 색 요소만을 얻고 싶은 경우, 아래와 같이 작성한다.

//td[@class="inventory"][1]/preceding-sibling::td

 이와 같이 작성하여 class 속성이 inventory의 td요소의 첫 번째 (td[@class='inventory'][1])의 1개 앞의 요소 (preceding-sibling::td) 즉 색을 얻을 수 있다.

 

중복되는 내용 없이 추출

<table>
  <tr>
    <td>레드</td>
    <td>레드</td>
    <td>레드</td>
  </tr>
  <tr>
    <td>블루</td>
  </tr>
  <tr>
    <td>그린</td>
  </tr>
</table>

 위와 같이 테이블이 구성되어 있을 때, 색에 대한 정보가 중복되지 않도록 추출하고 싶은 경우 아래와 같이 쓸 수 있다.

//td[not(.=preceding::td)]

 이 XPath는 td요소 중에 preceding, 즉 앞에 나오는 요소와 일치하지 않는 것들을 얻어 준다.

 

 

XPath관련 편리한 서비스


 마지막으로 XPath를 취득할 때에 추천하고 싶은 확장 기능을 소개한다. 그것은 "XPath Helper'이다. XPath Helper은 브라우저의 요소에 커서를 올리는 것만으로 XPath를 알려 주는 우수한 chrome 확장 기능이다.

사용예

 링크에서 다운로드 할 수 있다. 이 확장 기능을 사용하는 방법은 chrome 북마크바의 x라고 적힌 아이콘을 클릭, 혹은 쇼트커트로는 [Ctrl+Shift+X]로 기동할 수 있다. 그리고 취득하고 싶은 요소를 Shift를 누르고 선택하면 요소의 XPath를 간단히 취득할 수 있다.

 


참고자료

qiita.com/rllllho/items/cb1187cec0fb17fc650a

728x90