@crawlee/browser 4.0.0-beta.99 → 4.0.0-rc.1
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.
- package/README.md +1 -1
- package/internals/browser-crawler.d.ts +172 -168
- package/internals/browser-crawler.js +149 -170
- package/internals/browser-launcher.d.ts +55 -23
- package/internals/browser-launcher.js +66 -16
- package/package.json +9 -10
|
@@ -1,6 +1,8 @@
|
|
|
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';
|
|
5
|
+
import { z } from 'zod';
|
|
4
6
|
export interface BrowserLaunchContext<TOptions, Launcher> extends BrowserPluginOptions<TOptions> {
|
|
5
7
|
/**
|
|
6
8
|
* URL to an HTTP proxy server. It must define the port number,
|
|
@@ -64,11 +66,25 @@ export interface BrowserLaunchContext<TOptions, Launcher> extends BrowserPluginO
|
|
|
64
66
|
*/
|
|
65
67
|
launcher?: Launcher;
|
|
66
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
|
+
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
|
+
type LauncherRemoteBrowserPoolOptions = Omit<RemoteBrowserPoolOptions, 'browserPlugins'>;
|
|
67
82
|
/**
|
|
68
83
|
* Abstract class for creating browser launchers, such as `PlaywrightLauncher` and `PuppeteerLauncher`.
|
|
69
84
|
* @ignore
|
|
70
85
|
*/
|
|
71
86
|
export declare abstract class BrowserLauncher<Plugin extends BrowserPlugin, Launcher = Plugin['library'], T extends Constructor<Plugin> = Constructor<Plugin>, LaunchOptions extends Dictionary<any> | undefined = Partial<Parameters<Plugin['launch']>[0]>, LaunchResult extends ReturnType<Plugin['launch']> = ReturnType<Plugin['launch']>> {
|
|
87
|
+
#private;
|
|
72
88
|
readonly configuration: Configuration;
|
|
73
89
|
launcher: Launcher;
|
|
74
90
|
proxyUrl?: string;
|
|
@@ -77,24 +93,30 @@ export declare abstract class BrowserLauncher<Plugin extends BrowserPlugin, Laun
|
|
|
77
93
|
otherLaunchContextProps: Dictionary;
|
|
78
94
|
Plugin: T;
|
|
79
95
|
userAgent?: string;
|
|
96
|
+
/**
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
80
99
|
protected static optionsShape: {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
90
|
-
ignoreProxyCertificate: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
|
|
91
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
92
|
-
userDataDir: import("ow").StringPredicate & import("ow").BasePredicate<string | undefined>;
|
|
93
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
94
|
-
launchOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
95
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
96
|
-
userAgent: import("ow").StringPredicate & import("ow").BasePredicate<string | undefined>;
|
|
100
|
+
proxyUrl: z.ZodOptional<z.ZodURL>;
|
|
101
|
+
useChrome: z.ZodOptional<z.ZodBoolean>;
|
|
102
|
+
useIncognitoPages: z.ZodOptional<z.ZodBoolean>;
|
|
103
|
+
browserPerProxy: z.ZodOptional<z.ZodBoolean>;
|
|
104
|
+
ignoreProxyCertificate: z.ZodOptional<z.ZodBoolean>;
|
|
105
|
+
userDataDir: z.ZodOptional<z.ZodString>;
|
|
106
|
+
launchOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
107
|
+
userAgent: z.ZodOptional<z.ZodString>;
|
|
97
108
|
};
|
|
109
|
+
/** @internal */
|
|
110
|
+
protected static optionsSchema: z.ZodObject<{
|
|
111
|
+
proxyUrl: z.ZodOptional<z.ZodURL>;
|
|
112
|
+
useChrome: z.ZodOptional<z.ZodBoolean>;
|
|
113
|
+
useIncognitoPages: z.ZodOptional<z.ZodBoolean>;
|
|
114
|
+
browserPerProxy: z.ZodOptional<z.ZodBoolean>;
|
|
115
|
+
ignoreProxyCertificate: z.ZodOptional<z.ZodBoolean>;
|
|
116
|
+
userDataDir: z.ZodOptional<z.ZodString>;
|
|
117
|
+
launchOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
118
|
+
userAgent: z.ZodOptional<z.ZodString>;
|
|
119
|
+
}, z.core.$strict>;
|
|
98
120
|
static requireLauncherOrThrow<T>(launcher: string, apifyImageName: string): T;
|
|
99
121
|
/**
|
|
100
122
|
* All `BrowserLauncher` parameters are passed via an launchContext object.
|
|
@@ -104,6 +126,21 @@ export declare abstract class BrowserLauncher<Plugin extends BrowserPlugin, Laun
|
|
|
104
126
|
* @ignore
|
|
105
127
|
*/
|
|
106
128
|
createBrowserPlugin(): Plugin;
|
|
129
|
+
/**
|
|
130
|
+
* Builds a {@link BrowserPool} running a single plugin for this launcher's browser. Shared body of the
|
|
131
|
+
* per-library `*BrowserPool()` factories, which exist so that configuring a pool never requires assembling
|
|
132
|
+
* a plugin by hand — and therefore never lets the plugin drift away from the crawler it is used with.
|
|
133
|
+
* @internal
|
|
134
|
+
*/
|
|
135
|
+
createBrowserPool(options?: LauncherBrowserPoolOptions): BrowserPool<{
|
|
136
|
+
browserPlugins: [Plugin];
|
|
137
|
+
}, [Plugin]>;
|
|
138
|
+
/**
|
|
139
|
+
* The {@link RemoteBrowserPool} counterpart of {@link BrowserLauncher.createBrowserPool}: the launcher
|
|
140
|
+
* supplies the plugin, the caller supplies the remote connection details.
|
|
141
|
+
* @internal
|
|
142
|
+
*/
|
|
143
|
+
createRemoteBrowserPool<Page>(options: LauncherRemoteBrowserPoolOptions): RemoteBrowserPool<Page>;
|
|
107
144
|
/**
|
|
108
145
|
* Launches a browser instance based on the plugin.
|
|
109
146
|
* @returns Browser instance.
|
|
@@ -111,10 +148,5 @@ export declare abstract class BrowserLauncher<Plugin extends BrowserPlugin, Laun
|
|
|
111
148
|
launch(): LaunchResult;
|
|
112
149
|
createLaunchOptions(): Dictionary;
|
|
113
150
|
protected getDefaultHeadlessOption(): boolean;
|
|
114
|
-
private getChromeExecutablePath;
|
|
115
|
-
/**
|
|
116
|
-
* Gets a typical path to Chrome executable, depending on the current operating system.
|
|
117
|
-
*/
|
|
118
|
-
private getTypicalChromeExecutablePath;
|
|
119
|
-
private validateProxyUrlProtocol;
|
|
120
151
|
}
|
|
152
|
+
export {};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { Configuration } from '@crawlee/basic';
|
|
5
|
-
import
|
|
4
|
+
import { Configuration, serviceLocator } from '@crawlee/basic';
|
|
5
|
+
import { BrowserPool, RemoteBrowserPool } from '@crawlee/browser-pool';
|
|
6
|
+
import { schemas } from '@crawlee/utils/internal';
|
|
7
|
+
import { z } from 'zod';
|
|
6
8
|
const DEFAULT_VIEWPORT = {
|
|
7
9
|
width: 1366,
|
|
8
10
|
height: 768,
|
|
@@ -22,16 +24,21 @@ export class BrowserLauncher {
|
|
|
22
24
|
// to be provided by child classes;
|
|
23
25
|
Plugin;
|
|
24
26
|
userAgent;
|
|
27
|
+
/**
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
25
30
|
static optionsShape = {
|
|
26
|
-
proxyUrl:
|
|
27
|
-
useChrome:
|
|
28
|
-
useIncognitoPages:
|
|
29
|
-
browserPerProxy:
|
|
30
|
-
ignoreProxyCertificate:
|
|
31
|
-
userDataDir:
|
|
32
|
-
launchOptions:
|
|
33
|
-
userAgent:
|
|
31
|
+
proxyUrl: z.url().optional(),
|
|
32
|
+
useChrome: z.boolean().optional(),
|
|
33
|
+
useIncognitoPages: z.boolean().optional(),
|
|
34
|
+
browserPerProxy: z.boolean().optional(),
|
|
35
|
+
ignoreProxyCertificate: z.boolean().optional(),
|
|
36
|
+
userDataDir: z.string().optional(),
|
|
37
|
+
launchOptions: schemas.anyObject.optional(),
|
|
38
|
+
userAgent: z.string().optional(),
|
|
34
39
|
};
|
|
40
|
+
/** @internal */
|
|
41
|
+
static optionsSchema = z.strictObject(BrowserLauncher.optionsShape);
|
|
35
42
|
static requireLauncherOrThrow(launcher, apifyImageName) {
|
|
36
43
|
try {
|
|
37
44
|
return require(launcher); // eslint-disable-line
|
|
@@ -54,7 +61,7 @@ export class BrowserLauncher {
|
|
|
54
61
|
constructor(launchContext, configuration = Configuration.getGlobalConfiguration()) {
|
|
55
62
|
this.configuration = configuration;
|
|
56
63
|
const { launcher, proxyUrl, useChrome, userAgent, launchOptions = {}, ...otherLaunchContextProps } = launchContext;
|
|
57
|
-
this
|
|
64
|
+
this.#validateProxyUrlProtocol(proxyUrl);
|
|
58
65
|
// those need to be reassigned otherwise they are {} in types
|
|
59
66
|
this.launcher = launcher;
|
|
60
67
|
this.proxyUrl = proxyUrl;
|
|
@@ -73,6 +80,49 @@ export class BrowserLauncher {
|
|
|
73
80
|
...this.otherLaunchContextProps,
|
|
74
81
|
});
|
|
75
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Builds a {@link BrowserPool} running a single plugin for this launcher's browser. Shared body of the
|
|
85
|
+
* per-library `*BrowserPool()` factories, which exist so that configuring a pool never requires assembling
|
|
86
|
+
* a plugin by hand — and therefore never lets the plugin drift away from the crawler it is used with.
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
createBrowserPool(options = {}) {
|
|
90
|
+
// The hook types `BrowserPool` derives from `Plugin` are unresolvable while `Plugin` is still a free type
|
|
91
|
+
// parameter, so the argument cannot be checked here. The concrete `*BrowserPool()` factories are where the
|
|
92
|
+
// caller-facing hook types get pinned down.
|
|
93
|
+
return new BrowserPool({
|
|
94
|
+
...this.#resolveFingerprinting(options),
|
|
95
|
+
browserPlugins: [this.createBrowserPlugin()],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The {@link RemoteBrowserPool} counterpart of {@link BrowserLauncher.createBrowserPool}: the launcher
|
|
100
|
+
* supplies the plugin, the caller supplies the remote connection details.
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
createRemoteBrowserPool(options) {
|
|
104
|
+
return new RemoteBrowserPool({
|
|
105
|
+
...options,
|
|
106
|
+
browserPlugins: [this.createBrowserPlugin()],
|
|
107
|
+
browserPoolOptions: this.#resolveFingerprinting(options.browserPoolOptions ?? {}),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* A custom `userAgent` and Crawlee's fingerprint injection would both write the same headers, so an
|
|
112
|
+
* explicitly requested user agent wins.
|
|
113
|
+
*/
|
|
114
|
+
#resolveFingerprinting(options) {
|
|
115
|
+
if (!this.userAgent) {
|
|
116
|
+
return options;
|
|
117
|
+
}
|
|
118
|
+
if (options.useFingerprints) {
|
|
119
|
+
serviceLocator
|
|
120
|
+
.getLogger()
|
|
121
|
+
.child({ prefix: 'BrowserLauncher' })
|
|
122
|
+
.info('Custom user agent provided, disabling automatic browser fingerprint injection!');
|
|
123
|
+
}
|
|
124
|
+
return { ...options, useFingerprints: false };
|
|
125
|
+
}
|
|
76
126
|
/**
|
|
77
127
|
* Launches a browser instance based on the plugin.
|
|
78
128
|
* @returns Browser instance.
|
|
@@ -98,20 +148,20 @@ export class BrowserLauncher {
|
|
|
98
148
|
launchOptions.headless = this.getDefaultHeadlessOption();
|
|
99
149
|
}
|
|
100
150
|
if (this.useChrome && !launchOptions.executablePath) {
|
|
101
|
-
launchOptions.executablePath = this
|
|
151
|
+
launchOptions.executablePath = this.#getChromeExecutablePath();
|
|
102
152
|
}
|
|
103
153
|
return launchOptions;
|
|
104
154
|
}
|
|
105
155
|
getDefaultHeadlessOption() {
|
|
106
156
|
return this.configuration.headless && !this.configuration.xvfb;
|
|
107
157
|
}
|
|
108
|
-
getChromeExecutablePath() {
|
|
109
|
-
return this.configuration.chromeExecutablePath ?? this
|
|
158
|
+
#getChromeExecutablePath() {
|
|
159
|
+
return this.configuration.chromeExecutablePath ?? this.#getTypicalChromeExecutablePath();
|
|
110
160
|
}
|
|
111
161
|
/**
|
|
112
162
|
* Gets a typical path to Chrome executable, depending on the current operating system.
|
|
113
163
|
*/
|
|
114
|
-
getTypicalChromeExecutablePath() {
|
|
164
|
+
#getTypicalChromeExecutablePath() {
|
|
115
165
|
/**
|
|
116
166
|
* Returns path of Chrome executable by its OS environment variable to deal with non-english language OS.
|
|
117
167
|
* Taking also into account the old [chrome 380177 issue](https://bugs.chromium.org/p/chromium/issues/detail?id=380177).
|
|
@@ -139,7 +189,7 @@ export class BrowserLauncher {
|
|
|
139
189
|
return '/usr/bin/google-chrome';
|
|
140
190
|
}
|
|
141
191
|
}
|
|
142
|
-
validateProxyUrlProtocol(proxyUrl) {
|
|
192
|
+
#validateProxyUrlProtocol(proxyUrl) {
|
|
143
193
|
if (!proxyUrl)
|
|
144
194
|
return;
|
|
145
195
|
if (!/^(http|https|socks4|socks5)/i.test(proxyUrl)) {
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/browser",
|
|
3
|
-
"version": "4.0.0-
|
|
3
|
+
"version": "4.0.0-rc.1",
|
|
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
|
-
"node": ">=22.
|
|
6
|
+
"node": ">=22.13.0"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
9
|
"exports": {
|
|
@@ -47,14 +47,13 @@
|
|
|
47
47
|
"access": "public"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@apify/timeout": "^0.
|
|
51
|
-
"@crawlee/basic": "4.0.0-
|
|
52
|
-
"@crawlee/browser-pool": "4.0.0-
|
|
53
|
-
"@crawlee/types": "4.0.0-
|
|
54
|
-
"@crawlee/utils": "4.0.0-
|
|
55
|
-
"ow": "^2.0.0",
|
|
50
|
+
"@apify/timeout": "^1.0.1",
|
|
51
|
+
"@crawlee/basic": "4.0.0-rc.1",
|
|
52
|
+
"@crawlee/browser-pool": "4.0.0-rc.1",
|
|
53
|
+
"@crawlee/types": "4.0.0-rc.1",
|
|
54
|
+
"@crawlee/utils": "4.0.0-rc.1",
|
|
56
55
|
"tslib": "^2.8.1",
|
|
57
|
-
"
|
|
56
|
+
"zod": "^4.5.4"
|
|
58
57
|
},
|
|
59
58
|
"peerDependencies": {
|
|
60
59
|
"playwright": "*",
|
|
@@ -75,5 +74,5 @@
|
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
},
|
|
78
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "f354ca5e943bed1a5c657a1fce1974c053f9fff0"
|
|
79
78
|
}
|