Skip to content

Commit 366e19f

Browse files
authored
feat(mongodb): migrate TypeScript scraper to Stagehand v3 (#62)
- Use stagehand.extract with instruction and Zod schema - V3Options config, projectId on session create params, utils alignment Made-with: Cursor
1 parent 2373066 commit 366e19f

5 files changed

Lines changed: 3113 additions & 529 deletions

File tree

examples/integrations/mongodb/typescript/index.ts

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Stagehand, Page, BrowserContext } from "@browserbasehq/stagehand";
1+
import { Stagehand, type Page } from "@browserbasehq/stagehand";
22
import StagehandConfig from "./stagehand.config.js";
33
import chalk from "chalk";
44
import boxen from "boxen";
@@ -274,7 +274,11 @@ async function aggregateData<T>(
274274
/**
275275
* Scrapes a product list from an Amazon category page
276276
*/
277-
async function scrapeProductList(page: Page, categoryUrl: string): Promise<ProductList> {
277+
async function scrapeProductList(
278+
page: Page,
279+
categoryUrl: string,
280+
stagehand: Stagehand,
281+
): Promise<ProductList> {
278282
// Navigate to Amazon homepage first
279283
await page.goto('https://www.amazon.com');
280284
await page.waitForTimeout(2000);
@@ -296,24 +300,26 @@ async function scrapeProductList(page: Page, categoryUrl: string): Promise<Produ
296300
});
297301
await page.waitForTimeout(1000);
298302

299-
// Extract product data using Stagehand
300-
const data = await page.extract({
301-
instruction: "Extract all product information from this Amazon category page, including product names, prices, URLs, ratings",
302-
schema: z.object({
303-
products: z.array(z.object({
303+
const listSchema = z.object({
304+
products: z.array(
305+
z.object({
304306
name: z.string(),
305307
price: z.string(),
306308
url: z.string(),
307309
rating: z.number().optional(),
308310
reviewCount: z.number().optional(),
309-
})),
310-
category: z.string(),
311-
totalProducts: z.number().optional(),
312-
}),
311+
}),
312+
),
313+
category: z.string(),
314+
totalProducts: z.number().optional(),
313315
});
314316

315-
// Process the extracted data
316-
const products = data.products.map(product => ({
317+
const data = await stagehand.extract(
318+
"Extract all product information from this Amazon category page, including product names, prices, URLs, ratings",
319+
listSchema,
320+
);
321+
322+
const products = data.products.map((product: (typeof data.products)[number]) => ({
317323
...product,
318324
dateScraped: new Date(),
319325
}));
@@ -337,7 +343,11 @@ async function scrapeProductList(page: Page, categoryUrl: string): Promise<Produ
337343
/**
338344
* Scrapes detailed information for a single product
339345
*/
340-
async function scrapeProductDetails(page: Page, productUrl: string): Promise<Product> {
346+
async function scrapeProductDetails(
347+
page: Page,
348+
productUrl: string,
349+
stagehand: Stagehand,
350+
): Promise<Product> {
341351
await page.goto(productUrl);
342352

343353
// Wait for the page to load
@@ -353,11 +363,10 @@ async function scrapeProductDetails(page: Page, productUrl: string): Promise<Pro
353363
});
354364
await page.waitForTimeout(1000);
355365

356-
// Extract product details using Stagehand
357-
const product = await page.extract({
358-
instruction: "Extract detailed product information from this Amazon product page, including name, price, description, specifications, brand, category, image URL, rating, review count, and availability",
359-
schema: ProductSchema.omit({ dateScraped: true }),
360-
});
366+
const product = await stagehand.extract(
367+
"Extract detailed product information from this Amazon product page, including name, price, description, specifications, brand, category, image URL, rating, review count, and availability",
368+
ProductSchema.omit({ dateScraped: true }),
369+
);
361370

362371
// Add additional data
363372
const completeProduct: Product = {
@@ -445,11 +454,9 @@ async function runQueries(): Promise<void> {
445454
// ========== Main Function ==========
446455
async function main({
447456
page,
448-
context,
449457
stagehand,
450458
}: {
451459
page: Page;
452-
context: BrowserContext;
453460
stagehand: Stagehand;
454461
}) {
455462
try {
@@ -471,7 +478,7 @@ async function main({
471478
console.log(chalk.blue("📊 Starting to scrape product listing..."));
472479

473480
// Scrape product listing
474-
const productList = await scrapeProductList(page, categoryUrl);
481+
const productList = await scrapeProductList(page, categoryUrl, stagehand);
475482
console.log(chalk.green(`✅ Scraped ${productList.products.length} products from category: ${productList.category}`));
476483

477484
// Scrape detailed information for the first 3 products
@@ -482,7 +489,7 @@ async function main({
482489

483490
try {
484491
// Scrape product details
485-
const detailedProduct = await scrapeProductDetails(page, product.url);
492+
const detailedProduct = await scrapeProductDetails(page, product.url, stagehand);
486493
console.log(chalk.green(`✅ Scraped detailed information for: ${detailedProduct.name}`));
487494

488495
// Wait between requests to avoid rate limiting
@@ -526,12 +533,10 @@ async function run() {
526533
);
527534
}
528535

529-
const page = stagehand.page;
530-
const context = stagehand.context;
531-
536+
const page = stagehand.context.pages()[0];
537+
532538
await main({
533539
page,
534-
context,
535540
stagehand,
536541
});
537542

0 commit comments

Comments
 (0)