Obscura Skill
by @felipeoff
Use when scraping the web, driving headless browser automation, or running E2E tests from Claude Code. Obscura is a Rust-based, drop-in headless Chrome repla...
1. Fetch a single page
# Get the page title
obscura fetch https://example.com --eval "document.title"Dump the rendered HTML (after JS executes)
obscura fetch https://news.ycombinator.com --dump htmlDump only the links
obscura fetch https://example.com --dump linksDump plain text
obscura fetch https://example.com --dump textWait for network to be idle before reading
obscura fetch https://example.com --wait-until networkidle0Wait for a specific element
obscura fetch https://example.com --selector ".article-body"
--dump accepts: html, text, links.
--wait-until accepts: load, domcontentloaded, networkidle0.
2. Scrape many URLs in parallel
obscura scrape \
https://example.com/page-1 \
https://example.com/page-2 \
https://example.com/page-3 \
--concurrency 25 \
--eval "document.querySelector('h1').textContent" \
--format json
--format accepts: json or text. Use json when piping into jq.
3. Start a CDP server for Puppeteer / Playwright
obscura serve --port 9222With anti-detection + tracker blocking
obscura serve --port 9222 --stealthThrough an HTTP/SOCKS5 proxy
obscura serve --port 9222 --proxy socks5://127.0.0.1:1080More worker processes for higher throughput
obscura serve --port 9222 --workers 4
Then connect from Node:
Puppeteer:
import puppeteer from 'puppeteer-core';const browser = await puppeteer.connect({
browserWSEndpoint: 'ws://127.0.0.1:9222/devtools/browser',
});
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com');
const stories = await page.evaluate(() =>
Array.from(document.querySelectorAll('.titleline > a'))
.map(a => ({ title: a.textContent, url: a.href }))
);
console.log(stories);
await browser.disconnect();
Playwright:
import { chromium } from 'playwright-core';const browser = await chromium.connectOverCDP({
endpointURL: 'ws://127.0.0.1:9222',
});
const ctx = await browser.newContext();
const page = await ctx.newPage();
await page.goto('https://en.wikipedia.org/wiki/Web_scraping');
console.log(await page.title());
await browser.close();
4. Form submission & login
Obscura handles POSTs, follows 302 redirects, and maintains cookies natively.
await page.goto('https://quotes.toscrape.com/login');
await page.evaluate(() => {
document.querySelector('#username').value = 'admin';
document.querySelector('#password').value = 'admin';
document.querySelector('form').submit();
});
| Symptom | Likely Cause | Fix |
|---|---|---|
| connection refused from Puppeteer | Server not running | obscura serve --port 9222 first |
| Page renders empty HTML | JS hasn't finished | Add --wait-until networkidle0 |
| Site detects automation | webdriver leak | Build with --features stealth, run with --stealth |
| Build fails on v8 | Rust < 1.75 | rustup update stable |
| Slow first build | V8 compiling | Expected ~5 min, cached after |
clawhub install obscura-skill