مقدمه
در هر اسکریپ وب با مرورگر، اولین قدم این است که عنصر مورد نظر را پیدا کنیم. بدون انتخاب درست عنصر، نمیتوانیم کلیک کنیم، فرم پر کنیم یا متن را بخوانیم. در این مقاله قدمبهقدم یاد میگیریم چگونه با CSS selector عناصر را در Playwright پیدا و با آنها کار کنیم. مخاطب این مطلب توسعهدهندهٔ پایتون در سطح متوسط است ولی مثالها هم با Node.js و هم با Python آورده شدهاند تا تطبیقپذیری را ببینید.
اصول کلی و API مورد استفاده
ایدهٔ کلی: در Playwright برای هدفگیری عناصر معمولاً از page.locator استفاده میکنیم. یک Locator نمایندهٔ مجموعهای از عناصر قابل تعامل است و متدهایی مثل first، nth، count، textContent و fill دارد.
وردی: یک رشتهٔ CSS selector. خروجی: یک شیء Locator که میتوانیم روی آن عملیات انجام دهیم یا مقدار آن را واکشی کنیم.
مثال پایه — پیدا کردن با کلاس (Node.js)
ایدهٔ کلی: برای انتخاب اولین عنصر با کلاس خاص از page.locator(".my-class").first() استفاده میکنیم. مثال زیر کد کامل اجرا در Node.js را نشان میدهد.
const playwright = require('playwright');
async function main() {
const browser = await playwright.chromium.launch();
const page = await browser.newPage();
await page.goto('quotes.toscrape'); // نام سایت بهعنوان مثال
// ورودی: رشتهٔ CSS selector، خروجی: Locator
const firstTag = page.locator('.tag').first();
// واکشی متن: خروجی یک رشته یا null
const text = await firstTag.textContent();
console.log('First tag:', text);
await browser.close();
}
main();شرح خطبهخط:
- راهاندازی مرورگر با playwright.chromium.launch().
- باز کردن برگهٔ جدید با browser.newPage().
- ناوبری به صفحه (در مثال بالا نام سایت بهصورت نمایشی ذکر شده است).
- page.locator('.tag').first() یک Locator برای اولین عنصری با کلاس tag میسازد.
- textContent() محتوای متنی عنصر را برمیگرداند؛ این همان خروجیای است که معمولاً ذخیره یا چاپ میکنیم.
معادل ساده در Playwright برای Python
اگر با پایتون کار میکنید، API مشابه است اما نوشتار پایتون خواهد بود:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('quotes.toscrape')
first_tag = page.locator('.tag').first
text = first_tag.text_content()
print('First tag:', text)
browser.close()در پایتون first بهصورت property استفاده میشود و text_content() مقدار را بازمیگرداند.
پیدا کردن با شناسه و تگ
چند شیوهٔ متداول:
- By ID: page.locator('#username') — برای فیلدهای فرم یا المانهایی که id یکتا دارند مناسب است.
- By tag: page.locator('h1') — وقتی میخواهیم تمام تگهای از نوع مشخص را هدف بگیریم.
مثال عملی (پر کردن فیلد با شناسه):
const username = page.locator('#username').first();
await username.fill('my-user');
await page.screenshot({ path: 'filled.png' });ورودیها/خروجیها: fill مقدار رشتهای میگیرد و خروجیای برنمیگرداند؛ در عوض اثرش در DOM دیده میشود. همیشه بعد از عملیات تعاملی منطقی است که سعی کنید با انتظار مناسب از بروز خطا جلوگیری کنید.
سِلکتورهای Attribute — قدرتمند و منعطف
CSS attribute selectors به شما اجازه میدهند آیتمها را براساس وجود یا مقدار صفتها فیلتر کنید:
- [attr] — وجود صفت
- [attr='value'] — مقدار برابر
- [attr*='val'] — شامل (contains)
- [attr^='val'] — شروع با (starts with)
- [attr$='val'] — پایان با (ends with)
مثال: پیدا کردن همهٔ لینکهایی که در href خود کلمهٔ author دارند و خواندن متن آنها:
const items = page.locator("[href*='author']");
const count = await items.count();
for (let i = 0; i < count; i++) {
const text = await items.nth(i).textContent();
console.log('Author link text:', text);
}نکات عملی:
- Locator خودش آرایهٔ واقعی نیست؛ برای پیمایش از count() و nth(i) استفاده کنید.
- عملیاتهای دستهای ممکن است کند باشند—در صورت امکان از evaluate یا evaluateAll برای خواندن سریعتر استفاده کنید.
سِلکتورهای ترکیبی و ساختار (Descendant / Child / Siblings)
برای هدفگیری دقیقتر میتوانید سلکتورها را ترکیب کنید:
- Descendant: div span a — عنصری که در داخل سلسلهمراتبی قرار گرفته.
- Child: ul > li — فقط فرزندان مستقیم.
- Adjacent sibling: h2 + p — عنصر بلافصل بعد از یک عنصر دیگر.
- General sibling: h2 ~ p — تمام خواهران بعدی.
مثال ترکیبی:
const items = page.locator('body > div:first-child');
const n = await items.count();
// هر عنصر را بخوانیم
for (let i = 0; i < n; i++) {
console.log(await items.nth(i).textContent());
}توجه: pseudo-elements مثل ::before یا ::after در DOM وجود ندارند، پس قابل انتخاب با Locator نیستند؛ اما pseudo-classes مثل :first-child و :nth-child قابل استفادهاند.
خطاها، همزمانی و راههای پایدار کردن اسکریپ
مواردی که باید همیشه در نظر بگیرید:
- Timeouts: برای عملیاتهای حیاتی timeout مناسب تعیین کنید و از try/catch یا معادل پایتون استفاده کنید.
- Retries: در مواجهه با خطاهای شبکه یا صفحاتی که گاهی بارگذاری میشوند، مکانیزم retry با backoff اضافه کنید.
- همزمانی: فراخوانیهای زیاد همزمان به منابع فشار میآورند؛ محدودیت concurrency را اعمال کنید.
- پاکسازی منابع: حتماً browser.close() را در بلوک نهایی اجرا کنید تا منابع آزاد شوند.
انتظار برای المانهای داینامیک (Waiting)
سه روش معمول:
- Hard wait: page.waitForTimeout(ms) — ساده اما غیرقابل اعتماد برای محتوای داینامیک.
- Load / network wait: page.waitForLoadState('networkidle') — منتظر میماند تا شبکه ساکن شود.
- Explicit wait on locator: await page.locator('.selector').waitFor() — بهترین روش برای اطمینان از حضور عنصر خاص قبل از تعامل.
// مثال: انتظار صریح برای عنصر
await page.locator('#username').waitFor({ state: 'visible', timeout: 5000 });
await page.locator('#username').fill('user1');توضیح: waitFor ورودیهایی مثل حالت (visible/hidden/attached) و timeout میگیرد. این الگوی صریح و قابل پیشبینیتر از waitForTimeout است.
عملکرد، امنیت و اخلاقیات
نکات عملی:
- Performance: انتخاب سلکتورهای خیلی کلی (مثلاً '*') منجر به جستجوی سنگین در DOM میشود؛ سعی کنید دقیق ولی مقاوم بنویسید.
- Security: محتوای دریافتی را قبل از ذخیره یا نمایش sanitize کنید. دادهها ممکن است شامل HTML یا اسکریپتهای ناخواسته باشند.
- Ethics & compliance: همیشه قوانین سایت و رباتها (robots.txt) و شرایط استفاده را بررسی کنید و نرخ درخواستها را قابل احترام نگه دارید.
اشتباهات رایج و راهحلها
- استفاده از سلکتور خیلی اختصاصی—منجر به شکست در تغییرات کوچک DOM میشود. راهحل: انتخاب ترکیبی از class/id یا data-* attributes که پایدارترند.
- نادیده گرفتن زمانبندی بارگذاری—استفاده از waitFor یا state-based waits را فراموش نکنید.
- پیمایش مستقیم فرضی آرایهٔ Locator—به جای آن از count() و nth() استفاده کنید یا از evaluate برای عملیات جمعی بهره ببرید.
جمعبندی و توصیههای سریع
خلاصهٔ عملی:
- برای بیشتر موارد از page.locator("css") استفاده کنید؛ این روش هم دقیق است و هم قابل تعامل.
- اگر عنصر یکتا دارید از ID (#id)؛ اگر میخواهید گروهی را انتخاب کنید از کلاس (.class) یا attribute selectors استفاده کنید.
- برای پایداری، سلکتورهایی را ترجیح دهید که به ساختار منطقی صفحه وابستهاند (مثلاً data-testid) و از صفتهای ظاهری یا موقعیت DOM خیلی حساس دوری کنید.
- همیشه برای عناصر داینامیک از waitFor یا waitForLoadState استفاده کنید و مدیریت خطا و پاکسازی منابع را فراموش نکنید.
با ترکیب این اصول و مثالهای بالا میتوانید اسکریپهایی بنویسید که قابل اعتماد، قابل نگهداری و مناسب برای پردازش صفحات واقعی وب باشند.





