Obxcura
A small Ruby client for the Obscura headless browser, driven over the Chrome DevTools Protocol.
A Browser owns one WebSocket connection; each Page is a CDP target with its
own attached session. One connection, many pages.
Obxcura vs Obscura. This gem is
Obxcura. The browser it drives isobscura— a separate binary you run yourself. Thexkeeps them apart.
Installation
bundle add obxcura
You also need the obscura browser binary, which is a separate project. The
repo ships an installer for it:
scripts/obscura/install.sh
That pulls the latest release for your platform into /usr/local/bin (asking for
sudo only if the prefix is not writable), then verifies the install and prints
the checksums.
scripts/obscura/install.sh --version v0.2.0 # pin a release
scripts/obscura/install.sh --prefix ~/.local/bin # no sudo
scripts/obscura/install.sh --no-stealth # plain build
scripts/obscura/install.sh --no-render # smaller, no screenshot/pdf
scripts/obscura/install.sh --help
Prefer it to a manual download, because the packaging has sharp edges:
-
Since 0.2.0 there are four builds per platform, and the suffix decides what works:
Asset suffix Render engine Stealth (none) yes no -stealthyes yes -no-renderno no -no-render-stealthno yes The script defaults to
-stealth: it carries the render engine that#screenshotand#pdfneed, and it is the build that gets past commercial bot detection.--no-rendertrades both away for a smaller download. -
--versioncannot tell the builds apart — all four of a release report the same string. That is why the script prints checksums: they are the only way to identify later which build is actually installed. -
The archive holds two binaries,
obscuraandobscura-worker, andservelooks for the worker as a sibling. The script always moves the pair, since installing one alone silently mixes builds. -
On macOS it removes the target before copying. Overwriting a binary in place leaves a stale cached code signature on that inode, and the kernel then
SIGKILLs it on exec — even though the bytes are fine.
Driving anything on localhost also needs --allow-private-network, which is off
by default:
obscura serve --allow-private-network
Usage
Start the browser first (defaults to port 9222):
obscura serve
Then:
require "obxcura"
browser = Obxcura::Browser.new # or Obxcura.start
begin
page = browser.go_to("https://example.com")
page.title # => "Example Domain"
page.current_url # => "https://example.com/"
page.html # rendered DOM, post-JS
page.evaluate("1 + 2") # => 3
page.at_css("h1").text # => "Example Domain"
page.css("a").map { |a| a["href"] }
page.at_css("#username").type("guillermo")
page.at_css("form").submit
page.screenshot(path: "shot.png")
page.pdf(path: "page.pdf", paper: :a4)
ensure
browser.quit # always, so targets don't leak
end
That is the whole shape of it: a Browser owns the connection, a Page is a
target on it, and #at_css / #css hand back live Node handles you can read
from and act on.
Documentation
Full reference in docs/, one page per area:
| | |
|---|---|
| Connecting | Obxcura.start, Browser.new, ports, timeouts, shutdown |
| Pages | #goto, #refresh, #close, raw #command |
| Reading content | #html, #title, #current_url |
| Querying the DOM | #at_css, #css, and every Node method |
| Running JavaScript | #evaluate, #evaluate_func, #call_on |
| Forms and input | #focus, #type, #submit, #value |
| HTTP from the page | #post, #network_log |
| Cookies | #cookies — read, set, remove, replay a session |
| Headers | #headers — read and set extra HTTP headers |
| Screenshots | #screenshot and every option |
| PDF | #pdf and every option |
| Errors | The exception hierarchy, and what does not raise |
| Browser constraints | Obscura limits that shape this API |
Runnable examples live in examples/.
Two constraints are worth knowing before you write anything, both covered in
Browser constraints: Obscura stops running background
JavaScript shortly after load, so goto + sleep does not do what you expect;
and an in-page throw comes back as nil rather than raising.
Development
bin/setup
bundle exec rake # rubocop + rspec
bundle exec rake doc # YARD docs into doc/
bin/console # IRB with the gem loaded
The integration specs boot a real obscura serve against a local WEBrick test
site. Point them at your binary:
OBSCURA_BIN=/path/to/obscura bundle exec rspec
Without the binary those specs skip cleanly, so bundle exec rspec still passes.
The #screenshot and #pdf specs skip the same way on a -no-render build, so
run them against a build that has the render engine — either the default from
scripts/obscura/install.sh or the unsuffixed asset — if you touch either.
Contributing
Bug reports and pull requests are welcome at https://github.com/memoxmrdl/obxcura.
License
MIT. See LICENSE.txt.