@crawlee/playwright 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/index.d.ts +3 -1
- package/index.js +2 -1
- package/internals/adaptive-playwright-crawler.d.ts +62 -72
- package/internals/adaptive-playwright-crawler.js +306 -232
- package/internals/enqueue-links/click-elements.d.ts +18 -54
- package/internals/enqueue-links/click-elements.js +33 -55
- package/internals/playwright-browser-pool.d.ts +71 -0
- package/internals/playwright-browser-pool.js +61 -0
- package/internals/playwright-crawler.d.ts +136 -153
- package/internals/playwright-crawler.js +38 -40
- package/internals/playwright-launcher.d.ts +26 -17
- package/internals/playwright-launcher.js +13 -7
- package/internals/utils/playwright-utils.d.ts +8 -39
- package/internals/utils/playwright-utils.js +60 -96
- package/internals/utils/rendering-type-prediction.d.ts +23 -10
- package/internals/utils/rendering-type-prediction.js +68 -43
- package/package.json +13 -19
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IRequestManager, RequestTransform, SkippedRequestCallback, UrlPatternInput } from '@crawlee/browser';
|
|
2
2
|
import type { BatchAddRequestsResult, Dictionary } from '@crawlee/types';
|
|
3
3
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
4
4
|
import type { Page } from 'playwright';
|
|
@@ -26,64 +26,29 @@ export interface EnqueueLinksByClickingElementsOptions {
|
|
|
26
26
|
*/
|
|
27
27
|
clickOptions?: ClickOptions;
|
|
28
28
|
/**
|
|
29
|
-
* An array of
|
|
30
|
-
* containing glob pattern strings matching the URLs to be enqueued.
|
|
29
|
+
* An array of URL patterns that URLs must match to be enqueued.
|
|
31
30
|
*
|
|
32
|
-
*
|
|
33
|
-
* All remaining keys will be used as request options for the corresponding enqueued {@link Request} objects.
|
|
34
|
-
*
|
|
35
|
-
* The matching is always case-insensitive.
|
|
36
|
-
* If you need case-sensitive matching, use `regexps` property directly.
|
|
37
|
-
*
|
|
38
|
-
* If `globs` is an empty array or `undefined`, then the function
|
|
39
|
-
* enqueues all the intercepted navigation requests produced by the page
|
|
40
|
-
* after clicking on elements matching the provided CSS selector.
|
|
41
|
-
*/
|
|
42
|
-
globs?: GlobInput[];
|
|
43
|
-
/**
|
|
44
|
-
* An array of glob pattern strings, regexp patterns or plain objects
|
|
45
|
-
* containing patterns matching URLs that will **never** be enqueued.
|
|
46
|
-
*
|
|
47
|
-
* The plain objects must include either the `glob` property or the `regexp` property.
|
|
31
|
+
* Accepts glob pattern strings, `{ glob: string }` objects, `RegExp` instances, or `{ regexp: RegExp }` objects.
|
|
48
32
|
*
|
|
49
33
|
* Glob matching is always case-insensitive.
|
|
50
|
-
* If you need case-sensitive matching,
|
|
51
|
-
*/
|
|
52
|
-
exclude?: readonly (GlobInput | RegExpInput)[];
|
|
53
|
-
/**
|
|
54
|
-
* An array of regular expressions or plain objects
|
|
55
|
-
* containing regular expressions matching the URLs to be enqueued.
|
|
56
|
-
*
|
|
57
|
-
* The plain objects must include at least the `regexp` property, which holds the regular expression.
|
|
58
|
-
* All remaining keys will be used as request options for the corresponding enqueued {@link Request} objects.
|
|
34
|
+
* If you need case-sensitive matching, use a `RegExp`.
|
|
59
35
|
*
|
|
60
|
-
* If `
|
|
36
|
+
* If `include` is an empty array or `undefined`, then the function
|
|
61
37
|
* enqueues all the intercepted navigation requests produced by the page
|
|
62
38
|
* after clicking on elements matching the provided CSS selector.
|
|
63
39
|
*/
|
|
64
|
-
|
|
40
|
+
include?: UrlPatternInput[];
|
|
65
41
|
/**
|
|
66
|
-
*
|
|
67
|
-
* Please use `globs` or `regexps` instead.
|
|
68
|
-
*
|
|
69
|
-
* An array of {@link PseudoUrl} strings or plain objects
|
|
70
|
-
* containing {@link PseudoUrl} strings matching the URLs to be enqueued.
|
|
71
|
-
*
|
|
72
|
-
* The plain objects must include at least the `purl` property, which holds the pseudo-URL pattern string.
|
|
73
|
-
* All remaining keys will be used as request options for the corresponding enqueued {@link Request} objects.
|
|
42
|
+
* An array of URL patterns. Matching URLs will **not** be enqueued.
|
|
74
43
|
*
|
|
75
|
-
*
|
|
76
|
-
* If you need case-sensitive matching, use `regexps` property directly.
|
|
44
|
+
* Accepts glob pattern strings, `{ glob: string }` objects, `RegExp` instances, or `{ regexp: RegExp }` objects.
|
|
77
45
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* after clicking on elements matching the provided CSS selector.
|
|
81
|
-
*
|
|
82
|
-
* @deprecated prefer using `globs` or `regexps` instead
|
|
46
|
+
* Glob matching is always case-insensitive.
|
|
47
|
+
* If you need case-sensitive matching, use a `RegExp`.
|
|
83
48
|
*/
|
|
84
|
-
|
|
49
|
+
exclude?: readonly UrlPatternInput[];
|
|
85
50
|
/**
|
|
86
|
-
* After
|
|
51
|
+
* After request options are filtered by `include`/`exclude` patterns,
|
|
87
52
|
* this function can be used to remove them or modify their contents such as `userData`, `payload` or, most importantly
|
|
88
53
|
* `uniqueKey`. This is useful when you need to enqueue multiple `Requests` to the queue that share the same URL,
|
|
89
54
|
* but differ in methods or payloads, or to dynamically update or create `userData`.
|
|
@@ -98,8 +63,8 @@ export interface EnqueueLinksByClickingElementsOptions {
|
|
|
98
63
|
* }
|
|
99
64
|
* ```
|
|
100
65
|
*
|
|
101
|
-
* Note that `transformRequestFunction` has the highest priority and can overwrite
|
|
102
|
-
*
|
|
66
|
+
* Note that `transformRequestFunction` has the highest priority and can overwrite
|
|
67
|
+
* the global `label` option.
|
|
103
68
|
*
|
|
104
69
|
* The function receives a {@link RequestOptions} object and can return either:
|
|
105
70
|
* - The modified {@link RequestOptions} object
|
|
@@ -160,8 +125,7 @@ export interface EnqueueLinksByClickingElementsOptions {
|
|
|
160
125
|
* in `href` elements, but rather navigations are triggered in click handlers.
|
|
161
126
|
* If you're looking to find URLs in `href` attributes of the page, see {@link enqueueLinks}.
|
|
162
127
|
*
|
|
163
|
-
* Optionally, the function allows you to filter the target links' URLs using an array of
|
|
164
|
-
* and override settings of the enqueued {@link Request} objects.
|
|
128
|
+
* Optionally, the function allows you to filter the target links' URLs using an array of glob or regexp patterns.
|
|
165
129
|
*
|
|
166
130
|
* **IMPORTANT**: To be able to do this, this function uses various mutations on the page,
|
|
167
131
|
* such as changing the Z-index of elements being clicked and their visibility. Therefore,
|
|
@@ -183,9 +147,9 @@ export interface EnqueueLinksByClickingElementsOptions {
|
|
|
183
147
|
* page,
|
|
184
148
|
* requestManager,
|
|
185
149
|
* selector: 'a.product-detail',
|
|
186
|
-
*
|
|
187
|
-
* 'https://www.example.com/handbags
|
|
188
|
-
* 'https://www.example.com/purses
|
|
150
|
+
* include: [
|
|
151
|
+
* 'https://www.example.com/handbags/*',
|
|
152
|
+
* 'https://www.example.com/purses/*',
|
|
189
153
|
* ],
|
|
190
154
|
* });
|
|
191
155
|
* ```
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import { URL } from 'node:url';
|
|
2
|
-
import { applyRequestTransform,
|
|
3
|
-
import
|
|
2
|
+
import { applyRequestTransform, constructUrlPatternObjects, createRequestOptions, createSkippedRequestArgs, filterRequestOptionsByPatterns, urlPatternSchema, Request as CrawleeRequest, serviceLocator, } from '@crawlee/browser';
|
|
3
|
+
import { parseArgument, schemas } from '@crawlee/utils/internal';
|
|
4
|
+
import { z } from 'zod';
|
|
4
5
|
const STARTING_Z_INDEX = 2147400000;
|
|
5
6
|
const getLog = () => serviceLocator.getChildLog('Playwright Click Elements');
|
|
7
|
+
const enqueueLinksByClickingElementsOptionsSchema = z.strictObject({
|
|
8
|
+
page: schemas.objectWithKeys(['goto', 'evaluate']),
|
|
9
|
+
requestManager: schemas.objectWithKeys(['fetchNextRequest', 'addRequestsBatched']),
|
|
10
|
+
selector: z.string(),
|
|
11
|
+
userData: schemas.anyObject.optional(),
|
|
12
|
+
clickOptions: schemas.anyObject.optional(),
|
|
13
|
+
include: schemas.arrayOf(urlPatternSchema, 'URL patterns').optional(),
|
|
14
|
+
exclude: schemas.arrayOf(urlPatternSchema, 'URL patterns').optional(),
|
|
15
|
+
transformRequestFunction: schemas.anyFunction.optional(),
|
|
16
|
+
waitForPageIdleSecs: schemas.anyNumber.default(1),
|
|
17
|
+
maxWaitForPageIdleSecs: schemas.anyNumber.default(5),
|
|
18
|
+
label: z.string().optional(),
|
|
19
|
+
forefront: z.boolean().optional(),
|
|
20
|
+
skipNavigation: z.boolean().optional(),
|
|
21
|
+
onSkippedRequest: schemas.anyFunction.optional(),
|
|
22
|
+
});
|
|
6
23
|
/**
|
|
7
24
|
* The function finds elements matching a specific CSS selector in a Playwright page,
|
|
8
25
|
* clicks all those elements using a mouse move and a left mouse button click and intercepts
|
|
@@ -12,8 +29,7 @@ const getLog = () => serviceLocator.getChildLog('Playwright Click Elements');
|
|
|
12
29
|
* in `href` elements, but rather navigations are triggered in click handlers.
|
|
13
30
|
* If you're looking to find URLs in `href` attributes of the page, see {@link enqueueLinks}.
|
|
14
31
|
*
|
|
15
|
-
* Optionally, the function allows you to filter the target links' URLs using an array of
|
|
16
|
-
* and override settings of the enqueued {@link Request} objects.
|
|
32
|
+
* Optionally, the function allows you to filter the target links' URLs using an array of glob or regexp patterns.
|
|
17
33
|
*
|
|
18
34
|
* **IMPORTANT**: To be able to do this, this function uses various mutations on the page,
|
|
19
35
|
* such as changing the Z-index of elements being clicked and their visibility. Therefore,
|
|
@@ -35,9 +51,9 @@ const getLog = () => serviceLocator.getChildLog('Playwright Click Elements');
|
|
|
35
51
|
* page,
|
|
36
52
|
* requestManager,
|
|
37
53
|
* selector: 'a.product-detail',
|
|
38
|
-
*
|
|
39
|
-
* 'https://www.example.com/handbags
|
|
40
|
-
* 'https://www.example.com/purses
|
|
54
|
+
* include: [
|
|
55
|
+
* 'https://www.example.com/handbags/*',
|
|
56
|
+
* 'https://www.example.com/purses/*',
|
|
41
57
|
* ],
|
|
42
58
|
* });
|
|
43
59
|
* ```
|
|
@@ -45,51 +61,13 @@ const getLog = () => serviceLocator.getChildLog('Playwright Click Elements');
|
|
|
45
61
|
* @returns Promise that resolves to {@link BatchAddRequestsResult} object.
|
|
46
62
|
*/
|
|
47
63
|
export async function enqueueLinksByClickingElements(options) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
requestManager: ow.object.hasKeys('fetchNextRequest', 'addRequestsBatched'),
|
|
51
|
-
selector: ow.string,
|
|
52
|
-
userData: ow.optional.object,
|
|
53
|
-
clickOptions: ow.optional.object,
|
|
54
|
-
pseudoUrls: ow.optional.array.ofType(ow.any(ow.string, ow.object.hasKeys('purl'))),
|
|
55
|
-
globs: ow.optional.array.ofType(ow.any(ow.string, ow.object.hasKeys('glob'))),
|
|
56
|
-
regexps: ow.optional.array.ofType(ow.any(ow.regExp, ow.object.hasKeys('regexp'))),
|
|
57
|
-
exclude: ow.optional.array.ofType(ow.any(ow.string, ow.regExp, ow.object.hasKeys('glob'), ow.object.hasKeys('regexp'))),
|
|
58
|
-
transformRequestFunction: ow.optional.function,
|
|
59
|
-
waitForPageIdleSecs: ow.optional.number,
|
|
60
|
-
maxWaitForPageIdleSecs: ow.optional.number,
|
|
61
|
-
label: ow.optional.string,
|
|
62
|
-
forefront: ow.optional.boolean,
|
|
63
|
-
skipNavigation: ow.optional.boolean,
|
|
64
|
-
onSkippedRequest: ow.optional.function,
|
|
65
|
-
}));
|
|
66
|
-
const { page, requestManager, selector, clickOptions,
|
|
67
|
-
// oxlint-disable-next-line typescript/no-deprecated -- still accepted for backwards compat
|
|
68
|
-
pseudoUrls, globs, regexps, transformRequestFunction, waitForPageIdleSecs = 1, maxWaitForPageIdleSecs = 5, forefront, exclude, onSkippedRequest, } = options;
|
|
64
|
+
const parsedOptions = parseArgument(options, enqueueLinksByClickingElementsOptionsSchema, 'EnqueueLinksByClickingElementsOptions');
|
|
65
|
+
const { page, requestManager, selector, clickOptions, include, exclude, transformRequestFunction, waitForPageIdleSecs, maxWaitForPageIdleSecs, forefront, onSkippedRequest, } = parsedOptions;
|
|
69
66
|
const waitForPageIdleMillis = waitForPageIdleSecs * 1000;
|
|
70
67
|
const maxWaitForPageIdleMillis = maxWaitForPageIdleSecs * 1000;
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
for (const excl of exclude) {
|
|
75
|
-
if (typeof excl === 'string' || 'glob' in excl) {
|
|
76
|
-
urlExcludePatternObjects.push(...constructGlobObjectsFromGlobs([excl]));
|
|
77
|
-
}
|
|
78
|
-
else if (excl instanceof RegExp || 'regexp' in excl) {
|
|
79
|
-
urlExcludePatternObjects.push(...constructRegExpObjectsFromRegExps([excl]));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (pseudoUrls?.length) {
|
|
84
|
-
serviceLocator.getLogger().deprecated('`pseudoUrls` option is deprecated, use `globs` or `regexps` instead');
|
|
85
|
-
urlPatternObjects.push(...constructRegExpObjectsFromPseudoUrls(pseudoUrls));
|
|
86
|
-
}
|
|
87
|
-
if (globs?.length) {
|
|
88
|
-
urlPatternObjects.push(...constructGlobObjectsFromGlobs(globs));
|
|
89
|
-
}
|
|
90
|
-
if (regexps?.length) {
|
|
91
|
-
urlPatternObjects.push(...constructRegExpObjectsFromRegExps(regexps));
|
|
92
|
-
}
|
|
68
|
+
const hasOnSkippedRequest = onSkippedRequest !== undefined;
|
|
69
|
+
const urlExcludePatternObjects = exclude?.length ? constructUrlPatternObjects(exclude) : [];
|
|
70
|
+
const urlPatternObjects = include?.length ? constructUrlPatternObjects(include) : [];
|
|
93
71
|
const interceptedRequests = await clickElementsAndInterceptNavigationRequests({
|
|
94
72
|
page,
|
|
95
73
|
selector,
|
|
@@ -97,17 +75,17 @@ export async function enqueueLinksByClickingElements(options) {
|
|
|
97
75
|
maxWaitForPageIdleMillis,
|
|
98
76
|
clickOptions,
|
|
99
77
|
});
|
|
100
|
-
const requestOptions = createRequestOptions(interceptedRequests,
|
|
78
|
+
const requestOptions = createRequestOptions(interceptedRequests, parsedOptions);
|
|
101
79
|
const skippedByFilters = [];
|
|
102
|
-
let filteredOptions = filterRequestOptionsByPatterns(requestOptions, urlPatternObjects.length > 0 ? urlPatternObjects : undefined, urlExcludePatternObjects, undefined, (
|
|
80
|
+
let filteredOptions = filterRequestOptionsByPatterns(requestOptions, urlPatternObjects.length > 0 ? urlPatternObjects : undefined, urlExcludePatternObjects, undefined, hasOnSkippedRequest ? (opts) => skippedByFilters.push(opts) : undefined);
|
|
103
81
|
if (onSkippedRequest && skippedByFilters.length > 0) {
|
|
104
|
-
await Promise.all(skippedByFilters.map(async (
|
|
82
|
+
await Promise.all(skippedByFilters.map(async (opts) => onSkippedRequest(createSkippedRequestArgs(opts, 'filters'))));
|
|
105
83
|
}
|
|
106
84
|
if (transformRequestFunction) {
|
|
107
85
|
const skippedByTransform = [];
|
|
108
|
-
filteredOptions = applyRequestTransform(filteredOptions, transformRequestFunction, (r) => skippedByTransform.push(r));
|
|
86
|
+
filteredOptions = applyRequestTransform(filteredOptions, transformRequestFunction, hasOnSkippedRequest ? (r) => skippedByTransform.push(r) : undefined);
|
|
109
87
|
if (onSkippedRequest && skippedByTransform.length > 0) {
|
|
110
|
-
await Promise.all(skippedByTransform.map(async (r) => onSkippedRequest(
|
|
88
|
+
await Promise.all(skippedByTransform.map(async (r) => onSkippedRequest(createSkippedRequestArgs(r, 'transform'))));
|
|
111
89
|
}
|
|
112
90
|
}
|
|
113
91
|
const requests = filteredOptions.map((opts) => new CrawleeRequest(opts));
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Configuration } from '@crawlee/browser';
|
|
2
|
+
import type { BrowserPool, BrowserPoolHooks, BrowserPoolOptions, PlaywrightPlugin, RemoteBrowserPool, RemoteBrowserPoolOptions } from '@crawlee/browser-pool';
|
|
3
|
+
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
4
|
+
import type { Page } from 'playwright';
|
|
5
|
+
import type { PlaywrightLaunchContext } from './playwright-launcher.js';
|
|
6
|
+
/** A {@link BrowserPool} of Playwright browsers, as built by {@link playwrightBrowserPool}. */
|
|
7
|
+
export type PlaywrightBrowserPool = BrowserPool<{
|
|
8
|
+
browserPlugins: [PlaywrightPlugin];
|
|
9
|
+
}, [PlaywrightPlugin]>;
|
|
10
|
+
export interface PlaywrightBrowserPoolOptions extends Omit<BrowserPoolOptions, 'browserPlugins'>, BrowserPoolHooks<ReturnType<PlaywrightPlugin['createController']>, ReturnType<PlaywrightPlugin['createLaunchContext']>, Page> {
|
|
11
|
+
/** How to launch the browser: which Playwright browser type, proxy, user data dir, ... */
|
|
12
|
+
launchContext?: PlaywrightLaunchContext;
|
|
13
|
+
/**
|
|
14
|
+
* Whether to run the browser in headless mode. Shorthand for `launchContext.launchOptions.headless`.
|
|
15
|
+
* Defaults to `true`, and can also be set via {@link Configuration}.
|
|
16
|
+
*/
|
|
17
|
+
headless?: boolean;
|
|
18
|
+
/** Configuration to read the browser defaults from. Defaults to the global configuration. */
|
|
19
|
+
configuration?: Configuration;
|
|
20
|
+
}
|
|
21
|
+
export interface RemotePlaywrightBrowserPoolOptions extends Pick<PlaywrightBrowserPoolOptions, 'launchContext' | 'headless' | 'configuration'>, Omit<RemoteBrowserPoolOptions, 'browserPlugins'> {
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Builds a {@link BrowserPool} of Playwright browsers to pass to a {@link PlaywrightCrawler} as its
|
|
25
|
+
* {@link BrowserCrawlerOptions.browserPool|`browserPool`}.
|
|
26
|
+
*
|
|
27
|
+
* It accepts every {@link BrowserPoolOptions|`BrowserPool` option} plus the crawler's own `launchContext` and
|
|
28
|
+
* `headless`, and derives the browser plugin from them - so a pool built here always matches the crawler it is
|
|
29
|
+
* given to, and configuring one never means assembling a {@link PlaywrightPlugin} by hand.
|
|
30
|
+
*
|
|
31
|
+
* **Example usage:**
|
|
32
|
+
*
|
|
33
|
+
* ```javascript
|
|
34
|
+
* const crawler = new PlaywrightCrawler({
|
|
35
|
+
* browserPool: playwrightBrowserPool({
|
|
36
|
+
* maxOpenPagesPerBrowser: 1,
|
|
37
|
+
* launchContext: { launcher: firefox },
|
|
38
|
+
* }),
|
|
39
|
+
* requestHandler: async ({ page }) => { ... },
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* The returned pool is *not* torn down by the crawler, which is what makes it shareable between crawlers.
|
|
44
|
+
*
|
|
45
|
+
* @category Browser management
|
|
46
|
+
*/
|
|
47
|
+
export declare function playwrightBrowserPool(options?: PlaywrightBrowserPoolOptions): PlaywrightBrowserPool;
|
|
48
|
+
/**
|
|
49
|
+
* The {@link RemoteBrowserPool} counterpart of {@link playwrightBrowserPool}: connects to a remote browser
|
|
50
|
+
* service (Browserbase, Browserless, Steel, ...) with a Playwright plugin derived from `launchContext`.
|
|
51
|
+
*
|
|
52
|
+
* A {@link PlaywrightCrawler} accepts the same connection details directly via
|
|
53
|
+
* {@link BrowserCrawlerOptions.remoteBrowser|`remoteBrowser`}; reach for this factory when you also need to
|
|
54
|
+
* tune the wrapping pool, or to share one remote pool between crawlers.
|
|
55
|
+
*
|
|
56
|
+
* **Example usage:**
|
|
57
|
+
*
|
|
58
|
+
* ```javascript
|
|
59
|
+
* const crawler = new PlaywrightCrawler({
|
|
60
|
+
* browserPool: remotePlaywrightBrowserPool({
|
|
61
|
+
* endpoint: 'wss://production-sfo.browserless.io?token=xxx',
|
|
62
|
+
* maxOpenBrowsers: 2,
|
|
63
|
+
* browserPoolOptions: { useFingerprints: false },
|
|
64
|
+
* }),
|
|
65
|
+
* requestHandler: async ({ page }) => { ... },
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @category Browser management
|
|
70
|
+
*/
|
|
71
|
+
export declare function remotePlaywrightBrowserPool(options: RemotePlaywrightBrowserPoolOptions): RemoteBrowserPool<Page>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { PlaywrightLauncher } from './playwright-launcher.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a {@link BrowserPool} of Playwright browsers to pass to a {@link PlaywrightCrawler} as its
|
|
4
|
+
* {@link BrowserCrawlerOptions.browserPool|`browserPool`}.
|
|
5
|
+
*
|
|
6
|
+
* It accepts every {@link BrowserPoolOptions|`BrowserPool` option} plus the crawler's own `launchContext` and
|
|
7
|
+
* `headless`, and derives the browser plugin from them - so a pool built here always matches the crawler it is
|
|
8
|
+
* given to, and configuring one never means assembling a {@link PlaywrightPlugin} by hand.
|
|
9
|
+
*
|
|
10
|
+
* **Example usage:**
|
|
11
|
+
*
|
|
12
|
+
* ```javascript
|
|
13
|
+
* const crawler = new PlaywrightCrawler({
|
|
14
|
+
* browserPool: playwrightBrowserPool({
|
|
15
|
+
* maxOpenPagesPerBrowser: 1,
|
|
16
|
+
* launchContext: { launcher: firefox },
|
|
17
|
+
* }),
|
|
18
|
+
* requestHandler: async ({ page }) => { ... },
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* The returned pool is *not* torn down by the crawler, which is what makes it shareable between crawlers.
|
|
23
|
+
*
|
|
24
|
+
* @category Browser management
|
|
25
|
+
*/
|
|
26
|
+
export function playwrightBrowserPool(options = {}) {
|
|
27
|
+
const { launchContext, headless, configuration, ...poolOptions } = options;
|
|
28
|
+
return playwrightLauncher(launchContext, headless, configuration).createBrowserPool(poolOptions);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The {@link RemoteBrowserPool} counterpart of {@link playwrightBrowserPool}: connects to a remote browser
|
|
32
|
+
* service (Browserbase, Browserless, Steel, ...) with a Playwright plugin derived from `launchContext`.
|
|
33
|
+
*
|
|
34
|
+
* A {@link PlaywrightCrawler} accepts the same connection details directly via
|
|
35
|
+
* {@link BrowserCrawlerOptions.remoteBrowser|`remoteBrowser`}; reach for this factory when you also need to
|
|
36
|
+
* tune the wrapping pool, or to share one remote pool between crawlers.
|
|
37
|
+
*
|
|
38
|
+
* **Example usage:**
|
|
39
|
+
*
|
|
40
|
+
* ```javascript
|
|
41
|
+
* const crawler = new PlaywrightCrawler({
|
|
42
|
+
* browserPool: remotePlaywrightBrowserPool({
|
|
43
|
+
* endpoint: 'wss://production-sfo.browserless.io?token=xxx',
|
|
44
|
+
* maxOpenBrowsers: 2,
|
|
45
|
+
* browserPoolOptions: { useFingerprints: false },
|
|
46
|
+
* }),
|
|
47
|
+
* requestHandler: async ({ page }) => { ... },
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @category Browser management
|
|
52
|
+
*/
|
|
53
|
+
export function remotePlaywrightBrowserPool(options) {
|
|
54
|
+
const { launchContext, headless, configuration, ...remoteOptions } = options;
|
|
55
|
+
return playwrightLauncher(launchContext, headless, configuration).createRemoteBrowserPool(remoteOptions);
|
|
56
|
+
}
|
|
57
|
+
function playwrightLauncher(launchContext = {}, headless, configuration) {
|
|
58
|
+
return new PlaywrightLauncher(headless == null
|
|
59
|
+
? launchContext
|
|
60
|
+
: { ...launchContext, launchOptions: { ...launchContext.launchOptions, headless } }, configuration);
|
|
61
|
+
}
|