XPath Nedir? Nasıl Kullanılır?
XPath İle Temel İşlemler
R programlama dili ile ilgili son örneklerde XML ile çeşitli işlemler yapmış, Rcrawler ile bir web sitesini taramış ve taradığımız bir web sitesi için site haritaları oluşturmuştuk. Bu yazılar içerisinde bahsi geçen XPath kullanımına ayrıca değinmek faydalı olacaktır.
R, PHP ve daha pek çok programlama dili aracılığı ile, gereksinimler doğrultusunda XML dosyalarını okumakta ve/veya eldeki verileri XML formatında kayıt etmekteyiz. Bu gibi durumlarda verinin ilgili alanlardan (düğüm ve/veya öznitelik) çekilmesi veya bu alanların oluşturulması için çeşitli fonksiyonlar / metodlar kullanılanılmakta. XML ve Temel Kavramlar başlıklı yazımda XML yapısına kısaca değinmeye çalışmıştım. XML yapısı ile ilgili bu yazıya göz atabilirsiniz.

XPath
XPath XML yapısı (HTML de buna dahil) içerisindeki veriyi edinme sürecinde sıklıkla kullanılan, sürekli geliştirilen bir sorgu (query) dili ve yazım-sözdizim-imla (sözdizimi) kılavuzudur1. W3C standardı olan XPath sayesinde XML dosyası içerisinde (node'lar arasında) yol belirteçleri (path expression) sayesinde hareket edebilmekteyiz2. Güncel durumda, 2017 yılında yayınlanan XPath 3.1 sürümüne sahiptir3, ancak XPath 1.0 hala en yaygın olarak kullanılan sürümdür. XPath'e daha basit bir alternatif olarak başka bir W3C standardı olan CSS Seçiciler örnek gösterilebilir.
Sözdizimi ve Semantik
Her dilde olduğu üzere, XPath ile işlemler gerçekleştirebilmek adına izlenmesi ve uyulması gereken bazı kurallar bulunmakta. Önceki yazılarla da ilişkili olması adına site hartası yapısını kullanacağım.
Tam Yazım Biçimi | Kısaltılmış Biçim | Açıklama |
---|---|---|
ancestor | İlgili node'u kapsayan üst node'ları ifade eder ve geriye doğru işlem gerçekleştirir | |
ancestor-or-self | İlgili nodun da belirtilen kapsayıcı ile ilişkilenmesi durumunda onu da dahil eder ve geriye doğru işlem gerçekleştirir | |
attribute | @class |
Node'a ait özniteliği seçer. Diğer yazım biçimi attribute::class |
child | loc |
Alt seviyedeki node'u belirtir. Diğer yazım biçimi child::loc |
descendant | İlgili nodun alt seviyesindeki node'ları ifade eder | |
descendant-or-self | // |
İlgili nodun alt seviyesindeki node'ları ve eşleşiyorsa kendisini de seçer. Diğer yazım biçimi /descendant-or-self::node()/ |
parent | .. |
Kapsayıcı node'u ifade eder. Diğer yazım biçimi parent::node() |
preceding | Geriye doğru ilk node'u seçer | |
preceding-sibling | Geriye doğru ilk node'u ve onun kardeşini seçer | |
self | . |
İlgili node'un kendisini ifade eder. Diğer yazım biçimi self::node() |
Aşağıda yer alan işlemleri herhangi bir XPath Tester ya da tarayıcınızın web geliştirici aracı ile test edebilirsiniz4.
<urlset>
<url>
<loc>https://domain.com</loc>
<priority>1.00</priority>
<lastmod>2021-03-26T11:40:09+03:00</lastmod>
<changefreq>always</changefreq>
</url>
<url>
<loc>https://domain.com/about</loc>
<priority>1.00</priority>
<lastmod>2021-03-26T11:40:09+03:00</lastmod>
<changefreq>always</changefreq>
</url>
</urlset>
XPath ile bir node (düğüm) sıralaması 1 (bir) ile başlar. Dolayısıyla, parent-child ve aynı seviyedeki node'lar (text node da dahil) arasındaki ilişkinin bu sıralamaya göre ele alınması gerekir. Node'lar arasındaki hiyerarşi /
(slash) ile ifade edilir.
Yukarıdaki örnek XML içeriği içerisinde node içeriklerine ulaşmak için en temelden daha kapsamlı biçimlere doğru örneklendirmelere başlayalım. İlk olarak, kesin (absolute) bir hiyerarşi belirten yönteme bakalım.
/urlset/url/loc
Görüldüğü üzere kökten (root) itibaren her seviye ayrı bir şekidle belirtilmiş durumda. Node seviyesi ne kadar derinleşir ve karmaşıklaşırsa kesin tanımlarda o kadar hata yapma olasığı ortaya çıkacaktır. Bu sıralı sözdizimi bize https://domain.com
ve https://domain.com/about
değerlerini döndürecektir. Görüldüğü üzere hiyerarşik olarak tüm node tanımları belirtilmiş duruma. Ancak, kapsamlı yapılarda elbette bu sıralı yapının takip edilmesi mümkün ya da pratik olmayabilir. Bu durumda, yine hiyerarşik olarak genel ve/veya özel tanımlar yapılabilmekte. İlişkisel (relative) olan bu yapıda kesin bir yol belirtilmemektedir.
//loc
Bu tanım da yine bize aynı değerleri döndürecektir. XPath, sıralaması her ne olursa olsun loc
isimli node'u tarar, bulur ve içeriğini döndürür. Özel karakterlerle de bu işlem gerçekleştirilebilir. Aşağıdaki örnek içerisinde yer alan *
(asterisk) hiyararşi gözetmeksizin tarama yapılmasına izin verir.
//*/loc
Bu ifade ile aradaki node'lar kriter olarak alınmaksızın loc
içerikleri döndürülür. //./loc
da bize aynı sonucu döndürecektir. Ancak, burada yer alan .
aslında self::node()
ile aynı şekilde node'un kendisini temsil eder. Ayrıca, h3[.='See also']
biçiminde olduğu gibi text()
yerine de kullanılabilmektedir. ..
veya parent::node()
ile de parent node'a erişebiliriz.
Sıralama belirtmek için ise []
ile de değer belirtmemiz yeterli olacaktır. Aşağıda hem sıra hem de ..
içeren bir örnek kullanım yer almakta.
//../url[1]/loc
Yukarıdaki tanım bize sadece ilk url
içerisindeki loc
içeriğini verecektir. Şimdi bu sıra konusunu test format olarak ifade edilen tanımlayıcılarla biraz daha detaylandıralım.
//../url[1]/node()
Bu tanım bize ilk node tarafından kapsanan diğer node listesini verecektir. node()
dışında text()
ile textNode içeriğine, comment()
ile de <!-- Comment -->
şeklinde belirtilen yorumlara erişebiliriz. Şimdi birkaç konuyu bir arada ele alalım. Aşağıdaki 2 satırı ayrı ayrı olarak deneyelim.
(//url/*)[1]
//url/*[1]
Parantez içerisindeki tanım burada genel değil bir node'un kendisini işaret eder. Sonrasında gelen [1]
ise bu node'un sırasını belirtmektedir. [2]
olarak sırayı değiştirdiğimizde priority
içeriğine ulaşırız. Parantezleri kaldırdığımızda ise ilgili sorguyla eşleyen tüm node'lar içerisinde belirtilen sıra değerleri dönecektir. Şimdi, bu sorguya bir de text()
ekleyelim.
(//url/*/text())[1]
(//url/*[2]/text())[1]
//url/*/text()[1]
//url/*[2]/text()[1]
İlk tanım bize ilk node içeriği içerisinde yer alan loc
değerini döndürürken, ikinci tanım yine bu node içerisindeki priority
değerini döndürür. Üçüncü tanım tüm url
node'ları arafından kapsanan node'ların değerlerini sunarken, son tanım tüm bu node'lar içerisindeki 2. node değerini döndürür.
Elbette sadece node'lar arasında değil, bir node'a ait özniteliklere (attribute) de erişebilmekteyiz. Bu işlem için @
veya attribute::
tanımlarını kullanabiliriz.
Yukarıdaki örnek XML yapısında bir attribute olmadığı için farklı bir örnek ekleyeceğim.
<html lang="en">
<head>
<title>Title</title>
</head>
<body class="body-class">
<h1 id="mainTitle" class="title">Main Title</h1>
<p lang="en" class="text p-1">Lorem ipsum dolor sit amet, <br /> consectetur <a href="https://google.com" class="link ">adipiscing</a> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p lang="tr" class="text p-2">Ut enim ad minim veniam, quis nostrud <a href="https://google.com" class="link" target="_blank" rel="noopener">exercitation ullamco</a> laboris nisi ut aliquip ex ea commodo consequat.</p>
<p lang="fr" class="text p-3">Excepteur sint occaecat cupidatat non proident, <a href="https://google.com" class="link external" target="_blank" rel="noopener">exercitation</a> in culpa qui officia deserunt mollit anim id est laborum.</p>
</body>
</html>
Şimdi yukarıdaki HTML örneği içerisinde yer alan text p-1
class'ına sahip paragrafa ulaşalım.
//body/*[@class='text p-1']
//body/p[@class='text p-1']
Şimdi bu tanıma aşağıdaki eklemeleri yaptığınızda nelerin değiştiğine bir bakın.
//body/p[@class='text p-1']/*
//body/p[@class='text p-1']/node()
//body/p[@class='text p-1']/text()
//body/p[@class='text p-1']/..
//body/p[contains(@class, 'text')]
İlgili tanımları daha da kompleks hale getirebiliriz ve hatta çeşitli koşullara da sözdizimi içerisinde yer verebiliriz.
//body/p[@lang='tr' and @class='text p-2']/a[@href, contains = 'google']/@target
//p/a[contains(text(), 'exercitation')]/parent::node()[@lang = 'tr']
//p/a[contains(., 'exercitation')]/parent::node()[@lang = 'tr']
//p[starts-with(., 'Ut')]/a[contains(text(), 'exercitation')]
//p/a[@class = 'link secure' and contains(text(), 'exercitation')]
XPath kullanımı ile ilgili örneklere operatörler ve concat()
, length()
, substring()
gibi fonksiyonlar da dahil edilebilir5 6. Ancak, XPath ile ilgili örneklere JavaScript7 8 ve Selenium Driver9 bağlamında ayrıca değineceğim için şimdilik örnekleri sonlandırıyorum.
- XPath 3. Wikipedia ↩
- XPath. Wikipedia ↩
- XML Path Language (XPath) 3.1 ↩
- XPath Online Real-time Tester, Evaluator and Generator for XML & HTML ↩
- Sevilay Ağıl. (2020). Etkili XPATH Kullanımı ↩
- Barış Ekici. (2020). Selenium Web Driver ve XPath ↩
- Introduction to using XPath in JavaScript. MDN Web Docs ↩
- XPath functions. MDN Web Docs ↩
- RSelenium: R Bindings for 'Selenium WebDriver' ↩