مقدمه
در این مقاله به صورت عملی و مرحلهبهمرحله یاد میگیرید چگونه از XPath در ترکیب با Playwright برای وب اسکریپینگ (یا بهاختصار «اسکریپ کردن») استفاده کنید. فرض میکنیم شما توسعهدهنده پایتون در سطح متوسط هستید و دنبال مثالهای واقعی، نکات عملکردی و بهترینروشها هستید. در پایان این مقاله میتوانید عناصر صفحه را با XPath پیدا کنید، برای آنها صبر کنید، کلیک کنید، متن استخراج کنید، اسکرول اتوماتیک انجام دهید و سناریوهای ورود بهحساب و استخراج داده را پیادهسازی کنید.
مبانی XPath و چرا در وب اسکریپینگ کاربردی است
XPath زبانی برای پیمایش در ساختار درختی XML/HTML است. وقتی عناصر دارای شناسه یا کلاس قابل اتکا نیستند، XPath به شما امکان میدهد بر اساس موقعیت، متن، صفتها یا روابط بین نودها هدفگیری دقیقتری داشته باشید. در وب اسکریپینگ، این دقت زمانی که ساختار صفحه پیچیده یا دینامیک است بسیار مفید است.
درک سینتکس پایه XPath
چند مثال ساده از اجزای معمول:
- //div — همه <div>ها در صفحه
- //h3[@class='country-name'] — هر h3 که کلاس آن برابر country-name است
- //a[contains(text(), 'Login')] — هر لینک که متن آن شامل «Login» است
- (//ul/li)[1] — اولین <li> در هر <ul>
دو نوع XPath داریم: Absolute (مسیر کامل از ریشه، شکننده) و Relative (قابل نگهداریتر). در اغلب اسکریپها relative XPath توصیه میشود.
پیدا کردن XPath: DevTools و بازرسی دستی
روش سریع: در مرورگر روی عنصر راستکلیک → 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
Selecting Elements با Playwright (پایتون)
در 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()
توضیح ورودی/خروجی و نقش توابع:
- sync_playwright(): شروع رانتایم Playwright.
- p.chromium.launch(): اجرای مرورگر Chromium؛ آرگومان headless قابل تنظیم است.
- page.goto(url): پیمایش به آدرس مشخص.
- page.locator(selector): برمیگرداند یک Locator که قبل از هر action به عنصر حل میشود.
- locator.inner_text(): متن نمایش دادهشده داخل عنصر را برمیگرداند.
استخراج چند مقدار (مثال جمعآوری نام کشورها)
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 است و سادهتر از اجرای اسکریپت در صفحه.
منتظر ماندن برای XPath (انتظار صریح)
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 پیشفرض را در مواقع لازم افزایش دهید، اما از مقدارهای بسیار بلند دوری کنید تا منابع بلااستفاده نگهداری نشوند.
انجام عملها با XPath: کلیک، پر کردن فرم و اسکرینشات
مثال کلیک روی لینکی که متن خاصی دارد:
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 جلوگیری شود.
اسکرول اتوماتیک برای بارگذاری محتوای تنبل (lazy-loading)
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) ترکیب کنید تا بیشتر قابل اعتماد شود.
مثال واقعی: ورود (Automated Authentication) با XPath
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های نسبتاً کوتاه و هدفدار استفاده کنید: به جای مسیرهای طولانی که از شاخصهای عددی زیاد استفاده میکنند، به دنبال صفتهای منحصر به فرد یا ترکیب contains() باشید.
- از مسیرهای مطلق دوری کنید: شکنندهاند و با کوچکترین تغییر DOM میشکنند.
- صبر و زمانبندی مناسب: از wait_for_selector یا خودِ Locator.wait_for برای جلوگیری از race condition استفاده کنید.
- تعامل با صفحات دینامیک: اگر محتوای صفحه از طریق XHR/Fetch بارگذاری میشود، ترکیب wait_for_load_state('networkidle') یا صبر برای یک عنصر شاخص را در نظر بگیرید.
- مدیریت خطا و retry: شبکه و تغییرات صفحه باعث خطا میشود؛ درخواستها و عملیات DOM را با try/except احاطه کنید و مکانیزم retry (با backoff) اضافه کنید.
- پرفورمنس: از انتخابکنندههای ناکارا که تمام درخت را جستوجو میکنند پرهیز کنید؛ محدود کردن جستوجو با یک ancestor پایدار کارایی را بهبود میدهد.
- امنیت و رعایت قوانین: قبل از اسکریپ کردن از قوانین سایت و شرایط استفاده پیروی کنید، از ارسال درخواستهای بیشازحد کوتاهمدت خودداری کنید و به rate-limit احترام بگذارید.
جمعبندی
استفاده از XPath همراه با Playwright در پایتون ترکیبی قدرتمند برای وب اسکریپینگ فراهم میکند: XPath دقت انتخاب را بالا میبرد و Playwright مدیریت مرورگر، صبر داخلی و APIهای عملیاتی را فراهم میآورد. در عمل از XPathهای نسبی و مقاوم استفاده کنید، صبر (waiting) را درست تنظیم کنید، و عملیات استخراج را با مدیریت خطا و retry ترکیب کنید تا اسکریپهای پایدار و قابل نگهداری بسازید.





