-
Notifications
You must be signed in to change notification settings - Fork 38
Expand file tree
/
Copy pathbrowser-session.ts
More file actions
102 lines (90 loc) · 3.18 KB
/
browser-session.ts
File metadata and controls
102 lines (90 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/usr/bin/env node
/**
* Browser Session Example
*
* Demonstrates the browser() primitive — launches a Hero-stealthed
* Chrome and returns a CDP WebSocket URL for Playwright/Puppeteer.
*
* This example:
* 1. Creates a browser session via ReaderClient
* 2. Connects Playwright via connectOverCDP (one-line change)
* 3. Navigates to Hacker News and extracts the top stories
* 4. Takes a screenshot
* 5. Cleans up the session
*
* Install: npm install playwright-core
* Run: npx tsx examples/basic/browser-session.ts
*/
import { ReaderClient } from "@vakra-dev/reader";
import { chromium } from "playwright-core";
async function main() {
const reader = new ReaderClient({ verbose: true });
try {
// Create a browser session — returns a CDP WebSocket URL
console.log("Creating browser session...\n");
const session = await reader.browser({
timeoutMs: 60_000,
verbose: true,
showChrome: true,
});
console.log(`\nSession ready: ${session.wsEndpoint}\n`);
// Connect Playwright — this is the only line that changes
// from a normal Playwright script
const browser = await chromium.connectOverCDP(session.wsEndpoint);
const context = await browser.newContext();
const page = await context.newPage();
// Navigate to Hacker News
console.log("Navigating to Hacker News...");
await page.goto("https://news.ycombinator.com/", {
waitUntil: "domcontentloaded",
timeout: 15_000,
});
console.log(`Title: ${await page.title()}`);
console.log(`URL: ${page.url()}\n`);
// Extract the top 10 stories
const stories = await page.evaluate(() => {
const rows = document.querySelectorAll(".athing");
return Array.from(rows)
.slice(0, 10)
.map((row) => {
const titleEl = row.querySelector(".titleline > a");
const siteEl = row.querySelector(".sitestr");
const scoreRow = row.nextElementSibling;
const scoreEl = scoreRow?.querySelector(".score");
return {
rank: row.querySelector(".rank")?.textContent?.trim(),
title: titleEl?.textContent?.trim(),
url: titleEl?.getAttribute("href"),
site: siteEl?.textContent?.trim() ?? null,
points: scoreEl?.textContent?.trim() ?? null,
};
});
});
console.log("Top 10 Hacker News stories:");
console.log("─".repeat(60));
for (const story of stories) {
console.log(`${story.rank} ${story.title}`);
if (story.site) console.log(` ${story.site} | ${story.points ?? "no score"}`);
console.log();
}
// Take a screenshot
await page.screenshot({ fullPage: true, path: "hn-screenshot.png" });
console.log(`Screenshot saved to hn-screenshot.png\n`);
// Stealth check
const stealth = await page.evaluate(() => ({
webdriver: (navigator as any).webdriver,
languages: navigator.languages,
}));
console.log(
`Stealth: webdriver=${stealth.webdriver}, languages=${JSON.stringify(stealth.languages)}`
);
// Cleanup
await browser.close();
await session.close();
console.log("\nDone.");
} finally {
await reader.close();
process.exit(0);
}
}
main();