Geonode logo
Geonode Team

Geonode Team

Güncellenme: 7 Ekim 2026

Yayınlanma: 2 Eylül 2026

XPath “preceding-sibling”: Nasıl Çalışır? (Örneklerle)

`preceding-sibling` Ağacın aynı seviyesinde, bağlam düğümünden önce gelen düğümleri seçer. Oldukça basit — ta ki ``preceding-sibling::td[1]`` yazıp beklediğinizden farklı bir hücre elde edene kadar. Bunun nedeni, ``preceding-sibling``’in ters eksen olması ve ters eksende konum numaralandırmasının geriye doğru işlemesidir. Bu kılavuz, hangi öğeleri seçtiğini, numaralandırma tuzağını, `preceding` komutundan farkını ve çözmek için tasarlandığı ayıklama kalıplarını ele almaktadır.

Durumumuzu açıkça ifade edelim: Biz Geonode’iz ve veri toplayan kişilere proxy satıyoruz; dolayısıyla XPath, işimizin bir parçasıdır. Şunu belirtmek gerekir ki, bozuk bir seçici asla bir proxy sorunu değildir. Eğer veri toplama işleminiz çalışmaz hale geldiyse, sitenin işaretlemesi değişmiş demektir ve ne kadar bant genişliği kullanırsanız kullanın ya da adres rotasyonu yaparsanız yapın bu sorunu çözemezsiniz. Bu iki durum birbiriyle karıştırılır çünkü her ikisi de “veri toplayıcım veri döndürmeyi durdurdu” şeklinde ortaya çıkar — ancak bir proxy hatası engellenmiş yanıtlar ve hata sayfaları üretirken, seçici hatası ise hiçbir sonuç vermeyen başarılı yanıtlar üretir. Başka herhangi bir şeyi kontrol etmeden önce ham HTML kodunu kontrol edin. Sayfa mevcutsa ve XPath'iniz boş bir düğüm kümesi döndürüyorsa, bu makale sizin için geçerlidir ve proxy'nizde bir sorun yoktur.

“preceding-sibling” Aslında Neleri Seçer?

XPath 1.0 spesifikasyonu bunu tek bir satırda tanımlar: “preceding-sibling” ekseni “bağlam düğümünün tüm önceki kardeşlerini içerir; bağlam düğümü bir öznitelik düğümü veya ad alanı düğümü ise, “preceding-sibling” ekseni boştur”.

Bu anlamı iki kelime ifade eder. Kardeş düğümler, aynı ebeveyni paylaşan düğümler anlamına gelir — kuzenler, atalar veya farklı derinlikteki düğümler değil. Önceki ise belge sırasına göre daha önce gelen anlamına gelir.

<div>
  <p>First</p>
  <p>Second</p>
  <span id="here">Context</span>
  <p>Third</p>
</div>

span'i bağlam düğümü olarak ele alalım:

preceding-sibling::p        → First, Second
following-sibling::p        → Third
preceding-sibling::*        → both p elements

Özellik düğümüyle ilgili bu uyarıyı hatırlamakta fayda var, çünkü bu, bir tür boş sonucu açıklıyor. Bir özelliğe — //@class — gittiyseniz, o noktadan itibaren preceding-sibling tanım gereği boştur; özelliğin ait olduğu öğeyi çevreleyen öğeler ne olursa olsun. Özellikler hiçbir şeyin kardeş düğümü değildir.

Ters Eksen Tuzağı: “[1]

” İlk Anlamına Gelmez Bu, yanlış sonuçların en yaygın tek kaynağıdır ve spesifikasyon bunun nedenini tam olarak açıklamaktadır.

XPath, ancestor , ancestor-or-self , preceding ve preceding-sibling adreslerini ters eksenler olarak sınıflandırır. Ters eksenler için yakınlık konumu, düğümlerin ters belge sırasına göre sıralanmasıyla belirlenir.

Dolayısıyla, preceding-sibling adresinde 1 numaralı konum, belgedeki ilk düğüm değil, en yakın önceki kardeş düğümdür — yani bağlam düğümünün hemen önündeki düğüm.

<div>
  <p>Alpha</p>
  <p>Beta</p>
  <p>Gamma</p>
  <span id="here">Context</span>
</div>
preceding-sibling::p[1]     → Gamma   (nearest)
preceding-sibling::p[2]     → Beta
preceding-sibling::p[3]     → Alpha   (furthest)
(preceding-sibling::p)[1]   → Alpha   (first in document order)

Parantezler her şeyi değiştirir. Parantezler olmadan, [1] ters eksen boyunca uygulanan bir yüklemdir ve "en yakın" anlamına gelir. Parantezler varsa, eksen sonucu önce belge sırasına göre bir düğüm kümesine toplanır ve [1] bu kümeye göre indeksleme yapar, yani "ilk" anlamına gelir.

İkisinin çakıştığı ileri eksen olan following-sibling ile karşılaştırın:

following-sibling::p[1]     → the next one
(following-sibling::p)[1]   → also the next one

Bu asimetri, following-sibling üzerinden öğrenen kişilerin preceding-sibling tuzağına düşmelerinin nedenidir. Alışkanlık aktarılır, ancak anlam aktarılmaz.

Uygulamada, parantezsiz biçim neredeyse her zaman istediğiniz sonuçtur. “Bu değerin hemen önündeki etiket” genellikle aranan şarttır ve bu da preceding-sibling::label[1] şeklindedir. Parantezleri yalnızca gerçekten “belgedeki ilk öğe” demek istediğinizde kullanın; bu, kulağa geldiğinden daha nadir bir ihtiyaçtır.

preceding-sibling ve preceding Karşılaştırması

Adları birbirine çok benzeyen ve kafa karıştırıcı olan iki farklı eksen; aralarındaki fark önemlidir.

Spesifikasyon, "preceding" ifadesini "bağlam düğümüyle aynı belgede bulunan, belge sırasına göre bağlam düğümünden önce gelen, tüm ataları hariç tutulan ve öznitelik düğümleri ile ad alanı düğümleri hariç tutulan tüm düğümler" olarak tanımlar.

Dolayısıyla, "preceding", atalar hariç tutulduğunda, belgenin herhangi bir derinlikte bulunan, bağlam düğümünden önceki tüm düğümleri ifade eder. "preceding-sibling" ise yalnızca aynı üst düğümü paylaşan düğümleri ifade eder.

<body>
  <header><h1>Title</h1></header>
  <div>
    <p>One</p>
    <span id="here">Context</span>
  </div>
</body>

span'dan:

preceding-sibling::*    → the p only
preceding::*            → the p, the h1, and the header

Ataların hariç tutulması, insanları şaşırtan kısımdır. Span'i içeren div, açılış etiketi kaynakta daha önce görünse bile preceding içinde bulunmaz. Atalar, tanım gereği hariç tutulur; çünkü eksen, sizi içeren öğeyle değil, sizden önce gelen öğelerle ilgilidir.

Ne zaman hangisini kullanmalı? Yapılandırılmış ilişkiler için preceding-sibling — bir etiket ve değeri, bir başlık ve altındaki paragraf, bir satırdaki hücreler. "İç içe geçme durumuna bakılmaksızın bu öğenin üstündeki en yakın başlık" gibi gerçekten gevşek ilişkiler için preceding. preceding daha geniştir, oldukça yavaştır ve istemediğiniz bir şeyle eşleşme olasılığı çok daha yüksektir.

Etiket-Değer Kalıbı

preceding-sibling

'in veri kazıma çalışmalarında var olmasının nedeni budur ve çoğu gerçek işaretleme bu kalıbın bir çeşidi olduğu için bunu doğru bir şekilde kurmaya değer.

Sorun şu: Yalnızca yanında bulunan etiketle tanımlanabilen bir değere ihtiyacınız var. Değerin kendisinde yararlı bir sınıf, bir kimlik ya da ayırt edici hiçbir özellik yok.

Tanım listeleri:

<dl>
  <dt>Price</dt>
  <dd>£42.00</dd>
  <dt>Stock</dt>
  <dd>In stock</dd>
</dl>

Fiyatı seçmek, en yakın önündeki dt

öğesinde "Price" yazan dd

öğesini bulmak anlamına gelir:

//dd[preceding-sibling::dt[1] = 'Price']

Bunu tersten okuyun: her dd

öğesi için, en yakın önündeki dt

öğesini alın; bu metin "Price" ise dd

öğesini saklayın. [1]

'in zorunlu olduğuna dikkat edin. Bu olmadan, herhangi bir önceki dt

eşleşirse preceding-sibling::dt = 'Price'

doğru olur, dolayısıyla ikinci dd

de uygun olur. Bu, gerçek ve sık görülen bir hatadır.

Tablo hücreleri:

<tr>
  <td>SKU</td>
  <td>ABC-123</td>
</tr>
//td[preceding-sibling::td[1] = 'SKU']

Veya iki sütunlu bir özellik tablosunun bir satırında başlık sütunundan:

//th[normalize-space() = 'Weight']/following-sibling::td[1]

Etiket bir th

olduğunda genellikle ikinci biçim tercih edilir, çünkü bu biçim ileriye doğru okunur ve tablonun yapısına uygundur.

Bunların gerçek işaretlemeyle uyumlu olmasını sağlayan sağlamlık iyileştirmeleri:

//dd[preceding-sibling::dt[1][normalize-space() = 'Price']]

normalize-space()

iç boşlukları birleştirir ve uçları kırpar; bu sayede, aksi takdirde tam dize karşılaştırmasını bozacak olan düzgün biçimlendirilmiş HTML ile başa çıkılır. Bu, XPath ile veri kazıma işleminde en yüksek değere sahip tek işlevdir.

Kısmi eşleşmeler için, contains()

daha esnektir ve buna bağlı olarak daha az hassastır:

//dd[preceding-sibling::dt[1][contains(., 'Price')]]

Dikkat edin: contains(., 'Price')

, "KDV hariç fiyat" ve "Geçmiş fiyat" ifadeleriyle de eşleşir. Sayfada bu türden birkaç etiket varsa, birkaç sonuç alırsınız ve kodunuz ilk sonucu sessizce alır.

Başlıklar ve takip eden içerik, bu da aynı kalıbın tersi yönüdür:

//h2[normalize-space() = 'Specifications']/following-sibling::table[1]

Bu başlıktan sonraki ilk tablo. Bu, dokümantasyon ve ürün sayfalarında gerçekten yaygın bir gereksinimdir ve "bu belirli başlıktan sonraki ilk tablo" ifadesini karşılayan bir CSS eşdeğeri yoktur.

Yargılar ve Koşullarla Birleştirme

preceding-sibling

, XPath’in geri kalanıyla birleştirilebilir ve bazı kombinasyonları bilmek faydalıdır.

Aynı düzeyde öğeleri sayma — ilk veya son öğeyi bulmak ya da konumunu kontrol etmek için kullanışlıdır:

//li[count(preceding-sibling::li) = 0]     first li
//li[count(preceding-sibling::li) < 3]     first three

Varlığı kontrol etme — boole bağlamındaki bir düğüm kümesi, boş olmadığı sürece true değerindedir:

//p[preceding-sibling::h2]                 paragraphs with an h2 somewhere before
//p[not(preceding-sibling::p)]             first paragraph among its siblings

Eksenleri zincirleme:

//span[@class='value']/preceding-sibling::*[1]/text()

Etiketten bağımsız olarak hemen önceki öğe ve metni.

Birden fazla koşul:

//td[preceding-sibling::td[1] = 'Status'][normalize-space() != '']

Sırayla uygulanan iki yüklem: "Status" etiketinden sonraki hücre ve boş olmaması.

..

kısayolu hakkında bir not; bu kısayol genellikle bir eksenden daha temizdir:

//dt[.='Price']/following-sibling::dd[1]

aynı sonuç için preceding-sibling

biçiminden genellikle daha okunaklıdır ve belgenin yazıldığı yönde okunur. Bağlantı noktası etiket olduğunda bunu tercih edin.

CSS Seçicilerinin Bunun Yerine Geçebileceği ve Geçemeyeceği Durumlar

“CSS geriye doğru bakamaz” şeklindeki yaygın kanı artık güncellenmelidir.

CSS artık :has() ile kardeş öğe koşulları oluşturabilir. Modern tarayıcılarda yaygın olarak desteklenen bu özellik, bir seçicinin kardeş öğe ilişkisine bağlı olarak koşullu olmasını sağlar:

dt:has(+ dd)          a dt immediately followed by a dd
li:has(~ li.active)   an li with a later sibling that is active

CSS’in hâlâ yapamadığı şey:

Metin içeriğine göre seçim yapmak. [text() = 'Price'] komutunun CSS'de bir karşılığı yoktur. Etiket-değer eşleşmesi metin eşleşmesi olduğu için, etiket-değer kalıbının hâlâ XPath'in alanı olarak kalmasının tek nedeni budur.

İstenilen bir ataya gitmek. :has(), bir tür üst öğe seçimi sağlar, ancak genel bir ata ekseni yoktur.

Ters eksen boyunca indeksleme. CSS'de "bu türdeki en yakın önceki öğe" anlamına gelen bir yapı yoktur.

Ve CSS'in daha iyi yaptığı şeyler: basit olan her şey. Sınıf ve id seçimi, alt öğe ilişkileri, öznitelik eşleştirme. CSS seçicileri daha okunaklıdır, araçlar tarafından daha iyi desteklenir ve çoğu motorunda daha hızlıdır.

Mantıklı yaklaşım, varsayılan olarak CSS'yi kullanmak ve metin eşleştirme veya geriye doğru gezinme gerektiği belirli noktalarda XPath'e başvurmaktır. Bunları tek bir kod tabanında karıştırmak sorun değildir; seçicilerinin %90'ında CSS'yi, zorlu %10'unda ise XPath'i kullanan bir veri kazıyıcı, sadece birine bağlı olan bir kazıyıcıya göre bakımı daha kolaydır.

Performans ve Kırılganlık

İki pratik kısıtlama.

Performans. preceding-sibling, genellikle az sayıdaki kardeş düğüm sayısı ile sınırlıdır — bu sorun teşkil etmez. preceding ise belgedeki bağlam düğümünden önceki her şeyi tarar; bu, büyük bir sayfada yüksek maliyetlidir ve bir döngü içinde karesel bir zaman karmaşıklığına sahiptir. preceding kullanan bir seçici yavaşsa, bunun nedeni neredeyse kesinlikle budur. Daha yakın bir bağlam düğümünden preceding-sibling kullanarak yeniden yazmak, genellikle bu sorunu çözer.

Kırılganlık. Kardeş öğelere dayalı seçiciler belge yapısına bağlıdır; yeniden tasarım ise tam da bu yapıyı değiştirir. preceding-sibling::td[1], birisi bir sütun eklediği gün sessizce bozulur. Hata mesajı çıkmaz; seçici farklı bir hücreyle eşleşir ve verileriniz fark edilmeden yanlış olur.

Gerçekten yardımcı olan üç önlem:

Mümkün olduğunda konum yerine metne dayandırın. //dt[.='Price']/following-sibling::dd[1], listenin yeniden sıralanmasından etkilenmez. (//dd)[3] ise etkilenir.

Çıkardığınız verileri doğrulayın. Bir fiyatın para birimi formatına uyması gerekiyorsa, bunu kontrol edin. Fiyat yerine hisse senedi durumunu döndürmeye başlayan bir seçici, biçimi doğrulayan bir şey olmadıkça fark edilmez.

Varsa, gömülü yapılandırılmış verileri tercih edin. Sayfa, <script type="application/ld+json"> bloğunda JSON-LD içeriyorsa, bunun yerine onu ayrıştırın. Bu, makine tarafından okunacak şekilde tasarlanmıştır, yeniden tasarımlar arasında çok daha kararlıdır ve seçicilerin kırılganlığı sorununu tamamen ortadan kaldırır. Herhangi bir XPath yazmadan önce bunu kontrol etmek, harcadığınız otuz saniyeye değer.

Görünmez yapısal hatalar, proxy'leri test etmenin önemi başlıklı yazımızda anlattığımız sorun türüyle aynıdır — istek başarılı olur, ayrıştırma başarılı olur, ancak veriler yanlıştır.

Ne Zaman Kullanılmamalı

Öğenin kullanılabilir bir tanımlayıcısı olduğunda. Bir id, class veya data özniteliği varsa, bunları kullanın. Yapıya dayalı bir seçici, geliştiricinin kasıtlı olarak seçtiği bir isme dayalı seçiciden kesinlikle daha kırılgandır.

Yapılandırılmış veriler mevcut olduğunda. JSON-LD, mikroveriler, bir XHR'nin arkasındaki bir JSON yükü. Bunların herhangi biri, işlenmiş HTML'yi ayrıştırmaktan daha iyidir.

İlişki gerçekten gevşek olduğunda. Kendinizi preceding::*[5] yazarken bulursanız, yapı size aslında hiçbir şey söylemiyor demektir ve seçici bir sonraki dağıtımda bozulacaktır. Yaklaşımı yeniden gözden geçirin.

CSS yeterli olduğunda. Basit seçimler için CSS daha okunaklıdır ve daha iyi desteklenir. XPath’i metin eşleştirme ve geriye doğru gezinme için saklayın.

Tarayıcıda XPath 2.0 özelliklerine güveniyorsanız. Tarayıcılar, document.evaluate aracılığıyla XPath 1.0’ı uygular ve Selenium da tarayıcıyı takip eder. Yani matches(), düzenli ifadeler, upper-case() ve sıra türleri kullanılamaz. lxml gibi sunucu tarafı kütüphaneleri de ortak API için XPath 1.0'ı kullanır. İnternette bulduğunuz bir kod parçacığı çalışmıyorsa, daha yeni bir sürümde bulunan bir işlev kullanıp kullanmadığını kontrol edin.

Sık Sorulan Sorular

XPath’te preceding-sibling ne işe yarar?

Bağlam düğümüyle aynı ebeveyni paylaşan ve belge sırasına göre ondan önce gelen tüm düğümleri seçer. Ataları, torunları veya ağacın diğer seviyelerindeki düğümleri içermez — yalnızca kardeş düğümleri içerir. Bağlam düğümü bir öznitelik veya ad alanı düğümü ise boş olur.

preceding-sibling[1] neden yanlış öğeyi verir?

Çünkü preceding-sibling ters bir eksendir ve ters eksenlerdeki konum numaralandırması, belge sırasının tersi yönde ilerler. Dolayısıyla [1], belgedeki ilk kardeş düğümü değil, en yakın önceki kardeş düğümü anlamına gelir. Belge sırasına göre ilk olanı bulmak için ekseni parantez içine alın: (preceding-sibling::p)[1].

preceding ile preceding-sibling arasındaki fark nedir?

preceding-sibling yalnızca aynı ebeveyne sahip düğümleri kapsar. preceding ise ataları ve öznitelik düğümleri hariç, belgedeki herhangi bir derinlikte daha önce gelen her düğümü kapsar. preceding çok daha geniştir, önemli ölçüde daha yavaştır ve istenmeyen bir şeyle eşleşme olasılığı çok daha yüksektir.

XPath'te bir değeri etiketine göre nasıl seçerim?

Etiketi metne göre eşleştirin ve ardından bitişik öğeyi alın: //dt[normalize-space()='Price']/following-sibling::dd[1], ya da değer tarafından: //dd[preceding-sibling::dt[1]='Price']. [1] önemli bir rol oynar — bu olmadan, herhangi bir preceding etiketi eşleşirse yüklem doğru olur.

CSS seçicileri preceding-sibling'in yaptığını yapabilir mi?

Kısmen. :has(), modern tarayıcılarda kardeş öğe koşullu seçimi sağlar. CSS'in hâlâ yapamadığı şey, metin içeriğine göre seçim yapmak veya ters eksen boyunca indekslemektir; metin eşleştirme ise tam da etiket-değer modelinin gerektirdiği şeydir. Basit seçimler için CSS'i, metin veya geriye doğru gezinme gerektiğinde ise XPath'i kullanın.

preceding-sibling, Selenium ve tarayıcılarda çalışır mı?

Evet. Tarayıcılar, document.evaluate aracılığıyla XPath 1.0'ı uygular ve Selenium, tarayıcının motorunu kullanır. Eksen, XPath 1.0'dır ve evrensel olarak kullanılabilir. Kullanılamayan ise XPath 2.0 veya sonraki sürümlerdeki özelliklerdir — düzenli ifadeler, matches() ve upper-case() kullanılamaz.

preceding-sibling yavaş mıdır?

Genellikle değildir. Bu, genellikle az sayıda olan kardeş öğelerin sayısıyla sınırlıdır. "preceding" ekseni yavaştır, çünkü belgenin başındaki her şeyi tarar ve bu işlem, ikinci dereceden bir döngü içinde gerçekleşir. Kardeş öğe tabanlı bir seçici yavaşsa, gerçekten "preceding" kullandığınızı kontrol edin.

XPath seçicilerini nasıl daha dayanıklı hale getirebilirim?

Mümkün olduğunda konum yerine metne dayandırın, boşluk farklılıklarının üstesinden gelmek için normalize-space() kullanın, yapı yerine tanımlayıcıları ve veri özniteliklerini tercih edin, HTML'yi ayrıştırmaya başlamadan önce gömülü JSON-LD olup olmadığını kontrol edin ve çıkardığınız verinin biçimini doğrulayın; böylece yanlış öğeye uyan bir seçici sessizce değil, açıkça hata verir.

Sonuç

preceding-sibling, tek bir işlevi olan sınırlı bir araçtır: aynı düzeydeki düğümler arasında geriye doğru erişim sağlamak. Unutulmaması gereken nokta, bunun ters bir eksen olduğu; dolayısıyla [1], “ilk” yerine “en yakın” anlamına gelir ve parantez eklenmesi bu anlamı tamamen tersine çevirir. Bu tek ayrım, kullanıcıların bu yöntemden aldıkları yanlış sonuçların çoğunun sebebidir.

Bu seçicinin asıl değeri, etiket-değer modelindedir — yani, yalnızca yanında bulunan metinle tanımlanabilen bir alanı ayıklamaktır. CSS, :has() ile bu boşluğu kısmen kapatmıştır, ancak hâlâ metin içeriğine göre seçim yapamaz ve metin tam da bir etiketin ne olduğu. İşte bu durumda, XPath alışılmış araçtan ziyade doğru araç olmaya devam eder.

Dikkat edilmesi gereken nokta, yapısal seçicilerin sessizce bozulabilmesidir. Yeni bir sütun, yeniden sıralanmış bir liste, bir sarmalayıcı div; her şey normal görünmeye devam ederken seçiciniz başka bir şeyle eşleşir. Mümkün olduğunca metne dayanın, herhangi bir seçici yazmadan önce gömülü yapılandırılmış verileri kontrol edin ve çıktıyı doğrulayın — çünkü görebildiğiniz hata asla en pahalı olanı değildir.