Skip to content

Playwright & Cơ chế Recording

Playwright là trái tim của hệ thống, chịu trách nhiệm tương tác với website. Tại sao chúng ta chọn Playwright thay vì Selenium hay Puppeteer? Và cơ chế "ghi hình" hoạt động như thế nào?

1. Tại sao chọn Playwright?

Tính năngPlaywrightSeleniumPuppeteer
Tốc độRất nhanh (WebSocket)Trung bình (HTTP JSON Wire)Nhanh
Độ ổn địnhCao (Auto-wait)Thấp (Flaky tests)Cao
Đa trình duyệtChromium, Firefox, WebKitTất cảChỉ Chromium
CodegenTích hợp sẵn, rất mạnhCần pluginKhông có sẵn
Ngôn ngữPython, JS, Java, C#Tất cảJS/TS

🏆 Lựa chọn: Playwright thắng thế nhờ tính năng Codegen (tự sinh code) và cơ chế Auto-wait (tự đợi element xuất hiện), giúp giảm thiểu code xử lý sleep hay wait_until.

2. Cơ chế Recording (Codegen)

Playwright có một tính năng gọi là codegen. Khi kích hoạt, nó sẽ mở ra một trình duyệt đặc biệt và một cửa sổ Inspector.

Cách hoạt động:

  1. Playwright inject một đoạn JavaScript vào mọi trang web được mở.
  2. Script này lắng nghe các sự kiện DOM: click, input, change, submit.
  3. Khi sự kiện xảy ra, nó tính toán Selector tốt nhất (ưu tiên ID, Text, CSS, XPath).
  4. Nó bắn sự kiện về process Python để ghi thành code.

Trong guide_ai.py:

Chúng ta sử dụng subprocess để gọi lệnh playwright codegen và lưu output vào file tạm:

python
# Pseudocode logic
def start_recording(url, output_file):
    cmd = [
        "playwright", "codegen",
        url,
        "--target", "python",
        "--output", output_file
    ]
    subprocess.run(cmd)

3. Cơ chế Replay & Snapshot

Sau khi có file script, hệ thống parsereplay từng action riêng lẻ thay vì chạy script trực tiếp.

Kỹ thuật: Parse → Execute → Capture

Trong src/replayer.py, quy trình như sau:

python
def _replay_with_browser(browser, actions, images_dir, upload_enabled, upload_fn):
    context = browser.new_context(viewport={'width': 1280, 'height': 800})
    page = context.new_page()
    
    for i, action in enumerate(actions):
        step_num = i + 1
        
        # 1. Execute action
        execute_action(page, action)
        
        # 2. Wait for stabilization
        page.wait_for_timeout(800)
        page.wait_for_load_state('domcontentloaded', timeout=3000)
        
        # 3. Capture screenshot
        screenshot_path = images_dir / f"step_{step_num}.png"
        page.screenshot(path=str(screenshot_path))
        
        # 4. Upload if enabled
        if upload_enabled and upload_fn:
            remote_url = upload_fn(str(screenshot_path))

Logging chi tiết (Professional UX)

Hệ thống hiển thị progress chi tiết:

🌐 Đang khởi tạo trình duyệt...
✓ Trình duyệt sẵn sàng (1280x800)

📋 Bắt đầu thực thi 10 thao tác...
──────────────────────────────────────────────────

[01/10] 🎯 Điều hướng → https://portal.inet.vn
        ✓ Thực thi thành công (1250ms)
        ⏳ Đang chờ trang ổn định...
        📸 Đã chụp màn hình
        ☁️  Đang tải lên cloud...
        ✓ Tải lên thành công

4. Xử lý các vấn đề khó

4.1. Fallback Selector Strategy

Trong src/replayer.py, hàm _find_element_with_fallback thử nhiều cách tìm element:

python
def _find_element_with_fallback(page, code):
    # 1. Thử selector gốc (get_by_role, get_by_text...)
    # 2. Nếu không tìm thấy, thử CSS selector
    # 3. Fallback: tìm theo tag + text
    for tag in ['button', 'a', 'span', 'div', 'li', 'label']:
        fallback = page.locator(f'{tag}:has-text("{text}")').first
        if fallback.is_visible(timeout=1000):
            return fallback

Ưu điểm

  • Resilient: Không fail ngay khi selector gốc không hoạt động
  • Flexible: Hỗ trợ nhiều loại selector

Nhược điểm

  • Performance: Thử nhiều selector tốn thời gian
  • False positive: Có thể click nhầm element

4.2. Action Type Detection

Hệ thống tự động nhận diện loại action để hiển thị log đẹp:

python
def _get_action_type_display(code: str) -> str:
    if '.goto(' in code:
        return f"Điều hướng → {url}"
    if '.fill(' in code:
        return f"Nhập liệu → {field}"
    if '.click()' in code:
        return f"Nhấn chuột → {name}"
    # ...

4.3. Anti-Bot & Headless Detection

  • Giải pháp hiện tại: Chạy headless=True cho tốc độ
  • Nếu bị chặn: Đổi sang headless=False hoặc dùng playwright-stealth

5. Best Practices khi Record

Để có script sạch và dễ replay:

  1. Thao tác chậm rãi: Đợi trang load xong hãy click.
  2. Tránh click thừa: Đừng bôi đen text hay click lung tung, Playwright sẽ ghi lại hết.
  3. Dùng phím Tab: Để chuyển giữa các ô input (giúp Playwright hiểu flow tốt hơn).
  4. Kết thúc rõ ràng: Đăng xuất hoặc đóng tab khi xong việc.

Internal documentation for iNET Portal