

در این مقاله عملی و فنی میخواهیم چگونگی استفاده از XPath همراه با Playwright را برای وب اسکریپینگ توضیح دهیم. هدف این است که پس از خواندنِ این راهنما، بتوانید عناصر صفحه را با XPath پیدا کنید، رفتارهای منتظرمانی (waiting) را درست مدیریت کنید، روی عناصر کلیک و اسکرینشات بگیرید، و در نهایت دادهها را از صفحات دینامیک استخراج کنید. مثالهای عملی با Playwright به زبان Python آورده شده تا مناسب توسعهدهندههای پایتون سطح متوسط باشد.
CSS سلکتورها ساده و خوانا هستند و برای اغلب موارد مناسباند؛ اما وقتی ساختار صفحه پیچیده است، عنصر شناساییشده id/class ندارد، یا نیاز به انتخاب بر اساس متن/نسبت اجداد/خواهران است، XPath تواناییهای بیشتری دارد. در انتخاب بین این دو به پایداری ساختار صفحه و خوانایی کد توجه کنید.
نمادهای پایهای:
مثال ساده: //h3[@class='country-name'] هر تگ h3 با کلاس country-name را در سراسر سند انتخاب میکند.
مثال زیر نشان میدهد چگونه با Playwright به یک صفحه برویم، عنصرِ منتخب را با XPath پیدا کنیم و متن آن را استخراج کنیم. ورودی: آدرس صفحه. خروجی: لیست متنهای استخراجشده.
from playwright.async_api import async_playwright
import asyncio
async def get_country_names():
async with async_playwright() as p:
browser = await p.chromium.launch()
context = await browser.new_context()
page = await context.new_page()
# ورودی: URL صفحه
await page.goto('https://www.scrapethissite.com/pages/simple/')
# Locator با XPath (نکته: Playwright XPath را مستقیم میپذیرد)
locator = page.locator('//h3[@class="country-name"]')
# خروجی: لیست متنها
names = await locator.all_text_contents()
print(names)
await browser.close()
asyncio.run(get_country_names())
توضیح خطبهخط:
Absolute XPath مسیر کامل از ریشه است (/html/body/...), که در صفحات پایدار مفید اما شکننده است. Relative XPath با // انعطافپذیرتر و قابل نگهداریتر است؛ مخصوصا برای سایتهایی که DOM آنها تغییر میکند، توصیه میشود از relative و فیلترهای کلاس/attribute یا متن استفاده کنید.
Playwright در بسیاری از عملیاتها انتظار داخلی دارد، اما در موارد زیر از explicit waits استفاده کنید:
# مثال: منتظر ماندن برای وجود XPath
await page.wait_for_load_state('networkidle')
await page.wait_for_selector('//*[@class="product_pod"]/h3')
توجه: Locatorها خودشان اغلب صبر و retry توکار دارند؛ یعنی وقتی از متدهای اقدام روی locator استفاده میکنید (مثل click() یا fill())، Playwright تلاش میکند تا شرایط لازم برقرار شود و سپس عمل را انجام دهد. اما برای extract یا evaluate ها بهتر است ابتدا با wait_for_selector یا wait_for_load_state اطمینان حاصل کنید.
مثال: کلیک روی آیتمی که متن مشخصی دارد.
# کلیک روی عنصر بر اساس متن با XPath
link_xpath = '//a[contains(text(), "Samsung galaxy s6")]'
await page.wait_for_selector(link_xpath)
await page.click(link_xpath)
برای گرفتن اسکرینشات:
await page.screenshot(path='screenshot.png', full_page=True)
نکته: page.click میتواند مستقیم XPath را بپذیرد؛ ولی برای عملیات پیچیدهتر بهتر است locator ساخته و از آن استفاده کنید تا readability و قابلیت reuse افزایش یابد.
برای سایتهایی که با اسکرول بارگذاری بیشتر میشوند، از حلقه اسکرول استفاده کنید تا تا پایان محتوا پیمایش شود.
# نمونه ساده اسکرول تا زمانی که ارتفاع تغییر نکند
previous_height = 0
while True:
await page.evaluate('window.scrollBy(0, window.innerHeight)')
await page.wait_for_timeout(1500)
current_height = await page.evaluate('document.documentElement.scrollHeight')
if current_height == previous_height:
break
previous_height = current_height
این روش کمک میکند جاوااسکریپتِ بارگذاریِ تنبل content را فراخوانی کند و سپس بتوانید عناصر جدید را با XPath انتخاب کنید.
ورودی: نام کاربری/رمز عبور (ترجیحا از متغیر محیطی). خروجی: صفحه پس از ورود و اسکرینشات.
import os
from playwright.async_api import async_playwright
import asyncio
async def login_example():
username = os.getenv('SCRAPE_USERNAME')
password = os.getenv('SCRAPE_PASSWORD')
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto('https://demo.applitools.com/')
# استفاده از XPath برای فیلدها
await page.locator('//input[@id="username"]').fill(username)
await page.locator('//input[@id="password"]').fill(password)
await page.locator('//a[@id="log-in"]').click()
await page.wait_for_load_state('networkidle')
await page.screenshot(path='after_login.png')
await browser.close()
# توجه: اطلاعات حساس را در کد هاردکد نکنید؛ از محیط یا vault استفاده کنید.
نکات امنیتی و اخلاقی:
برای استخراج چند فیلد از هر آیتم، ترکیب XPath برای انتخاب container و سپس query داخلی برای هر فیلد مناسب است. مثال زیر نام، قیمت و امتیاز هر کتاب را از لیست میخواند.
async def extract_books(page):
# انتخاب کانتینرهای هر محصول (با CSS در این مثال برای خوانایی)
containers = await page.locator('li.col-xs-6.col-sm-4.col-md-3.col-lg-3').all()
results = []
for i in range(len(containers)):
c = page.locator(f'(//li[contains(@class, "col-xs-6")])[{i+1}]')
name = await c.locator('h3 a').inner_text()
price = await c.locator('p.price_color').inner_text()
rating_class = await c.locator('p.star-rating').get_attribute('class')
rating = rating_class.split()[1] if rating_class else ''
results.append({'name': name.strip(), 'price': price.strip(), 'rating': rating})
return results
نکته: استفاده از ترکیب CSS و XPath زمانی که یک روش سادهتر است بلامانع است—هدف خوانایی و پایداری است.
XPath ابزار قدرتمندی برای انتخاب دقیقِ عناصر در اسکریپها و تستها است و وقتی با Playwright بهدرستی ترکیب شود، امکان ساخت اسکریپهای قابلاعتماد برای وب اسکریپینگ فراهم میشود. نکات عملی این مقاله: از relative XPath استفاده کنید، منتظر بودن (waiting) را مدیریت کنید، از Locatorها برای کاهش خطا استفاده کنید، و برای امنیت و پایداری کد بهترینروشها را رعایت کنید. با کمی تمرین روی مثالهای بالا، میتوانید ترکیبهای قدرتمندی برای استخراج دادههای ساختیافته بسازید.


