Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
Built and signed on GitHub Actions
latest
lino-levan/astralAstral is the browser automation library for Deno
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212# Astral <img src="./docs/static/icon.png" height="200" width="200" align="right"/> Astral is a high-level puppeteer/playwright-like library that allows for control over a web browser (primarily for automation and testing). It is written from scratch with Deno in mind. ## Usage Take a screenshot of a website. ```ts // Import Astral import { launch } from "jsr:@astral/astral"; // Launch the browser const browser = await launch(); // Open a new page const page = await browser.newPage("https://deno.land"); // Take a screenshot of the page and save that to disk const screenshot = await page.screenshot(); Deno.writeFileSync("screenshot.png", screenshot); // Close the browser await browser.close(); ``` You can use the evaluate function to run code in the context of the browser. ```ts // Import Astral import { launch } from "jsr:@astral/astral"; // Launch the browser const browser = await launch(); // Open a new page const page = await browser.newPage("https://deno.land"); // Run code in the context of the browser const value = await page.evaluate(() => { return document.body.innerHTML; }); console.log(value); // Run code with args const result = await page.evaluate((x, y) => { return `The result of adding ${x}+${y} = ${x + y}`; }, { args: [10, 15], }); console.log(result); // Close the browser await browser.close(); ``` You can navigate to a page and interact with it. ```ts // Import Astral import { launch } from "jsr:@astral/astral"; // Launch browser in headfull mode const browser = await launch({ headless: false }); // Open the webpage const page = await browser.newPage("https://deno.land"); // Click the search button const button = await page.$("button"); await button!.click(); // Type in the search input const input = await page.$("#search-input"); await input!.type("pyro", { delay: 1000 }); // Wait for the search results to come back await page.waitForNetworkIdle({ idleConnections: 0, idleTime: 1000 }); // Click the 'pyro' link const xLink = await page.$("a.justify-between:nth-child(1)"); await Promise.all([ page.waitForNavigation(), xLink!.click(), ]); // Click the link to 'pyro.deno.dev' const dLink = await page.$( ".markdown-body > p:nth-child(8) > a:nth-child(1)", ); await Promise.all([ page.waitForNavigation(), dLink!.click(), ]); // Close browser await browser.close(); ``` TODO: Document the locator API. ## Advanced Usage If you already have a browser process running somewhere else or you're using a service that provides remote browsers for automation (such as [browserless.io](https://www.browserless.io/)), it is possible to directly connect to its endpoint rather than spawning a new process. ```ts // Import Astral import { connect } from "jsr:@astral/astral"; // Connect to remote endpoint const browser = await connect({ wsEndpoint: "wss://remote-browser-endpoint.example.com", }); // Do stuff const page = await browser.newPage("http://example.com"); console.log(await page.evaluate(() => document.title)); // Close connection await browser.close(); ``` If you'd like to instead re-use a browser that you already launched, astral exposes the WebSocket endpoint through `browser.wsEndpoint()`. ```ts // Spawn a browser process const browser = await launch(); // Connect to first browser instead const anotherBrowser = await connect({ wsEndpoint: browser.wsEndpoint() }); ``` ### Page authenticate [authenticate example code](https://github.com/lino-levan/astral/blob/main/examples/authenticate.ts): ```ts // Open a new page const page = await browser.newPage(); // Provide credentials for HTTP authentication. const url = "https://postman-echo.com/basic-auth"; await page.authenticate({ username: "postman", password: "password" }); await page.goto(url, { waitUntil: "networkidle2" }); ``` ## BYOB - Bring Your Own Browser Essentially the process is as simple as running a chromium-like binary with the following flags: ``` chromium --remote-debugging-port=1337 \ --headless=new \ --no-first-run \ --password-store=basic \ --use-mock-keychain \ --hide-scrollbars ``` Technically, only the first flag is necessary, though I've found that these flags generally get the best result. Once your browser process is running, connecting to it is as simple as ```typescript // Import Astral import { connect } from "jsr:@astral/astral"; // Connect to remote endpoint const browser = await connect({ wsEndpoint: "<WS-ENDPOINT>", headless: false, }); console.log(browser.wsEndpoint()); // Do stuff const page = await browser.newPage("http://example.com"); console.log(await page.evaluate(() => document.title)); // Close connection await browser.close(); ``` ## FAQ ### Launch FAQ #### "No usable sandbox!" with user namespace cloning enabled > Ubuntu 23.10+ (or possibly other Linux distros in the future) ship an AppArmor > profile that applies to Chrome stable binaries installed at > /opt/google/chrome/chrome (the default installation path). This policy is > stored at /etc/apparmor.d/chrome. This AppArmor policy prevents Chrome for > Testing binaries downloaded by Puppeteer from using user namespaces resulting > in the No usable sandbox! error when trying to launch the browser. For > workarounds, see > https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md The following command removes AppArmor restrictions on user namespaces, allowing Puppeteer to launch Chrome without the "No usable sandbox!" error (see [puppeteer#13196](https://github.com/puppeteer/puppeteer/pull/13196)): echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns