סקירת chromedp: דפדפן אמיתי עדיין צריך תנאי מוכנות נכון

עודכן לאחרונה ב-August 17, 2026
סקירת chromedp: דפדפן אמיתי עדיין צריך תנאי מוכנות נכון
סיכום AI

chromedp היא ספריית Go טהורה, ברישיון MIT, שמפעילה Chrome אמיתי דרך Chrome DevTools Protocol. היא קוראת את ה-DOM אחרי ש-JavaScript של הדף רץ מתוך תוכנית Go, בלי צורך ב-WebDriver נפרד או סביבת Node. מודול ה-Go מתקמפל לתוך האפליקציה, אבל המערכת שרצה עדיין דורשת קובץ Chrome חיצוני, ואת משך החיים שלו מנהלים באמצעות Go contexts. ההתקנה הייתה פשוט go get אחד ו-Chrome שהייתי צריך לספק בעצמי: לבנות allocator, לגזור context, ולהעביר ל-Run רשימת פעולות.

chromedp היא ספריית Go טהורה, ברישיון MIT, שמפעילה Chrome אמיתי דרך Chrome DevTools Protocol. היא קוראת את ה-DOM אחרי ש-JavaScript של הדף מסיים לרוץ מתוך תוכנית Go, בלי צורך ב-WebDriver נפרד או בסביבת Node. מודול ה-Go מתקמפל יחד עם האפליקציה, אבל המערכת שרצה עדיין דורשת קובץ Chrome חיצוני, ואת משך החיים שלו מנהלים באמצעות Go contexts.

ההתקנה הייתה פשוט go get אחד ו-Chrome שהייתי צריך לספק בעצמי: לבנות allocator, לגזור context, ולהעביר ל-Run רשימת פעולות. על מכונת macOS arm64 הזו, עם headless shell חם שכבר היה על הדיסק, תהליך חדש עד לתוצאת הסקריפט הראשונה הגיע לחציון של 102 מ״ש. זו נקודת ייחוס מקומית, לא טענה כללית ש-startup אף פעם לא הופך לצוואר בקבוק. בפיקסצ׳ר שמזריק קישור 800 מילישניות אחרי הטעינה, שתי אסטרטגיות קריאה מתוך ארבע החזירו תוצאה לפני שהקישור בכלל היה קיים.

הדפדפן היה אמיתי, התוכן היה שם, והקוד פשוט לא חיכה לו. הפער הזה הוא הדבר הכי שימושי ש-chromedp לימד אותי, והוא לא באג — הוא ההבדל בין "רינדרתי את הדף" לבין "חיכיתי לדבר שבאמת רציתי", הבחנה שהמיתולוגיה סביב דפדפנים חסרי ממשק נוהגת לטשטש. אסטרטגיית ההמתנה, לא הדפדפן, היא זו שקובעת אם תקבלו את הנתונים. כל מספר כאן מגיע מפיקסצ׳ר מקומי שבשליטתי, עם ground truth שנרשם לפני כל הרצה, והסיכומים הגולמיים נמצאים ב-chromedp folder of our benchmark repo.

מה chromedp באמת היא

המחסנית של chromedp קצרה בכוונה. בלי Selenium server. בלי שכבת WebDriver. בלי runtime של Node שמתחבא מתחת. תוכנית ה-Go שלך פותחת WebSocket למופע של Chrome ומדברת איתו ב-CDP ישירות, בערך באותו פרוטוקול wire ש-Puppeteer משתמשת בו, רק בלי JavaScript באמצע.

כשבדקתי ב-27 ביולי 2026, הריפו עמד על 13,212 כוכבים ו-178 issues פתוחים, ברישיון MIT. הגרסה שבדקתי היא v0.16.0, שהיא התג החדש ביותר בריפו. שווה לציין לפני שזה מבלבל: דף Releases של GitHub עדיין מציג את v0.15.1 (פורסמה ב-2026-04-01) כאובייקט ה-release האחרון, בעוד ש-go get github.com/chromedp/chromedp@latest נפתר ל-v0.16.0. כאן מודולי Go ואובייקטי release של GitHub פשוט התרחקו זה מזה. לא שבור, רק מעצבן כשמנסים להבין מה באמת מריצים.

המודל המנטלי הוא Go contexts עד הסוף. בונים allocator context (שיודע איך להפעיל את Chrome), גוזרים ממנו browser context, ואז קוראים ל-chromedp.Run(ctx, actions...) עם רשימת Actions. context-בן של browser context הוא טאב חדש. מבטלים context והדבר שהוא מייצג נעלם. אם עשיתם עבודה כלשהי עם concurrency ב-Go, זה מרגיש מוכר מיד; ואם לא, המדריך שלנו להתחלה עם web scraping ב-Go הוא מסלול כניסה הרבה יותר עדין מה-godoc של chromedp.

נקודת גבול אחת שחשוב להציב מהר: chromedp נותנת לכם DOM מרונדר. היא לא נותנת לכם נתונים מובנים. כל מה שתוציאו מה-DOM הזה — שדות, טבלאות, מחירים — הוא קוד שאתם כותבים ומתחזקים. זו דרייבר, לא framework ל-scraping.

המכניקה שמתחת למכסה

הכול ב-chromedp הוא Action, ו-Run מבצע אותם כסלסלה אחת לפי הסדר מול target. Navigate, Click, Evaluate, OuterHTML, WaitVisible — כולם אותו ממשק, כולם ניתנים להרכבה, וכולם בסך הכול פקודות CDP לבושות בטיפוסי Go. האחידות הזו היא החלטת העיצוב הכי טובה של הספרייה, כי היא גורמת ל-sugar ולפרוטוקול הגולמי לחיות באותו גובה.

וזה חשוב, כי ה-sugar כאן דק בכוונה. chromedp בנויה על cdproto, סט גנרטיבי של bindings טיפוסיים ב-Go שמכסה את כל DevTools Protocol, וה-chromedp godoc מתעד את שתי השכבות זו לצד זו. כשאין action נוח, יורדים לקריאה של הדומיין עצמו — network.Enable(), page.CaptureScreenshot(), runtime.Evaluate() — בתוך אותו Run. אין חומה בין "ה-API הנחמד" לבין "ה-API האמיתי", וזה לא נכון לגבי כל דרייבר דפדפן.

פעולות ההמתנה הן המקום שבו מתקבל השיפוט היומיומי, ויש יותר מהן ממה שאנשים משתמשים בפועל:

פעולת המתנהעל מה היא חוסמת
WaitReady(sel)עד שהצומת מצורף ל-DOM
WaitVisible(sel)עד שהצומת באמת גלוי
WaitNotPresent(sel) / WaitNotVisible(sel)ההפכים, שימושיים לספינרים
Poll(js, res)מריצה תנאי JavaScript במרווחים עד שהוא הופך ל-true

ניהול תהליכים הוא עוד רכיב מכני ששווה להכיר, כי הוא קובע אם התוכנית שלכם תשאיר מאחוריה דפדפן רץ. chromedp מפעילה את Chrome דרך exec.CommandContext של Go. ביטול ה-context הזה הורג את התהליך. פרט מימוש יחיד כזה מסביר גם את ההתנהגות הטובה וגם את הקצה החדות שפגשתי בבדיקה.

ההתקנה היא בינארי Go ועוד Chrome שאתם חייבים לספק

go get github.com/chromedp/chromedp נפתר נקי ל-v0.16.0 בלי דרמה, ועץ התלויות לא כולל imports של cgo. לכן הטענה "pure Go, בלי תלויות חיצוניות" שאתם רואים חוזרת על עצמה אכן נכונה — לגבי מודול ה-Go.

אבל היא לא נכונה לגבי ה-runtime. chromedp מפעילה Chrome חיצוני, ובלי Chrome על המחשב הרצה נכשלת מיד. כל מדידה שעשיתי סיפקה את קובץ ההפעלה המדויק דרך chromedp.ExecPath, כשהוא מצביע על Chrome for Testing headless shell בגרסה 151.0.7922.10. זו לא ביקורת — כדי להפעיל דפדפן צריך דפדפן — אבל "אין תלויות חיצוניות" ו"צריך לשנע Chrome של 155 MB לצד הבינארי" הם שני סיפורי פריסה שונים לגמרי, ורק אחד מהם מופיע ב-README.

מלכודת התקנה שנייה גזלה לי זמן ושווה להכיר לפני שכותבים קוד. issue #1591 ב-chromedp מדווח על כך ש-runner של go test ב-Go 1.25+ מבטל את NewExecAllocator באמצע ההפעלה; אותו קוד בדיוק רץ מצוין כבינארי מקומפל. בניתי בינארי probe עם go build והרצתי אותו לכל מדידה במקום לנהוג הכול דרך go test. ה-Go כאן היה 1.26.5, macOS arm64. אם החוויה הראשונה שלכם עם chromedp היא קובץ test שנופל בזמן עליית Chrome, זה ה-issue לקרוא לפני שמאשימים את הקוד שלכם.

Hands-on: ארבע דרכים לקרוא את אותו דף, שתיים מהן ריקות

Measured results chart: Which read strategy saw each link?

הפיקסצ׳ר הוא שרת מקומי על 127.0.0.1 שמגיש שלוש מחלקות תוכן שנבדלות רק ב-מתי הן נכנסות ל-DOM: <a> סטטי בתוך הבייטים שהוגשו, <a> שנוצר על ידי <script> פנימי בזמן ה-parse הראשוני, ו-<a> שנוצר על ידי setTimeout מספר מילישניות ניתן להגדרה אחרי אירוע ה-load. ה-markers וה-hrefs של שני הקישורים שנוצרו בסקריפט מורכבים מחלקי מחרוזת ב-JavaScript, כך שאין שום ליטרל רציף בכל הבייטים שהוגשו. לכן "נמצא" מוכיח ש-Chrome הריצה JavaScript, לא שמישהו פשוט קרא HTML.

ה-Recall מחושב ב-Python מול ground-truth markers שנרשמו מראש, לא בתוך ה-probe של Go, כך שה-probe לא יכול לרמות על ידי ידיעת התשובה. כל אסטרטגיה רצה שלוש פעמים; קבוצות ה-found היו זהות בכל שלוש ההרצות.

אסטרטגיית קריאהקישור סטטי ב-HTMLמוזרק בזמן ה-parseמוזרק 800 מ״ש אחרי loadזמן חלוף
Navigate + קריאה, בלי המתנהנמצאנמצאהוחמץ317 מ״ש
WaitReady("body")נמצאנמצאהוחמץ107 מ״ש
WaitVisible("#delayed-injected")נמצאנמצאנמצא912 מ״ש
Poll עד שה-marker מופיענמצאנמצאנמצא972 מ״ש

שתי שורות חוזרות עם שני קישורים מתוך שלושה. הקריאה הנאיבית מחמיצה כי Navigate חוזר על אירוע ה-load, והקישור השלישי עדיין לא קיים. WaitReady("body") מחמיץ מסיבה עדינה יותר, ובפועל גרועה יותר: body כבר מצורף ב-load, אז ההמתנה נחשבת מסופקת מיד, ומרגישים שעשיתם את הדבר האחראי. הוא חזר תוך 107 מ״ש, מהר יותר מהמסלול בלי המתנה, ונתן לכם אותו דף חסר.

כדי לאמת את המנגנון במקום להניח אותו, סרקתי את דיליי ההזרקה והרצתי מחדש את שני הקצוות (recall-summary.json):

דיליי הזרקה אחרי loadקריאה בלי המתנה רואה את זהWaitVisible רואה את זהזמן WaitVisible
0 מ״שכן (מירוץ)כן109 מ״ש
100 מ״שלאכן208 מ״ש
400 מ״שלאכן519 מ״ש
800 מ״שלאכן911 מ״ש
1500 מ״שלאכן1625 מ״ש

הזמן של WaitVisible עוקב אחרי דיליי ההזרקה בפיקסצ׳ר הזה — 100 ל-208, 400 ל-519, 800 ל-911, 1500 ל-1625 — עדות לכך שהוא חסם עד שהצומת הופיע במקום לקרוא מוקדם מדי. שורת ה-0 מ״ש היא גבול המקרה: setTimeout(…, 0) יכול לרוץ לפני הקריאה המיידית, ולכן המסלול בלי המתנה יכול לתפוס אותו. ב-100 מ״ש ומעלה בסריקה הזו, המסלול בלי המתנה החמיץ אותו בכל הרצה.

בייצור, טעות תזמון דומה יכולה לתת HTML תקין עם אפס שורות שחולצו ולצאת 0 אלא אם ה-pipeline בודק את כמות הפלט. זהו כשל סביר שנתמך על ידי התנהגות הפיקסצ׳ר, לא אירוע שנמדד כאן. רינדור הוא רק חצי מהדרישה; הקריאה צריכה להמתין לתנאי ברמת האפליקציה שמחובר לנתון הרצוי.

WaitReady ו-WaitVisible לא מדורגות — הן עונות על שאלות שונות

הניסוח הנפוץ הוא ש-WaitVisible "אמין יותר" מ-WaitReady. זו אמירה לא מדויקת מספיק כדי להזיק. בדף עם צומת שמצורף ל-DOM אבל מעוצב עם display: none, השניים מתפצלים בצורה חדה (waitsem-summary.json, שלוש הרצות זהות):

צומת יעדפעולהתוצאהזמן
מצורף, display:noneWaitReadyחוזר~6 מ״ש
מצורף, display:noneWaitVisibleפג זמן, context deadline exceeded4000 מ״ש
צומת גלויWaitVisible, שאילתה ברירת מחדלחוזר4–12 מ״ש
צומת גלויWaitVisible, ByIDחוזר1–2 מ״ש
צומת גלויWaitVisible, ByQueryחוזר1 מ״ש

WaitReady פירושו מצורף. WaitVisible פירושו גלוי. אם שואלים את הפעולה הלא נכונה, או שתעברו הלאה על תוכן שמעולם לא רונדר, או שתיתקעו על כל הזמן המוקצב על צומת שמעולם לא היה אמור להיות גלוי מלכתחילה. התנהגות ה-deadline עצמה נקייה — context deadline exceeded מסודר בדיוק אחרי 4 שניות, בלי תקיעה, בלי מצב זומבי — שזה יותר ממה שכמה דרייברים מצליחים לספק.

מלכודת מדווחת אחת לא הופיעה כאן. issue #440 מדווח על WaitVisible("#id") שנתקע עם שאילתה ברירת מחדל, וזה לא שוחזר ב-v0.16.0 — שאילתת ברירת המחדל, ByID ו-ByQuery כולן חזרו על הצומת הגלוי בכל הרצה. לא שוחזר זה לא אותו דבר כמו תוקן: זו צורת selector אחת על דף אחד, וזה לא סוגר את ה-issue.

ה-defer cancel() שדילגתם עליו מחזיק את הגג

סופרים תהליכים, לא ערכי חזרה. כל הרצת lifecycle השתמשה ב---user-data-dir ייחודי וספרה תהליכי Chrome browser אמיתיים עם pgrep, תוך סינון ילדי renderer. כל מסלול רץ שלוש פעמים (lifecycle-summary.json).

מסלול יציאה (macOS, 3 הרצות לכל אחד)מה קרה ל-chrome-headless-shell שנפתחתזמון
מבטלים את ה-context ואת allocatorנעלם13, 13, ו-12 מילישניות
יוצאים מתהליך ה-Go בלי לבטלשורד אחרי שהתוכנית שלכם מסתיימת — אפס תהליכי browser לפני, אחד אחרי שה-probe יצא; נהיה orphan בשלוש מתוך שלוש הרצות—

ביטול הוא נקי, מהיר, בדיוק מה ש-exec.CommandContext מבטיח. (כל orphan נמחץ בכוח על ידי harness אחר כך; ה-host נוקה לגמרי.)

זהו התנהגות ידועה, מתועדת, ומוגבלת לפלטפורמה — המדידה שלי, לא הגילוי. המעקב של chromedp מכסה את זה מכמה זוויות: #774 מתאר את אותו חוסר-יציאה ב-FreeBSD, #752 מדווח על תהליכי Chromium שנתלים ב-macOS, ו-#562 יחד עם #1566 מפרטים את המנגנון. מה שאני הוספתי הוא ספירת התהליכים והזמן משני צדי ההשוואה, משהו שהדיווחים האיכותניים האלה לא מספקים.

המנגנון עצמו הוא סיפור של build tags שכדאי להכיר. בקוד המקור של v0.16.0, הקובץ allocate_linux.go מגדיר Pdeathsig = SIGKILL לתהליך הילד, כך ש-Linux מקבל signal ברמת kernel על מוות של הורה. הקובץ allocate_other.go, שהוא מה שמקמפל ב-macOS, הופך את הקריאה הזו ל-no-op. אין signal מקביל ב-darwin, ולכן שום דבר לא הורג את Chrome כשהתוכנית שלכם מסתיימת. בינתיים, ה-godoc מנוסח כהבטחה כללית — ברירת המחדל "שולחת SIGKILL לכל הדפדפנים הפתוחים כשהתוכנית ב-Go יוצאת" — כאשר ההיקף ללינוקס חבוי רק בקוד עם build tags, כזה שצריך ללכת לקרוא. לקרוא לזה הבטחת-יתר תיעודית זה הוגן; לקרוא לזה באג ב-chromedp לא.

המסקנה המעשית זהה בכל מקרה: ב-macOS, defer cancel() הוא נושא עומס. דלגו עליו, ובכל הרצה תדלוף לכם מופע דפדפן. לא בדקתי Linux, אז אני לא מכליל לשם את תוצאת ה-orphan — הקוד מרמז שלינוקס מתנהג אחרת, ורמז אינו מדידה.

Cold start, concurrency, והדברים המשעממים שקובעים את הפריסה שלכם

Measured results chart: Cold start and two concurrency shapes

102 מ״ש היה החציון מתהליך חדש, allocator, context, ניווט ל-localhost, ועד Evaluate ראשון, על פני חמישה תהליכים, בטווח של 98 עד 111 מ״ש (coldstart-summary.json). בפיקסצ׳ר ה-macOS arm64 הזה, עם headless shell חם על הדיסק, ה-startup היה קטן יחסית להמתנה לתוכן המתעכב. containers, filesystem קר, CI, סביבות serverless וניווטים ב-production לא נמדדו.

ב-concurrency, chromedp נותנת שתי צורות — דפדפן אחד עם כמה contexts-ילדים (טאבים), או כמה דפדפנים עצמאיים. ארבע ניווטים, שלוש הרצות כל אחד (concurrency-summary.json):

מצבזמן כולל (p50)טווחשיא תהליכי Chrome browser
דפדפן משותף, 4 contexts-ילדים214 מ״ש209–2191
4 דפדפנים נפרדים264 מ״ש261–2784

הממצא שנמדד כאן הוא מספר תהליכים: תהליך Chrome browser אחד מול ארבעה עבור ארבע ניווטים מקומיים ופשוטים אלה. טווחי זמן הקיר לא חפפו, אבל נשארים כיוון ולא benchmark של throughput. RSS ו-PSS לא נמדדו, לכן הבדיקה הזו לא מוכיחה חיסכון בזיכרון.

הבדיקת ה-error path הקטנה כאן לא מפורטת מספיק כדי לתמוך בטענת robustness: הטיוטה לא מזהה האם HTTP status, שגיאת ניווט, event או לוגיקה של harness חשפו כל מצב. התייחסו לטיפול ב-500/קישור שבור כלא מדווח עד שיתפרסם ה-API result המדויק וה-artifact הגולמי.

מה שלא נבדק, ולכן מחוץ לכיסוי של המספרים כאן: התנהגות lifecycle בלינוקס, concurrency מעבר ל-N=4 או עם עבודה אמיתית לכל דף, דלתות זיכרון (ספרתי תהליכים, לא RSS), network interception ו-capture של בקשות, וגם דוחות timeout פתוחים ב-WaitReady ב-#168 ו-#1593 — הם מתארים timeouts לא עקביים, בעוד שמה שמדדתי הוא סמנטיקה של המתנה, שאלה אחרת לגמרי. מכונה אחת, גרסת Chrome אחת.

chromedp אינה דרייבר ה-CDP היחיד ב-Go על הספסל הזה: rod עבר את אותו פיקסצ׳ר, אותו harness, אותו host ואותה גרסת Chrome באותו סשן, ויקבל סקירה משלו.

יתרונות וחסרונות

יתרונות:

  • גישה אמיתית ל-CDP — פעולות הנוחות וקריאות ה-cdproto הגולמיות מתחברות יחד באותו Run, כך שלא פוגעים בתקרת API.
  • baseline מקומי של cold cycle עם p50 של 102 מ״ש ופיזור של 98–111 מ״ש בפיקסצ׳ר ה-macOS שנבדק.
  • ביטול context קוצר את Chrome בכ-13 מ״ש, בעקביות, בכל הרצה.
  • contexts-ילדים חולקים תהליך browser אחד עבור N טאבים (תהליך אחד מול 4 בדפדפנים נפרדים).
  • דטרמיניסטית בבדיקה — קבוצות recall, סמנטיקת המתנה ותוצאות lifecycle היו זהות בשלוש חזרות לכל מדד.
  • טיפול נקי ב-deadline: WaitVisible על תנאי שלא ניתן להגיע אליו חזר עם context deadline exceeded תקין בדיוק ב-4 שניות במקום להיתקע.
  • מודול pure-Go (ללא cgo), ברישיון MIT; עדיין נדרש executable חיצוני של Chrome בזמן ריצה.

חסרונות:

  • דורשת Chrome חיצוני בזמן ריצה; המוניטין של "בלי תלויות" מתייחס רק למודול Go.
  • WaitReady("body") היא מלכודת שמרגישה נכונה ומפספסת בשקט תוכן שמגיע אחרי load — היא חזרה ב-107 מ״ש עם דף לא שלם.
  • המסלול הנאיבי של Navigate + קריאה מפספס כל דבר שהוזרק ≥ ~100 מ״ש אחרי load, באופן דטרמיניסטי, בלי שגיאה.
  • ב-macOS, יציאה בלי cancel() משאירה browser orphaned (3/3 הרצות). ידוע ומוגבל לפלטפורמה, אבל קל מאוד ליפול לזה.
  • הניסוח ב-godoc על SIGKILL ביציאה נשמע אוניברסלי, למרות שהמנגנון עצמו נמצא בקוד עם build tags ללינוקס בלבד.
  • go test ב-Go 1.25+ יכול לבטל את עליית ה-allocator (#1591); עדיף לבנות בינארי.
  • היא מחזירה DOM, לא נתונים מובנים — כל שדה שתרצו הוא קוד parsing שאתם כותבים ומתחזקים.
  • תגית הגרסה האחרונה (v0.16.0) מקדימה את אובייקט ה-GitHub Release האחרון (v0.15.1), מה שהופך בדיקת גרסאות למבלבלת לרגע.

למי chromedp מתאימה, ולמי עדיף לדלג

אם השירות שלכם כבר כתוב ב-Go ואתם צריכים דפדפן אמיתי בתוך המערכת, chromedp היא כמעט הבחירה הברורה. בלי תהליך Node לניהול, בלי שרת WebDriver להחזיק חי, בינארי אחד מקומפל ועוד Chrome שאתם משנעים או מתקינים. מודל ה-context ממפה את עצמו ישירות לפרימיטיבים של concurrency ב-Go, כך שהחיים של הדפדפן מנוהלים בסוף באותה משמעת defer כמו שאר הקוד שלכם. ואם אתם צריכים משהו שה-API הנוח לא מכסה — אירועי CDP network, hooks מדויקים ללייפסייקל של הדף, טריקים ברמת פרוטוקול — פשוט יורדים ל-cdproto בלי לעזוב את הספרייה.

זו גם בחירה טובה כשאתם רוצים שליטה מפורשת בהמתנה. פעולות ההמתנה הן פרימיטיבים, לא היוריסטיקות; הן עושות בדיוק את מה שהן אומרות, וזה יתרון ברגע שמקבלים שהבחירה הנכונה היא עכשיו אחריותכם.

עדיף לדלג אם הצוות שלכם לא כותב Go — המחויבות לשפה היא המחיר האמיתי, לא הספרייה. עדיף לדלג אם אתם רוצים נוחות של auto-waiting שמנחשת נכון במקומכם, כי chromedp לא תנחש; היא תעשה בדיוק מה שביקשתם ותחזיר מה שהיה בדף באותו רגע. עדיף לדלג, או לתקצב את זה ברצינות, אם מה שאתם צריכים בפועל הוא רשומות מובנות ולא DOM: כל שדה הוא selector שאתם כותבים, בודקים ומתקנים כשהאתר משתנה. ואם כל הדרישה היא "רק תביאו לי את הנתונים מ-500 ה-URLs האלה", להקים orchestration של דפדפן ב-Go זה הרבה מכניקה בשביל הבקשה. המדריך שלנו ל-browser automation עובר על מתי המכונה הזאת מצדיקה את עצמה ומתי לא.

חלופות, כולל איפה ה-stack שלנו נכנס

בתוך קטגוריית דרייברי הדפדפן, rod היא דרייבר Go CDP נוסף, בעוד ש-Playwright ו-Puppeteer הן אפשרויות בצד של Node, שמכוסות ב-Playwright versus Puppeteer breakdown שלנו. חוזי ההמתנה שלהן אינם ניתנים להחלפה. Playwright עושה auto-wait ל-actionability לפני פעולות רבות; זה עדיין לא אומר לה מתי הנתונים האפליקטיביים סיימו להגיע אחרי הפעולה. chromedp נותנת לכם פרימיטיבי המתנה נמוכים יותר ומשאירה גם את ה-actionability וגם את תנאי המוכנות ברמת האפליקציה למי שקורא לספרייה. אם אתם סוקרים את השוק הרחב יותר, הסקירה שלנו על scrapers בקוד פתוח שבדקנו מכסה crawlers סטטיים וספריות חילוץ בצד השני של הקו הזה.

סקירה קשורה: Browserless review.

שירות חילוץ מנוהל הוא קטגוריה אחרת. הוא מחליף שליטה בדפדפן וב-selectors ב-rendering ובבניית schema שמבוצעים מחוץ למערכת. זה יכול להיות שימושי כשהתוצר הוא רשומות מובנות ולא DOM, בעוד ש-chromedp מתאימה יותר כשהדפדפן חייב להישאר תחת השליטה של שירות ה-Go שלכם. אנחנו בונים את Thunderbit, שירות כזה, אבל לא הרצנו אותו מול הפיקסצ׳ר הזה; לכן הבדיקה הזו לא תומכת בשום השוואה של שקילות, latency, איכות חילוץ או עלות מול chromedp.

נסו את Thunderbit לחילוץ נתוני רשת

פסק דין

האם כדאי להשתמש ב-chromedp? כן, אם אתם כותבים Go ורוצים דפדפן אמיתי בשליטתכם. בפיקסצ׳ר המקומי הזה היא הגיעה לתוצאת הסקריפט הראשונה ב-102 מ״ש p50, קיצרה את Chrome בכ-13 מ״ש אחרי ביטול, והשתמשה בתהליך browser אחד עבור ארבעה טאבים בו-זמנית. אלה תצפיות תחומות, לא הבטחות ביצועים אוניברסליות; המשיכה הקבועה היא גישה ישירה ל-CDP מתוך Go כשהשכבה הנוחה כבר לא מספיקה.

רק צריך לכייל נכון את הטענות, כי המוניטין מוכר יותר מדי בשתי נקודות. "pure Go, בלי תלויות" מתאר את המודול; בזמן ריצה אתם משנעים ומנהלים בינארי של Chrome. ו"תשתמשו בדפדפן headless ותקבלו את התוכן הדינמי" נכון רק כשה-wait שלכם מכוון לצומת שאתם רוצים — קריאה נאיבית ו-WaitReady("body") שניהם החזירו לי דף שחסר בו תוכן שהוזרק 800 מ״ש אחרי load, בשקט, בכל פעם. ב-macOS, defer cancel() הוא לא עניין של סגנון; תדלגו עליו ותדליפו browser בכל הרצה, שזה התנהגות ידועה של הפלטפורמה אבל עדיין בעיה שלכם לנהל. תעשו שלושת הדברים האלה נכון, ו-chromedp היא אחד מדרייברי הדפדפן היותר צפויים שמדדתי. תעשו אותם לא נכון, והיא תיכשל בשקט — וזו הדרך הכי גרועה ש-scraper יכול להיכשל בה.

נסו את Thunderbit לחילוץ נתוני רשת Get Started Free

שאלות נפוצות

האם chromedp באמת רואה תוכן שמורנדר על ידי JavaScript?
כן — אבל רק עם המתנה שמכוונת לצומת שרוצים. בפיקסצ׳ר שבו קישור הוזרק 800 מ״ש אחרי אירוע ה-load, Navigate יחד עם קריאה החמיצו אותו וגם WaitReady("body") החמיצה, בעוד ש-WaitVisible על אותו צומת ו-poll של JavaScript החזירו אותו, שלוש מתוך שלוש הרצות לכל אחד. כשסורקים את הדיליי, הקריאה בלי המתנה החמיצה את הצומת בכל ההגדרות מ-100 מ״ש ומעלה. רינדור הכרחי; ההמתנה הנכונה היא מה שהופך אותו למספיק.

מה ההבדל בין WaitReady ל-WaitVisible?
WaitReady חוסם עד שהצומת מצורף ל-DOM. WaitVisible חוסם עד שהוא באמת גלוי. על צומת שמצורף אבל מעוצב כ-display: none, WaitReady חזר אחרי בערך 6 מ״ש, בעוד WaitVisible חסם עד סוף ה-deadline של 4 שניות וחזר עם context deadline exceeded נקי. אף אחת מהן לא "אמינה יותר" — הן עונות על שאלות שונות, והבחירה הלא נכונה היא המלכודת האמיתית.

באמת צריך defer cancel() עם chromedp?
ב-macOS, כן. ביטול ה-context וה-allocator קיצרו את Chrome שנפתח ל-12–13 מ״ש בכל הרצה; יציאה מתהליך ה-Go בלי ביטול השאירה תהליך browser orphaned מאחור בכל שלוש ההרצות. זו התנהגות ידועה ומוגבלת לפלטפורמה — המעקב של chromedp מתעד את אותו דפוס אי-יציאה גם על מערכות אחרות שאינן Linux, ומנגנון ה-parent-death kill שמטפל בזה יושב בקוד עם build tags ללינוקס בלבד. לא בדקתי Linux, אז התייחסו לתוצאת ה-orphan כאל תוצאה שמוגבלת ל-macOS.

האם chromedp דורשת התקנה נפרדת של Chrome?
כן. מודול ה-Go עצמו הוא pure Go בלי cgo, אבל הוא מפעיל דפדפן חיצוני ונכשל מיד בלעדיו. סיפקתי headless shell של Chrome for Testing 151.0.7922.10 במפורש דרך chromedp.ExecPath. היתרון הוא שעלות ה-startup קטנה: מחזור cold מלא עד לתוצאת הסקריפט הראשונה עמד על חציון של 102 מ״ש על פני חמישה תהליכים חדשים, בטווח 98–111 מ״ש.

עדיף לשתף browser אחד בין טאבים או להרים כמה browsers נפרדים?
אם המטרה היא למזער את מספר תהליכי ה-browser, עדיף contexts-ילדים. ארבע ניווטים ל-localhost דרך browser אחד השתמשו ב-1 תהליך Chrome browser; ארבעה browsers נפרדים השתמשו ב-4. גם זמן הקיר נטה לטובת ההגדרה המשותפת (214 מ״ש מול 264 מ״ש בחציון), אבל ארבעה דפים מקומיים ופשוטים אינם benchmark של throughput. זיכרון לא נמדד. browsers נפרדים עדיין יכולים להיות הבחירה הנכונה כשצריך בידוד סשן חזק יותר, proxies שונים, או רדיוס קריסה קטן יותר; הפשרות האלה היו מחוץ לבדיקה הזו.

Ke
Ke
CTO ב-Thunderbit | מדען נתונים בכיר ומומחה ב-ML עם כמעט עשור של ניסיון בלמידת מכונה ובמדע הנתונים, קה שן הוא בוגר אוניברסיטת קולומביה ולשעבר מדען נתונים בכיר ב-Walmart Labs. עם מומחיות עמוקה ומוכרת בקרב עמיתים ב-Python, R, Java וסטטיסטיקה, הוא משתף תובנות מוכחות-בקרב על המעבר מאלגוריתמי AI מורכבים מתיאוריה לארכיטקטורה ברמת production.
תוכן עניינים
Thunderbit · סוכן נתוני אינטרנט מבוסס AI

חלץ נתונים מכל דף בתוך קליק אחד

למעלה מ-250,000 משתמשים סומכים עליו
יש תוכנית חינמית
מדף אינטרנט לגיליון נתונים
תארו מה אתם צריכים — סוכן ה-AI של Thunderbit מחלץ את זה ומייצא ל-Excel, Google Sheets, Airtable או Notion. חינמי להתחלה.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week