Playwright คือเฟรมเวิร์กสำหรับคุมเบราว์เซอร์ของ Microsoft: ไลบรารีสัญญาอนุญาต Apache-2.0 ที่เน้น TypeScript เป็นหลัก เปิดเบราว์เซอร์จริง คุมผ่าน API เดียว และส่งคืนหน้าเว็บหลังจาก JavaScript ทำงานเสร็จแล้ว ถึงจะถูกวางตัวไว้เป็นเครื่องมือทดสอบแบบ end-to-end แต่เอ็นจินข้างในนี่แหละที่หลายคนแอบหยิบมาใช้ตอนส่ง HTTP request ไปแล้วเจอแต่หน้าโล่ง ทั้งที่ข้อมูลควรจะอยู่ตรงนั้น มันอยู่ในกลุ่มเดียวกับ Puppeteer และ Selenium — คือใช้สคริปต์คุมเบราว์เซอร์จริง ไม่ใช่ HTTP client สำหรับ parsing ข้อมูล
ผมรัน microsoft/playwright เวอร์ชัน 1.56.0 กับชุดทดสอบ scraping แบบตายตัวชุดหนึ่ง — ทั้งแคตตาล็อกแบบ static ที่มี pagination, บทความ, แคตตาล็อกที่เรนเดอร์ด้วย JavaScript, JSON API, หน้า 500 ที่พัง, crawl graph ขนาดเล็ก และเว็บไซต์สาธิต 2 แห่ง — บน Node v22.22.3, macOS arm64 และใช้แค่ Chromium เท่านั้น ฝั่ง rendering ผ่านฉลุย แต่ฝั่ง crawling ไม่มีมาให้เลย และนี่คือช่องว่างสำคัญที่สุดที่ต้องรู้ก่อนตัดสินใจใช้
สิ่งที่เด่นชัดที่สุด
มีผลลัพธ์ 2 แบบที่อยู่บนสุดของกอง และพาไปคนละทิศทางนิดหน่อย
อย่างแรกคือผลลัพธ์จากเบราว์เซอร์ตามที่คาดไว้ โดยผูกกับเงื่อนไข wait ที่ผมใช้ หลัง page.goto(..., { waitUntil: 'domcontentloaded' }) แล้ว เทสต์ของ dynamic fixture จะรอ #dynamic-products article.product-card ส่วนเทสต์ Quotes to Scrape ที่เป็นสาธารณะจะรอ .quote พอ selector เฉพาะของแอปโผล่ขึ้นมา การรันก็คืนข้อมูล 8/8 รายการจาก fixture และ 10 quotes จากเว็บไซต์สาธารณะ โดยผลของ fixture มี recall 1.0 เทียบกับ ground truth 8 รายการ ไม่ต้องเขียน polling loop เอง แต่ Playwright ก็ไม่ได้ทำให้ปัญหา readiness หายไป — เทสต์ยังต้องเป็นคนกำหนดเองว่า “พร้อมแล้ว” คือเมื่อไร และยังสามารถเซฟ screenshot เต็มหน้าได้ตั้งแต่ครั้งแรกที่เรียก
ผลลัพธ์ที่สองใช้ browserContext.request โดยเฉพาะคือ ctx.request.get(...) หลังจาก Chromium เปิดขึ้นมาแล้วและมี browser context อยู่แล้ว มันเรียก JSON endpoint ของ fixture โดยตรง และดึงข้อมูลสินค้าได้ 8/8 ชิ้นโดยไม่ต้องสร้างหรือเรนเดอร์หน้าเว็บจริง ข้อดีคือข้ามงาน DOM ไปได้ แต่ไม่ได้ลดต้นทุนของ browser process ในชุดทดสอบนี้ request client ที่ผูกกับ context แชร์ cookie state กับหน้าเว็บใน browser ได้ ส่วน playwright.request.newContext() แบบ standalone จะไม่ต้องพึ่ง browser context แต่ก็ไม่ได้แชร์ session กับหน้าเว็บโดยอัตโนมัติ เทสต์นี้ครอบคลุมแค่เส้นทางแรก
Playwright เองก็ไม่มี crawl queue, dataset writer หรือ throttling อัตโนมัติมาให้ เทสต์ crawl graph ของผม — เดิน internal links, เก็บ depth, และไม่กลับไปเยี่ยมหน้าเดิม — ไปได้ 12 หน้า โดยมีความลึก {0:1, 1:4, 2:7} แต่การเดินแบบ breadth-first เป็นโค้ดที่ผมเขียนเอง Playwright มีไว้เปิดหน้าและอ่านหน้า ส่วนเรื่อง frontier persistence, policy ของ URL, retry และ scheduling ต้องไปอยู่ในอีกชั้นหนึ่ง
จริง ๆ แล้ว Playwright คืออะไร
เครื่องมือนี้ — microsoft/playwright บน GitHub — เขียนด้วย TypeScript, ใช้ไลเซนส์ Apache-2.0 และดูแลโดย Microsoft บิลด์ที่ทดสอบในรีวิวนี้คือ 1.56.0 เมื่อวันที่ 9 กรกฎาคม 2026 ผลลัพธ์ทั้งหมดอ้างอิงจากบิลด์นี้โดยเฉพาะ ไม่ใช่การสรุปความเข้ากันได้กับเวอร์ชันถัดไป
การวางตำแหน่งอย่างเป็นทางการชัดมาก: เป็นเฟรมเวิร์กสำหรับทดสอบและทำ automation บนเว็บ ที่คุม Chromium, Firefox และ WebKit ผ่าน API เดียว เส้นทางหลักของ Playwright คือ test runner ที่มี fixture, assertion และ trace viewer การเอามาใช้เป็นแกนของ scraping คือการใช้ตาม Library mode ที่เอกสารระบุไว้: เริ่มจาก chromium.launch(), จากนั้น context, แล้ว page โดยอยู่นอก test harness ทุกอย่างในรีวิวนี้ใช้ API สาธารณะชุดนั้นทั้งหมด ผมไม่ได้รันการทดสอบ cross-version ดังนั้นจึงไม่ใช่การยืนยันว่าพฤติกรรมทุกอย่างจะเหมือนเดิมทุกเวอร์ชัน
ความสามารถที่ครอบคลุมหลายเบราว์เซอร์คือจุดขายที่เจอได้ทุกที่ แต่ขอพูดให้แม่นยำ Playwright คุม browser engine 3 ตัว — Chromium, Firefox และ WebKit — ผ่าน API เดียว และยังมี client แบบ first-class สำหรับ Python, Java และ .NET บนฐาน JavaScript ด้วย ข้อนี้มีเอกสารรองรับ และเป็นความต่างเชิงโครงสร้างที่เด่นที่สุดของเครื่องมือ แต่สิ่งที่ผมได้ทดสอบจริงในรอบนี้แคบกว่านั้น:
| ความสามารถ | สถานะในรีวิวนี้ |
|---|---|
| Chromium engine | ทดสอบแล้ว — ทุกเทสต์ในรีวิวนี้รันบน Chromium |
| Firefox engine | มีในเอกสาร แต่ไม่ได้ตรวจสอบในรีวิวนี้ |
| WebKit engine | มีในเอกสาร แต่ไม่ได้ตรวจสอบในรีวิวนี้ |
| API เดียวครอบคลุมทั้ง 3 engine | มีในเอกสาร แต่ไม่ได้ตรวจสอบในรีวิวนี้ |
| Python, Java และ .NET clients | มีในเอกสาร แต่ไม่ได้ตรวจสอบในรีวิวนี้ |
| Proxying | ยังไม่ทดสอบ |
| การทำงานแบบหลาย context พร้อมกัน | ยังไม่ทดสอบ |
| การดัก network เพื่อ scraping แบบ API-first | ยังไม่ทดสอบ |
ถ้าเป้าหมายแสดงผลต่างไปเมื่อใช้ WebKit ของ Safari หรือทีมของคุณใช้ Python ความครอบคลุมตรงนี้คือเหตุผลที่ Playwright น่าสนใจ แต่อย่าเอาผลของผมไปตีความว่า Firefox หรือ WebKit ให้ผลเท่ากัน เพราะผมไม่ได้ทดสอบสองตัวนั้น
มันทำงานอย่างไรภายใน
ภาพในหัวที่ควรมีคือเอ็นจินเบราว์เซอร์ที่เราคุมด้วยสคริปต์ chromium.launch() จะเริ่ม process ของเบราว์เซอร์ขึ้นมา context คือเซสชันแยกอิสระที่มี cookie, storage และ cache ของตัวเอง ส่วน page คือแท็บหนึ่งใบภายใน context นั้น คุณเรียก page.goto(url) รอเงื่อนไขที่แทนความพร้อมของแอป แล้วอ่าน DOM ที่ได้ด้วย helper อย่าง page.$$eval วิธีนี้ใกล้กับการใช้งานเบราว์เซอร์จริงมากกว่าการ parse HTTP response แต่ก็ยังไม่เท่ากับสภาพแวดล้อมจริงทั้งหมด เพราะ headless mode, viewport, locale, fonts, สถานะโปรไฟล์, เส้นทาง TLS/network และมาตรการป้องกันของเว็บไซต์ยังเปลี่ยนสิ่งที่ถูกส่งมาได้ รีวิวนี้ไม่ได้ทดสอบพฤติกรรมต้านบอทหรือความเหมือนกับ browser จริงใน production
page.screenshot() ใช้จับภาพหน้าที่เรนเดอร์แล้วได้ ทั้งแบบเต็มหน้าหรือเฉพาะส่วน ซึ่งในการรันของผมก็ทำงานสำเร็จตั้งแต่ครั้งแรกที่เรียก ส่วน request API ที่พูดถึง — context.request.get — จะใช้ cookie ของ context เดียวกัน แต่ไม่ต้องเรนเดอร์หน้า ทำให้สคริปต์เดียวกันผสมทั้ง "โหลดหน้าและอ่าน DOM" กับ "ยิง JSON endpoint ตรง ๆ" ได้โดยไม่ต้องสลับเครื่องมือ
สิ่งที่ไม่ได้มีมาในตัวคือเครื่องจักรสำหรับ crawling ไม่มี scheduler ของ request, ไม่มี visited set แบบเก็บถาวร, ไม่มีนโยบายความสุภาพต่อเว็บ, และไม่มี pipeline สำหรับ export งานแบบครบชุด การเดินแบบ bounded traversal ทำได้ไม่ยาก แต่ถ้าจะให้เชื่อถือได้จริง ยังต้องมี URL normalization, การจัดการ redirect, retry, scope rules, throttling และ recovery อีกหลายชั้น คุณต้องสร้างชั้นนั้นขึ้นมาเอง หรือใช้เฟรมเวิร์กที่ห่อ browser engine มาให้แล้ว
ความจริงเรื่องการติดตั้งและใช้งาน
การติดตั้งมี 2 ขั้นตอน และขั้นตอนที่สองคือส่วนที่แบกน้ำหนักการ deploy จริง ๆ npm install playwright จะติดตั้งไลบรารี ส่วน npx playwright install แยกต่างหากเพื่อดาวน์โหลด browser build (ในกรณีของผมคือ Chromium) ควรวางงบสำหรับพื้นที่ดิสก์ เวลาโหลด cache ของเบราว์เซอร์ใน CI และการจัดการ process ไม่ใช่มองว่าแพ็กเกจ npm คือทั้งระบบที่พร้อมรันทันที
ถ้าคุณติดตั้ง Playwright เพราะคิดว่าจะได้ scraper แล้วไปตาม tutorial ฝั่ง testing คุณจะเริ่มจากไฟล์ทดสอบและ expect() assertion แต่ถ้าจะเขียน scraping จริง คุณต้องใช้ library API โดยตรง ทั้งสองแบบมีเอกสารรองรับ แต่ความต่างนี้สำคัญมากเวลาค้นหาตัวอย่างและเลือกคำสั่ง deploy
ในการรันรอบนี้ ergonomics ที่ใช้ได้จริงมีชัดเจน browser context แยก session state ออกได้ clean, async call ต่อกันได้ลื่น, screenshot ใช้เพียงคำสั่งเดียว, และ HTTP 500 ยังดูรายละเอียดจาก response object ได้ ส่วนจุดสะดุดเป็นเรื่อง operation มากกว่า syntax คือ browser build ต้องติดตั้งแยกและต้องจัด lifecycle ของมันต่างหากจากไลบรารี
ผลลัพธ์จากการลงมือทดสอบ

ตัวเลขทั้งหมดรันทับ fixture server บน 127.0.0.1 โดยมี ground truth เขียนไว้ก่อนเริ่ม crawl โค้ด harness ที่ใช้จริงอยู่ใน run_playwright_material_tests.mjs และ raw artifacts ที่ commit ไว้แล้วใน raw artifacts ก็รวม ground truth กับผลลัพธ์รายเทสต์ไว้ด้วย สิ่งเหล่านี้ยังเป็นข้อสังเกตจากผู้เขียนบนเครื่องเดียวและการรันครั้งเดียว ลิงก์ดังกล่าวมีไว้เพื่อเปิดเผยวิธีทำซ้ำ ไม่ได้แปลว่าเป็น benchmark กว้าง ๆ
| เทสต์ | เป้าหมาย | ผลลัพธ์ |
|---|---|---|
| Static catalog + pagination | local fixture | 12/12 products, recall 1.0 |
| Article extraction | local fixture | title + 3/3 paragraphs, boilerplate kept separate |
| Dynamic JS page (native render) | local fixture | 8/8, recall 1.0, full-page screenshot saved |
Dynamic JSON API (page.request) | local fixture | 8/8, recall 1.0, no DOM rendered |
| HTTP 500 handling | local fixture | status 500 inspectable, navigation did not throw |
| Crawl graph (hand-written BFS) | local fixture | 12 pages, depths {0:1, 1:4, 2:7} |
| Books to Scrape | public demo | 20 products |
| Quotes JS (JS-rendered) | public demo | 10 quotes, rendered natively |
ลูป pagination เดินตามลิงก์ถัดไปแบบตรงไปตรงมา Playwright ไม่ได้ค้นหาหน้าให้เอง ส่วน article selector ช่วยให้ผลลัพธ์ของ body แยก navigation และ footer text ออกไปได้ เมื่อเจอเส้นทางที่ล้มเหลว การนำทางจะได้ response object ที่มี status 500 แต่ไม่ throw ทำให้คนเรียกต้องตัดสินใจเองว่าจะ log, retry หรือไปต่อ เทสต์สาธารณะสองตัวคืนจำนวนตามตาราง
มีขอบเขตที่ต้องพูดให้ตรงไปตรงมา: ทุกอย่างในที่นี้รันบน Chromium บนเครื่องเดียวเพียงครั้งเดียว ตารางความสามารถแยกความกว้างที่มีในเอกสารออกจากพฤติกรรมที่ทดสอบจริง ผมไม่ได้รันชุดทดสอบนี้ซ้ำบน Playwright build อื่น ดังนั้นจึงสรุปข้ามเวอร์ชันไม่ได้ และผมก็ไม่ได้ใช้ stopwatch เพื่อทำ benchmark เปรียบเทียบความเร็ว เพราะการจับเวลารอบเดียวบนแล็ปท็อปเครื่องเดียวไม่พอสำหรับการเทียบสปีดอย่างมีนัยสำคัญ
ความพร้อมคือส่วนหนึ่งของสัญญาการดึงข้อมูล
ผลลัพธ์แบบ dynamic พึ่งพา wait ที่แทนข้อมูลที่ผมต้องการ ไม่ใช่แค่การนำทางของเบราว์เซอร์ สำหรับ catalog ในเครื่อง เทสต์นำทางด้วย waitUntil: 'domcontentloaded' แล้วตามด้วย waitForSelector('#dynamic-products article.product-card') ด้วย timeout 15 วินาที ส่วนรอบ Quotes JS ใช้ state การนำทางแบบเดียวกันและรอ .quote ด้วย timeout 20 วินาที โดยเริ่ม extraction ก็ต่อเมื่อ selector เหล่านั้นปรากฏแล้วเท่านั้น
ความต่างนี้สำคัญเวลาเอาสคริปต์ไปใช้จริง domcontentloaded บอกแค่ว่าเอกสารถูก parse เริ่มต้นแล้ว ไม่ได้บอกว่า API ที่ตอบช้าได้ส่งข้อมูลกลับมา, hydration เสร็จแล้ว, list ที่โหลดต่อเนื่องหยุดยืดแล้ว หรือ row แบบ virtualized โผล่ขึ้นมาใน viewport หรือยัง selector มีประโยชน์เมื่อมี element ตรงกันเพียง 1 ตัวก็พอ แต่ถ้าความครบถ้วนขึ้นอยู่กับ response ที่รู้ล่วงหน้า, จำนวน item, สถานะแอป, หรือช่วงที่ network เงียบ ควรรอเงื่อนไขนั้นแทน เงื่อนไขควรผูกกับสัญญาของผลลัพธ์: “มีการ์ดอย่างน้อย 1 ใบ” กับ “ทุกหน้าที่คาดหวังถูกโหลดครบแล้ว” เป็นคนละ assertion กัน

การจัดการ timeout ก็เป็นหน้าที่ของคนเรียกเช่นกัน เทสต์นี้ใช้ selector timeout แบบจำกัดเวลา แต่ไม่ได้ศึกษา retry policy หรือแยกความต่างระหว่างหน้าโหลดช้า กับ selector ที่เปลี่ยนไปถาวร wrapper สำหรับ production ควรบันทึกว่าเงื่อนไข readiness ข้อไหนล้มเหลว เก็บ state ของหน้าให้พอวิเคราะห์ได้ และตัดสินใจว่า retry รอบถัดไปปลอดภัยหรือไม่ Playwright ให้ event และ DOM มาได้ แต่ไม่สามารถเดาได้ว่าคำว่า “ข้อมูลครบ” หมายถึงอะไรสำหรับงานของคุณ
ขอบเขตแบบนี้ควรถูกเขียนไว้ข้าง extractor ทุกตัว ไม่ควรถูกปล่อยให้เป็นแค่ timeout ที่ซ่อนอยู่
เส้นทางผ่าน API ก็มีสัญญาอีกแบบหนึ่ง ctx.request.get เหมาะเพราะ browser context มีอยู่แล้ว และการแชร์ session อาจมีประโยชน์ หากงานไหนพบว่า endpoint ของข้อมูลใช้งานได้โดยไม่ต้องมี browser session เลย standalone request context ก็เป็นสถาปัตยกรรมอีกแบบที่มี lifecycle และพฤติกรรม cookie ต่างออกไป รอบนี้ไม่ได้เปรียบเทียบสองแบบนี้ ให้ถือข้อเท็จจริงว่า “ไม่ต้องเรนเดอร์ DOM” ก่อน แล้วค่อยตัดสินแยกต่างหากว่ากระบวนการ browser จำเป็นต่อ workflow ที่กว้างกว่านั้นหรือไม่
คำถามเรื่อง crawling
ผล crawl graph คือผลที่บอกได้ชัดที่สุดว่าควรมอง Playwright ยังไง 12 หน้า 3 ระดับลึก ถูกต้อง — และทุกชิ้นของ logic การเดินล้วนเป็นของผม Playwright ให้ครึ่งหนึ่งคือ “เปิด URL นี้แล้วอ่านหน้า” ส่วน queue, visited-set และการเก็บ depth ผมเป็นคนเขียน
สำหรับงานเล็กที่มีขอบเขตชัด นี่ไม่ใช่ปัญหา แต่ถ้าเป็นงานระดับ crawl-scale หมายความว่าคุณกำลังเขียน crawler บน browser library เอง หรือเอา Playwright ไปต่อกับสิ่งที่มี crawler อยู่แล้ว รูปแบบที่มีเอกสารรองรับคือ Crawlee ซึ่งห่อ Playwright (และ Puppeteer) ด้วย request queue จริง, dataset storage และ auto-throttling — คุณยังได้ rendering ของ Playwright อยู่ แต่ยืม orchestration มาใช้ ถ้าคุณอยากให้ queue ฝังอยู่ในเฟรมเวิร์กเลยแทนที่จะเติมทีหลัง นั่นคือแนวทางหลักของ Scrapy แม้ว่า Scrapy จะเป็นแบบ HTTP-first และไม่เรนเดอร์ JavaScript ด้วยตัวเอง จุดสำคัญไม่ใช่ Playwright ไม่ดี แต่คือ “browser automation” กับ “crawling” เป็นคนละงาน และ Playwright รับผิดชอบแค่งานเดียว

BFS 12 หน้าทำให้ขอบเขตความรับผิดชอบเห็นชัด มันจัดหา queue, visited set และ depth tracking ให้กับกราฟที่ควบคุมได้ แต่ frontier แบบ production ยังต้องนิยาม URL canonicalization, redirect handling, host ที่อนุญาต, duplicate key, retry, concurrency, delay ต่อ host, persistence และ restart semantics อีกด้วย การ export ก็เป็นอีกการตัดสินใจหนึ่ง: fixture เขียนเป็น JSON และ CSV เพราะ harness ทำแบบนั้น ไม่ใช่เพราะ Playwright มี abstraction ของ dataset มาให้
การออกแบบ session ก็ส่งผลต่อ wrapper เช่นกัน เบราว์เซอร์หนึ่งตัวมีหลาย context ได้ โดย cookie และ storage แยกกัน แต่รีวิวนี้ไม่ได้วัดขนาดของหลาย context พร้อมกันหรือการแยกความเสียหายเมื่อเกิด failure การ reuse context อาจช่วยรักษา login และลดงาน setup ส่วนการสร้าง context แยกกันอาจช่วยกัน state รั่วข้ามงานได้ ทั้งหมดนี้เป็นนโยบายระดับ crawler แม้ว่า Playwright จะให้ primitive อย่าง context มาแล้วก็ตาม ควรวัด lifecycle ที่เลือกกับ browser build และสภาพแวดล้อม deploy จริงของคุณ
ข้อดีและข้อเสีย
ข้อดี:
- รัน JavaScript ผ่าน Chromium จริงได้ และทั้งสอง target แบบ dynamic ไปถึง selector ที่ใช้เป็นเงื่อนไขความพร้อม
- จับ full-page screenshot ได้ตั้งแต่การเรียกครั้งแรก
- ใช้ selector ดึงข้อมูล catalog แบบ static และ article fields จาก fixture ที่ควบคุมได้อย่างแม่นยำ
browserContext.requestเรียก JSON endpoint ได้โดยไม่ต้องเรนเดอร์หน้า ขณะที่ browser process ที่เปิดค้างอยู่ยังเป็นส่วนหนึ่งของ harness- รับมือ response แย่ได้ดี: ตรวจ status 500 ได้และ navigation ไม่ throw
- มีการรองรับ 3 engine ในเอกสาร (Chromium, Firefox, WebKit) ผ่าน API เดียว พร้อม client สำหรับ Python, Java และ .NET (มีในเอกสาร; ในรีวิวนี้ใช้จริงแค่ Chromium)
- ใช้ไลเซนส์ Apache-2.0 และดูแลโดย Microsoft
- ประสบการณ์นักพัฒนาดีเมื่ออยู่ใน library mode: API เดียวข้าม engine, async ที่ลื่น, screenshot ง่ายมาก
ข้อเสีย:
- ไม่มี crawl queue, dataset หรือ auto-throttle ในตัว — งานระดับ crawl ต้องเขียนเองหรือใช้ wrapper อย่าง Crawlee
- ตัวเบราว์เซอร์หนัก: ทั้งการดาวน์โหลด binary และต้นทุนต่อหน้าคือภาษีจริงเมื่อเทียบกับเครื่องมือแบบ HTTP-only
- กรอบเริ่มต้นของเครื่องมือคือ test runner; ถ้าจะ scrape ต้องรู้ว่ามี library mode แล้วเดินออกจากเส้นทางที่ marketing ไว้
- มีการทดสอบจริงแค่ Playwright 1.56.0 และ Chromium; ไม่ได้ทดสอบความเท่ากันข้ามเวอร์ชันหรือข้าม engine
- ไม่ได้มี output แบบ structured JSON ตาม schema ในตัวเอง; คุณต้องเขียน selector และจัดรูปข้อมูลเอง
เหมาะกับใคร และใครควรข้าม
ถ้าปัญหาของคุณคือหน้าเว็บที่ข้อมูลโผล่หลัง JavaScript ทำงาน หรืออยากเก็บ screenshot คู่กับ DOM data, Playwright เป็นตัวเลือกที่สมเหตุสมผลสำหรับเอาไปลองกับ target ของคุณ ทีมที่ใช้งาน Playwright สำหรับทดสอบอยู่แล้วสามารถเอาแนวคิดและทักษะการเขียน selector เดิมกลับมาใช้ใน library mode ได้ Client สำหรับ Python, Java และ .NET มีอยู่ในเอกสาร แต่รีวิวนี้ทดสอบแค่ Node และ Chromium
ควรพิจารณาชั้นอื่นใน 3 กรณี ถ้าข้อมูลที่ต้องการมีอยู่แล้วใน HTTP response เครื่องมือแบบ HTTP-first จะไม่ต้องเสียต้นทุนการเปิดเบราว์เซอร์และเรนเดอร์ Colly เป็น crawler library ในกลุ่มนั้น ส่วน Trafilatura เน้นการดึงบทความ ถ้าคุณต้องการ queue, persistence และ throttling ให้ใช้ crawler framework หรือ wrapper ของ Playwright ถ้าคุณต้องการผลลัพธ์เป็น schema โดยไม่ต้องดูแล selector เอง ให้เทียบกับบริการ extraction แบบ managed ทางเลือกเหล่านี้ไม่ได้ถูก benchmark ในรีวิวนี้
ถ้าคุณกำลังตัดสินใจระหว่าง Playwright กับ Puppeteer โดยเฉพาะ นั่นเป็นการเทียบแบบตัวต่อตัวอีกเรื่องหนึ่ง; บทความ เปรียบเทียบกันแบบ side-by-side ของเราทดสอบทั้งสองตัวด้วย fixture เดียวกันและอธิบายว่าตัวเลือกสุดท้ายลงตรงไหน
ทางเลือกอื่น และ managed extraction เข้ามาตรงไหน
Playwright ฟรี ใช้ไลเซนส์ Apache-2.0 และ self-hosted เองได้ แต่คุณต้องดูแลการ deploy เบราว์เซอร์, selector, เงื่อนไข readiness, โค้ด crawl, การอัปเดต และการจัดการข้อผิดพลาดทั้งหมด รีวิวนี้ไม่ได้วัดประสิทธิภาพในการต้านบอทหรือเทียบต้นทุนรวมกับบริการแบบ managed
ในโลก open source การเปรียบเทียบที่มีประโยชน์ต้องดูตาม “งาน” สำหรับงาน crawl ขนาดใหญ่บนเบราว์เซอร์ Crawlee จะเพิ่ม queue และ dataset ที่ Playwright ไม่มีให้ ถ้าเป้าหมายผลลัพธ์ของคุณคือ Markdown พร้อมใช้กับ LLM จากเบราว์เซอร์จริงแทนที่จะเป็นแถวข้อมูลที่คุณจัดเอง Crawl4AI จะเปิดเบราว์เซอร์และสร้าง Markdown ให้กับ pipeline นั้น และถ้าคุณกำลังชั่งหลายตัวพร้อมกัน บทสรุป open-source scraper roundup ของเราจะจัดหมวดหมู่ให้เห็นเทียบกันชัด ๆ
คำชี้แจง: Thunderbit เป็นผลิตภัณฑ์ของผู้เผยแพร่และไม่ได้ถูกรันผ่าน fixture ของ Playwright นี้ มันอยู่ในกลุ่มบริการ extraction แบบ managed: บริการเป็นคนดูแลการเรนเดอร์และส่งคืนข้อความหน้าเว็บหรือ record ตาม schema ขณะที่ Playwright ฝากภาระการเปิดเบราว์เซอร์และ logic ของ selector ไว้กับนักพัฒนา ดังนั้นการเทียบกันจึงเป็นเรื่องของโมเดลโฮสต์, รูปแบบ output และต้นทุน ไม่ใช่ผลการวัดประสิทธิภาพจากรีวิวนี้
ลองใช้ Thunderbit สำหรับดึงข้อมูลเว็บ
บทสรุป
ใช้ Playwright เมื่อ target ของคุณต้องใช้ browser engine และคุณพร้อมจะรับผิดชอบเรื่อง readiness condition, selector และการ orchestration สำหรับ crawl เอง ตารางผลลัพธ์แสดงให้เห็นว่า library mode บน Chromium รับมือทั้ง fixture แบบ static, dynamic, API, screenshot และ failure ได้ตามที่ควรในรอบทดสอบที่ผมรันเอง
แต่ต้องรักษาขอบเขตของหลักฐานไว้ให้ชัด: ทดสอบแค่ Chromium และ Node, การเดิน 12 หน้าอาศัย BFS ที่เขียนเอง, browserContext.request ข้ามการเรนเดอร์หน้าแต่ยังไม่ข้าม browser process ที่รันอยู่แล้ว, และทุกการ extraction แบบ dynamic ใช้ selector บอกความพร้อมอย่างชัดเจน ข้อจำกัดเหล่านี้ทำให้ Playwright ในรีวิวนี้เป็น primitive สำหรับคุมเบราว์เซอร์ ไม่ใช่ระบบ crawling แบบ end-to-end ที่วัดผลครบถ้วน
ลองใช้ Thunderbit สำหรับดึงข้อมูลเว็บ Get Started Free
คำถามที่พบบ่อย
ยังต้องใช้ wait อยู่ไหมเวลา scrape ด้วย Playwright?
ต้องใช้ เพราะการรันในเบราว์เซอร์ไม่ได้บอกสคริปต์ของคุณว่าเมื่อไรข้อมูลของแอปพร้อมแล้ว เทสต์เหล่านี้นำทางไปที่ domcontentloaded แล้วค่อยรอ selector เฉพาะของเป้าหมายก่อนเริ่มดึงข้อมูล หน้า production อาจต้องใช้สัญญาณแบบอื่น เช่น response, locator state หรือ application event
Playwright crawl ทั้งเว็บได้เองเลยไหม? ไม่ได้มีให้ในตัว ไม่มี request queue, dataset writer หรือ auto-throttle — เทสต์ crawl graph ของผมไปได้ 12 หน้า โดยมีความลึก {0:1, 1:4, 2:7} ก็เพราะผมเขียน breadth-first search เอง สำหรับงานระดับ crawl-scale ให้ใช้ Playwright คู่กับ Crawlee ซึ่งห่อมันด้วย crawling layer จริง หรือใช้ crawler framework แทน
ควรใช้ browserContext.request เมื่อไร แล้วเมื่อไรควรใช้ standalone request context?
ใช้ browserContext.request เมื่อ HTTP call ควรแชร์ cookie กับหน้าเว็บใน browser context ที่มีอยู่แล้ว ใช้ playwright.request.newContext() เมื่ออยากได้ context แบบ API-only โดยไม่เปิด browser และไม่ต้องแชร์ cookie กับหน้าเว็บโดยอัตโนมัติ รอบนี้ทดสอบแค่เส้นทางแรก
รีวิวนี้ทดสอบ Firefox และ WebKit ด้วยไหม? ไม่ทดสอบ ทุกเทสต์รันบน Chromium บนเครื่องเดียวและรันเพียงครั้งเดียว การรองรับ 3 engine ของ Playwright (Chromium, Firefox, WebKit) และ client สำหรับ Python, Java และ .NET เป็นความสามารถที่มีในเอกสาร แต่ไม่ได้ตรวจสอบในรีวิวนี้ — ความเท่ากันของ Firefox และ WebKit, proxying, parallel scale และ network interception ล้วนอยู่นอกขอบเขตของตัวเลขชุดนี้
รีวิวนี้ครอบคลุมสภาพแวดล้อมไหน? Playwright 1.56.0, Node v22.22.3, macOS arm64 และ Chromium เท่านั้น Firefox, WebKit, proxying, parallel scale, พฤติกรรมต้านบอท และเวอร์ชัน Playwright ที่ใหม่กว่านี้อยู่นอกการรันครั้งนี้ การติดตั้งต้องลงทั้งไลบรารีและดาวน์โหลด browser build แยกต่างหาก


