import http from 'node:http'; import { Actor } from 'apify'; import { PlaywrightCrawler, Dataset } from '@crawlee/playwright'; // Self-contained fixture: a start page that uses so the // crawler can only discover the linked pages if it honors the base href when // resolving the otherwise-relative values. const pages = { '/start': ` Start A B C Elsewhere (absolute) `, '/sub/a': 'AA', '/sub/b': 'BB', '/sub/c': 'CC', '/elsewhere': 'ElsewhereElsewhere', }; const server = http.createServer((req, res) => { const body = pages[req.url]; if (body === undefined) { res.statusCode = 404; res.end('Not Found'); return; } res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }); res.end(body); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const { port } = server.address(); const baseUrl = `http://127.0.0.1:${port}`; const mainOptions = { exit: Actor.isAtHome(), storage: process.env.STORAGE_IMPLEMENTATION === 'LOCAL' ? new (await import('@apify/storage-local')).ApifyStorageLocal() : undefined, }; await Actor.main(async () => { const crawler = new PlaywrightCrawler({ maxRequestsPerCrawl: 30, async requestHandler({ parseWithCheerio, enqueueLinks, request, log }) { const { url, loadedUrl } = request; const $ = await parseWithCheerio('title', 1_000); const pageTitle = $('title').first().text(); log.info(`URL: ${url}; LOADED_URL: ${loadedUrl}; TITLE: ${pageTitle}`); await Dataset.pushData({ url, loadedUrl, pageTitle }); // Only the /sub/ links should match — the absolute /elsewhere link // and any unresolved relative URLs (which would land at /a, /b, /c // without base-href handling) are filtered out by the glob. await enqueueLinks({ globs: [`${baseUrl}/sub/**`], }); }, }); await crawler.run([`${baseUrl}/start`]); }, mainOptions); server.close();