

در این مقاله به صورت عملی و مرحلهبهمرحله یاد میگیرید چگونه از XPath در ترکیب با Playwright برای وب اسکریپینگ (یا بهاختصار «اسکریپ کردن») استفاده کنید. فرض میکنیم شما توسعهدهنده پایتون در سطح متوسط هستید و دنبال مثالهای واقعی، نکات عملکردی و بهترینروشها هستید. در پایان این مقاله میتوانید عناصر صفحه را با XPath پیدا کنید، برای آنها صبر کنید، کلیک کنید، متن استخراج کنید، اسکرول اتوماتیک انجام دهید و سناریوهای ورود بهحساب و استخراج داده را پیادهسازی کنید.
XPath زبانی برای پیمایش در ساختار درختی XML/HTML است. وقتی عناصر دارای شناسه یا کلاس قابل اتکا نیستند، XPath به شما امکان میدهد بر اساس موقعیت، متن، صفتها یا روابط بین نودها هدفگیری دقیقتری داشته باشید. در وب اسکریپینگ، این دقت زمانی که ساختار صفحه پیچیده یا دینامیک است بسیار مفید است.
چند مثال ساده از اجزای معمول:
دو نوع XPath داریم: Absolute (مسیر کامل از ریشه، شکننده) و Relative (قابل نگهداریتر). در اغلب اسکریپها relative XPath توصیه میشود.
روش سریع: در مرورگر روی عنصر راستکلیک → Inspect → راستکلیک روی عنصر در DevTools → Copy → Copy XPath یا Copy full XPath. معمولاً Copy XPath یک مسیر نسبی مفید میدهد و Copy full XPath مسیر مطلقی خواهد بود که مستعد شکست با تغییرات صفحه است.
روش دستی: از یک جد (ancestor) پایدار شروع کنید، سپس با استفاده از نام تگها، صفتها و توابع مانند contains() یا starts-with() مسیرهای مقاوم بسازید.
مثال خروجی از DevTools:
//*[@id="countries"]/div/div[4]/div[1]/h3
یا مسیر مطلق:
/html/body/div/section/div/div[4]/div[1]/h3
در Playwright پایتون معمولاً از API همگام (sync) یا ناهمگام (async) استفاده میکنیم. برای مثالهای آموزشی از API همگام استفاده میکنیم تا خواناتر باشد. یک الگوی متداول برای گرفتن متن یک عنصر با XPath:
from playwright.sync_api import sync_playwright
def get_country():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://www.scrapethissite.com/pages/simple/")
# استفاده از XPath داخل locator
locator = page.locator("xpath=//*[@id='countries']/div/div[4]/div[1]/h3")
text = locator.inner_text()
print(text) # خروجی: Andorra
browser.close()
get_country()
توضیح ورودی/خروجی و نقش توابع:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://www.scrapethissite.com/pages/simple/")
locator = page.locator("xpath=//h3")
names = locator.all_text_contents() # تمام متنهای h3 را به صورت لیست برمیگرداند
print(names)
browser.close()
توضیح: all_text_contents() لیستی از رشتهها برمیگرداند؛ جایگزین مناسب برای evaluateAll در نسخههای JS است و سادهتر از اجرای اسکریپت در صفحه.
Playwright اغلب خودش منتظر بارگذاری میماند اما در صفحات با محتوای دینامیک باید صریحتر عمل کنید. نمونه:
# انتظار برای XPath مشخص
page.wait_for_selector("xpath=//*[@class='product_pod']/h3", timeout=10000)
# سپس میتوانید locator را استفاده کنید
product_titles = page.locator("xpath=//*[@class='product_pod']/h3").all_text_contents()
نکات: زمان timeout پیشفرض را در مواقع لازم افزایش دهید، اما از مقدارهای بسیار بلند دوری کنید تا منابع بلااستفاده نگهداری نشوند.
مثال کلیک روی لینکی که متن خاصی دارد:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://www.demoblaze.com/")
link_xpath = "xpath=//a[contains(., 'Samsung galaxy s6')]
"
page.wait_for_selector(link_xpath)
page.click(link_xpath)
page.wait_for_load_state("networkidle")
page.screenshot(path="s6.png", full_page=True)
browser.close()
توضیح: در page.click() از selector مبتنی بر XPath استفاده میکنیم؛ ابتدا عنصر را صبر میکنیم تا ظاهر شود سپس کلیک میکنیم تا از race condition جلوگیری شود.
import time
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com/long-page")
previous_height = 0
while True:
page.evaluate("window.scrollBy(0, window.innerHeight);")
time.sleep(2)
current_height = page.evaluate("() => document.documentElement.scrollHeight")
if current_height == previous_height:
break
previous_height = current_height
# بعد از اسکرول، عناصر جدید قابل هدفگیری با XPath خواهند بود
browser.close()
نکته: از sleepهای کوتاه استفاده کنید و اگر امکان دارد از رویدادهای لود شبکه (wait_for_load_state) ترکیب کنید تا بیشتر قابل اعتماد شود.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://demo.applitools.com/")
page.locator("xpath=//input[@id='username']").fill("myuser")
page.locator("xpath=//input[@id='password']").fill("mypass")
page.locator("xpath=//a[@id='log-in']").click()
page.wait_for_load_state("networkidle")
page.screenshot(path="login-screenshot.png")
browser.close()
ورودیها: رشته نام کاربری و رمز عبور. خروجی: عکس صفحه پس از ورود و معمولاً یک تغییر وضعیت در DOM که نشاندهنده ورود موفق است. همیشه اطلاعات حساس را در متغیرهای محیطی یا vault ذخیره کنید و در کد صریح قرار ندهید.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://books.toscrape.com/catalogue/page-1.html")
containers = page.locator("xpath=//ol[@class='row']/li")
results = []
for i in range(containers.count()):
item = containers.nth(i)
name = item.locator("xpath=.//h3/a").inner_text()
price = item.locator("xpath=.//p[@class='price_color']").inner_text()
rating_class = item.locator("xpath=.//p[contains(@class,'star-rating')]").get_attribute("class")
rating = rating_class.split()[-1] if rating_class else ""
results.append({"name": name, "price": price, "rating": rating})
print(results)
browser.close()
خطبهخط: ابتدا همه کانتینرهای محصول را میگیریم، سپس برای هر آیتم به صورت محلی (با XPath نسبی شروعشده با .) عناصر داخلی را استخراج میکنیم تا XPathها نسبت به کانتینر پایدارتر بمانند.
استفاده از XPath همراه با Playwright در پایتون ترکیبی قدرتمند برای وب اسکریپینگ فراهم میکند: XPath دقت انتخاب را بالا میبرد و Playwright مدیریت مرورگر، صبر داخلی و APIهای عملیاتی را فراهم میآورد. در عمل از XPathهای نسبی و مقاوم استفاده کنید، صبر (waiting) را درست تنظیم کنید، و عملیات استخراج را با مدیریت خطا و retry ترکیب کنید تا اسکریپهای پایدار و قابل نگهداری بسازید.


