@crawlee/playwright 4.0.0-beta.211 → 4.0.0-beta.212

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -185,18 +185,7 @@ export declare class AdaptivePlaywrightCrawler<ContextExtension = Dictionary<nev
185
185
  #private;
186
186
  constructor(options?: AdaptivePlaywrightCrawlerOptions<ContextExtension, ExtendedContext, Routes, StatisticStateExtension>);
187
187
  protected init(): Promise<void>;
188
- private adaptCheerioContext;
189
- private adaptPlaywrightContext;
190
- /**
191
- * Runs one request handler attempt inside its own {@link StorageTransaction}, wrapping the inner
192
- * (static or browser) context pipeline. The transaction is pushed to `transactions` *at creation
193
- * time, before the `try`* - the `ok: false` branch of the returned {@link Result} carries no
194
- * result, and failed attempts are routine here. The caller owns the outcome and disposal.
195
- */
196
- private crawlOne;
197
188
  protected runRequestHandler(crawlingContext: CrawlingContext): Promise<void>;
198
- private enqueueLinks;
199
- private createLogProxy;
200
189
  /**
201
190
  * Number of rendering type detections that have not settled yet, including results the predictor is
202
191
  * still persisting.
@@ -185,8 +185,8 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
185
185
  });
186
186
  this.#staticCrawler = staticCrawler;
187
187
  this.#browserCrawler = browserCrawler;
188
- this.#staticContextPipeline = staticCrawler.contextPipeline.compose(this.adaptCheerioContext.bind(this));
189
- this.#browserContextPipeline = browserCrawler.contextPipeline.compose(this.adaptPlaywrightContext.bind(this));
188
+ this.#staticContextPipeline = staticCrawler.contextPipeline.compose(this.#adaptCheerioContext.bind(this));
189
+ this.#browserContextPipeline = browserCrawler.contextPipeline.compose(this.#adaptPlaywrightContext.bind(this));
190
190
  }
191
191
  async init() {
192
192
  // A crawler can be run again after a teardown.
@@ -225,7 +225,7 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
225
225
  },
226
226
  }));
227
227
  }
228
- async adaptCheerioContext(cheerioContext) {
228
+ async #adaptCheerioContext(cheerioContext) {
229
229
  return {
230
230
  get page() {
231
231
  throw new Error('Page object was used in HTTP-only request handler');
@@ -238,12 +238,12 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
238
238
  },
239
239
  enqueueLinks: async (options = {}) => {
240
240
  const urls = extractUrlsFromCheerio(cheerioContext.$, options.selector, options.baseUrl ?? cheerioContext.request.loadedUrl);
241
- return (await this.enqueueLinks(urls, options, cheerioContext.request));
241
+ return (await this.#enqueueLinks(urls, options, cheerioContext.request));
242
242
  },
243
243
  response: cheerioContext.response,
244
244
  };
245
245
  }
246
- async adaptPlaywrightContext(playwrightContext) {
246
+ async #adaptPlaywrightContext(playwrightContext) {
247
247
  // Capture the original response to avoid infinite recursion when the getter is copied to the context
248
248
  const originalResponse = playwrightContext.response;
249
249
  return {
@@ -270,7 +270,7 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
270
270
  const locator = playwrightContext.page.locator(selector).first();
271
271
  await locator.waitFor({ timeout: timeoutMs, state: 'attached' });
272
272
  const urls = await extractUrlsFromPage(playwrightContext.page, selector, options.baseUrl ?? playwrightContext.request.loadedUrl);
273
- return (await this.enqueueLinks(urls, options, playwrightContext.request));
273
+ return (await this.#enqueueLinks(urls, options, playwrightContext.request));
274
274
  },
275
275
  };
276
276
  }
@@ -280,7 +280,7 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
280
280
  * time, before the `try`* - the `ok: false` branch of the returned {@link Result} carries no
281
281
  * result, and failed attempts are routine here. The caller owns the outcome and disposal.
282
282
  */
283
- async crawlOne(renderingType, context, useStateFunction, transactions) {
283
+ async #crawlOne(renderingType, context, useStateFunction, transactions) {
284
284
  const transaction = createStorageTransaction({
285
285
  policy: this.#attemptWritePolicy,
286
286
  commitTimeoutMillis: this.internalTimeoutMillis,
@@ -290,7 +290,7 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
290
290
  const deferredCleanup = [];
291
291
  const attemptBoundContextHelpers = {
292
292
  useState: useStateFunction,
293
- log: this.createLogProxy(context.log, logs),
293
+ log: this.#createLogProxy(context.log, logs),
294
294
  registerDeferredCleanup: (cleanup) => deferredCleanup.push(cleanup),
295
295
  };
296
296
  const subCrawlerContext = Object.defineProperties({}, Object.getOwnPropertyDescriptors(context));
@@ -340,7 +340,7 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
340
340
  if (renderingTypePrediction.renderingType === 'static' && !shouldDetectRenderingType) {
341
341
  crawlingContext.log.debug(`Running HTTP-only request handler for ${crawlingContext.request.url}`);
342
342
  this.statistics.state.httpOnlyRequestHandlerRuns++;
343
- const plainHTTPRun = await this.crawlOne('static', crawlingContext, crawlingContext.useState, transactions);
343
+ const plainHTTPRun = await this.#crawlOne('static', crawlingContext, crawlingContext.useState, transactions);
344
344
  if (plainHTTPRun.ok && this.#resultChecker(plainHTTPRun.result)) {
345
345
  crawlingContext.log.debug(`HTTP-only request handler succeeded for ${crawlingContext.request.url}`);
346
346
  plainHTTPRun.logs?.forEach(([log, method, ...args]) => log[method](...args));
@@ -385,7 +385,7 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
385
385
  return this.stateCopy;
386
386
  },
387
387
  };
388
- const browserRun = await this.crawlOne('clientOnly', crawlingContext, stateTracker.getLiveState.bind(stateTracker), transactions);
388
+ const browserRun = await this.#crawlOne('clientOnly', crawlingContext, stateTracker.getLiveState.bind(stateTracker), transactions);
389
389
  if (!browserRun.ok) {
390
390
  throw browserRun.error;
391
391
  }
@@ -396,7 +396,7 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
396
396
  crawlingContext.log.debug(`Detecting rendering type for ${crawlingContext.request.url}`);
397
397
  // The detection attempt's transaction is never committed - its writes exist only for the
398
398
  // result comparison.
399
- const plainHTTPRun = await this.crawlOne('static', crawlingContext, stateTracker.getStateCopy.bind(stateTracker), transactions);
399
+ const plainHTTPRun = await this.#crawlOne('static', crawlingContext, stateTracker.getStateCopy.bind(stateTracker), transactions);
400
400
  const detectionResult = (() => {
401
401
  if (!plainHTTPRun.ok) {
402
402
  return 'clientOnly';
@@ -432,7 +432,7 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
432
432
  }
433
433
  }
434
434
  }
435
- async enqueueLinks(urls, options, request) {
435
+ async #enqueueLinks(urls, options, request) {
436
436
  const baseUrl = resolveBaseUrlForEnqueueLinksFiltering({
437
437
  enqueueStrategy: options?.strategy,
438
438
  finalRequestUrl: request.loadedUrl,
@@ -448,7 +448,7 @@ export class AdaptivePlaywrightCrawler extends BasicCrawler {
448
448
  strategy: options.strategy ?? EnqueueStrategy.SameHostname,
449
449
  });
450
450
  }
451
- createLogProxy(log, logs) {
451
+ #createLogProxy(log, logs) {
452
452
  return new Proxy(log, {
453
453
  get(target, propertyName) {
454
454
  if (proxyLogMethods.includes(propertyName)) {
@@ -249,7 +249,6 @@ export declare class PlaywrightCrawler<ContextExtension = Dictionary<never>, Ext
249
249
  */
250
250
  constructor(options?: PlaywrightCrawlerOptions<ContextExtension, ExtendedContext, Routes, StatisticStateExtension>);
251
251
  protected navigationHandler(crawlingContext: PlaywrightCrawlingContext, gotoOptions: DirectNavigationOptions): Promise<Response | null>;
252
- private enhanceContext;
253
252
  }
254
253
  /**
255
254
  * Returns a `postNavigationHooks`-ready hook that runs {@link PlaywrightContextUtils.handleCloudflareChallenge}
@@ -106,12 +106,12 @@ export class PlaywrightCrawler extends BrowserCrawler {
106
106
  });
107
107
  }
108
108
  #buildContextPipeline() {
109
- return this.buildContextPipeline().compose(this.enhanceContext.bind(this));
109
+ return this.buildContextPipeline().compose(this.#enhanceContext.bind(this));
110
110
  }
111
111
  async navigationHandler(crawlingContext, gotoOptions) {
112
112
  return gotoExtended(crawlingContext.page, crawlingContext.request, gotoOptions);
113
113
  }
114
- async enhanceContext(context) {
114
+ async #enhanceContext(context) {
115
115
  const waitForSelector = async (selector, timeoutMs = 5_000) => {
116
116
  const locator = context.page.locator(selector).first();
117
117
  await locator.waitFor({ timeout: timeoutMs, state: 'attached' });
@@ -60,7 +60,4 @@ export declare class RenderingTypePredictor implements IRenderingTypePredictor {
60
60
  * Store the rendering type for a given URL and request label. This updates the underlying prediction model, which may be costly.
61
61
  */
62
62
  storeResult(requests: Request | Request[], renderingType: RenderingType): void;
63
- private resultCount;
64
- private calculateFeatureVector;
65
- private retrain;
66
63
  }
@@ -104,14 +104,14 @@ export class RenderingTypePredictor {
104
104
  return { renderingType: 'clientOnly', detectionProbabilityRecommendation: 1 };
105
105
  }
106
106
  const predictionUrl = new URL(loadedUrl ?? url);
107
- const urlFeature = new Matrix([this.calculateFeatureVector(urlComponents(predictionUrl), label)]);
107
+ const urlFeature = new Matrix([this.#calculateFeatureVector(urlComponents(predictionUrl), label)]);
108
108
  const [prediction] = logreg.predict(urlFeature);
109
109
  const scores = [logreg.classifiers[0].testScores(urlFeature), logreg.classifiers[1].testScores(urlFeature)];
110
110
  return {
111
111
  renderingType: prediction === 1 ? 'static' : 'clientOnly',
112
112
  detectionProbabilityRecommendation: Math.abs(scores[0] - scores[1]) < 0.1
113
113
  ? 1
114
- : this.#detectionRatio * Math.max(1, 5 - this.resultCount(label)),
114
+ : this.#detectionRatio * Math.max(1, 5 - this.#resultCount(label)),
115
115
  };
116
116
  }
117
117
  /**
@@ -129,20 +129,20 @@ export class RenderingTypePredictor {
129
129
  }
130
130
  state.detectionResults.get(renderingType).get(label).push(urlComponents(resultUrl));
131
131
  }
132
- this.retrain();
132
+ this.#retrain();
133
133
  }
134
- resultCount(label) {
134
+ #resultCount(label) {
135
135
  return Array.from(this.#state.currentValue.detectionResults.values())
136
136
  .map((results) => results.get(label)?.length ?? 0)
137
137
  .reduce((acc, value) => acc + value, 0);
138
138
  }
139
- calculateFeatureVector(url, label) {
139
+ #calculateFeatureVector(url, label) {
140
140
  return [
141
141
  mean((this.#state.currentValue.detectionResults.get('static')?.get(label) ?? []).map((otherUrl) => calculateUrlSimilarity(url, otherUrl) ?? 0)) ?? 0,
142
142
  mean((this.#state.currentValue.detectionResults.get('clientOnly')?.get(label) ?? []).map((otherUrl) => calculateUrlSimilarity(url, otherUrl) ?? 0)) ?? 0,
143
143
  ];
144
144
  }
145
- retrain() {
145
+ #retrain() {
146
146
  const X = [
147
147
  [0, 1],
148
148
  [1, 0],
@@ -151,7 +151,7 @@ export class RenderingTypePredictor {
151
151
  for (const [renderingType, urlsByLabel] of this.#state.currentValue.detectionResults.entries()) {
152
152
  for (const [label, urls] of urlsByLabel) {
153
153
  for (const url of urls) {
154
- X.push(this.calculateFeatureVector(url, label));
154
+ X.push(this.#calculateFeatureVector(url, label));
155
155
  Y.push(renderingType === 'static' ? 1 : 0);
156
156
  }
157
157
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlee/playwright",
3
- "version": "4.0.0-beta.211",
3
+ "version": "4.0.0-beta.212",
4
4
  "description": "The scalable web crawling and scraping library for JavaScript/Node.js. Enables development of data extraction and web automation jobs (not only) with headless Chrome and Puppeteer.",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"
@@ -49,13 +49,13 @@
49
49
  "dependencies": {
50
50
  "@apify/datastructures": "^3.0.1",
51
51
  "@apify/timeout": "^1.0.1",
52
- "@crawlee/basic": "4.0.0-beta.211",
53
- "@crawlee/browser": "4.0.0-beta.211",
54
- "@crawlee/browser-pool": "4.0.0-beta.211",
55
- "@crawlee/cheerio": "4.0.0-beta.211",
56
- "@crawlee/core": "4.0.0-beta.211",
57
- "@crawlee/types": "4.0.0-beta.211",
58
- "@crawlee/utils": "4.0.0-beta.211",
52
+ "@crawlee/basic": "4.0.0-beta.212",
53
+ "@crawlee/browser": "4.0.0-beta.212",
54
+ "@crawlee/browser-pool": "4.0.0-beta.212",
55
+ "@crawlee/cheerio": "4.0.0-beta.212",
56
+ "@crawlee/core": "4.0.0-beta.212",
57
+ "@crawlee/types": "4.0.0-beta.212",
58
+ "@crawlee/utils": "4.0.0-beta.212",
59
59
  "cheerio": "^1.0.0",
60
60
  "jquery": "^3.7.1",
61
61
  "ml-logistic-regression": "^2.0.0",
@@ -80,5 +80,5 @@
80
80
  }
81
81
  }
82
82
  },
83
- "gitHead": "17bab8ab5b98554ca2db1d09bc5f61c59194a760"
83
+ "gitHead": "171a63a5d7f2a6d74cbce58479204ac20d30b87e"
84
84
  }