راهنمای XPath

مرجع کامل XPath به زبان فارسی — برای استخراج داده از HTML در Scrapy و ابزارهای وب‌اسکرپینگ. بر پایه devhints.io/xpath

تست و آزمایش

محیط تست XPath

برای آزمایش عبارت‌های XPath می‌توانید از محیط‌های آنلاین استفاده کنید:

کنسول مرورگر

در Firefox و Chromium می‌توانید مستقیماً در کنسول توسعه‌دهنده XPath اجرا کنید:

$x("//div")

انتخابگرها

جدول‌های زیر معادل CSS و XPath را نشان می‌دهند.

انتخابگرهای فرزند (Descendant)

CSSXPathتوضیح
h1//h1همه تگ‌های h1
div p//div//pp داخل div (هر سطحی)
ul > li//ul/lili فرزند مستقیم ul
ul > li > a//ul/li/aلینک داخل li داخل ul
div > *//div/*همه فرزندان مستقیم div
:root/ریشه سند
:root > body/bodyتگ body

انتخابگرهای ویژگی (Attribute)

CSSXPathتوضیح
#id//*[@id="id"]عنصر با id مشخص
.class//*[@class="class"]تقریباً معادل class
input[type="submit"]//input[@type="submit"]input با type مشخص
a#abc[for="xyz"]//a[@id="abc"][@for="xyz"]چند ویژگی همزمان
a[rel]//a[@rel]وجود ویژگی rel
a[href^='/']//a[starts-with(@href, '/')]شروع با /
a[href$='pdf']//a[ends-with(@href, '.pdf')]پایان با .pdf
a[href*='://']//a[contains(@href, '://')]شامل ://
a[rel~='help']//a[contains(@rel, 'help')]تقریباً معادل

انتخابگرهای ترتیب (Order)

CSSXPathتوضیح
ul > li:first-of-type//ul/li[1]اولین li
ul > li:nth-of-type(2)//ul/li[2]دومین li
ul > li:last-of-type//ul/li[last()]آخرین li
li#id:first-of-type//li[1][@id="id"]اولین li با id
a:first-child//*[1][name()="a"]اولین فرزند که a است
a:last-child//*[last()][name()="a"]آخرین فرزند که a است

خواهر و برادر (Siblings)

CSSXPathتوضیح
h1 ~ ul//h1/following-sibling::ulul بعد از h1
h1 + ul//h1/following-sibling::ul[1]ul بلافاصله بعد از h1
h1 ~ #id//h1/following-sibling::*[@id="id"]خواهر با id مشخص

معادل jQuery

jQueryXPathتوضیح
$('ul > li').parent()//ul/li/..والد
$('li').closest('section')//li/ancestor-or-self::sectionنزدیک‌ترین section
$('a').attr('href')//a/@hrefمقدار ویژگی href
$('span').text()//span/text()متن داخل span

سایر موارد

CSS / موردXPathتوضیح
h1:not([id])//h1[not(@id)]بدون id
تطابق متن//button[text()="Submit"]متن دقیق
تطابق جزئی متن//button[contains(text(),"Go")]شامل Go
مقایسه عددی//product[@price > 2.50]قیمت بیشتر از ۲.۵
دارای فرزند//ul[*]ul با هر فرزندی
فرزند مشخص//ul[li]ul با li
منطق OR//a[@name or @href]یکی از ویژگی‌ها
اتحاد (Union)//a | //divترکیب نتایج

بررسی کلاس (Class)

XPath عملگر «عضو لیست جداشده با فاصله» ندارد؛ از این روش استفاده کنید:

//div[contains(concat(' ',normalize-space(@class),' '),' foobar ')]

عبارت‌ها، محورها و پیشوندها

ساختار یک عبارت

هر عبارت XPath از محور (Axis) و گام (Step) تشکیل شده:

//  ul  /  a[@id='link']
↑   ↑   ↑   ↑
محور گام محور گام

پیشوندها

پیشوندمثالمعنی
////hr[@class='edge']هر جای سند
././aنسبت به گره فعلی
//html/body/divاز ریشه

محورها

محورمثالمعنی
///ul/li/aفرزند مستقیم
////*[@id="list"]//aنواده (هر سطحی)

گام‌ها را با / جدا کنید. برای انتخاب غیرمستقیم از // استفاده کنید.

گام‌ها (Steps)

//div
//div[@name='box']
//*[@id='link']

هر گام می‌تواند نام عنصر (div) و پیش‌شرط ([...]) داشته باشد. هر دو اختیاری‌اند:

//a/text()     #=> "Go home"
//a/@href      #=> "index.html"
//a/*          #=> همه فرزندان a

پیش‌شرط‌ها (Predicates)

تعریف

پیش‌شرط‌ها مجموعه گره‌ها را فقط وقتی شرط برقرار باشد محدود می‌کنند. قابل زنجیر شدن‌اند:

//div[true()]
//div[@class="head"]
//div[@class="head"][@id="top"]

عملگرها

# مقایسه
//a[@id = "xyz"]
//a[@id != "xyz"]
//a[@price > 25]
# منطق (and/or)
//div[@id="head" and position()=2]
//div[(x and y) or not(z)]

استفاده از گره‌ها در پیش‌شرط

//ul[count(li) > 2]
//ul[count(li[@class='hide']) > 0]

# ul که فرزند li دارد
//ul[li]

ایندکس‌گذاری

//a[1]                  # اولین a
//a[last()]             # آخرین a
//ol/li[2]              # دومین li
//ol/li[position()=2]   # همان بالا
//ol/li[position()>1]   # معادل :not(:first-of-type)

ترتیب زنجیره‌سازی

ترتیب مهم است — این دو متفاوت‌اند:

a[1][@href='/']
a[@href='/'][1]

پیش‌شرط‌های تو در تو

//section[.//h1[@id='hi']]

section‌ای را برمی‌گرداند که نواده h1 با id='hi' دارد.

توابع

توابع گره

name()                     # //[starts-with(name(), 'h')]
text()                     # //button[text()="Submit"]
                           # //button/text()
lang(str)
namespace-uri()

count()                    # //table[count(tr)=1]
position()                 # //ol/li[position()=2]

توابع بولی

not(expr)                  # button[not(starts-with(text(),"Submit"))]

توابع رشته

contains()                 # font[contains(@class,"head")]
starts-with()              # font[starts-with(@class,"head")]
ends-with()                # font[ends-with(@class,"head")]

concat(x,y)
substring(str, start, len)
substring-before("01/02", "/")  #=> 01
substring-after("01/02", "/")   #=> 02
translate()
normalize-space()
string-length()

تبدیل نوع

string()
number()
boolean()

محورها (Axes)

استفاده از محورها

//ul/li                       # ul > li
//ul/child::li                # ul > li (یکسان)
//ul/following-sibling::li    # ul ~ li
//ul/descendant-or-self::li   # ul li
//ul/ancestor-or-self::li     # معادل $('ul').closest('li')

گام‌ها معمولاً با / جدا می‌شوند. می‌توانید محور متفاوت با :: مشخص کنید.

محور child (فرزند)

# هر دو یکسان
//ul/li/a
//child::ul/child::li/child::a

# child::li درست است، پس پیش‌شرط موفق می‌شود
//ul[li]
//ul[child::li]

//ul[count(li) > 2]
//ul[count(child::li) > 2]

محور descendant-or-self (نواده)

//div//h4
//div/descendant-or-self::h4

//ul//*[last()]
//ul/descendant-or-self::*[last()]

// مخفف محور descendant-or-self:: است.

سایر محورها

محورمخففتوضیح
ancestorاجداد
ancestor-or-selfخود و اجداد
attribute@@href = attribute::href
childنام تگdiv = child::div
descendantنوادگان
descendant-or-self//خود و نوادگان
namespaceفضای نام
self.. = self::node()
parent.... = parent::node()
followingبعد از گره فعلی
following-siblingخواهر بعدی
precedingقبل از گره فعلی
preceding-siblingخواهر قبلی

اتحاد (Union)

//a | //span

با | دو عبارت را ترکیب کنید.

مثال‌های بیشتر

//*                 # همه عناصر
count(//*)          # شمارش همه عناصر
(//h1)[1]/text()    # متن اولین h1
//li[span]          # li با span داخلش
                    # ... گسترش: //li[child::span]
//ul/li/..          # انتخاب والد با ..

یافتن والد

# section که مستقیماً h1#section-name دارد
//section[h1[@id='section-name']]

# section که h1#section-name در هر سطحی دارد
//section[//h1[@id='section-name']]

نزدیک‌ترین عنصر (Closest)

./ancestor-or-self::*[@class="box"]

معادل jQuery: $().closest('.box')

ویژگی‌ها با محاسبه

//item[@price > 2*@discount]

item‌هایی که قیمتشان بیش از دو برابر تخفیف است.