SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-10-06

آموزش نصب و میزبانی Moli به عنوان مرورگر headless

مرورگر Moli جایگزین سبک برای headless Chrome در VPSهای کوچک است. با اجرای این موتور مبتنی بر Rust و اتصال Playwright به localhost، ایجنت‌های خود را با مصرف رم بسیار کمتر اجرا کنید.

یک مرورگر headless که روی یک VPS کوچک اجرا می‌شود

Moli یک مرورگر headless برای ایجنت‌های هوش مصنوعی است و آن‌قدر سبک است که می‌توان آن را روی VPSهایی که توان اجرای headless Chrome را ندارند، میزبانی کرد. این یک موتور مرورگر است که با Rust نوشته شده و یک wrapper برای Chromium نیست؛ همچنین از پروتکل Chrome DevTools Protocol (CDP) پشتیبانی می‌کند، همان پروتکلی که کتابخانه اتوماسیون شما هم‌اکنون از آن استفاده می‌کند. شما یک فایل باینری را نصب می‌کنید، moli serve را اجرا می‌کنید و سپس Playwright یا کد ایجنت خود را به http://127.0.0.1:9222 متصل می‌کنید.

پیش از نصب، محدودیت‌ها را مطالعه کنید. این پروژه محدوده کاری خود را به‌صراحت بیان کرده است: بدون رابط کاربری گرافیکی (GUI)، بدون GPU compositor، بدون تطابق پیکسل‌به‌پیکسل با Chrome و بدون پشتیبانی از Canvas با دقت بالا یا پخش رسانه. صفحاتی که به این قابلیت‌ها نیاز دارند، با خطا مواجه خواهند شد. Chrome واقعی در کنار Playwright همچنان گزینه جایگزین باقی می‌ماند و بخش آخر به شما نشان می‌دهد که چگونه تشخیص دهید کدام صفحات به آن نیاز دارند.

تمام دستورات زیر از فایل README پروژه و فایل‌های مهارتی منتشرشده آن استخراج شده و در اوت 2026 بررسی شده‌اند. تمام اعداد موجود در نمودارها، ارقامی هستند که پروژه درباره موتور خود منتشر کرده است (نه اندازه‌گیری‌های این سایت) و زیرنویس هر نمودار به این موضوع اشاره دارد. اگر هنوز در حال انتخاب موتور هستید، بررسی جامع‌تر مرورگرهای headless برای ایجنت‌ها روی VPS گزینه‌های جایگزین را پوشش می‌دهد.

چرا headless Chrome از حافظه زیادی استفاده می‌کند؟

Chrome یک مرورگر چند‌پردازشی است. هر تب و هر iframe بین‌سایتی، پردازش renderer مخصوص به خود را دارد و هر renderer دارای heap اختصاصی V8 و بافرهای گرافیکی خود است. این طراحی برای دسکتاپ مناسب است، جایی که کرش کردن یک تب نباید کل پنجره را از کار بیندازد. در یک VPS با 2 گیگابایت رم، این یعنی یک عملیات ساده مرور وب می‌تواند حافظه بیشتری نسبت به برنامه‌ای که واقعاً در حال اجرای آن هستید، مصرف کند.

این پروژه 192 آدرس URL عمومی مختلف را با چهار موتور بررسی کرد و نتایج را منتشر نمود.

ChartMixed public web crawl, 192 URLs, figures published by the Moli project
The data behind this chart
[
  {
    "engine": "Moli",
    "useful_pages": 103,
    "median_rss_mib": 73
  },
  {
    "engine": "Chrome Headless",
    "useful_pages": 101,
    "median_rss_mib": 773
  },
  {
    "engine": "Lightpanda",
    "useful_pages": 85,
    "median_rss_mib": 40
  },
  {
    "engine": "Obscura",
    "useful_pages": 57,
    "median_rss_mib": 39
  }
]

نسخه Headless Chrome تعداد 101 صفحه مفید و Moli تعداد 103 صفحه را بازگرداند، بنابراین در آن نمونه، هر دو موتور سهم تقریباً یکسانی از وب را خواندند. تفاوت اصلی در میزان مصرف حافظه است: میانه RSS (اندازه مجموعه مقیم، یعنی حافظه‌ای که یک پردازش واقعاً در RAM نگه می‌دارد) برای Chrome برابر با 773 مگابایت و برای Moli برابر با 73 مگابایت است. این روند منطقی است زیرا از معماری پردازشی آن‌ها ناشی می‌شود. نسبت دقیق مصرف حافظه در صفحات شما لزوماً با این اعداد یکی نیست.

آنچه شما را دچار مشکل می‌کند، میانه نیست؛ بلکه اوج مصرف حافظه است. وقتی یک سرور 2 گیگابایتی با کمبود حافظه مواجه می‌شود، هسته سیستم‌عامل یک پردازش را انتخاب و آن را متوقف (kill) می‌کند و گزارش آن در dmesg -T یا journalctl -k ثبت می‌شود:

Out of memory: Killed process 4211 (chrome) total-vm:2318936kB, anon-rss:1418324kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:3540kB oom_score_adj:0

عامل (agent) شما هرگز آن خط را نمی‌بیند. آنچه می‌بیند مرورگری است که پاسخ‌دهی را متوقف کرده، که معمولاً به صورت خطای Playwright مانند page.goto: Page crashed یا یک target بسته شده ظاهر می‌شود. هیچ‌چیز در آن خطا به حافظه اشاره نمی‌کند، به همین دلیل است که وقتی یک عامل به صورت تصادفی روی یک سرور کوچک شکست می‌خورد، OOM (کمبود حافظه) اولین چیزی است که باید بررسی کنید. تعیین اندازه برای اوج مصرف حافظه، همان تمرینی است که برای انتخاب RAM و CPU برای VPS عامل انجام می‌دهید.

نصب باینری Moli با نسخه ثابت

این پروژه یک نصب‌کننده shell و فایل‌های tarball پیش‌ساخته را در GitHub releases منتشر می‌کند. تا اوت 2026، نسخه فعلی 1.0.1 است که در 18 اوت 2026 منتشر شده است. ارقام بنچمارک ذکر شده در این راهنما توسط تیم پروژه روی نسخه 0.1.1 اندازه‌گیری شده‌اند؛ بنابراین آن‌ها را به عنوان الگوی کلی موتور در نظر بگیرید، نه تضمینی برای نسخه‌ای که نصب می‌کنید.

نسخه را ثابت (Pin) کنید. نصب‌کننده‌ای که همیشه latest را فراخوانی می‌کند، در بازسازی بعدی، agent شما را به یک موتور مرورگر متفاوت منتقل می‌کند؛ تغییر در رفتار مرورگر از آن دسته تغییراتی است که باید برای آن برنامه‌ریزی کنید، نه اینکه به‌طور اتفاقی با آن مواجه شوید.

نصب‌کننده shell سریع‌ترین راه است و پیش از اجرا، ارزش خواندن دارد.

curl --proto '=https' --tlsv1.2 -fsSL \
  -o /tmp/moli-installer.sh \
  https://github.com/lexmount/moli/releases/download/v1.0.1/moli-installer.sh
less /tmp/moli-installer.sh
sh /tmp/moli-installer.sh

پیش از اجرای اسکریپت، آن را بخوانید. اسکریپت کوتاه است. این فایل یک آرشیو را از uname -m انتخاب کرده و سپس یک باینری واحد را در ~/.local/bin استخراج می‌کند. روی x86_64 از moli-x86_64-unknown-linux-gnu.tar.gz و روی سرور Arm از آرشیو aarch64 استفاده می‌کند، بنابراین هر دو پلن VPS یعنی Arm و x86 پشتیبانی می‌شوند. برای نصب در مسیری دیگر، MOLI_INSTALL_DIR را تنظیم کنید. توجه کنید که اسکریپت برای نسخه چه چیزی را resolve می‌کند: جدیدترین نسخه منتشر شده، نه تگی که اسکریپت را از آن دریافت کرده‌اید. این برای بررسی اولیه مناسب است، اما برای بازسازی‌هایی که می‌خواهید تکرارپذیر باشند، روش درستی نیست.

بنابراین برای هر مورد دائمی، کاری که نصب‌کننده انجام می‌دهد را به‌صورت دستی انجام دهید و آرشیو دقیق را خودتان نام‌گذاری کنید. این روش همچنین باعث می‌شود باینری را در جایی قرار دهید که سرویس سیستم به آن دسترسی داشته باشد و از pipe کردن مستقیم اسکریپت دانلود شده به shell جلوگیری می‌کند.

cd /tmp
curl --proto '=https' --tlsv1.2 -fsSLO \
  https://github.com/lexmount/moli/releases/download/v1.0.1/moli-x86_64-unknown-linux-gnu.tar.gz
mkdir -p moli-pkg
tar -xzf moli-x86_64-unknown-linux-gnu.tar.gz -C moli-pkg --strip-components=1
sudo install -m 0755 moli-pkg/moli /usr/local/bin/moli
moli --version

moli --version چاپ نسخه‌ای که ثابت کرده‌اید، کل بررسی لازم است. خطای moli: command not found بلافاصله پس از نصب به این معنی است که دایرکتوری نصب در PATH شما قرار ندارد؛ نصب‌کننده در خروجی خود خطی را چاپ می‌کند که نام دایرکتوری مورد نیاز برای اضافه کردن را نشان می‌دهد.

استخراج یک‌باره با moli fetch

بسیاری از وظایفی که یک عامل (agent) به مرورگر محول می‌کند، از نوع «این URL را بارگذاری کن و بگو چه محتوایی دارد» هستند. این کار اصلاً نیازی به سرور ندارد. moli fetch موتور را راه‌اندازی می‌کند، یک صفحه را بارگذاری می‌کند، یک آرتیفکت را در خروجی استاندارد می‌نویسد و سپس خارج می‌شود؛ بنابراین هیچ چیزی بین فراخوانی‌ها در حافظه باقی نمی‌ماند.

moli fetch --dump markdown --wait-until networkidle https://example.com
moli fetch --dump semantic_tree_text --wait-selector "main" https://example.com
moli fetch --dump json --wait-until networkidle https://example.com > page.json

دستور اول صفحه را به صورت Markdown چاپ می‌کند که با # Example Domain شروع می‌شود. Markdown ارزان‌ترین فرمت برای ارائه به مدل است، زیرا نشانه‌گذاری‌ها را حذف کرده و متن را حفظ می‌کند. semantic_tree_text نقش‌ها و ساختار را حفظ می‌کند که در صفحات دارای ناوبری سنگین، جایی که لینک‌ها به اندازه متن اهمیت دارند، مورد نیاز است. --dump json وضعیت HTTP و ردپای درخواست را به همراه دارد؛ بنابراین زمانی که یک fetch با نتیجه خالی بازمی‌گردد و نیاز دارید دلیل آن را بدانید، از آن استفاده کنید.

استراتژی انتظار تعیین می‌کند که محتوا را دریافت می‌کنید یا یک پوسته خالی. --wait-until networkidle به محض آرام شدن شبکه بازمی‌گردد. --wait-until domstable زمانی بازمی‌گردد که تغییرات DOM متوقف شود؛ این گزینه برای صفحاتی که در پس‌زمینه polling انجام می‌دهند و هرگز کاملاً آرام نمی‌شوند، انتخاب بهتری است. --wait-selector منتظر یک انتخابگر (selector) خاص می‌ماند که شما تعیین می‌کنید. این تنها استراتژی است که از محتوای صفحه‌ای که در حال دریافت آن هستید آگاهی دارد و به همین دلیل، هر زمان که هدف را می‌شناسید، مطمئن‌ترین گزینه است.

اسکرین‌شات و PDF به چیدمان (layout) واقعی نیاز دارند و چیدمان به‌صورت پیش‌فرض غیرفعال است:

moli fetch --layout --dump screenshot https://example.com > page.png
moli fetch --layout --dump screenshot_full https://example.com > full-page.png
moli fetch --layout --dump pdf https://example.com > page.pdf

فایل README سیاست چیدمان پیش‌فرض را LayoutPolicy::Mock نام‌گذاری می‌کند: هندسه شبیه‌سازی می‌شود و چیزی ترسیم (paint) نمی‌شود، زیرا چیدمان و ترسیم، نیمه پرهزینه کارکرد یک مرورگر هستند. آن پیش‌فرض دلیل ارقام حافظه ذکر شده در بالا است. همچنین به این معنی است که یک PNG خالی معمولاً به دلیل نبود پرچم --layout است، نه خرابی صفحه.

برای URLهایی که عامل شما پیدا کرده است (به جای URLهایی که خودتان انتخاب کرده‌اید)، از --block-private-networks استفاده کنید. عاملی که لینک‌های خوانده‌شده در یک صفحه را دنبال می‌کند، ممکن است به سمت دریافت http://169.254.169.254/ برای اعتبارنامه‌های نمونه ابری (cloud instance) یا پورت دیتابیس روی localhost هدایت شود که هرگز قرار نبوده در معرض وب قرار بگیرد. این پرچم از ناوبری به فضای آدرس خصوصی جلوگیری می‌کند و --block-cidrs این محدودیت را دقیق‌تر می‌کند. زمانی که وظیفه به جای خواندن یک صفحه، خزیدن (crawling) در سایت است، شکل آن خط لوله در جایگزین‌های self-hosted برای Firecrawl پوشش داده شده است و مرحله پیش از آن، یعنی یافتن URLها، در مهارت جستجوی مبتنی بر SearXNG برای عامل‌ها توضیح داده شده است.

اتصال یک عامل (Agent) به Moli از طریق CDP

برای عاملی که در مراحل مختلف پیمایش کرده و کلیک می‌کند، به‌جای آن از سرور استفاده کنید.

moli serve --host 127.0.0.1 --port 9222

مقدار 127.0.0.1 و پورت 9222 مقادیر پیش‌فرض هستند، بنابراین یک moli serve ساده به‌طور پیش‌فرض فقط به loopback متصل می‌شود. با این حال، در هر پیکربندی دائمی، هر دو را به‌طور کامل بنویسید؛ چرا که نفر بعدی که فایل سرویس شما را می‌خواند، نباید مجبور باشد مقادیر پیش‌فرض را به خاطر بسپارد.

پیش از اتصال کلاینت، سرور را بررسی کنید:

curl -s http://127.0.0.1:9222/json/version

یک سرور سالم با یک شیء JSON پاسخ می‌دهد که حاوی فیلد webSocketDebuggerUrl است؛ این همان URL است که کلاینت CDP به آن متصل می‌شود. خطای curl: (7) Failed to connect to 127.0.0.1 port 9222: Connection refused به این معناست که هیچ سرویسی در حال گوش دادن نیست؛ بنابراین ترمینالی که آن را در آن اجرا کرده‌اید بررسی کنید، یا اگر به صورت سرویس است، از journalctl -u moli -n 50 استفاده کنید. دستور /json/list اهداف (targets) باز را فهرست می‌کند و /json/protocol دامنه‌هایی را که این بیلد پیاده‌سازی کرده است نشان می‌دهد؛ این روشی است که متوجه شوید آیا متد CDP مورد نیاز شما در اینجا وجود دارد یا خیر.

Playwright به‌جای اجرای یک مرورگر مستقل، به آن endpoint متصل می‌شود:

import { chromium } from "playwright";

const browser = await chromium.connectOverCDP("http://127.0.0.1:9222");
const context = browser.contexts()[0];
const page = context.pages()[0] ?? await context.newPage();

await page.goto("https://example.com");
console.log(await page.locator("body").innerText());

await browser.close();

خطی که اهمیت دارد connectOverCDP است، نه chromium.launch(). در اینجا هیچ پردازش فرزند Chromium وجود ندارد، بنابراین executablePath و فلگ‌های معمول کانتینر مانند --no-sandbox تأثیری ندارند. به همین دلیل، تنظیمات پروکسی، کوکی و user-agent باید به عنوان فلگ‌های خودِ سرور Moli اعمال شوند. انتظار پوشش کامل پروتکل Chrome را نداشته باشید و تنها بخشی از CDP را در نظر بگیرید: خطای صریح unsupported-method نشان‌دهنده محدودیت موتور است، نه باگ در کد شما.

دو فلگ سرور تعیین می‌کنند که عامل چه کارهایی می‌تواند انجام دهد. --layout هندسه واقعی را فعال می‌کند که برای کلیک بر اساس مختصات و اسکرین‌شات‌ها ضروری است. --resource تصاویر، فونت‌ها و رسانه‌های اختیاری را دریافت می‌کند؛ این کار باعث مصرف پهنای باند و حافظه در هر بار بارگذاری صفحه می‌شود، بنابراین تا زمانی که صفحه واقعاً به آن‌ها نیاز نداشته باشد، آن را غیرفعال نگه دارید. --profile-dir کوکی‌ها و حافظه را بین اجراها حفظ می‌کند و بدون آن، هر اجرا کاملاً مستقل و موقتی خواهد بود.

ChartOne agent episode, Moli against Chromium, figures published by the Moli project
The data behind this chart
[
  {
    "engine": "Moli",
    "cdp_ready_ms": 34.85,
    "peak_pss_mib": 102.46,
    "processes": 1
  },
  {
    "engine": "Chromium",
    "cdp_ready_ms": 169.37,
    "peak_pss_mib": 348.82,
    "processes": 11
  }
]

در نمونه بار کاری عامل پروژه، Moli اتصال CDP را پس از 34.85 میلی‌ثانیه پذیرفت، در حالی که این زمان برای Chromium برابر با 169.37 میلی‌ثانیه بود. حداکثر PSS (اندازه تناسبی مجموعه، حافظه‌ای که با تقسیم صفحات مشترک بین پردازش‌های استفاده‌کننده محاسبه می‌شود) برابر با 102.46 MiB در مقابل 348.82 MiB بود. تفاوت ساختاری در ستون آخر نهفته است: 1 پردازش در مقابل 11 پردازش. یک پردازش، یک موجودیت واحد برای نظارت توسط systemd و یک cgroup برای محدودسازی است؛ همین موضوع باعث می‌شود بخش بعدی کوتاه باشد.

اجرای moli serve به‌عنوان یک سرویس systemd روی loopback

زمانی که یک agent به مرورگری نیاز دارد که در انتظار آن باشد، سرور را به‌عنوان یک سرویس اجرا کنید. در غیر این صورت، همچنان از moli fetch برای هر URL استفاده کنید، زیرا سرورِ در حالت idle همچنان حافظه را اشغال می‌کند.

پورت 9222 را روی رابط عمومی (public interface) قرار ندهید. پروتکل CDP هیچ‌گونه مرحله احراز هویتی ندارد. هر کسی که به آن پورت دسترسی داشته باشد، می‌تواند مرورگر را کنترل کرده و هر چیزی که مرورگر به آن دسترسی دارد، از جمله کوکی‌های موجود در دایرکتوری پروفایل شما را بخواند. آن را روی 127.0.0.1 نگه دارید. از طریق یک SSH tunnel (ssh -L 9222:127.0.0.1:9222 user@your-vps) یا یک رابط VPN خصوصی به آن متصل شوید و اجازه دهید agent در سمت خودِ تونل به http://127.0.0.1:9222 وصل شود.

یک کاربر سرویس ایجاد کنید و سپس فایل unit را بسازید:

sudo useradd --system --home-dir /var/lib/moli --shell /usr/sbin/nologin moli

فایل /etc/systemd/system/moli.service را بنویسید:

[Unit]
Description=Moli headless browser CDP server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=moli
Group=moli
ExecStart=/usr/local/bin/moli serve --host 127.0.0.1 --port 9222 --profile-dir /var/lib/moli/profile --block-private-networks
Restart=on-failure
RestartSec=2
StateDirectory=moli
MemoryAccounting=yes
MemoryMax=768M
NoNewPrivileges=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now moli.service
systemctl status moli.service
curl -s http://127.0.0.1:9222/json/version

systemctl status باید active (running) را نشان دهد و curl باید JSON مربوط به discovery را بازگرداند. ProtectSystem=strict کل فایل‌سیستم را برای این unit به‌صورت read-only mount می‌کند؛ به همین دلیل است که StateDirectory=moli در اینجا اختیاری نیست: این دستور /var/lib/moli را ایجاد می‌کند که مالک آن کاربر سرویس است و فقط همان مسیر را قابل‌نوشتن می‌کند. unitای که شروع می‌شود و سپس با خطای مجوز در journalctl -u moli از کار می‌افتد، تقریباً همیشه در حال تلاش برای نوشتن در جایی است که ProtectSystem آن را read-only کرده است؛ بنابراین آن مسیر را به زیر دایرکتوری state منتقل کنید.

MemoryMax=768M همان چیزی است که اجرای این سرویس را در کنار برنامه شما ایمن می‌کند. این unit یک cgroup اختصاصی دریافت می‌کند و زمانی که آن cgroup از حد مجاز خود فراتر رود، هسته سیستم‌عامل (kernel) پردازشی را در داخل آن می‌کشد و بقیه سیستم را دست‌نخورده باقی می‌گذارد. journal این اتفاق را ثبت می‌کند:

moli.service: A process of this unit has been killed by the OOM killer.

این خط را به‌عنوان سیگنالی برای تعیین اندازه (sizing) در نظر بگیرید. یا صفحات وب سنگین‌تر از آن چیزی هستند که برنامه‌ریزی کرده بودید، یا حد مجاز بسیار پایین است. عدد را بر اساس اندازه‌گیری صفحات خود تنظیم کنید که موضوع بخش بعدی است. همان فلگ‌های accounting، سایر سرویس‌های موجود روی سیستم را نیز محدود می‌کنند و محدود کردن حافظه و CPU با systemd برای بقیه آن‌ها نیز کاربرد دارد.

اندازه‌گیری اوج مصرف حافظه توسط خودتان

ارقام منتشرشده از سخت‌افزار و صفحات وب دیگران به‌دست آمده‌اند. اوج مصرف حافظه تعیین می‌کند که آیا سرور شما پایدار می‌ماند یا خیر، و این مقدار کاملاً به آنچه بارگذاری می‌کنید بستگی دارد. پیش از تعیین ابعاد (Sizing)، اندازه‌گیری کنید.

برای دریافت‌های تک‌مرحله‌ای (One-shot)، از باینری time استفاده کنید که اطلاعات بسیار بیشتری نسبت به دستور داخلی (builtin) شل با همان نام ارائه می‌دهد:

sudo apt update && sudo apt install -y time
/usr/bin/time -v moli fetch --dump markdown --wait-until networkidle https://example.com > /dev/null

خروجی با بلوکی از آمار منابع پایان می‌یابد که شامل Maximum resident set size (kbytes) است. آن را بر 1024 تقسیم کنید تا مقدار به MiB به‌دست آید. این دستور را روی ده صفحه‌ای که عامل (agent) شما واقعاً بازدید می‌کند اجرا کنید، نه روی example.com؛ و به‌جای میانگین، بدترین نتیجه را ملاک قرار دهید، زیرا OOM killer به اوج‌های مصرف واکنش نشان می‌دهد.

برای سرویس، شمارنده‌ای را بخوانید که هسته (kernel) از قبل برای cgroup آن نگهداری می‌کند:

cat /sys/fs/cgroup/system.slice/moli.service/memory.peak
systemd-cgtop -m

memory.peak یک شمارش بایت است و نشان‌دهنده بالاترین حد ثبت‌شده (high-water mark) از زمان آخرین راه‌اندازی واحد است، بنابراین با ری‌استارت کردن، این مقدار بازنشانی می‌شود. این همان رقمی است که MemoryMax باید بالاتر از آن قرار بگیرد، به‌طوری که فضایی نیز برای سنگین‌ترین صفحه‌ای که هنوز بازدید نکرده‌اید، باقی بماند. systemd-cgtop -m میزان مصرف لحظه‌ای هر واحد را نشان می‌دهد که سریع‌ترین راه برای تشخیص این است که کدام سرویس روی سرور، در حال حاضر پرمصرف‌ترین است.

Moli کجا شکست می‌خورد و چه زمانی همچنان به Chrome نیاز دارید؟

این پروژه همچنین یک بنچمارک شامل 1,308 وظیفهٔ خودکارسازی مرورگر مشابه را اجرا کرده و امتیاز چندین موتور را منتشر می‌کند.

ChartLexbench headless browser suite, 1,308 tasks, figures published by the Moli project
The data behind this chart
[
  {
    "engine": "Chrome",
    "success_rate_pct": 99.85
  },
  {
    "engine": "Moli 0.1.1",
    "success_rate_pct": 81.88
  },
  {
    "engine": "Kitesurf",
    "success_rate_pct": 62.08
  },
  {
    "engine": "Lightpanda",
    "success_rate_pct": 53.29
  },
  {
    "engine": "Obscura",
    "success_rate_pct": 44.88
  }
]

در میان آن 5 موتور، Moli نسخه 0.1.1 موفق به انجام 81.88 درصد از وظایف شد و Chrome، که موتور مرجع است، 99.85 درصد را تکمیل کرد. از آنجا که این پروژه در حال امتیازدهی به خود در مجموعه تست‌های خودش است، آن را به عنوان یک ادعا در نظر بگیرید، نه یک نتیجه مستقل.

برداشت کاربردی ساده است. تقریباً از هر پنج وظیفه، یکی در Moli شکست خورد که Chrome آن را با موفقیت انجام داد. اگر عامل (agent) شما از مجموعه صفحات ثابتی بازدید می‌کند که تحت کنترل شما هستند، این نسبت اهمیت چندانی ندارد، زیرا صفحات شما یا کار می‌کنند یا نمی‌کنند و می‌توانید همین امروز بعدازظهر این موضوع را بررسی کنید. اما اگر عامل شما در وب آزاد جستجو می‌کند، این یک نرخ شکست واقعی است که باید برای آن برنامه‌ریزی کنید.

آنچه شکست می‌خورد، با توجه به دامنهٔ تعریف‌شدهٔ پروژه قابل پیش‌بینی است:

  • برنامه‌هایی که رابط کاربری خود را به جای DOM در یک عنصر Canvas ترسیم می‌کنند، زیرا دقت Canvas صراحتاً خارج از محدودهٔ پروژه است.
  • هر چیزی که به WebGL یا ترکیب‌کننده GPU نیاز دارد، زیرا هیچ GPU compositor وجود ندارد.
  • ویدیوهای دارای محافظت DRM و پخش رسانه‌های سنگین.
  • تست‌های بصری که اسکرین‌شات‌های دقیق پیکسلی را با Chrome مقایسه می‌کنند، زیرا برابری با Chrome هدف پروژه نیست.

عدد دیگری که پروژه ذکر می‌کند، یعنی عبور از 1.612 میلیون تست پلتفرم وب در یک اجرای کامل، بیانیه‌ای درباره پوشش استانداردهاست. این یک تضمین برای سایت‌هایی که عامل شما بازدید خواهد کرد، نیست. یک صفحه می‌تواند فقط از استانداردهای کاملاً پشتیبانی‌شده استفاده کند و همچنان در بررسی‌های ضد ربات (bot check) شکست بخورد، و هیچ امتیاز موتوری این مورد را پوشش نمی‌دهد.

بنابراین، راهکار جایگزین (fallback) را در طراحی خود حفظ کنید. ابتدا هر URL را به Moli بفرستید. زمانی که صفحه خالی بازگشت یا یک selector ظاهر نشد، آن URL خاص را با استفاده از Playwright و هدایت Chrome واقعی، روی یک ماشین بزرگ‌تر یا در زمان‌بندی مشخصی که پردازش 773 MiB در آن مقرون‌به‌صرفه است، دوباره امتحان کنید. اکثر عامل‌ها بیشترین زمان خود را در صفحات معمولی صرف می‌کنند، بنابراین موتور سبک حجم کار را بر عهده می‌گیرد و موتور گران‌قیمت موارد خاص و دشوار را مدیریت می‌کند.

FAQ

آیا Moli می‌تواند جایگزین headless Chrome برای ایجنت من باشد؟

برای خواندن صفحات، استخراج متن و کلیک‌های معمولی، معمولاً بله. در بنچمارک اختصاصی این پروژه که شامل 1308 تسک است، این ابزار به نرخ موفقیت 81.88 درصد در مقابل 99.85 درصد برای Chrome دست یافته است؛ بنابراین از هر پنج تسک، حدود یک مورد به قابلیتی نیاز دارد که Moli فاقد آن است. برنامه‌های رندر شده با Canvas، WebGL و ویدیوهای دارای DRM از موارد شناخته‌شده‌ای هستند که در آن‌ها ضعف دارد. برای این URLها، به‌جای بازگرداندن کل سیستم، آن‌ها را به یک نمونه واقعی Chrome هدایت کنید.

Moli روی یک VPS به چه مقدار RAM نیاز دارد؟

این پروژه میانگین RSS معادل 73 MiB را در یک خزیدن (crawl) روی 192 URL و حداکثر PSS معادل 102.46 MiB را در یک نمونه از اجرای ایجنت گزارش می‌دهد، در حالی که این مقدار برای headless Chrome به‌طور میانگین 773 MiB است. این‌ها ارقام گزارش‌شده در مستندات خودشان هستند. مقادیر مصرفی خود را با استفاده از /usr/bin/time -v در اطراف یک فراخوانی moli fetch برای استفاده تک‌مرحله‌ای اندازه‌گیری کنید، یا برای سرویس، /sys/fs/cgroup/system.slice/moli.service/memory.peak را بخوانید، سپس MemoryMax را روی مقداری بالاتر از بدترین عددی که مشاهده کردید، تنظیم کنید.

آیا باز گذاشتن پورت 9222 روی اینترنت امن است؟

خیر. پروتکل CDP فاقد احراز هویت است، بنابراین هر کسی که به این پورت دسترسی داشته باشد می‌تواند مرورگر شما را کنترل کرده و هر چیزی که مرورگر به آن دسترسی دارد را بخواند. پورت را روی --host 127.0.0.1 نگه دارید و از طریق یک تونل SSH یا یک رابط VPN خصوصی از دستگاه دیگری به endpoint متصل شوید. اگر مجبور به bind کردن روی آدرس دیگری هستید، آن را روی یک رابط خصوصی قرار دهید و دسترسی را با فایروال کنترل کنید.

چرا اسکرین‌شات من خالی است یا کلیک‌هایم به هدف نمی‌خورند؟

طرح‌بندی (Layout) به‌صورت پیش‌فرض غیرفعال است. در فایل README، سیاست پیش‌فرض LayoutPolicy::Mock ذکر شده است، بنابراین هندسه عناصر واقعی نیست و هر چیزی که به کادر (box) روی صفحه وابسته باشد، داده‌ای برای پردازش ندارد. سرور را با moli serve --layout راه‌اندازی کنید یا --layout را به moli fetch اضافه کنید تا اسکرین‌شات و مسیرهای مختصات به‌درستی کار کنند. تصاویر گمشده مربوط به فلگ متفاوتی هستند: --resource.

کدام نسخه از Moli را باید نصب کنم؟

یک نسخه را انتخاب و آن را ثبت کنید. تا اوت 2026، نسخه فعلی 1.0.1 است، در حالی که ارقام بنچمارک منتشرشده توسط پروژه بر اساس نسخه 0.1.1 اندازه‌گیری شده‌اند؛ بنابراین هنگام مقایسه نتایج با دیگران، این دو نسخه قابل جایگزینی نیستند. فایل moli-x86_64-unknown-linux-gnu.tar.gz مربوط به آن تگ را دانلود کنید و به‌جای تکیه بر نصب‌کننده shell (که به‌جای تگ مورد نظر شما، جدیدترین نسخه را دریافت می‌کند)، باینری را شخصاً نصب کنید و سپس با moli --version آن را تأیید کنید.