Yapay Zeka / AI

Moli: Işıkları Kapalı Tarayıcı – AI Ajanları İçin Rust ile Headless Browser

Moli, Chromium'u sarmalamayan Rust tabanlı bir headless browser: yerel testte tek süreç ve 43 MiB, Chromium'da 7 süreç ve 159 MiB. Ama varsayılan bekleme sayfanın JavaScript verisini beklemiyor; boş tablo çıkış kodu 0 ile geliyor.

7 dk okuma

Bu yazının İngilizcesi de var →
Işıkları kapalı, kesiti açık iki katlı bir ev; içeride musluk akıyor, kapının yanında amber ışıklı bir zil ve yarı kalkmış bir şalter var, headless browser benzetmesi.

Crawl4AI yazısında bir sayfayı modele kılçıksız yedirmeyi konuşmuştuk. O yazıda sayfayı açan şey Playwright'ın ayağa kaldırdığı Chromium'du. Bir sayfanın metnini okumak için çok süreçli bir masaüstü tarayıcısını uyandırıyorduk ve kimse bunu garip bulmuyordu.

Bu haftanın radar konusu tam bu israfa itiraz ediyor: Moli, AI ajanları için yazılmış, Rust tabanlı bir headless browser (görüntüsüz tarayıcı). Depo 10 Ağustos 2026'da açıldı ve 20 Ekim itibarıyla 19.751 yıldızda; findarepo'nun 19 Ekim listesinde scraping kategorisinin haftalık en hızlı yükseleni. Moli headless browser'ı bulut sandbox'ımda indirip yerel bir test sayfasında çalıştırdım, Chromium'la yan yana ölçtüm. En ilginç bulgu ölçüm değil, ilk denemede gelen boş tablo oldu.

Moli nedir?#

Moli, Chromium'u sarmalamayan, varsayılan olarak yerleşim (layout) ve boyama (paint) yapmadan DOM'u ve JavaScript'i çalıştıran bağımsız bir tarayıcı çekirdeği. Altında V8, html5ever, Servo'nun stil motoru Stylo ve ağ için libcurl var. Aynı ikili dosya hem komut satırı aracı hem de CDP, WebDriver Classic ve WebDriver BiDi sunucusu.

Moli'yi ışıkları kapalı bir ev gibi düşünün. Su akıyor, elektrik var, kapı açılıyor, içeride her oda yerinde. Ama ışık yanmıyor, çünkü metni okumak için ışığa ihtiyacınız yok. Ekran görüntüsü ya da tıklanacak koordinat lazımsa --layout ile şalteri kaldırıyorsunuz; iş bitince ışık yine sönüyor.

Kurulum: boruyu kabuğa bağlamayın#

README tek satırlık curl ... | sh öneriyor. Çalışır, ama 2,5 KB'lık bir betiği okumadan kabuğa vermek alışkanlık olmamalı. Sürüm sayfası her dosyanın SHA-256 değerini yayımlıyor; ben arşivi indirip doğruladım:

Bash
curl -fsSLO https://github.com/lexmount/moli/releases/download/v1.1.18/moli-x86_64-unknown-linux-gnu.tar.gz
sha256sum moli-x86_64-unknown-linux-gnu.tar.gz
# sürüm sayfasındaki değer:
# 59715fbdb2b016bb9136e1edb59a2a195c0f98459fd06739e17edc62da6a48b1
tar xzf moli-x86_64-unknown-linux-gnu.tar.gz
./moli-v1.1.18-x86_64-unknown-linux-gnu/moli --version   # moli 1.1.18

Arşiv 45 MB, açılınca tek ikili dosya ve lisans metinleri çıkıyor. Ayrı bir tarayıcı ya da ChromeDriver indirmiyor.

Ölçtüm: tek süreç, kırk megabayt#

Test sayfası bilerek sıradan: üst menü, bir reklam kutusu, footer ve JavaScript'in /kurlar.json dosyasından doldurduğu üç satırlık bir döviz tablosu. İkisini de aynı yerel sayfaya beş kez gönderdim, medyanları aldım:

Araç ve komut Süre Bellek (PSS) Süreç Tablo dolu mu
Moli 1.1.18, --wait-selector 46 ms 42,8 MiB 1 Evet
Chromium headless shell 141, --dump-dom 355 ms 159,4 MiB 7 Evet

Rakamları olduğundan büyük okumayın: iki çekirdekli bir sandbox, bir kilobaytlık sayfa, belleği 5 ms aralıkla örnekledim. Taşınabilir kısmı süreç sayısı ve büyüklük sırası. Projenin kendi ölçümü daha büyük ölçekte benzer bir tablo çiziyor (192 gerçek URL'de medyan bellek Moli 73 MiB, Chrome 773 MiB), ama o ölçüm projenin kendisine ait. Kendi test takımları Lexbench'te Moli 1.308 görevin %81,88'ini geçiyor, Chrome %99,85'ini; yani her beş görevden biri Chrome'da çalışıp Moli'de çalışmıyor. O rakam da 13 Ağustos tarihli rapordaki 0.1.1 sürümüne ait.

Işık kapalıyken zile basmadan girmeyin#

Ölçümden önemli olan, ilk denemede ne gördüğüm:

Bash
moli fetch --dump markdown http://127.0.0.1:8765/

Çıkış kodu 0, süre 72 ms. Çıktıda başlık var, menü var, reklam var. Tabloda ise yalnızca başlık satırı ve üstünde "Yükleniyor..." yazısı duruyor. Varsayılan bekleme modu done, sayfanın kendi fetch() çağrısının dönmesini beklemedi. Hata yok, uyarı yok; boş tablo başarılı bir sonuç olarak teslim edildi.

Çözüm, verinin geldiğini kanıtlayan bir koşul vermek:

Bash
moli fetch --dump markdown --strip-mode ui \
  --wait-selector "#kurlar tbody tr" \
  --fail http://127.0.0.1:8765/

Bu hâliyle 39 ms'de üç satırın üçü de geldi. --wait networkidle de çalıştı ama 533 ms sürdü, domstable 390 ms. Bonus: --strip-mode ui menüyü, reklam kutusunu ve footer'ı attı, geriye başlık, güncelleme satırı ve tablo kaldı. Bu seçeneğin --help çıktısında tek satır açıklaması yok; denemeden bilemezsiniz.

Aynı sessizliğin üç kardeşi daha var:

  • Hata sayfası da "başarı": Var olmayan bir adreste çıkış kodu yine 0, 404 sayfası Markdown'a çevrilip geliyor. --fail verince kod 22 oluyor.
  • Zaman aşımı milisaniye: --timeout 20 yazdım, "timed out after 20 ms" aldım. fetch için birim milisaniye, varsayılan 25000. curl alışkanlığıyla saniye yazarsanız hiçbir sayfa açılmaz.
  • Kapı ardına kadar açık: Varsayılan User-Agent, Linux'ta çalışırken bile "Windows NT 10.0, Chrome/145" diyor. robots.txt kontrolü (--obey-robots) ve iç ağ engeli (--block-private-networks) kapalı geliyor. Ajanınız kullanıcıdan URL alıyorsa ikincisi açılmadan bu araç bir SSRF kapısı. Kendinizi tanıtmak için --user-agent-suffix var.

Ne zaman işe yarar, ne zaman yaramaz?#

Yarar: JavaScript ile dolan sayfalardan toplu metin ve DOM çekmek, ajanlara düşük bellekli bir tarayıcı vermek, değerlendirme ve RL ortamlarında yüzlerce oturumu aynı makinede tutmak. moli serve varsayılan olarak 127.0.0.1:9222'de dinliyor ve Playwright connectOverCDP ile doğrudan bağlanıyor.

Yaramaz: Piksel birebirliği, Canvas/WebGL, medya oynatma; README bunları açıkça kapsam dışı sayıyor. Lexmount'un kendi bulut sayfası da kalıcı oturum, eklenti, indirme ya da yüksek doğruluklu görüntü gerekiyorsa Chromium'u öneriyor. Statik HTML'den tek sayfa okuyacaksanız da curl ve bir parser yeter, tarayıcıya gerek yok.

Bakım durumu ve emeğin sahibi#

Lisans Apache-2.0 veya MIT, seçim sizin. İlk sürüm v0.1.0, 11 Ağustos 2026; son sürüm v1.1.18, 17 Ekim 2026. On haftada otuz sürüm. Açık konu 32, açık PR 372. Depoda SECURITY.md yok ve yayımlanmış güvenlik danışma kaydı da yok; iyi haber mi, yoksa henüz kimse bakmadı mı, bu yaşta bir projede ayırt etmek zor.

Moli'yi Lexmount geliştiriyor. Şirketin ticari ürünü Lexmount Browser, Moli'nin etrafına kurulmuş yönetilen bir bulut tarayıcı servisi; README açık kaynak tarayıcının bu servis olmadan da tam kullanılabildiğini yazıyor. Commit geçmişinin ezici çoğunluğu tek bir isme ait: 1.965 katkıyla Donough Liu (ldm0), profilinde LexMount ve Pekin yazıyor. Sonraki katkıcının sayısı 31. Hız buradan geliyor, risk de buradan.

Alternatiflerle karşılaştırma#

Araç Lisans Dil Yıldız Son sürüm Şu iş için
Moli Apache-2.0 / MIT Rust 19,8 bin v1.1.18 (17.10.2026) Düşük bellekle JS'li sayfadan metin ve DOM
Playwright + Chromium Apache-2.0 TypeScript 98,1 bin v1.64.1 (14.10.2026) Görsel doğruluk, oturum, tam uyumluluk
Lightpanda AGPL-3.0 Zig 36,8 bin 1.0.1 (15.10.2026) Daha hafif; Moli'nin kendi testinde başarı oranı daha düşük
Crawl4AI Apache-2.0 Python 85,9 bin v0.9.5 (14.10.2026) Tarayıcı değil, Chromium üstünde temizleyici crawler

Yıldızlar ve sürümler 20 Ekim 2026'da GitHub'dan alındı. Playwright'ı Microsoft, Lightpanda'yı Lightpanda ekibi, Crawl4AI'ı UncleCode sürdürüyor. Lightpanda'nın AGPL lisansı, onu bir servisin içine gömecekseniz Moli'den önce okunması gereken satır.

Ajan tarafında üç kapı#

Moli'yi bir ajana üç yoldan bağlayabilirsiniz ve hiçbiri tek bir araca kilitli değil. Birincisi düz komut: Claude Code, Codex ya da OpenCode gibi terminal ajanları moli fetch'i kabuk komutu olarak çağırıyor. İkincisi depodaki skills/ klasörü: SKILL.md biçiminde üç beceri var ve moli-webfetch'in açıklaması doğrudan Codex'i anıyor; aynı biçimi Claude Code da beceri olarak okuyor. Üçüncüsü CDP: Playwright, Puppeteer ya da CDP konuşan herhangi bir otomasyon katmanı moli serve'e bağlanıyor.

Native PHP'den çağırmak#

Benim tarafımdaki senaryo, JavaScript'le dolan bir fiyat tablosunu cron'dan çekip veritabanına yazmak. Moli'yi proc_open ile çağıran düz PHP fonksiyonu, yukarıdaki dört tuzağın dördünü de kapatıyor:

PHP
<?php
declare(strict_types=1);
​
function moliSayfa(
    string $url,
    string $bekleSecici,
    string $moli = 'moli',
    bool $icAg = false            // yalnızca yerel testte true
): string {
    $komut = [
        $moli, 'fetch',
        '--dump', 'markdown',
        '--strip-mode', 'ui',              // nav, aside, footer gitsin
        '--wait-selector', $bekleSecici,   // veri gelmeden çıkma
        '--fail',                          // 4xx/5xx'te çıkış kodu 22
        '--user-agent-suffix', 'halityesil-bot/1.0',
        '--timeout', '20000',              // milisaniye!
        $url,
    ];
    if (!$icAg) {
        array_splice($komut, -1, 0, ['--block-private-networks']); // iç ağa istek atma
    }
​
    $surec = proc_open($komut, [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $borular);
    if (!is_resource($surec)) {
        throw new RuntimeException('Moli başlatılamadı.');
    }
​
    $cikti = stream_get_contents($borular[1]);
    $hata  = stream_get_contents($borular[2]);
    fclose($borular[1]);
    fclose($borular[2]);
    $kod = proc_close($surec);
​
    if ($kod !== 0) {
        throw new RuntimeException("Moli çıkış kodu {$kod}: " . trim((string) $hata));
    }
    if (trim((string) $cikti) === '') {
        throw new RuntimeException('Moli boş çıktı döndürdü.');
    }
​
    return (string) $cikti;
}

php -l'den geçti ve PHP 8.3'te yerel sayfaya karşı çalıştı: dolu tabloyu döndürdü, 404'te çıkış kodu 22 ile istisna attı, iç ağ engeli açıkken 127.0.0.1'e gitmeyi reddetti. Gelen Markdown'daki fiyatların "41,82" gibi virgüllü metin olduğuna da dikkat; Kentsel Dönüşüm #1'deki floatval("49,350") tuzağı tarayıcı değişince ortadan kalkmıyor.

Işığı açmadan evi gezmek#

Moli'nin iddiası doğru bir yerden geliyor: ajanın işi çoğu zaman sayfanın yapısıyla, pikselleriyle değil. O yapıyı tek süreçte ve kırk megabaytta veriyor. Ama ışıkları kapalı bir evde boş oda ile içinde birinin oturduğu oda kapıdan aynı görünüyor. Bekleme koşulunu siz vermezseniz Moli size boş odayı "işte ev" diye teslim ediyor, üstelik çıkış koduyla onaylayarak.

Zile basın (--wait-selector), kapıdaki tabelayı okuyun (--fail), komşunun bahçesine girmeyin (--block-private-networks).

Teknolojiyle kalın, çıkış kodunu okuyun.

Yorumlar

0 yorum

Yorum yazın

kişisine yanıt

Yorumlar onaylandıktan sonra görünür. E-posta adresiniz kimseyle paylaşılmaz. Kişisel verileriniz nasıl işlenir?