All skills
jwynia avatar

/scraper-builder

@dbeae09
by J Wyniajwynia/agent-skills160 stars
20

Guide AI agents to generate complete PageObject pattern web scraper projects using Playwright and TypeScript with Docker deployment. Supports agent-browser site analysis for automated selector discovery. Keywords: scraper, playwright, pageobject, web scraping, docker, typescript, data extraction, automation.

Use this Skill: https://skilld.dev/gh/jwynia/agent-skills/scraper-builder

This session only. Nothing lands on disk.

assetstemplatespage-object.ts.md

≈1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Page Object Template

Site-specific page object with locators, extraction methods, and navigation. Extend this for each page type on the target site.

Usage

Create one page object per distinct page or view. Define locators in the constructor and data extraction in methods.

Template

import { Page, Locator } from 'playwright';
import { BasePage } from './BasePage';

/**
 * {{PageName}}Page — Scrapes data from {{url}}.
 *
 * Locators target {{description of what's on this page}}.
 * Adjust selectors after running agent-browser snapshot against the live site.
 */
export class {{PageName}}Page extends BasePage {
  // === Locators ===

  /** Container for each data item (card, row, list item) */
  readonly itemCards: Locator;

  /** Individual field locators within each item */
  readonly itemTitle: Locator;
  readonly itemPrice: Locator;
  readonly itemImage: Locator;
  readonly itemLink: Locator;

  // === Pagination (if applicable) ===
  readonly nextButton: Locator;

  constructor(page: Page) {
    super(page);

    // Item containers — adjust selector for the target site
    this.itemCards = page.locator('[data-testid="item-card"], .item-card, .product-card');

    // Field locators — scoped to page level; use card.locator() in scrape methods
    this.itemTitle = page.locator('[data-testid="item-title"]');
    this.itemPrice = page.locator('[data-testid="item-price"]');
    this.itemImage = page.locator('[data-testid="item-card"] img');
    this.itemLink = page.locator('[data-testid="item-card"] a');

    // Pagination
    this.nextButton = page.locator(
      '[aria-label="Next page"], a[rel="next"], button:has-text("Next")'
    );
  }

  /**
   * Scrape all items from the current page.
   *
   * Returns an array of extracted records. Fields that fail to extract
   * will be null rather than throwing.
   */
  async scrape(): Promise<Array<{{DataType}}>> {
    const items: Array<{{DataType}}> = [];
    const count = await this.itemCards.count();

    for (let i = 0; i < count; i++) {
      const card = this.itemCards.nth(i);

      items.push({
        title: (await card.locator('[data-testid="item-title"]').textContent()) ?? '',
        price: (await card.locator('[data-testid="item-price"]').textContent()) ?? '',
        imageUrl: await card.locator('img').getAttribute('src'),
        url: await card.locator('a').first().getAttribute('href'),
      });
    }

    return items;
  }

  /**
   * Check if there is a next page.
   */
  async hasNextPage(): Promise<boolean> {
    return this.nextButton.isVisible();
  }

  /**
   * Navigate to the next page. Returns false if no next page.
   */
  async goToNextPage(): Promise<boolean> {
    if (!(await this.hasNextPage())) return false;
    await this.nextButton.click();
    await this.waitForPageLoad();
    return true;
  }

  /**
   * Scrape all pages, following pagination until exhausted or limit reached.
   */
  async scrapeAllPages(maxPages = 10): Promise<Array<{{DataType}}>> {
    const allItems: Array<{{DataType}}> = [];
    let currentPage = 1;

    do {
      console.log(`Scraping page ${currentPage}...`);
      const items = await this.scrape();
      allItems.push(...items);
      currentPage++;
    } while (currentPage <= maxPages && (await this.goToNextPage()));

    console.log(`Scraped ${allItems.length} items across ${currentPage - 1} pages`);
    return allItems;
  }
}

Customization Notes

  • Replace {{PageName}} with the actual page class name (e.g., ProductListing, SearchResults)
  • Replace {{DataType}} with the TypeScript interface for extracted data
  • Replace {{url}} with the target page URL
  • Adjust selectors after analyzing the actual site with agent-browser or browser DevTools
  • Remove pagination if the page doesn't paginate
  • Add more fields by adding locators and extending the scrape() return object
  • Scoped extraction: The scrape() method uses card.locator() (scoped within each item) rather than page-level locators to avoid cross-item contamination

Source: SKILL.md on GitHub

2 warnings14d5 checks · Risk SAFE
  • Gen Agent Trust Hub14d

    This skill is a generator for web scraper projects using Playwright, TypeScript, and Docker. It follows security best practices by implementing data validation via Zod schemas, using non-root users in Docker containers, and referencing official images and tools from trusted organizations.

  • Socket14d

    No alerts

  • Snyk14d

    Risk: MEDIUM · 1 issue

  • Runlayer7mo

    22/22 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at dbeae09. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Dormantupdated 8 months ago
Other metadata
compatibility
Requires Deno for generator scripts. Generated projects use Node.js with Playwright.
metadata
{
  "author": "agent-skills",
  "version": "1.0",
  "domain": "development",
  "type": "generator",
  "mode": "generative"
}

README badge

README badge for jwynia/agent-skills/scraper-builder