nodriver รีวิว: ไดรเวอร์ CDP ขนาดจิ๋วที่ไม่ยอม import บน Python 3.14

อัปเดตล่าสุดเมื่อ August 18, 2026
nodriver รีวิว: ไดรเวอร์ CDP ขนาดจิ๋วที่ไม่ยอม import บน Python 3.14
สรุปด้วย AI
nodriver คือไลบรารีสำหรับควบคุมเบราว์เซอร์ด้วย Python จาก ultrafunkamsterdam ผู้สร้าง undetected-chromedriver และตัวโปรแกรมก็วางตำแหน่งตัวเองว่าเป็นทายาทของโปรเจกต์นั้น จุดเปลี่ยนสำคัญเชิงสถาปัตยกรรมของมันคือการแยกตัวออกจาก Selenium และ chromedriver อย่างชัดเจน: nodriver คุยกับ Chromium ผ่าน Chrome DevTools Protocol (CDP) โดยไม่ต้องมีไบนารี WebDriver มาคั่นกลาง และ API ของมันก็เป็นแบบ async ส่วน Playwright กับ Puppeteer อยู่คนละกลุ่มเปรียบเทียบกัน พวกนั้นก็เป็นตัวควบคุมเบราว์เซอร์ที่ขับด้วยโปรโตคอลเหมือนกัน ไม่ใช่สายลูกหลานของ WebDriver; ความต่างที่มีประโยชน์จริง ๆ คือการออกแบบ API การแพ็กเกจ การจัดเตรียมบราวเซอร์ และนโยบายความเข้ากันได้ รีวิวนี้เป็นการสำรวจ nodriver 0.50.3 มากกว่าจะทดสอบมันชนกับระบบป้องกันจริง ๆ

nodriver คือไลบรารีสำหรับควบคุมเบราว์เซอร์ด้วย Python จาก ultrafunkamsterdam ผู้สร้าง undetected-chromedriver และตัวโปรแกรมก็วางตัวเองไว้ชัดเจนว่าเป็นทายาทของโปรเจกต์นั้น จุดเปลี่ยนสำคัญเชิงสถาปัตยกรรมของมันคือการแยกตัวออกจาก Selenium และ chromedriver แบบชัดเจน: nodriver คุยกับ Chromium ผ่าน Chrome DevTools Protocol (CDP) โดยไม่ต้องมีไบนารี WebDriver มาคั่นกลาง และ API ของมันก็เป็นแบบ async ส่วน Playwright กับ Puppeteer อยู่คนละฝั่งของการเปรียบเทียบกัน พวกนั้นก็เป็นตัวควบคุมเบราว์เซอร์ที่ขับด้วยโปรโตคอลเหมือนกัน ไม่ใช่สายลูกหลานของ WebDriver; ความต่างที่ใช้งานได้จริงคือการออกแบบ API การแพ็กเกจ การจัดเตรียมบราวเซอร์ และนโยบายความเข้ากันได้

รีวิวนี้เป็นการสำรวจ nodriver 0.50.3 มากกว่าจะเอามันไปชนกับระบบป้องกันจริง ๆ ผมตรวจแพ็กเกจที่ติดตั้งไว้ ดูการ import พื้นผิวของ API ขนาดพื้นที่บนดิสก์ และไลเซนส์ จากนั้นจึงเปิดเบราว์เซอร์เฉพาะกับหน้าที่เสิร์ฟบน 127.0.0.1 เท่านั้น ไม่มีการแตะ target จริง บริการกันบอทจริง หรือ CAPTCHA จริง ๆ ผลลัพธ์ที่ได้จึงบอกพฤติกรรมของแพ็กเกจและสิ่งที่เบราว์เซอร์เปิดเผยตามค่าเริ่มต้น ไม่ได้ยืนยันประสิทธิภาพการหลบการตรวจจับใด ๆ

มีอยู่สามเรื่องที่เด่นชัดภายในขอบเขตนั้น อย่างแรก บน Python 3.14 ไลบรารีนี้ import ไม่ได้เลย — มีไบต์ตัวเดียวที่หลงมา ทำให้ทั้งแพ็กเกจล้มก่อนจะเรียกใช้อะไรได้ อย่างที่สอง ถ้ามองในกลุ่ม driver ประเภทนี้ มันเล็กมากแบบน่าทึ่ง: มี dependency ที่ประกาศไว้เพียงสามตัว บวกแพ็กเกจทรานซิทีฟที่ตามมาอีกหนึ่งตัว และกินพื้นที่ประมาณ 17 MB ในสภาพแวดล้อมที่วัดได้ อย่างที่สาม บนหน้าของผมเอง ค่าที่ nodriver ต่างจาก Playwright และ Puppeteer แบบ stock ให้เห็นชัดมีแค่ boolean ตัวเดียว — และสตริง user-agent ตอน headless ก็ยังบอก HeadlessChrome เหมือนกันทั้งสามตัว สิ่งที่ทำให้ประหลาดใจกว่านั้นอยู่ที่ไลเซนส์

จุดพังตอน import บน Python 3.14

เริ่มจากสิ่งที่น่าจะชนคุณก่อน เพราะมันเกิดก่อนที่โค้ดของคุณจะทำงานเสียอีก บน Python 3.14 แค่ import nodriver ธรรมดาก็ล้มทันที:

File ".../nodriver/cdp/network.py", line 1345
    #: JSON (±Inf).
             ^
SyntaxError: Non-UTF-8 code starting with '\xb1' on line 1345, but no encoding declared; see PEP 263

ไฟล์ที่พังเป็น cdp/network.py ที่ถูกสร้างอัตโนมัติ (หัวไฟล์ระบุว่า # DO NOT EDIT THIS FILE!) ภายในมีไบต์ที่ไม่ใช่ UTF-8 อยู่หนึ่งตัวคือ 0xb1 สำหรับเครื่องหมาย ± ในคอมเมนต์ #: JSON (±Inf). และไม่มีการประกาศ source encoding โมดูลถูกโหลดผ่าน nodriver/__init__ → cdp/__init__ → network ดังนั้น parse error นี้จึงหยุดการ import ทันที การสแกนแพ็กเกจไม่พบไฟล์ซอร์สอื่นที่ไม่ใช่ UTF-8

ขอบเขตของเวอร์ชันต้องพูดให้รอบคอบ Python 3.14.2 ปฏิเสธไฟล์นี้ ในขณะที่ Python 3.12.13 import ได้โดยไม่ต้องแพตช์ ไฟล์ network.py ที่ติดตั้งอยู่ในทั้งสองสภาพแวดล้อมเหมือนกันทุกไบต์ (SHA-256 ef755f41800d4efb593736f8b55b331bba68eec373ed62ece433e66e4b491cd6) ดังนั้นจึงไม่ใช่ว่ามี source artifact คนละชุดกันที่อธิบายผลลัพธ์นี้ รีวิวนี้ไม่ได้แยกให้ชัดว่าการเปลี่ยน tokenizer ของ CPython ตัวใดเป็นต้นเหตุ และไม่ได้ทดสอบ Python 3.13 ข้อมูลที่รายงานจึงเป็นผลจากการวัดสองจุดปลายทาง ไม่ได้สรุปว่า Python ก่อนหน้านั้นทุกเวอร์ชันจะเหมือน 3.12

จึงไม่มีข้อสรุปใด ๆ เกี่ยวกับ Python 3.13 จากสองจุดที่วัดนี้

นี่เป็นการทำซ้ำผล ไม่ใช่การค้นพบใหม่ traceback เดียวกันบน Python 3.14 ถูกบันทึกไว้ใน nodriver issue #35 และมีแนวทางแก้ใน pull request #36 เวอร์ชัน 0.50.3 ยังมีไบต์นั้นอยู่ เมทาดาทาจาก PyPI ระบุ classifier ถึง Python 3.13 และไม่ได้อ้างว่ารองรับ 3.14

วิธีแก้ชั่วคราวนั้นสั้นพอ ๆ กับบั๊ก: ใช้เวอร์ชัน Python ที่ทดสอบแล้ว หรือ re-encode ไฟล์นั้นไฟล์เดียวให้เป็น UTF-8 ตามที่ pull request #36 เสนอ หลังจาก re-encode แล้ว แพ็กเกจจะ import และ introspect บน 3.14 ได้โดยไม่มีตัวขวางอื่นเพิ่มเข้ามา ผลการรันที่สะอาดบน 3.12.13 ด้านล่างยืนยันเส้นทางหนึ่งที่ไม่ได้แพตช์ไว้ ส่วน Python 3.13 ไม่ได้ทดสอบในการรีวิวนี้

บน Python 3.12.13, import nodriver สำเร็จโดย ไม่ต้องแพตช์ ตัวเลขด้านการขับเบราว์เซอร์ทั้งหมดในรีวิวนี้มาจากการติดตั้งชุดนั้น ค่าขนาด footprint และเวลาการ import ใช้สำเนาที่ re-encode ไบต์เดียวบน 3.14 และจะมีการระบุไว้ตามนั้น Python 3.13 ยังไม่ถูกทดสอบที่นี่ ผลลัพธ์หนึ่งจาก 3.12 ไม่ได้แปลว่าครอบคลุมช่วงก่อนหน้านั้นทั้งหมด

ถ้าคุณใช้ interpreter รุ่นใหม่เป็นค่าเริ่มต้น — ซึ่งหลายทีมรีบอัปเกรด Python กันไวมาก — เรื่องนี้คือกำแพงจริง แม้จะแก้ได้ไม่ยาก ก็ควรรู้ไว้ตั้งแต่แรกเพื่อจะได้ไม่เสียบ่ายไปกับ SyntaxError ในไฟล์ที่คุณไม่ได้เขียนเอง

จริง ๆ แล้ว nodriver คืออะไร ภายในมันทำงานยังไง

คำอธิบายสั้น ๆ ว่า (“CDP-native, no webdriver”) ฟังดูเหมือนคำตลาดอยู่บ้าง จนกว่าจะเห็นสิ่งที่แพ็กมาใน wheel nodriver มีชุด DevTools Protocol bindings ของตัวเองครบถ้วน: แพ็กเกจ nodriver.cdp มี 57 โมดูล domain ของโปรโตคอล — accessibility, dom, network, page, fetch, runtime, target, storage, input, emulation และอีกยาวเหยียด นั่นคือกลไกที่อยู่เบื้องหลังคำอ้างว่า “direct CDP, no Selenium” แทนที่จะเรียกใช้ไฟล์ chromedriver ที่พูด WebDriver แล้วค่อยแปลงคำสั่งให้ nodriver สร้างอ็อบเจ็กต์ Python สำหรับ domain ของ CDP แล้วคุยโปรโตคอลโดยตรงผ่าน WebSocket เอง cdp/network.py — ไฟล์ที่มีไบต์ผิดปกตินั่น — ก็เป็นหนึ่งใน 57 โมดูลที่สร้างอัตโนมัติ จึงไม่แปลกที่ปัญหานี้จะไปซ่อนอยู่ในโค้ดที่ไม่มีใครแก้มือ

เหนือชั้นโปรโตคอลนั้น ยังมี object model ที่ใช้งานง่ายกว่าอยู่ด้วย อ็อบเจ็กต์ Tab เปิดให้เรียกใช้ public method ได้ 62 ตัว และขอบเขตการค้นหาก็หลากหลายกว่าที่ driver ส่วนใหญ่จะลงทุนทำ: ค้นหาข้อความผ่าน find() และ find_all() ค้นหา CSS ผ่าน select() และ select_all() และมีทางเข้า xpath() แบบ native ด้วย XPath, CSS และ text search อยู่บนอ็อบเจ็กต์เดียวกันถือว่าสะดวกจริง ๆ; ไลบรารีอื่นบางตัวทำให้คุณต้องถอยไปใช้ evaluate() แทนถ้าจะใช้ XPath คอนสตรักเตอร์ Config มี user_data_dir, headless, browser_executable_path, browser_args, sandbox, lang (ค่าเริ่มต้น 'en-US'), host, port, expert และ **kwargs ระหว่างทำ inventory ของ API นี้ การสร้าง Config(headless=True) ให้ 16 Chromium launch flags รวมถึง --no-first-run, --no-default-browser-check, --remote-allow-origins=*, และ --homepage=about:blank ผมไม่ได้เรียก start() ในขั้น introspection นี้ การทดสอบเบราว์เซอร์ด้านล่างเป็นการรันคนละชุด

System diagram: Direct CDP Control Path

ตัวไลบรารีเองก็มี API surface ที่ออกแบบมาสำหรับการหลบการตรวจจับอยู่บ้าง — เมธอดเหล่านี้ผมยืนยันการมีอยู่ได้ แต่ไม่ได้ทดลองพฤติกรรมกับ target ใด ๆ ผมขอพูดถึงแค่นี้แล้วเดินต่อ เพราะผลของมันกับบริการจริงเป็นสิ่งเดียวกับที่ผมตั้งใจไม่ไปทดสอบ สิ่งที่พูดได้แบบเป็นกลางคือ ชื่อฟังก์ชันของ nodriver ดูสุขุมกว่าเพื่อนร่วมสาย stealth บางตัว เรื่องราวของมันเป็นเชิงสถาปัตยกรรม — CDP-native, ใช้โปรไฟล์ใหม่ทุกครั้งที่รัน — มากกว่าจะเป็นแนวเมธอดชื่อ detect_and_bypass เต็มหน้าจอ อ่านสิ่งนี้เป็นข้อสังเกตด้านการออกแบบ API ไม่ใช่การฟันธงเรื่องผลลัพธ์

ตัวเลขเหล่านี้มาจากการ import แพ็กเกจแล้วใช้ introspection ของ Python เอง — inspect, ไล่โมดูล, นับแอตทริบิวต์ — ไม่มีการแตะเว็บไซต์ใด ๆ ถ้าคุณจะลองรันซ้ำ ตัวเลขก็ออกมาจากแพ็กเกจตรง ๆ ไม่ใช่ความรู้สึก

มันประกาศตัวเองว่าอะไร

Measured results chart: Browser disclosure fields in the tested stacks

มีคำถามหนึ่งที่ตอบได้โดยไม่ต้องไปชนระบบป้องกันจริงแม้แต่นิดเดียว: เวลาที่ nodriver ขับเบราว์เซอร์นั้น เบราว์เซอร์เปิดเผยข้อมูลอะไรเกี่ยวกับตัวเองให้หน้าที่มันกำลังเปิดดู? ผมเขียนหน้าเว็บที่อ่านค่าชัด ๆ เช่น navigator.webdriver, user-agent, platform, languages, จำนวน plugin และฮาร์ดแวร์, รูปร่างของ window.chrome, สิ่งที่ Permissions API บอก, เรขาคณิตของหน้าต่างและหน้าจอ แล้วเสิร์ฟบน 127.0.0.1 จากนั้นชี้สแตกสี่ชุดเข้าไป: nodriver, Botasaurus, และ Playwright stock กับ Puppeteer stock เป็นตัวควบคุม ทั้งสี่ชุดขับ Chrome build เดียวกัน (Chrome for Testing 151.0.7922.10) ดังนั้นสิ่งที่ต่างกันคือไลบรารี ไม่ใช่ตัวเบราว์เซอร์ มีทั้งแบบ headless และ headed อย่างละสามรอบ ค่าทุกตัวด้านล่างตรงกันทุกครั้งในทั้งสามรอบ

StackModenavigator.webdriverUser-agent tokennavigator.languages
nodriver 0.50.3headlessfalseHeadlessChrome/151.0.0.0["en-US"]
nodriver 0.50.3headedfalseChrome/151.0.0.0["en-US"]
Botasaurus 4.0.92headless / headedfalseHeadlessChrome/151 / Chrome/151["en-US"]
Playwright 1.56.0headless / headedtrueHeadlessChrome/151 / Chrome/151["en-US","en"]
Puppeteer 24.16.0headless / headedtrueHeadlessChrome/151 / Chrome/151["en-US"]

ความต่างที่ชัดที่สุดคือ boolean ตัวเดียวนี้ ภายใต้ launch configuration ค่าเริ่มต้น nodriver รายงาน navigator.webdriver เป็น false ในขณะที่ Playwright และ Puppeteer แบบ stock รายงานเป็น true ทั้งใน headless และ headed การทดสอบนี้บอกความต่างระดับ configuration เท่านั้น ไม่ได้พิสูจน์ว่าค่านี้เกิดจากการไม่มี WebDriver binary เพียงอย่างเดียว

ในทั้งสี่สแตก property นี้ยังคงเป็น getter ดั้งเดิมของเบราว์เซอร์บน Navigator.prototype อยู่ — function get webdriver() { [native code] } — ไม่ใช่ own-property บนอ็อบเจ็กต์ และไม่ได้มีฟังก์ชันถูกแทนที่โดย JavaScript ของหน้าเว็บหลังโหลด การทดสอบนี้ไม่ได้ไล่ดู launch argument หรือเส้นทาง source ที่ทำให้ค่าต่างกัน

รายละเอียดที่สองคือส่วนที่หักมุมคำตลาด ในโหมด headless, user-agent ของ nodriver ยังประกาศ HeadlessChrome/151.0.0.0 อยู่ — เหมือนกับ Puppeteer stock และ Playwright stock ทุกประการ พอรันแบบ headed ก็กลายเป็น Chrome/151.0.0.0 เหมือนกันอีก ถ้าคุณคิดว่าไลบรารีสายหลบการตรวจจับจะปิดสตริงที่บอกตัวตนชัดที่สุดใน browser automation ให้โดยอัตโนมัติ นั่นไม่ใช่กรณี คุณต้องตั้งเอง

อย่างอื่นแทบเหมือนกันทั้งหมดทั้งสี่สแตก และควรพูดให้ชัดเพราะมันทำให้เรื่องแคบลง — property ด้านล่างอ่านค่าเหมือนกันใน nodriver, Botasaurus, Playwright และ Puppeteer:

PropertyValue, identical on all four stacks
platformMacIntel
vendorGoogle Inc.
Pluginsfive
MIME typestwo
pdfViewerEnabledtrue
Logical corestwelve
Reported device memory16 GB
Touch pointszero
window.chromepresent, with app/csi/loadTimes and no runtime
WebGL renderer stringidentical across all four

ปัญหาคลาสสิกที่ Permissions API กับ Notification.permission ขัดแย้งกันไม่โผล่มาเลย — ทั้งสี่ตัวรายงาน default และ prompt ตรงกัน ผมยังไล่ดู document และ window หาเศษ cdc_ แบบที่ WebDriver รุ่นเก่าเคยทิ้งไว้ด้วย: ว่างทั้งหมด

จุดเดียวที่ nodriver ดูเหมือนเบราว์เซอร์อัตโนมัติแบบโล่ง ๆ มากกว่าตัวควบคุมคือเรขาคณิตของหน้าต่าง headless nodriver รายงาน outerWidth/outerHeight เป็น 0×0 บนหน้าจอ 800×600 ในขณะที่ headless Playwright รายงาน 1280×720 เพราะมันตั้ง viewport มาให้ Puppeteer อยู่ในระดับเดียวกับ nodriver ที่ 0×0 นั่นเป็นความต่างที่มาจากค่าเริ่มต้น ไม่ใช่ความสามารถ และคุณปรับได้

อีกเรื่องหนึ่งที่ควรรู้ก่อนเอาไปใช้งานจริง: nodriver ไม่ได้แถม browser มาด้วย ดังนั้น out of the box มันจึงใช้ Chrome ที่มีอยู่ในเครื่องคุณเอง บนเครื่องผมมันตรวจพบ /Applications/Google Chrome.app อัตโนมัติ — Chrome 150.0.7871.187 — และ user-agent ที่มันเปิดเผยก็คือเวอร์ชันนั้น ไม่ใช่เวอร์ชันที่ pin ไว้ เวอร์ชันเบราว์เซอร์ที่ traffic ของคุณประกาศออกไปก็คือเวอร์ชันที่มีอยู่ในเครื่องของคุณนั่นเอง

พูดให้ตรงว่าอะไรคืออะไรและอะไรไม่ใช่ นี่คือบันทึกว่าเมื่อมีการสั่งงานอัตโนมัติแล้วมันเปิดเผยอะไรออกมา ถ้าอยู่อีกฝั่งของการป้องกัน มันก็มีประโยชน์ ถ้าคุณอยากรู้ว่าเครื่องมือของตัวเองประกาศอะไรออกไป มันก็มีประโยชน์เช่นกัน แต่นี่ ไม่ใช่ การวัดว่าข้อมูลเหล่านั้นจะมีความหมายต่อบริการใดบริการหนึ่งแค่ไหน ผมไม่ได้ทดสอบส่วนนั้น และไม่มีแถวไหนด้านบนที่ควรถูกตีความว่าเป็นการรับประกันผลลัพธ์

มันดึงคอนเทนต์ออกจากหน้าเว็บได้จริงไหม

การประกาศตัวเองเป็นเรื่องหนึ่ง แต่การดึง HTML ที่ถูกต้องออกมาเป็นอีกเรื่องหนึ่ง ผมรัน nodriver กับ fixture ที่ใช้คอนเทนต์สามคลาสแบบเดียวกับ benchmark repo ส่วนที่เหลือ เพื่อให้ตัวเลขเทียบกับเครื่องมืออื่นที่วัดไว้ในที่นี้ได้ หน้าเว็บมีสามอย่าง: A ลิงก์แบบ static ที่ marker อยู่ในไบต์ที่เสิร์ฟออกไปตรง ๆ; B โหนดที่ถูกสร้างโดย inline script ระหว่าง parse โดยประกอบ marker และ URL จากชิ้นส่วนต่าง ๆ เพื่อให้ต้อง รัน JavaScript ก่อนถึงจะเห็น; และ C โหนดที่ถูก inject หลังจาก load event ไปแล้ว 800 ms โดยประกอบแบบเดียวกัน Class C คือฝั่งที่ชอบเล่นงานคนอ่านเร็ว — ถ้าอ่านตอน load event จะมองไม่เห็นมัน

StackDefault readWith an explicit wait
nodriver 0.50.32 of 3 (A + B, misses C)3 of 3
Botasaurus 4.0.922 of 33 of 3
Playwright 1.56.02 of 33 of 3
Puppeteer 24.16.02 of 33 of 3

nodriver ไปลงที่จุดเดียวกับกลุ่มตัวใหญ่พอดี browser.get() แล้วตามด้วย tab.get_content() ทันทีคือการ snapshot ตอน load: มันเรนเดอร์ JavaScript ได้ถูกต้อง — class B พิสูจน์แล้ว เพราะ class B ไม่มีอยู่ในไบต์ที่เสิร์ฟเลยแต่ก็ถูกดึงออกมาได้ — แต่จะพลาดอะไรที่ inject หลังจาก load ไปแล้ว ถ้าเพิ่ม tab.select("#delayed-injected") ก็จะได้ครบทั้งสาม คลาสเดียวกับที่ Playwright และ Puppeteer เจอ การเล่นพลาดเหมือนกัน วิธีแก้ก็เหมือนกัน ทำได้เสถียรตลอดสามรอบและสามการรันทั้งชุด ไม่มีเฟลกเลย

ผมไล่เลื่อนความล่าช้าในการ inject เพื่อหาจุดที่ default read เลิกรู้เรื่อง nodriver จะมองไม่เห็น class C เมื่อการ inject เกิดหลัง load ตั้งแต่ 100 ms ขึ้นไป — เป็นขอบเขตเดียวกับตัวควบคุม stock ทั้งสองตัว (Botasaurus เป็นข้อยกเว้นที่นี่ และเป็นความต่างที่น่าสนใจจริง ๆ ระหว่างไลบรารีสาย anti-detect สองตัว: get() ของมันรอให้หน้าโหลดครบโดยปริยาย ดังนั้น default read ยังจับการ inject ที่ 300 ms ได้ มันจ่ายเวลาราว 250 ms ต่อ navigation เพื่อสิ่งนี้)

ตัว wait เองก็มีพฤติกรรมเฉพาะที่ควรเผื่อไว้ ในการไล่นี้ tab.select() ของ nodriver ตอบกลับเป็นช่วง polling หยาบ ๆ มากกว่าจะไล่ตามความล่าช้าได้แนบแน่น:

Class-C delay0 ms100 ms400 ms800 ms1500 ms
nodriver select()124–152 ms1132–11411128–11292132–21772138–2150
Puppeteer waitForSelector113–129 ms203–216512–516911–9191608–1611

ใน fixture นี้ การ inject ที่ 100 ms ทำให้ select() ใช้เวลาประมาณ 1.1 วินาทีจริง ๆ ลูปที่ติดตั้งมาทำ await self แล้วตามด้วย await self.sleep(0.5) หลังพลาดหนึ่งครั้ง; รอบรวมที่วัดได้จึงใกล้หนึ่งวินาทีในเคสนี้ แต่ await self ยังไม่ได้พิสูจน์ว่าเป็นการ sleep ระยะคงที่แบบสากล ทุก node ที่ทดสอบในโหมด delay ถูกพบหมด Puppeteer waiter ไล่ตามความล่าช้าได้ใกล้กว่า ความต่างนี้ถ้ามีหลาย wait ต่อกันอาจสะสมได้ แม้ว่ารีวิวนี้จะไม่ได้ benchmark หน้าโปรดักชันที่มี 30 selector

การเปิดเบราว์เซอร์ก็เป็นอีกจุดที่คำว่า async และ lean มาชนกับความจริง การนำ browser ขึ้นทำให้ nodriver อยู่ในโซนเดียวกับ Botasaurus และ Puppeteer และช้ากว่า Playwright ชัดเจน:

StackBrowser launch, across runs
nodriver 0.50.3910–1583 ms
Botasaurus 4.0.92986–1151 ms
Puppeteer 24.16.0969–1008 ms
Playwright 1.56.0282–365 ms

เมื่อเริ่มทำงานแล้ว เส้นทาง navigate-and-read ของ nodriver กลับเร็วที่สุดในสี่ตัวที่ 119–129 ms เบาเมื่ออยู่หลังพวงมาลัย แต่ตอนสตาร์ตธรรมดา

การติดตั้งและ footprint: ส่วนที่ดีจริง ๆ

ตรงนี้คือจุดที่ nodriver สมชื่อว่า “focused” และเป็นข้อเท็จจริงด้านการติดตั้งที่ตรวจสอบได้แบบไม่มีพิษภัย ไม่ใช่เรื่องการ scrape แต่อย่างใด นี่คือสิ่งที่ pip install nodriver แบบสะอาด ๆ แก้ dependency ให้:

Install factnodriver 0.50.3
Declared direct runtime dependenciesthree — websockets, mss, deprecated
Resolved transitive dependencywrapt (via deprecated)
Measured site-packages totalabout 17.2 MB across 6 dist-info directories, including the environment's pip
nodriver's own share of that3.7 MB
numpy / lxmlneither
Browser binary at install timenone pulled down

สำหรับหมวดที่มักลาก rendering stack เข้ามาด้วย ตัวนี้ถือว่าเบามาก

ความต่างระหว่างแพ็กเกจตรงกับทรานซิทีฟมีความหมายต่อการดูแลรักษา เมทาดาทาของ nodriver ขอ mss, websockets, และ deprecated; wrapt ตามมาจากการที่ deprecated ต้องใช้มัน ไดเรกทอรี dist-info ตัวที่หกในสภาพแวดล้อม 17.2 MB คือ pip ซึ่งมีอยู่ใน virtual environment นั้นแล้ว ดังนั้น “17.2 MB across six distributions” คือการบรรยายสภาพแวดล้อมที่วัดได้ ส่วน “แพ็กเกจรันไทม์ที่เพิ่มเข้ามาสี่ตัว” คือสิ่งที่การติดตั้งแก้ dependency ออกมา ตัวเลขสองชุดนี้สัมพันธ์กัน แต่แทนกันไม่ได้

ความต่างที่ทำให้ตัวเลขนี้มีความหมาย: บนเครื่องเดียวกัน framework พี่น้องอย่าง Botasaurus หนัก 122.3 MB across 44 packages — ประมาณ เจ็ดเท่า ของ footprint บนดิสก์ นี่คือความต่างระหว่าง CDP driver แบบโฟกัสกับ framework แบบมีของครบ และมันมีสองด้าน nodriver ให้ dependency tree ที่บางและอ่านง่ายพอจะ audit ได้จริง; Botasaurus ให้ของมาเยอะกว่าตั้งแต่แกะกล่อง แต่ก็แลกกับพื้นที่ดิสก์และ surface ของ dependency ที่ใหญ่กว่า ไม่มีตัวไหน “ดีกว่า” แบบสัมบูรณ์ — ขึ้นอยู่กับว่าคุณต้องการ driver หรือ framework — แต่ถ้าคุณให้ค่าน้ำหนักกับการติดตั้งที่เล็กและตรวจสอบได้ nodriver ถือว่าสะอาดมากสำหรับสิ่งที่มันทำ

พื้นที่ดิสก์ที่เล็ก ไม่ได้ แปลว่า import จะเบา บนสำเนา Python 3.14 ที่แก้ไบต์แล้ว import nodriver ใช้เวลาราว 158 ms (มัธยฐานจากการ import ผ่าน subprocess ใหม่ ๆ ประมาณ 151–199 ms) ส่วน 3.14 ที่ไม่ได้แพตช์จะ import ไม่ได้ ดังนั้นจึงไม่มีการรายงานเวลาให้มัน ชุด CDP ทั้ง 57 โมดูลถูกโหลดแบบ eager และหน่วยความจำ resident หลัง import อยู่ที่ 31.5–31.9 MB ก่อนจะมี Chrome process ใด ๆ โผล่ขึ้นมา เบราว์เซอร์ที่เปิดจริงจะกินมากกว่านั้นเยอะ และไม่ได้ถูกรวมในค่าหน่วยความจำนี้

มีข้อควรระวังสองข้อที่แปะไว้กับตัวเลขเหล่านี้โดยเฉพาะ ข้อมูลมาจากเครื่องเดียว — macOS arm64 — และค่าขนาด footprint กับเวลา import ถูกวัดบน Python 3.14 ที่ใช้สำเนาแก้ไบต์เดียว เพราะแพ็กเกจต้นฉบับจะ import ไม่ได้เลยบน interpreter ตัวนั้น สำหรับ Python ที่รองรับจริง คุณไม่ต้องแพตช์: ผมยืนยันการ import unpatched ที่สะอาดบน 3.12.13 ซึ่งเป็นแหล่งที่มาของตัวเลขด้านเบราว์เซอร์ทั้งหมด และยังต้องใช้ Chrome, Chromium, Edge หรือ Brave จริง ๆ ในตอน runtime — nodriver ควบคุมเบราว์เซอร์ที่มีอยู่แล้ว ไม่ได้แถมมาให้ ดังนั้นต้นทุนส่วนนี้อยู่นอก 17 MB และดังที่ส่วน disclosure แสดง ไม่ว่า Chrome ในเครื่องคุณคือเวอร์ชันไหน traffic ของคุณก็จะประกาศเวอร์ชันนั้น

ไลเซนส์คือคำตัดสินการใช้งานที่แท้จริง

System diagram: The license is the real adoption decision

รีวิวจำนวนมากของเครื่องมือฟรีมักถือว่า “เป็นโอเพนซอร์ส” คือจบการคุยเรื่องไลเซนส์ แต่สำหรับ nodriver นั่นเป็นแค่จุดเริ่ม เพราะไลเซนส์ของมันคือ AGPL-3.0 — ยืนยันได้ทั้งจาก LICENSE.txt ใน wheel และ spdx_id ใน repo นี่คือไลเซนส์แบบ network copyleft ที่แข็งแรง และเป็นพันธะที่ต่างจากเพื่อนบ้านอย่างมีนัยสำคัญ

เครื่องมือใกล้เคียงที่คนมักเอามาเทียบกับ nodriver นั้นส่วนใหญ่เป็นสาย permissive ซึ่งทำให้ความต่างเห็นชัด:

ToolLicenseIf you run a modified copy as a network service
nodriverAGPL-3.0Section 13 can require an operator to offer the corresponding source of its modified version to remote users
PlaywrightApache-2.0no equivalent network-copyleft clause
PuppeteerApache-2.0as above
BotasaurusMITas above

ขอบเขตสำคัญ AGPL Section 13 พูดถึงโปรแกรมที่ถูกแก้ไขแล้วและถูกใช้สำหรับปฏิสัมพันธ์ผ่านเครือข่ายระยะไกล รีวิวนี้ไม่ได้ตัดสินว่าโค้ดบริการรอบข้างแยกออกจากงานที่ครอบคลุมหรือไม่ และไม่ได้แก้กรณี edge case ภายในองค์กรหรือขอบเขตการใช้งานภายใน ถ้าคุณมีผลิตภัณฑ์แบบ hosted ที่ดัดแปลง nodriver ให้เอาไลเซนส์และสถาปัตยกรรมไปคุยกับที่ปรึกษากฎหมายก่อน นี่คือธงด้านเทคนิคสำหรับการนำไปใช้ ไม่ใช่คำแนะนำทางกฎหมาย

ผมไม่ได้จะตัดสินว่า AGPL ดีหรือแย่ — copyleft เป็นตัวเลือกที่ถูกต้อง และหลายโปรเจกต์จริงจังใช้มันอยู่ ผมแค่ชี้ว่า “nodriver เป็น free and open source” นั้นจริงแต่ไม่ครบถ้วน ภาระผูกพันมีอยู่จริง มันต่างจากค่าเริ่มต้นแบบ permissive ในมุมนี้ของ ecosystem และควรถูกใส่ไว้ในการตัดสินใจ ไม่ใช่ถูกย่อจนเหลือแค่คำว่า “ฟรี” (พูดเผื่อไว้ PyPI เองไม่ได้ติด license classifier มาเลย; ตัว AGPL อยู่ใน wheel และ SPDX id อยู่ใน repo ดังนั้นอย่าหวังพึ่ง package index ให้บอกเรื่องนี้แทนคุณ)

เมทาดาทา ณ เวลาหนึ่ง

ตัวเลข repo และ package ณ เวลาที่ตรวจ มาจาก GitHub API และ PyPI โดยตรง:

FactValue on July 14, 2026
Stars4,511
Forks422
Open issues14
CreatedFebruary 2024
Last pushedMay 2026
Latest PyPI release0.50.3
Wheelpure-Python py3-none-any
requires-python>=3.9
Python classifiers3.7–3.13

สัญญาณด้านการดูแลยังปนกันอยู่: repo ถูก push ในเดือนพฤษภาคม 2026 ในขณะที่แพ็กเกจที่วัดได้ล่าสุดยังมีปัญหา import บน Python 3.14 และ fix ที่เสนอไว้ยังไม่ได้ปล่อยในวันที่ทำวิจัย ดาวและจำนวน issue เปิดไม่ได้ตอบว่าจังหวะการพัฒนานั้นตรงกับเกณฑ์การดูแลของคุณหรือไม่

ข้อดีและข้อเสีย

ข้อดี:

  • ติดตั้งเล็กและอ่านง่าย: dependency ที่ประกาศไว้สามตัว บวก wrapt ที่ตามมาโดยทรานซิทีฟ, ประมาณ 17.2 MB across six dist-info directories ในสภาพแวดล้อมที่วัดได้ (หนึ่งในนั้นคือ pip), ไม่มี numpy/lxml และไม่มีการดาวน์โหลด browser ตอนติดตั้ง
  • เป็น CDP-native จริง: แพ็ก DevTools Protocol bindings ของตัวเองมา 57 โมดูล และคุยกับโปรโตคอลโดยตรง โดยไม่มี chromedriver/Selenium binary คั่นกลาง
  • surface สำหรับหา element บน Tab กว้างมาก (public methods 62 ตัว) มี XPath, CSS และ text search แบบ first-class — ไม่ต้องถอยไปใช้ evaluate() แบบดิบ ๆ เพื่อ XPath
  • ออกแบบมาเป็น async มีโปรไฟล์ใหม่ทุกครั้งที่รัน และมี Config ที่เรียบสะอาดสำหรับคีย์หลัก ๆ (headless, executable path, args, lang, ports)
  • ดึงคอนเทนต์จากหน้าเว็บที่ประกอบด้วย JavaScript ได้ดีเท่ากลุ่มตัวใหญ่: ได้ 2 จาก 3 class ด้วย default read และ 3 จาก 3 เมื่อใส่ explicit wait — เหมือน Playwright และ Puppeteer stock บน fixture เดียวกัน และเสถียรตลอดสามรอบ navigate-and-read เร็วที่สุดในสี่ตัวที่ 119–129 ms
  • รายงาน navigator.webdriver เป็น false โดยปริยายในจุดที่ control stock ทั้งสองรายงาน true โดยไม่ต้อง patch property — descriptor ยังเป็น native getter ของเบราว์เซอร์เอง
  • มีเอกลักษณ์เชิงสถาปัตยกรรมชัดเจนในฐานะผู้สืบทอดแบบ CDP-native ของ undetected-chromedriver

ข้อเสีย:

  • ไม่ import บน Python 3.14 ทันทีจากกล่อง — ไฟล์ non-UTF-8 เพียงไบต์เดียว (cdp/network.py) ทำให้เกิด SyntaxError ตอน import ทำซ้ำได้จาก open issue #35 และยังไม่ถูกแก้ใน 0.50.3 ควร pin ≤3.13 (ยืนยันว่าใช้ได้บน 3.12.13) หรือ re-encode ไฟล์
  • AGPL-3.0 เป็นเรื่องที่ต้องคิดจริงสำหรับใครก็ตามที่รันสำเนาที่แก้แล้วเป็น network service — เข้มกว่า Apache/MIT ของคู่เทียบ
  • footprint บนดิสก์ที่เล็กไม่ได้แปลว่า import จะเบา: cold-start ราว 158 ms และ resident memory ราว 31.5 MB ก่อนมี browser เพราะ CDP modules ทั้ง 57 ตัวถูกโหลดล่วงหน้า
  • tab.select() ใช้ polling แบบหน่วงครึ่งวินาที ดังนั้นการรอสั้น ๆ จะถูกปัดขึ้น — การรอ 100 ms อาจกินเวลาราว 1.1 s ในจุดที่ Puppeteer waiter ใช้ราว 210 ms ถูกทุกครั้ง แต่สะสมได้เมื่อมี wait เล็ก ๆ จำนวนมาก
  • ใน headless ยังประกาศ HeadlessChrome ใน user-agent ตามปกติ เหมือน control stock ทุกประการ; ค่าเริ่มต้นไม่ได้ปิดสตริงที่บอกตัวตนชัดที่สุดให้อัตโนมัติ
  • ยังต้องมี Chrome/Chromium/Edge/Brave ที่ใช้งานได้จริงตอน runtime; การติดตั้งผ่าน pip ที่เบาเป็นแค่ครึ่งหนึ่งของเรื่อง dependency และไม่ว่า Chrome ในเครื่องคุณเป็นอะไร เวอร์ชันนั้นคือสิ่งที่ traffic ของคุณเปิดเผย
  • ประสิทธิภาพต่อระบบ anti-bot จริงยังไม่ได้รับการยืนยันในรีวิวนี้ — สมมติฐานเรื่อง stealth ทั้งหมดไม่ได้ถูกทดสอบโดยตั้งใจ

สิ่งที่ผมไม่ได้ทดสอบ และจึงพูดไม่ได้: หน่วยความจำต่อแท็บ, CDP round-trip latency, การจัดการโปรไฟล์, throughput เมื่อใช้งานจำนวนมาก, Linux หรือ Windows, Python 3.13 โดยเฉพาะ, และ — เรื่องใหญ่ที่สุด — ประสิทธิภาพหลบระบบป้องกันจริงกับบริการ live ใด ๆ เบราว์เซอร์ทุกตัวในรีวิวนี้คุยกับเพียง fixture บน 127.0.0.1 เท่านั้น ตัวเลขทั้งหมดมาจากเครื่องเดียว (macOS arm64); ตัวเลขด้านการขับเบราว์เซอร์เป็น Python 3.12.13 แบบไม่แพตช์ และค่าขนาด footprint กับเวลา import แบบเก่ามาจาก Python 3.14 บนสำเนาที่แพตช์ไบต์เดียว

ผมยังไม่ได้รันเมทริกซ์อัปเกรดเบราว์เซอร์ ดังนั้นความเข้ากันได้กับ Chrome รุ่นในอนาคตยังเป็นการตรวจเชิงปฏิบัติการของผู้ใช้งาน ไม่ใช่ผลลัพธ์จากรีวิวนี้

เหมาะกับใคร และใครควรข้าม

nodriver เหมาะถ้าคุณต้องการ driver สำหรับ Chromium จริงที่เบา เป็น async และ CDP-native และคุณพร้อมจะดูแล browser การอัปเดต และ runtime เอง ต้นไม้ dependency ที่เล็กทำให้ง่ายต่อการ audit และการออกแบบแบบ CDP-first เหมาะกับการควบคุมระดับโปรโตคอล ความเหมาะกับ container ยังไม่ได้ทดสอบ: รีวิวนี้ไม่ได้ลอง Linux image, การติดตั้งเบราว์เซอร์, shared libraries, sandboxing หรือการเก็บ process หลังใช้งาน

มีสองกลุ่มที่ควรมองหาอย่างอื่น ถ้าคุณอยู่บน Python 3.14 และไม่อยาก pin interpreter หรือแพตช์ไฟล์ที่ vendored มาเอง ให้รอ fix ปล่อยก่อน — ตอนนี้ import break เป็นทางตันจริง และถ้า AGPL-3.0 ขัดกับวิธีที่คุณจะปล่อยของ — เช่นบริการ hosted ที่มีการแก้ไขภายใน — ไลเซนส์อย่างเดียวก็พอแล้วที่จะทำให้ต้องชั่งใจ alternative ที่ permissive กว่า ก่อนจะสร้างระบบบนตัวนี้ ทั้งสองเรื่องไม่ใช่การติงโค้ด แต่เป็นข้อจำกัดที่ควรรู้ตั้งแต่ตอนนี้มากกว่าตอนตรวจ compliance

และข้ามมันไปได้เลยถ้าสิ่งที่คุณต้องการจริง ๆ คือ ข้อมูล ไม่ใช่เบราว์เซอร์ที่คุณต้องคุมเอง nodriver ให้ tab ที่สั่งงานได้และเมธอดอีก 62 ตัว; การแปลงหน้าเว็บที่เรนเดอร์แล้วให้เป็นเรคคอร์ดที่สะอาดและมีโครงสร้างยังเป็นงานที่โค้ดของคุณต้องเขียนเอง นั่นคืออีกงานหนึ่ง และนั่นคือจุดเชื่อมที่ managed API เข้ามา

ทางเลือกอื่น และ Thunderbit อยู่ตรงไหน

เริ่มจากภาพรวมแบบตรงไปตรงมา: nodriver ฟรี เป็น AGPL และรันเอง คุณดูแลเบราว์เซอร์ อัปเดตเอง และรับผิดชอบ runtime กับความล้มเหลวทุกอย่างของมัน สำหรับนักพัฒนาที่ต้องการการควบคุมแบบนั้นเป๊ะ ๆ ไม่มีบริการที่ managed ตัวไหนแข่งราคาได้กับไลบรารีที่คุณมีอยู่แล้ว

ภายในโอเพนซอร์ส ให้เทียบตามงาน ไม่ใช่ตามโลโก้ ถ้าคุณกำลังชั่งใจระหว่าง driver สำหรับเบราว์เซอร์จริง Playwright และ Puppeteer comparison ของเราจะครอบคลุมสองยักษ์ใหญ่ที่ nodriver อยู่เคียงข้างในเชิงรูปแบบ Scrapling เป็นเพื่อนบ้านฝั่ง Python ที่ใกล้สุดในแกน stealth-oriented ถ้านั่นคือเหตุผลที่คุณมองหา ถ้าคุณอยากได้ output ที่พร้อมใช้กับ LLM มากกว่าควบคุมเบราว์เซอร์ดิบ ๆ Crawl4AI จะเรนเดอร์หน้าแล้วส่ง Markdown กลับมา และ Scrapy ก็ยังเป็น framework มาตรฐานสำหรับ crawl ขนาดใหญ่แบบไม่ต้องพึ่งเบราว์เซอร์ หากคุณกำลังเทียบหลายตัวพร้อมกัน บทสรุป open-source scraper roundup จะวางหมวดหมู่ไว้เคียงกันให้ดูง่าย

การเปิดเผยข้อมูล: บทความนี้เผยแพร่โดย Thunderbit Thunderbit เป็นบริการ extraction แบบ managed ดังนั้นการเปรียบเทียบจึงอิงตามงาน ไม่ใช่สถาปัตยกรรม nodriver ให้ชั้นควบคุมเบราว์เซอร์ที่คุณโฮสต์และเขียนโปรแกรมเอง; Thunderbit จัดการการเรนเดอร์และส่งคืนเนื้อหาของหน้า หรือข้อมูลในรูปแบบตาม schema ในฐานะบริการ ใช้ nodriver เมื่อคุณต้องการการควบคุมเบราว์เซอร์ระดับโปรโตคอลและต้องการโฮสต์เอง พิจารณา managed extractor เมื่อรูปแบบข้อมูลปลายทางและการส่งต่องานเชิงปฏิบัติการสำคัญกว่าการเป็นเจ้าของเบราว์เซอร์ รายละเอียด endpoint ที่เปลี่ยนได้ เครดิต และ batch limit ควรไปดูที่ pricing page ไม่ใช่ซ่อนไว้ใน benchmark ของไลบรารี

การแลกเปลี่ยนคือภาระงานไปอยู่ตรงไหน: nodriver เก็บการจัดการเบราว์เซอร์และการดูแล runtime ไว้ฝั่งคุณโดยไม่เก็บค่าบริการต่อคำขอ; ส่วน managed API รับภาระชั้นนั้นไปและคิดค่าการเรียกใช้

ลอง Thunderbit สำหรับการดึงข้อมูลเว็บ

บทสรุป

ใช้ nodriver ถ้าคุณต้องการ Chromium driver ที่เล็ก เป็น async และ CDP-native, ได้ยืนยันแล้วว่ามันทำงานบน Python เวอร์ชันของคุณ (ที่นี่ import สะอาดบน 3.12.13) และได้ตรวจไลเซนส์ AGPL-3.0 สำหรับวิธีที่คุณจะปล่อยงานของคุณ กราฟ runtime ที่ resolve แล้วเพิ่มแพ็กเกจเข้ามาสี่ตัว สภาพแวดล้อมที่วัดได้ใช้พื้นที่ราว 17 MB และไลบรารีแถมโมดูล CDP-domain มา 57 ตัว พร้อม surface ของ Tab 62 เมธอดและ XPath แบบ native บน fixture นี้มันดึงได้ 2 จาก 3 content class โดยไม่ต้องรอ และ 3 จาก 3 เมื่อใส่ wait ซึ่งเทียบเท่า Playwright และ Puppeteer stock

แต่อย่าลืมชั่งข้อจำกัดให้ตรงจริง ๆ บน Python 3.14 มัน import ไม่ได้เลยจนกว่าคุณจะแก้ไบต์ non-UTF-8 ตัวเดียว — เป็น issue ที่มีบันทึกไว้และยังเปิดอยู่ ไม่ใช่ปริศนา แต่เป็นทางตันทันทีที่คุณเจอ ไลเซนส์เป็น AGPL-3.0 ซึ่งเป็นเรื่องจริงจังสำหรับใครก็ตามที่รันสำเนาที่แก้แล้วเป็นบริการ ไม่ใช่พิธีการ การติดตั้งที่เล็กไม่ได้ทำให้ import เล็ก เพราะ CDP modules ทั้งหมดโหลดขึ้นมาก่อน และการ polling ครึ่งวินาทีของ select() ทำให้การรอสั้น ๆ แต่ละครั้งกลายเป็นเวลาประมาณหนึ่งวินาที ในประเด็นการเปิดเผยค่าเริ่มต้นที่ผมตอบได้จริง ภาพที่ได้แคบกว่าที่การตลาดสื่อ: boolean ตัวเดียวต่างจาก Puppeteer stock, user-agent ตอน headless ยังบอก HeadlessChrome, และ property อื่นที่วัดได้เหมือนกันหมดทั้งสี่สแตก และสมมติฐานด้าน anti-detection ทั้งหมด — เหตุผลที่หลายคนมาเจอ nodriver ตั้งแต่แรก — เป็นสิ่งที่ผมจงใจไม่ทดสอบ ผมสำรวจไลบรารีและขับมันกับหน้าบนเครื่องตัวเอง แทนที่จะเอาไปชนกับระบบป้องกันจริง และผมอยากบอกให้ตรงไปตรงมามากกว่าจะยื่นคำอ้างเรื่อง bypass ที่ผมรับรองไม่ได้ ในคำถามที่ผมตอบได้ nodriver คือไดรเวอร์ที่สร้างมาดีและเบาผิดปกติ แต่มีคมสองด้าน — กำแพงเรื่องเวอร์ชัน Python และไลเซนส์ copyleft — ที่คุณควรเห็นมันล่วงหน้า

ลอง Thunderbit สำหรับการดึงข้อมูลเว็บ Get Started Free

คำถามที่พบบ่อย

ทำไม import nodriver ถึงล้มบน Python 3.14? เพราะ cdp/network.py มีไบต์ ± ที่ไม่ใช่ UTF-8 โดยไม่มีการประกาศ source encoding Python 3.14.2 ปฏิเสธไฟล์และหยุดการ import แบบทรานซิทีฟ; ส่วน Python 3.12.13 import ไฟล์เดียวกันที่มีไบต์เหมือนกันทุกประการได้โดยไม่ต้องแพตช์ รีวิวนี้ไม่ได้แยกให้ชัดว่าการเปลี่ยนของ interpreter ตัวใดเป็นต้นเหตุ และไม่ได้ทดสอบ Python 3.13 เส้นทาง upstream คือ nodriver issue #35 และ pull request #36 ใช้เวอร์ชันที่คุณทดสอบแล้ว หรือ re-encode ไฟล์เป็น UTF-8

“CDP-native, no webdriver” ให้อะไรกับฉัน และค่าใช้จ่ายในการติดตั้งคืออะไร? nodriver แพ็กโมดูล DevTools Protocol domain มา 57 ตัว และคุย CDP ผ่าน WebSocket แทนการเรียก chromedriver ผ่าน Selenium Tab เปิดให้ใช้ 62 เมธอด รวมถึง XPath แบบ native เมทาดาทาของแพ็กเกจประกาศ runtime requirement สามตัว (websockets, mss, deprecated); เมื่อติดตั้งครบก็จะได้ wrapt ตามมา สภาพแวดล้อมที่วัดใช้ประมาณ 17.2 MB across six dist-info directories ซึ่งรวม pip ด้วย และไม่มี numpy, lxml หรือ browser binary ที่ดาวน์โหลดมา มีข้อควรระวังสองอย่าง: import บนสำเนาที่แพตช์แล้วใช้เวลาราว 158 ms เพราะ CDP modules ทั้ง 57 ตัวโหลดแบบ eager และยังต้องมี browser ตระกูล Chrome แยกต่างหากอยู่ดี

ไลเซนส์ AGPL-3.0 มีผลกับโปรเจกต์ของฉันไหม? ขึ้นอยู่กับวิธีที่คุณปล่อยงาน ถ้าคุณเอา nodriver เวอร์ชันที่แก้ไขแล้วไปรันเป็นบริการให้คนอื่นใช้ AGPL-3.0 จะบังคับให้คุณต้องเปิดซอร์สของเวอร์ชันที่แก้ไขนั้นให้ผู้ใช้ระยะไกลเห็นได้ สำหรับสคริปต์ส่วนตัวหรืองานภายในที่ไม่เปิดให้คนนอกเข้าถึง มันแทบไม่ใช่ปัญหา สำหรับโปรดักต์เชิงพาณิชย์แบบ hosted ที่สร้างบน nodriver ที่มีการแพตช์ นี่เป็นคำถามจริงที่ควรคุยกับคนดูแล compliance — และเข้มกว่า Apache-2.0 กับ MIT ของเครื่องมือที่ใกล้เคียงกัน

nodriver เปิดเผยอะไรเกี่ยวกับตัวเอง และนั่นแปลว่ามันชนะ Cloudflare ไหม? ส่วนแรกคือสิ่งที่วัดได้; ส่วนหลังไม่ใช่ และความต่างนี้สำคัญมาก บนหน้าที่ผมเสิร์ฟจาก 127.0.0.1 โดยใช้ Chrome build เดียวกับตัวควบคุม: navigator.webdriver ออกมาเป็น false ในขณะที่ Playwright stock และ Puppeteer stock ออกมาเป็น true ทั้งคู่ ค่าดังกล่าวถูกตั้งตอน browser-launch ไม่ใช่การ patch property — descriptor ยังเป็น native getter ของ Chrome เอง นอกเหนือจาก boolean นี้ แทบทุกอย่างเหมือนกับตัวควบคุม: platform string เดียวกัน, plugin ห้าตัว, core สิบสองคอร์, device memory 16 GB, รูปร่าง window.chrome เหมือนกัน, ไม่มีความขัดแย้งใน Permissions API และไม่มีเศษ cdc_ บน document หรือ window headless ยังประกาศ HeadlessChrome/151.0.0.0 ใน user-agent เหมือนตัวควบคุมทั้งสองตัว — ตรงนั้นไม่ได้ถูกปิดบังให้คุณ สิ่งที่ทั้งหมดนี้ไม่ได้บอกคือมันใช้ได้ผลกับบริการ anti-bot จริงหรือไม่ ผมไม่เคยชี้ nodriver ไปที่เว็บ live, ไม่เคยติดต่อบริการ anti-bot และไม่เคยแตะ CAPTCHA นั่นอยู่นอกขอบเขตโดยตั้งใจ ตาราง disclosure ข้างต้นบอกแค่ว่าสแตกประกาศอะไรออกมา มันไม่ได้บอกว่าใครกำลังฟังอยู่ หรือเขาทำอะไรกับมัน

nodriver จัดการคอนเทนต์ที่เรนเดอร์ด้วย JavaScript ได้ถูกต้องไหม? ได้ แต่มีเงื่อนไขตามเวลาอ่านตามเดิม บน fixture ที่มี content สามคลาส browser.get() + tab.get_content() แบบ default ดึงมาได้ 2 จาก 3 — มัน execute JavaScript ได้ถูกต้อง (class ที่ inject แบบ sync ไม่มีอยู่ในไบต์ที่เสิร์ฟออกไป แต่ก็ยังกลับมา) ทว่ามันอ่านตอน load event จึงพลาดสิ่งที่ถูก inject หลังจากนั้น ถ้าเพิ่ม tab.select("#delayed-injected") จะได้ 3 จาก 3 นั่นตรงกับ Playwright stock และ Puppeteer stock บนหน้าเดียวกัน มีคมเล็กที่ควรเผื่อไว้: select() ใช้ polling แบบหน่วงครึ่งวินาที ดังนั้นการรอ 100 ms จึงกินเวลาราว 1.1 วินาที

Ke
Ke
CTO ที่ Thunderbit | นักวิทยาศาสตร์ข้อมูลอาวุโสและผู้เชี่ยวชาญด้านแมชชีนเลิร์นนิง ด้วยประสบการณ์เกือบสิบปีในด้านแมชชีนเลิร์นนิงและวิทยาศาสตร์ข้อมูล เคเฉินเป็นศิษย์เก่ามหาวิทยาลัยโคลัมเบีย และอดีตนักวิทยาศาสตร์ข้อมูลอาวุโสที่ Walmart Labs ด้วยความเชี่ยวชาญลึกซึ้งที่ได้รับการยอมรับจากเพื่อนร่วมสายงานใน Python, R, Java และสถิติ เขาจึงแบ่งปันมุมมองที่ผ่านการพิสูจน์มาแล้วในการพัฒนาอัลกอริทึม AI ที่ซับซ้อนจากแนวคิดไปสู่สถาปัตยกรรมระดับใช้งานจริง
Topics
Web Scraping ToolsAI Web Scraper
สารบัญ
Thunderbit · เอเจนต์ข้อมูลเว็บด้วย AI

ดึงข้อมูลจากทุกหน้าได้ใน 1 คลิก

ได้รับความไว้วางใจจากผู้ใช้กว่า 250,000+ คน
มีแผนใช้ฟรี
จากหน้าเว็บสู่สเปรดชีต
บอกสิ่งที่ต้องการ — Thunderbit’s AI Agent จะดึงข้อมูลและส่งออกไปยัง Excel, Google Sheets, Airtable หรือ Notion เริ่มต้นได้ฟรี
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week