Skip to content

Latest commit

ย 

History

127 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Miku Miku Crawler

๐ŸŒธ Miku Miku Crawler ๐ŸŒธ

โœจ A Kawaii Web Crawler with Real-Time Visualization โœจ

Version Bun Elysia License Kawaii Level

A real-time web crawler with a Miku-themed UI and live visualization.
Watch pages get crawled in real-time, inspect essential page details, and export stored pages
โ€” all wrapped in a cute interface.

๐Ÿ”„ Live SSE streaming ยท ๐Ÿ“Š Content analysis ยท ๐Ÿ’พ Persistent storage ยท ๐ŸŽจ Miku-themed UI

Inspired by MikuMikuBeam by Sammwy ๐Ÿ’•

Miku Crawler Preview


๐ŸŒŸ Features

๐Ÿ•ท๏ธ Crawling

Feature
๐Ÿ“ก SSE streaming โ€” ordered live events with bounded Last-Event-ID replay
๐ŸŽญ Playwright โ€” renders JavaScript-heavy pages with headless Chromium
โšก Cheerio โ€” fast HTML extraction for static pages
๐Ÿค– robots.txt โ€” optional compliance with crawl rules and crawl-delay
๐Ÿ”€ Concurrency โ€” configurable parallel page jobs
๐Ÿ”„ Retry with backoff โ€” delayed retries for rate-limit and backpressure responses
๐Ÿ’พ Session resume โ€” interrupted crawls persist and resume from where they stopped
๐Ÿšฆ Domain throttling โ€” hostname-scoped page scheduling to be a polite crawler

๐Ÿ“Š Content Processing

Every crawled page is reduced to the data used by crawling, recovery, search, and export:

Analysis
๐ŸŒ Language โ€” detection via franc
๐Ÿ“– Reading time โ€” derived from the stored main-content word count
๐Ÿ“ Page metadata โ€” bounded title, description, and robots directives
๐Ÿ–ผ๏ธ Media count โ€” optional bounded count of unique image, video, and audio references
๐Ÿ”— Links โ€” canonical HTTP(S) targets with nofollow admission policy

๐ŸŽจ Interface

Component
CrawlerForm Configure and launch crawls
StatsGrid Live counters โ€” pages, data size, speed
ProgressBar Visual crawl progress
CrawledPagesSection Bounded page list with durable search
TheatreOverlay Timed crawl launch sequence with optional audio
ExportDialog JSON / CSV export
ResumeSessionsPanel Browse & resume interrupted sessions
LogsSection Live crawl log stream
MikuBanner โœจ Animated mascot โœจ

๐Ÿš€ Quick Start

Requires Bun 1.3.14 โ€” the repository's declared runtime.

git clone https://github.com/renbkna/mikumikucrawler
cd mikumikucrawler
bun install
bunx playwright install chromium
bun run dev

Playwright's managed Chromium is preferred. If it is not installed, local development can use chromium, chromium-browser, google-chrome, or google-chrome-stable from PATH. On Linux systems missing browser libraries, run bunx playwright install --with-deps chromium.

Service URL
๐ŸŽจ Frontend http://localhost:5173
โš™๏ธ Backend http://localhost:3000
๐Ÿ“‹ OpenAPI (development UI) http://localhost:3000/openapi

By default, the backend owns port 3000 exclusively and fails clearly if another process already owns it. The checked-in default is owned by shared/deploymentDefaults.ts; the documented URLs are projections of that value. Local Vite development proxies /api to PORT, so changing PORT needs no duplicate frontend setting. Set VITE_BACKEND_URL only when the browser must connect directly to a separate backend origin. It accepts an absolute HTTP(S) base URL (including a path prefix), without credentials, a query, or a fragment.

๐Ÿ”ง Environment Variables

Copy .env.example โ†’ .env. All variables have sensible defaults. Frontend vars need the VITE_ prefix.

PORT=3000
NODE_ENV=development
FRONTEND_URL=http://localhost:5173
# Optional direct-browser backend override; local Vite development uses PORT.
# VITE_BACKEND_URL=https://api.example.com
DB_PATH=./data/crawler.db
# SQLite allocation budget. Admission reserves an 8 MiB safety allowance per remaining page.
MAX_STORAGE_MB=2048
LOG_LEVEL=info
USER_AGENT=MikuCrawler/3.0.0
ROBOTS_PRODUCT_TOKEN=MikuCrawler
RENDER=false
# On Render this also trusts the platform's client-IP forwarding for rate limits.
# Browser rendering is skipped when process RSS exceeds this many MB.
# Defaults to 350 on Render and 600 elsewhere.

One process exclusively owns DB_PATH while it is running. New and resumed crawls reserve an 8 MiB safety allowance for each remaining page. When necessary, the storage owner removes the oldest completed, stopped, or failed runs first; active, paused, and interrupted checkpoints are never reclaimed automatically. server/storage/schema.sql is the only supported schema. A database whose schema differs is replaced at startup; releases do not migrate or preserve incompatible stored data.

โš™๏ธ Crawler Options
Setting Default Range
Crawl Depth 2 1โ€“5
Max Pages 50 1โ€“200
Max Pages Per Domain 0 0โ€“1000 (0 = unlimited)
Page Crawl Delay 1000ms 200โ€“10000ms
Method full links / media / full
Concurrent Page Jobs 5 1โ€“10
Retry Limit 3 0โ€“5
Dynamic Content true โ€”
Respect Robots true โ€”
Content Only false โ€”
Count Media (saveMedia) false โ€”

Dynamic pages use a separate fixed subrequest policy: at most four concurrent subrequests, at least 50 ms between same-host dispatches, and at most 100 requests or 20 MiB of response bodies per page.


๐Ÿ”Œ API

The OpenAPI JSON specification is always available at /openapi/json; the interactive /openapi UI is development-only.

Method Endpoint Description
๐Ÿ†• POST /api/crawls Create a crawl run
๐Ÿ“‹ GET /api/crawls List crawl runs
โ™ป๏ธ GET /api/crawls/resumable?limit=25 List paused/interrupted runs (limit defaults to 25, maximum 100)
๐Ÿ” GET /api/crawls/:id Get crawl state & counters
โ™ป๏ธ GET /api/crawls/:id/snapshot Recover crawl state, latest stored pages, and total stored count in one response
โน๏ธ POST /api/crawls/:id/stop Request pause or force stop
โ–ถ๏ธ POST /api/crawls/:id/resume Resume a paused or interrupted crawl
๐Ÿ“ก GET /api/crawls/:id/events SSE event stream
๐Ÿ“„ GET /api/crawls/:id/pages List the latest stored page summaries and total stored count
๐Ÿ“ฆ GET /api/crawls/:id/export Export pages (JSON / CSV)
๐Ÿ—‘๏ธ DELETE /api/crawls/:id Delete a stored crawl
๐Ÿ“„ GET /api/crawls/:id/pages/:pageId/content Fetch crawl-owned stored page content
๐Ÿ”Ž GET /api/search?crawlId=:id&q=keyword Search one crawl's stored pages (FTS5)
๐Ÿ’š GET /health Health check

๐Ÿ“ก Event Stream

const source = new EventSource(
  "http://localhost:3000/api/crawls/<crawl-id>/events"
);

source.addEventListener("crawl.progress", (event) => {
  const { sequence, payload } = JSON.parse(event.data);
  console.log(payload.counters);
});
Event When
crawl.started Crawl begins processing
crawl.progress Counter & queue stats update
crawl.page A page was persisted, with its positive row ID and post-commit stored-page count
crawl.log Runtime log message with explicit severity
crawl.completed Crawl finished normally
crawl.paused Paused by user and available to resume
crawl.stopped Stopped by user
crawl.failed Terminated due to error

Events are sequenced. Last-Event-ID replays recent in-memory events; after a restart or cleanup, recover from the backend-owned crawl snapshot, which contains the persisted crawl summary, bounded latest-page window, and total stored count. Settled streams close after their terminal frame; reconnects with no unseen terminal event receive 204 so native EventSource clients stop reconnecting. Search and export cover the full stored set.


๐Ÿ—๏ธ Tech Stack

๐ŸŽจ Frontend

Technology
โš›๏ธ React 19
๐Ÿ“˜ TypeScript
๐ŸŽจ Tailwind CSS 4
โšก Vite
๐Ÿ”— Eden Treaty
โœ๏ธ Lucide React

โš™๏ธ Backend

Technology
๐ŸฅŸ Bun + bun:sqlite
๐ŸฆŠ Elysia + OpenAPI
๐ŸŽญ Playwright
๐Ÿ“ Pino
๐Ÿ”’ IP validation + rate limiting
๐Ÿ“ Project Structure
server/
โ”œโ”€โ”€ api/                    # Elysia route handlers
โ”œโ”€โ”€ contracts/              # OpenAPI schemas + shared type re-exports
โ”œโ”€โ”€ domain/crawl/           # Core crawl logic
โ”‚   โ”œโ”€โ”€ CrawlQueue.ts      #   Durable FIFO/delayed queue
โ”‚   โ”œโ”€โ”€ CrawlState.ts      #   Counters, visited URLs, stop logic
โ”‚   โ”œโ”€โ”€ DynamicRenderer.ts  #   Playwright lifecycle
โ”‚   โ”œโ”€โ”€ FetchService.ts     #   HTTP fetching with security checks
โ”‚   โ”œโ”€โ”€ PagePipeline.ts     #   Fetch โ†’ process โ†’ store pipeline
โ”‚   โ”œโ”€โ”€ RobotsService.ts    #   robots.txt evaluation
โ”‚   โ””โ”€โ”€ UrlPolicy.ts        #   URL filtering and normalization
โ”œโ”€โ”€ runtime/                # Crawl execution layer
โ”‚   โ”œโ”€โ”€ CrawlRuntime.ts     #   Orchestrates a single crawl run
โ”‚   โ”œโ”€โ”€ CrawlManager.ts     #   Creates, stops, resumes, lists runs
โ”‚   โ””โ”€โ”€ EventStream.ts      #   Sequenced bounded live SSE publishing
โ”œโ”€โ”€ processors/             # Content analysis
โ”‚   โ”œโ”€โ”€ ContentProcessor.ts #   Dispatch by content type
โ”‚   โ”œโ”€โ”€ analysisUtils.ts    #   Word count, reading time, language
โ”‚   โ””โ”€โ”€ extractionUtils.ts  #   Main content, metadata, media count, links
โ”œโ”€โ”€ storage/                # SQLite persistence
โ”‚   โ”œโ”€โ”€ schema.sql          #   Current schema; incompatible databases reset
โ”‚   โ””โ”€โ”€ repos/              #   Query repositories
โ”œโ”€โ”€ outbound/               # SSRF-safe DNS resolution and pinned HTTP
โ”œโ”€โ”€ plugins/                # Elysia plugins (SSE, OpenAPI, static)
โ””โ”€โ”€ config/                 # Env validation, logging setup

shared/                     # Cross-boundary contracts and policy
โ”œโ”€โ”€ contracts/              #   Domain types (status, events, pages)
โ”œโ”€โ”€ crawl.ts                #   Crawl option bounds
โ”œโ”€โ”€ deploymentDefaults.ts   #   Deployment defaults
โ”œโ”€โ”€ ipPolicy.ts             #   Public-address policy
โ”œโ”€โ”€ text.ts                 #   Text/byte conversion helpers
โ””โ”€โ”€ url.ts                  #   URL validation & normalization

๐Ÿ”ฎ How It Works

graph TD
    A[๐ŸŒ Target URL] --> B[๐ŸŽต CrawlRuntime]
    B --> C[๐Ÿ“„ PagePipeline]
    C --> D{Dynamic?}
    D -->|Yes| E[๐ŸŽญ Playwright]
    D -->|No| F[โšก Fetch + Cheerio]
    E --> G[๐Ÿ“Š ContentProcessor]
    F --> G
    G --> C
    C --> B
    B --> H[๐Ÿ’พ SQLite]
    B --> I[๐Ÿ“ก EventStream]
    I --> J[๐ŸŽจ React UI]
Loading
  1. Client creates a crawl via POST /api/crawls
  2. CrawlManager spawns a CrawlRuntime with its own queue and state
  3. PagePipeline fetches each URL via FetchService (static) or Playwright (dynamic)
  4. ContentProcessor analyzes the page and PagePipeline admits discovered links
  5. CrawlRuntime commits each terminal result and its counters atomically, then EventStream publishes sequenced events โœจ

๐Ÿšข Deployment

bun run build
NODE_ENV=production \
FRONTEND_URL=https://crawler.example.com \
DB_PATH=./data/crawler.db \
bun start

Development mode permits localhost crawl targets and therefore binds only to 127.0.0.1. Production mode denies localhost targets and binds to 0.0.0.0. Set FRONTEND_URL to the browser-visible production origin.

Render's public load balancer owns Brotli/gzip response compression. Any direct self-hostโ€”including bun start and the container belowโ€”emits uncompressed responses unless a compression-capable reverse proxy is placed in front.

๐Ÿณ Docker
docker build -t mikumikucrawler .
docker run --rm --init --ipc=host \
  --security-opt seccomp=seccomp_profile.json \
  -p 3000:3000 \
  -v mikumikucrawler-data:/app/data \
  -e FRONTEND_URL=http://localhost:3000 \
  mikumikucrawler

The named volume owns SQLite state across container replacement. The final image runs as Playwright's unprivileged pwuser, explicitly enables Chromium's sandbox, and uses the checked-in seccomp profile required for user namespaces. That profile derives from Moby's seccomp/v0.2.1 default, adds Playwright's clone/setns/unshare user-namespace allowance, and keeps socketcall blocked. Its SHA-256 is dfea086789bff2999aab1f950ffa6e50cf3d38492a3c7476aee637780e42c75b.

Both the Bun build image and Playwright runtime/browser image are pinned by OCI digest. Update those digests, the Playwright dependency, and the seccomp profile together as one reviewed browser-runtime migration.

VITE_BACKEND_URL is a frontend build-time setting, not a container runtime variable. Same-origin deployments should omit it. To build a browser bundle that talks directly to a separate backend, use:

docker build \
  --build-arg VITE_BACKEND_URL=https://api.example.com \
  -t mikumikucrawler .
NODE_ENV=production
PORT=3000
FRONTEND_URL=https://your-domain.com
DB_PATH=/app/data/crawler.db
MAX_STORAGE_MB=2048

โœ… Verification

bun run check

Typecheck (tsgo) โ†’ Format check and lint (biome ci) โ†’ Tests โ†’ Build


โš ๏ธ Responsible Use

โœ… Get permission before crawling
โœ… Respect robots.txt and rate limits
โœ… Use reasonable delays
โŒ Don't overload servers
โŒ Don't scrape copyrighted content without authorization

๐Ÿค Contributing

  1. Fork the repo
  2. Create a feature branch: git checkout -b my-feature
  3. Commit changes: git commit -m 'Add feature'
  4. Push: git push origin my-feature
  5. Open a Pull Request

๐Ÿ‘จโ€๐Ÿ’ป Developer

renbkna โ€” Solo Developer & Miku Enthusiast

๐Ÿ™ Special Thanks

Sammwy โ€” Original MikuMikuBeam inspiration


๐Ÿ“œ MIT โ€” see LICENSE


Miku

๐ŸŒธ Miku Miku Crawler ๐ŸŒธ

Made with ๐Ÿ’– by a developer who thinks crawlers can be cute


GitHub stars GitHub forks GitHub issues

Releases

Packages

Used by

Contributors

Languages