@crawlee/browser 4.0.0-beta.125 → 4.0.0-beta.127
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AddRequestsBatchedResult, BasicCrawlerOptions, CrawlingContext, EnqueueLinksOptions, ErrorHandler, ExtractLinksOptions, GetUserDataFromRequest, LoadedRequest, Request, RequestHandler, RouterHandler } from '@crawlee/basic';
|
|
2
2
|
import { BasicCrawler, ContextPipeline } from '@crawlee/basic';
|
|
3
|
-
import type {
|
|
3
|
+
import type { CommonPage, CrawlerRemoteBrowserOptions } from '@crawlee/browser-pool';
|
|
4
4
|
import type { Awaitable, Dictionary, IBrowserPool } from '@crawlee/types';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import type { BrowserLaunchContext } from './browser-launcher.js';
|
|
@@ -9,6 +9,20 @@ interface BaseResponse {
|
|
|
9
9
|
/** Optional because only Playwright and Puppeteer responses are guaranteed to carry it. */
|
|
10
10
|
headers?(): Record<string, string>;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* The type of a browser pool the crawler builds (and therefore owns) for itself. It's an {@link IBrowserPool} that
|
|
14
|
+
* additionally exposes `destroy()` — the crawler only ever tears down pools it created, which is why {@link IBrowserPool}
|
|
15
|
+
* itself intentionally omits `destroy`.
|
|
16
|
+
*/
|
|
17
|
+
export type OwnedBrowserPool<Page> = IBrowserPool<Page> & {
|
|
18
|
+
destroy: () => Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Rejects options that exist only to configure the browser pool the crawler would have built for itself.
|
|
22
|
+
* Accepting them alongside a pre-built `browserPool` and quietly ignoring them is how `browserPoolOptions` grew
|
|
23
|
+
* into a second, half-working way of configuring the same pool.
|
|
24
|
+
*/
|
|
25
|
+
export declare function assertBrowserPoolNotConfigured(crawlerName: string, ignoredOptions: Dictionary): void;
|
|
12
26
|
export interface BrowserCrawlingContext<Page extends CommonPage = CommonPage, Response extends BaseResponse = BaseResponse, UserData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
|
|
13
27
|
GoToOptions extends Dictionary = Dictionary> extends CrawlingContext<UserData> {
|
|
14
28
|
/**
|
|
@@ -38,12 +52,20 @@ GoToOptions extends Dictionary = Dictionary> extends CrawlingContext<UserData> {
|
|
|
38
52
|
enqueueLinks: (options?: EnqueueLinksOptions) => Promise<AddRequestsBatchedResult>;
|
|
39
53
|
}
|
|
40
54
|
export type BrowserHook<Context = BrowserCrawlingContext, ContextExtension = {}> = (crawlingContext: Context & ContextExtension) => Awaitable<void | Partial<Context>>;
|
|
41
|
-
export interface BrowserCrawlerOptions<Page extends CommonPage = CommonPage, Response extends BaseResponse = BaseResponse, Context extends BrowserCrawlingContext<Page, Response> = BrowserCrawlingContext<Page, Response>, ContextExtension = Dictionary<never>, ExtendedContext extends Context = Context & ContextExtension,
|
|
55
|
+
export interface BrowserCrawlerOptions<Page extends CommonPage = CommonPage, Response extends BaseResponse = BaseResponse, Context extends BrowserCrawlingContext<Page, Response> = BrowserCrawlingContext<Page, Response>, ContextExtension = Dictionary<never>, ExtendedContext extends Context = Context & ContextExtension, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>, StatisticStateExtension extends object = {}> extends Omit<BasicCrawlerOptions<Context, ContextExtension, ExtendedContext, Routes, StatisticStateExtension>, 'requestHandler' | 'failedRequestHandler' | 'errorHandler'> {
|
|
42
56
|
launchContext?: BrowserLaunchContext<any, any>;
|
|
43
57
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
58
|
+
* The browser pool the crawler should serve its pages from. This is the single way to run a pool with
|
|
59
|
+
* non-default options: build one with the factory that matches your crawler
|
|
60
|
+
* ({@link playwrightBrowserPool}, {@link puppeteerBrowserPool}, {@link stagehandBrowserPool}) - it
|
|
61
|
+
* accepts every {@link BrowserPoolOptions|`BrowserPool` option} and supplies the correct browser plugin
|
|
62
|
+
* itself, so the pool can never mismatch the crawler.
|
|
63
|
+
*
|
|
64
|
+
* A pool passed in this way is borrowed, not owned: the crawler will not tear it down, which is what makes it
|
|
65
|
+
* shareable across crawlers. Since the crawler then builds nothing itself, the options that configure its own
|
|
66
|
+
* pool (`launchContext`, `headless`, `remoteBrowser`) are rejected rather than silently ignored.
|
|
67
|
+
*
|
|
68
|
+
* When omitted, the crawler builds - and tears down - a default pool for its own browser.
|
|
47
69
|
*/
|
|
48
70
|
browserPool?: IBrowserPool<Page>;
|
|
49
71
|
/**
|
|
@@ -54,8 +76,9 @@ export interface BrowserCrawlerOptions<Page extends CommonPage = CommonPage, Res
|
|
|
54
76
|
* crawler. Supply the connection details only: a static `endpoint` URL, a function returning one per launch,
|
|
55
77
|
* or a {@link RemoteBrowserProvider}.
|
|
56
78
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
79
|
+
* Cannot be combined with `browserPool`. To tune the pool wrapping the remote connection, or to share it
|
|
80
|
+
* across crawlers, build it with the remote factory for your crawler ({@link remotePlaywrightBrowserPool},
|
|
81
|
+
* {@link remotePuppeteerBrowserPool}, {@link remoteStagehandBrowserPool}) and pass it as `browserPool`.
|
|
59
82
|
*/
|
|
60
83
|
remoteBrowser?: CrawlerRemoteBrowserOptions;
|
|
61
84
|
/**
|
|
@@ -106,11 +129,6 @@ export interface BrowserCrawlerOptions<Page extends CommonPage = CommonPage, Res
|
|
|
106
129
|
* represents the last error thrown during processing of the request.
|
|
107
130
|
*/
|
|
108
131
|
failedRequestHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
|
|
109
|
-
/**
|
|
110
|
-
* Custom options passed to the underlying {@link BrowserPool} constructor.
|
|
111
|
-
* We can tweak those to fine-tune browser management.
|
|
112
|
-
*/
|
|
113
|
-
browserPoolOptions?: Partial<BrowserPoolOptions> & Partial<BrowserPoolHooks<__BrowserControllerReturn, __LaunchContextReturn>>;
|
|
114
132
|
/**
|
|
115
133
|
* Async functions that are sequentially evaluated before the navigation. Good for setting additional cookies
|
|
116
134
|
* or browser properties before navigation. The function receives the `crawlingContext`; the options object
|
|
@@ -175,11 +193,6 @@ export interface BrowserCrawlerOptions<Page extends CommonPage = CommonPage, Res
|
|
|
175
193
|
* Defines whether the cookies should be persisted for sessions. Enabled by default.
|
|
176
194
|
*/
|
|
177
195
|
saveResponseCookies?: boolean;
|
|
178
|
-
/**
|
|
179
|
-
* Whether to run browser in headless mode. Defaults to `true`.
|
|
180
|
-
* Can be also set via {@link Configuration}.
|
|
181
|
-
*/
|
|
182
|
-
headless?: boolean | 'new' | 'old';
|
|
183
196
|
/**
|
|
184
197
|
* Whether to ignore custom elements (and their #shadow-roots) when processing the page content via `parseWithCheerio` helper.
|
|
185
198
|
* By default, they are expanded automatically. Use this option to disable this behavior.
|
|
@@ -231,7 +244,7 @@ export interface BrowserCrawlerOptions<Page extends CommonPage = CommonPage, Res
|
|
|
231
244
|
*
|
|
232
245
|
* @category Crawlers
|
|
233
246
|
*/
|
|
234
|
-
export declare abstract class BrowserCrawler<Page extends CommonPage = CommonPage, Response extends BaseResponse = BaseResponse,
|
|
247
|
+
export declare abstract class BrowserCrawler<Page extends CommonPage = CommonPage, Response extends BaseResponse = BaseResponse, LaunchOptions extends Dictionary | undefined = Dictionary, Context extends BrowserCrawlingContext<Page, Response> = BrowserCrawlingContext<Page, Response>, ContextExtension = Dictionary<never>, ExtendedContext extends Context = Context & ContextExtension, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>, StatisticStateExtension extends object = {}, GoToOptions extends Dictionary = Dictionary> extends BasicCrawler<Context, ContextExtension, ExtendedContext, Routes, StatisticStateExtension> {
|
|
235
248
|
#private;
|
|
236
249
|
/**
|
|
237
250
|
* A reference to the underlying browser pool that manages the crawler's browsers. Typed as
|
|
@@ -246,10 +259,9 @@ export declare abstract class BrowserCrawler<Page extends CommonPage = CommonPag
|
|
|
246
259
|
preNavigationHooks: z.ZodDefault<z.ZodCustom<unknown[], unknown[]>>;
|
|
247
260
|
postNavigationHooks: z.ZodDefault<z.ZodCustom<unknown[], unknown[]>>;
|
|
248
261
|
launchContext: z.ZodDefault<z.ZodCustom<Dictionary, Dictionary>>;
|
|
249
|
-
headless: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
|
|
250
262
|
browserPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
263
|
+
browserPoolBuilder: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
251
264
|
remoteBrowser: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
252
|
-
browserPoolOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
253
265
|
saveResponseCookies: z.ZodDefault<z.ZodBoolean>;
|
|
254
266
|
proxyConfiguration: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
255
267
|
ignoreIframes: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -304,10 +316,9 @@ export declare abstract class BrowserCrawler<Page extends CommonPage = CommonPag
|
|
|
304
316
|
preNavigationHooks: z.ZodDefault<z.ZodCustom<unknown[], unknown[]>>;
|
|
305
317
|
postNavigationHooks: z.ZodDefault<z.ZodCustom<unknown[], unknown[]>>;
|
|
306
318
|
launchContext: z.ZodDefault<z.ZodCustom<Dictionary, Dictionary>>;
|
|
307
|
-
headless: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
|
|
308
319
|
browserPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
320
|
+
browserPoolBuilder: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
309
321
|
remoteBrowser: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
310
|
-
browserPoolOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
311
322
|
saveResponseCookies: z.ZodDefault<z.ZodBoolean>;
|
|
312
323
|
proxyConfiguration: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
313
324
|
ignoreIframes: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -360,8 +371,14 @@ export declare abstract class BrowserCrawler<Page extends CommonPage = CommonPag
|
|
|
360
371
|
/**
|
|
361
372
|
* All `BrowserCrawler` parameters are passed via an options object.
|
|
362
373
|
*/
|
|
363
|
-
protected constructor(options: BrowserCrawlerOptions<Page, Response, Context, ContextExtension, ExtendedContext,
|
|
374
|
+
protected constructor(options: BrowserCrawlerOptions<Page, Response, Context, ContextExtension, ExtendedContext, Routes, StatisticStateExtension> & {
|
|
364
375
|
contextPipelineBuilder: () => ContextPipeline<CrawlingContext, Context>;
|
|
376
|
+
/**
|
|
377
|
+
* Builds the pool the crawler owns, used only when the user injected no `browserPool`. Supplied by the
|
|
378
|
+
* concrete crawler, which is the only place that knows which browser plugin to run - that is also why
|
|
379
|
+
* `remoteBrowser` is handed to it rather than acted upon here.
|
|
380
|
+
*/
|
|
381
|
+
browserPoolBuilder: (remoteBrowser?: CrawlerRemoteBrowserOptions) => OwnedBrowserPool<Page>;
|
|
365
382
|
});
|
|
366
383
|
protected getNavigationTimeoutMillis(): number;
|
|
367
384
|
protected buildContextPipeline(): ContextPipeline<CrawlingContext, BrowserCrawlingContext<Page, Response, Dictionary>>;
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { BasicCrawler, browserPoolCookieToToughCookie, ContextPipeline, cookieStringToToughCookie, EnqueueStrategy, NavigationSkippedError, OwnedOrInjected, parseArgument, remainingNavigationWindowMillis, RequestState, RequestThrottledError, resolveBaseUrlForEnqueueLinksFiltering, schemas, SessionError, toughCookieToBrowserPoolCookie, tryAbsoluteURL, validators, } from '@crawlee/basic';
|
|
2
|
-
import { BrowserPool, RemoteBrowserPool } from '@crawlee/browser-pool';
|
|
3
2
|
import { CLOUDFLARE_RETRY_CSS_SELECTORS, RETRY_CSS_SELECTORS } from '@crawlee/utils/internal';
|
|
4
3
|
import { sleep } from '@crawlee/utils';
|
|
5
4
|
import { z } from 'zod';
|
|
6
5
|
import { addTimeoutToPromise, TimeoutError, tryCancel } from '@apify/timeout';
|
|
6
|
+
/**
|
|
7
|
+
* Rejects options that exist only to configure the browser pool the crawler would have built for itself.
|
|
8
|
+
* Accepting them alongside a pre-built `browserPool` and quietly ignoring them is how `browserPoolOptions` grew
|
|
9
|
+
* into a second, half-working way of configuring the same pool.
|
|
10
|
+
*/
|
|
11
|
+
export function assertBrowserPoolNotConfigured(crawlerName, ignoredOptions) {
|
|
12
|
+
const names = Object.keys(ignoredOptions).filter((name) => ignoredOptions[name] !== undefined);
|
|
13
|
+
if (names.length === 0) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
throw new Error(`${crawlerName}: ${names.map((name) => `\`${name}\``).join(', ')} cannot be combined with \`browserPool\`, ` +
|
|
17
|
+
`${names.length > 1 ? 'they configure' : 'it configures'} the browser pool the crawler would build for ` +
|
|
18
|
+
'itself. Configure the pool you pass in instead.');
|
|
19
|
+
}
|
|
7
20
|
const COOKIES_BEFORE_HOOKS = Symbol('cookiesBeforeHooks');
|
|
8
21
|
const readContextField = (ctx, key) => ctx[key];
|
|
9
22
|
/**
|
|
@@ -82,10 +95,9 @@ export class BrowserCrawler extends BasicCrawler {
|
|
|
82
95
|
preNavigationHooks: schemas.anyArray.default(() => []),
|
|
83
96
|
postNavigationHooks: schemas.anyArray.default(() => []),
|
|
84
97
|
launchContext: schemas.anyObject.default(() => ({})),
|
|
85
|
-
headless: z.union([z.boolean(), z.string()]).optional(),
|
|
86
98
|
browserPool: validators.browserPool.optional(),
|
|
99
|
+
browserPoolBuilder: schemas.anyFunction.optional(),
|
|
87
100
|
remoteBrowser: schemas.anyObject.optional(),
|
|
88
|
-
browserPoolOptions: schemas.anyObject.optional(),
|
|
89
101
|
saveResponseCookies: z.boolean().default(true),
|
|
90
102
|
proxyConfiguration: validators.proxyConfiguration.optional(),
|
|
91
103
|
ignoreIframes: z.boolean().default(false),
|
|
@@ -96,7 +108,10 @@ export class BrowserCrawler extends BasicCrawler {
|
|
|
96
108
|
* All `BrowserCrawler` parameters are passed via an options object.
|
|
97
109
|
*/
|
|
98
110
|
constructor(options) {
|
|
99
|
-
const { navigationTimeoutSecs, saveResponseCookies, launchContext, browserPool, remoteBrowser,
|
|
111
|
+
const { navigationTimeoutSecs, saveResponseCookies, launchContext, browserPool, remoteBrowser, preNavigationHooks, postNavigationHooks, ignoreIframes, ignoreShadowRoots, contextPipelineBuilder, browserPoolBuilder, extendContext, ...basicCrawlerOptions } = parseArgument(options, BrowserCrawler.optionsSchema, 'BrowserCrawlerOptions');
|
|
112
|
+
if (browserPool) {
|
|
113
|
+
assertBrowserPoolNotConfigured(new.target.name, { remoteBrowser });
|
|
114
|
+
}
|
|
100
115
|
const skipGuard = (action) => ({
|
|
101
116
|
action: async (ctx) => (ctx.request.skipNavigation ? {} : ((await action(ctx)) ?? {})),
|
|
102
117
|
});
|
|
@@ -138,39 +153,8 @@ export class BrowserCrawler extends BasicCrawler {
|
|
|
138
153
|
this.#postNavigationHooks = postNavigationHooks;
|
|
139
154
|
this.ignoreIframes = ignoreIframes;
|
|
140
155
|
this.ignoreShadowRoots = ignoreShadowRoots;
|
|
141
|
-
if (headless != null) {
|
|
142
|
-
this.launchContext.launchOptions ??= {};
|
|
143
|
-
this.launchContext.launchOptions.headless = headless;
|
|
144
|
-
}
|
|
145
156
|
this.#saveResponseCookies = saveResponseCookies;
|
|
146
|
-
|
|
147
|
-
// The default is only built when no pool was injected, so all the option/launchContext fiddling below stays
|
|
148
|
-
// inside the factory.
|
|
149
|
-
this.#browserPoolDep = OwnedOrInjected.resolve(browserPool, () => {
|
|
150
|
-
const resolvedBrowserPoolOptions = browserPoolOptions ?? {};
|
|
151
|
-
if (launchContext?.userAgent) {
|
|
152
|
-
if (resolvedBrowserPoolOptions.useFingerprints)
|
|
153
|
-
this.log.info('Custom user agent provided, disabling automatic browser fingerprint injection!');
|
|
154
|
-
resolvedBrowserPoolOptions.useFingerprints = false;
|
|
155
|
-
}
|
|
156
|
-
if (remoteBrowser) {
|
|
157
|
-
// The crawler already built the right plugin for its browser — hand it to a RemoteBrowserPool so the
|
|
158
|
-
// remote connection is always for the matching browser (no plugin to construct, no way to mismatch).
|
|
159
|
-
const { browserPlugins, ...remoteBrowserPoolOptions } = resolvedBrowserPoolOptions;
|
|
160
|
-
return new RemoteBrowserPool({
|
|
161
|
-
browserPlugins: browserPlugins,
|
|
162
|
-
...remoteBrowser,
|
|
163
|
-
browserPoolOptions: remoteBrowserPoolOptions,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
// Double cast: `BrowserPool` implements `IBrowserPool<PageReturn>`, where `PageReturn` is derived from the
|
|
167
|
-
// plugin/controller generics and doesn't overlap with the crawler's free `Page` type param, so TS won't
|
|
168
|
-
// narrow it directly. The concrete pool does satisfy the `Page`/`destroy` contract at runtime — this is the
|
|
169
|
-
// long-standing `Page` variance gap, not a `destroy`-related hole.
|
|
170
|
-
return new BrowserPool({
|
|
171
|
-
...resolvedBrowserPoolOptions,
|
|
172
|
-
});
|
|
173
|
-
});
|
|
157
|
+
this.#browserPoolDep = OwnedOrInjected.resolve(browserPool, () => browserPoolBuilder(remoteBrowser));
|
|
174
158
|
}
|
|
175
159
|
getNavigationTimeoutMillis() {
|
|
176
160
|
return this.#navigationTimeoutMillis;
|
|
@@ -415,7 +399,7 @@ export class BrowserCrawler extends BasicCrawler {
|
|
|
415
399
|
const { session, request, page } = crawlingContext;
|
|
416
400
|
if (typeof response === 'object' && typeof response.status === 'function') {
|
|
417
401
|
const status = response.status();
|
|
418
|
-
this.
|
|
402
|
+
this.statistics.registerStatusCode(status);
|
|
419
403
|
// Ahead of the error-status throw below: a 429 the user opted into treating as an error is still a
|
|
420
404
|
// rate limit the domain should back off from.
|
|
421
405
|
if (status === 429) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Configuration } from '@crawlee/basic';
|
|
2
|
-
import type { BrowserPlugin, BrowserPluginOptions } from '@crawlee/browser-pool';
|
|
2
|
+
import type { BrowserPlugin, BrowserPluginOptions, BrowserPoolHooks, BrowserPoolOptions, RemoteBrowserPoolOptions } from '@crawlee/browser-pool';
|
|
3
|
+
import { BrowserPool, RemoteBrowserPool } from '@crawlee/browser-pool';
|
|
3
4
|
import type { Constructor, Dictionary } from '@crawlee/types';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
export interface BrowserLaunchContext<TOptions, Launcher> extends BrowserPluginOptions<TOptions> {
|
|
@@ -65,6 +66,19 @@ export interface BrowserLaunchContext<TOptions, Launcher> extends BrowserPluginO
|
|
|
65
66
|
*/
|
|
66
67
|
launcher?: Launcher;
|
|
67
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* The {@link BrowserPool} options a launcher-built pool accepts: everything the pool itself takes except
|
|
71
|
+
* `browserPlugins`, which the launcher derives from its launch context. The hooks are deliberately unconstrained -
|
|
72
|
+
* the browser they run against is only known to the concrete `*BrowserPool()` factory, which is where the
|
|
73
|
+
* caller-facing types are pinned down.
|
|
74
|
+
*/
|
|
75
|
+
export type LauncherBrowserPoolOptions = Omit<BrowserPoolOptions, 'browserPlugins'> & {
|
|
76
|
+
[Hook in keyof BrowserPoolHooks<any, any, any>]?: readonly ((...args: any[]) => unknown)[];
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* The {@link RemoteBrowserPool} counterpart of {@link LauncherBrowserPoolOptions}.
|
|
80
|
+
*/
|
|
81
|
+
export type LauncherRemoteBrowserPoolOptions = Omit<RemoteBrowserPoolOptions, 'browserPlugins'>;
|
|
68
82
|
/**
|
|
69
83
|
* Abstract class for creating browser launchers, such as `PlaywrightLauncher` and `PuppeteerLauncher`.
|
|
70
84
|
* @ignore
|
|
@@ -107,6 +121,26 @@ export declare abstract class BrowserLauncher<Plugin extends BrowserPlugin, Laun
|
|
|
107
121
|
* @ignore
|
|
108
122
|
*/
|
|
109
123
|
createBrowserPlugin(): Plugin;
|
|
124
|
+
/**
|
|
125
|
+
* Builds a {@link BrowserPool} running a single plugin for this launcher's browser. Shared body of the
|
|
126
|
+
* per-library `*BrowserPool()` factories, which exist so that configuring a pool never requires assembling
|
|
127
|
+
* a plugin by hand — and therefore never lets the plugin drift away from the crawler it is used with.
|
|
128
|
+
* @internal
|
|
129
|
+
*/
|
|
130
|
+
createBrowserPool(options?: LauncherBrowserPoolOptions): BrowserPool<{
|
|
131
|
+
browserPlugins: [Plugin];
|
|
132
|
+
}, [Plugin]>;
|
|
133
|
+
/**
|
|
134
|
+
* The {@link RemoteBrowserPool} counterpart of {@link BrowserLauncher.createBrowserPool}: the launcher
|
|
135
|
+
* supplies the plugin, the caller supplies the remote connection details.
|
|
136
|
+
* @internal
|
|
137
|
+
*/
|
|
138
|
+
createRemoteBrowserPool<Page>(options: LauncherRemoteBrowserPoolOptions): RemoteBrowserPool<Page>;
|
|
139
|
+
/**
|
|
140
|
+
* A custom `userAgent` and Crawlee's fingerprint injection would both write the same headers, so an
|
|
141
|
+
* explicitly requested user agent wins.
|
|
142
|
+
*/
|
|
143
|
+
private resolveFingerprinting;
|
|
110
144
|
/**
|
|
111
145
|
* Launches a browser instance based on the plugin.
|
|
112
146
|
* @returns Browser instance.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { Configuration, schemas } from '@crawlee/basic';
|
|
4
|
+
import { Configuration, schemas, serviceLocator } from '@crawlee/basic';
|
|
5
|
+
import { BrowserPool, RemoteBrowserPool } from '@crawlee/browser-pool';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
const DEFAULT_VIEWPORT = {
|
|
7
8
|
width: 1366,
|
|
@@ -74,6 +75,49 @@ export class BrowserLauncher {
|
|
|
74
75
|
...this.otherLaunchContextProps,
|
|
75
76
|
});
|
|
76
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Builds a {@link BrowserPool} running a single plugin for this launcher's browser. Shared body of the
|
|
80
|
+
* per-library `*BrowserPool()` factories, which exist so that configuring a pool never requires assembling
|
|
81
|
+
* a plugin by hand — and therefore never lets the plugin drift away from the crawler it is used with.
|
|
82
|
+
* @internal
|
|
83
|
+
*/
|
|
84
|
+
createBrowserPool(options = {}) {
|
|
85
|
+
// The hook types `BrowserPool` derives from `Plugin` are unresolvable while `Plugin` is still a free type
|
|
86
|
+
// parameter, so the argument cannot be checked here. The concrete `*BrowserPool()` factories are where the
|
|
87
|
+
// caller-facing hook types get pinned down.
|
|
88
|
+
return new BrowserPool({
|
|
89
|
+
...this.resolveFingerprinting(options),
|
|
90
|
+
browserPlugins: [this.createBrowserPlugin()],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The {@link RemoteBrowserPool} counterpart of {@link BrowserLauncher.createBrowserPool}: the launcher
|
|
95
|
+
* supplies the plugin, the caller supplies the remote connection details.
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
createRemoteBrowserPool(options) {
|
|
99
|
+
return new RemoteBrowserPool({
|
|
100
|
+
...options,
|
|
101
|
+
browserPlugins: [this.createBrowserPlugin()],
|
|
102
|
+
browserPoolOptions: this.resolveFingerprinting(options.browserPoolOptions ?? {}),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* A custom `userAgent` and Crawlee's fingerprint injection would both write the same headers, so an
|
|
107
|
+
* explicitly requested user agent wins.
|
|
108
|
+
*/
|
|
109
|
+
resolveFingerprinting(options) {
|
|
110
|
+
if (!this.userAgent) {
|
|
111
|
+
return options;
|
|
112
|
+
}
|
|
113
|
+
if (options.useFingerprints) {
|
|
114
|
+
serviceLocator
|
|
115
|
+
.getLogger()
|
|
116
|
+
.child({ prefix: 'BrowserLauncher' })
|
|
117
|
+
.info('Custom user agent provided, disabling automatic browser fingerprint injection!');
|
|
118
|
+
}
|
|
119
|
+
return { ...options, useFingerprints: false };
|
|
120
|
+
}
|
|
77
121
|
/**
|
|
78
122
|
* Launches a browser instance based on the plugin.
|
|
79
123
|
* @returns Browser instance.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/browser",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.127",
|
|
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"
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@apify/timeout": "^0.4.4",
|
|
51
|
-
"@crawlee/basic": "4.0.0-beta.
|
|
52
|
-
"@crawlee/browser-pool": "4.0.0-beta.
|
|
53
|
-
"@crawlee/types": "4.0.0-beta.
|
|
54
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
51
|
+
"@crawlee/basic": "4.0.0-beta.127",
|
|
52
|
+
"@crawlee/browser-pool": "4.0.0-beta.127",
|
|
53
|
+
"@crawlee/types": "4.0.0-beta.127",
|
|
54
|
+
"@crawlee/utils": "4.0.0-beta.127",
|
|
55
55
|
"tslib": "^2.8.1",
|
|
56
56
|
"type-fest": "^4.41.0",
|
|
57
57
|
"zod": "^4.4.3"
|
|
@@ -75,5 +75,5 @@
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
|
-
"gitHead": "
|
|
78
|
+
"gitHead": "4aa4a4d8d105bb530649ac5cd9197a3169106bd9"
|
|
79
79
|
}
|