@crawlee/browser-pool 3.3.4-beta.1 → 3.3.4-beta.11

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 CHANGED
@@ -1,127 +1,831 @@
1
- <h1 align="center">
2
- <a href="https://crawlee.dev">
3
- <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/apify/crawlee/master/website/static/img/crawlee-dark.svg?sanitize=true">
5
- <img alt="Crawlee" src="https://raw.githubusercontent.com/apify/crawlee/master/website/static/img/crawlee-light.svg?sanitize=true" width="500">
6
- </picture>
7
- </a>
8
- <br>
9
- <small>A web scraping and browser automation library</small>
10
- </h1>
1
+ # Browser Pool - the headless browser manager
2
+ Browser Pool is a small, but powerful and extensible library, that allows you to seamlessly
3
+ control multiple headless browsers at the same time with only a little configuration, and a
4
+ single function call. Currently, it supports [Puppeteer](https://github.com/puppeteer/puppeteer),
5
+ [Playwright](https://github.com/microsoft/playwright), and it can be easily extended with plugins.
6
+
7
+ We created Browser Pool because we regularly needed to execute tasks concurrently in many
8
+ headless browsers and their pages, but we did not want to worry about launching browsers, closing
9
+ browsers, restarting them after crashes and so on. We also wanted to easily and reliably manage
10
+ the whole browser / page lifecycle.
11
+
12
+ You can use Browser Pool for scraping the internet at scale, testing your website
13
+ in multiple browsers at the same time or launching web automation robots.
14
+
15
+ <!-- toc -->
16
+
17
+ - [Installation](#installation)
18
+ - [Usage](#usage)
19
+ - [Launching multiple browsers](#launching-multiple-browsers)
20
+ - [Features](#features)
21
+ * [Simple configuration](#simple-configuration)
22
+ * [Proxy management](#proxy-management)
23
+ * [Lifecycle management with hooks](#lifecycle-management-with-hooks)
24
+ * [Manipulating playwright context using `pageOptions` or `launchOptions`](#manipulating-playwright-context-using-pageoptions-or-launchoptions)
25
+ * [Single API for common operations](#single-api-for-common-operations)
26
+ * [Graceful browser closing](#graceful-browser-closing)
27
+ * [Changing browser fingerprints a.k.a. browser signatures](#changing-browser-fingerprints-aka-browser-signatures)
28
+ * [(UNSTABLE) Extensibility with plugins](#unstable-extensibility-with-plugins)
29
+ - [API Reference](#api-reference)
30
+
31
+ <!-- tocstop -->
11
32
 
12
- <p align=center>
13
- <a href="https://www.npmjs.com/package/@crawlee/core" rel="nofollow"><img src="https://img.shields.io/npm/v/@crawlee/core.svg" alt="NPM latest version" data-canonical-src="https://img.shields.io/npm/v/@crawlee/core/next.svg" style="max-width: 100%;"></a>
14
- <a href="https://www.npmjs.com/package/@crawlee/core" rel="nofollow"><img src="https://img.shields.io/npm/dm/@crawlee/core.svg" alt="Downloads" data-canonical-src="https://img.shields.io/npm/dm/@crawlee/core.svg" style="max-width: 100%;"></a>
15
- <a href="https://discord.gg/jyEM2PRvMU" rel="nofollow"><img src="https://img.shields.io/discord/801163717915574323?label=discord" alt="Chat on discord" data-canonical-src="https://img.shields.io/discord/801163717915574323?label=discord" style="max-width: 100%;"></a>
16
- <a href="https://github.com/apify/crawlee/actions/workflows/test-and-release.yml"><img src="https://github.com/apify/crawlee/actions/workflows/test-and-release.yml/badge.svg?branch=master" alt="Build Status" style="max-width: 100%;"></a>
17
- </p>
33
+ ## Installation
34
+ Use NPM or Yarn to install `@crawlee/browser-pool`. Note that `@crawlee/browser-pool` does not come preinstalled
35
+ with browser automation libraries. This allows you to choose your own libraries and their
36
+ versions, and it also makes `@crawlee/browser-pool` much smaller.
18
37
 
19
- > ℹ️ Crawlee is the successor to [Apify SDK](https://sdk.apify.com). 🎉 Fully rewritten in **TypeScript** for a better developer experience, and with even more powerful anti-blocking features. The interface is almost the same as Apify SDK so upgrading is a breeze. Read [the upgrading guide](https://crawlee.dev/docs/upgrading/upgrading-to-v3) to learn about the changes. ℹ️
38
+ Run this command to install `@crawlee/browser-pool` and the `playwright` browser automation library.
39
+ ```bash
40
+ npm install @crawlee/browser-pool playwright
41
+ ```
20
42
 
21
- Crawlee covers your crawling and scraping end-to-end and **helps you build reliable scrapers. Fast.**
43
+ ## Usage
44
+ This simple example shows how to open a page in a browser using Browser Pool.
45
+ We use the provided `PlaywrightPlugin` to wrap a Playwright installation of
46
+ your own. By calling `browserPool.newPage()` you launch a new Firefox browser
47
+ and open a new page in that browser.
22
48
 
23
- Your crawlers will appear human-like and fly under the radar of modern bot protections even with the default configuration. Crawlee gives you the tools to crawl the web for links, scrape data, and store it to disk or cloud while staying configurable to suit your project's needs.
49
+ ```js
50
+ import { BrowserPool, PlaywrightPlugin } from '@crawlee/browser-pool';
51
+ import playwright from 'playwright';
24
52
 
25
- Crawlee is available as the [`crawlee`](https://www.npmjs.com/package/crawlee) NPM package.
53
+ const browserPool = new BrowserPool({
54
+ browserPlugins: [new PlaywrightPlugin(playwright.chromium)],
55
+ });
26
56
 
27
- > 👉 **View full documentation, guides and examples on the [Crawlee project website](https://crawlee.dev)** 👈
57
+ // An asynchronous IIFE (immediately invoked function expression)
58
+ // allows us to use the 'await' keyword.
59
+ (async () => {
60
+ // Launches Chromium with Playwright and returns a Playwright Page.
61
+ const page1 = await browserPool.newPage();
62
+ // You can interact with the page as you're used to.
63
+ await page1.goto('https://example.com');
64
+ // When you're done, close the page.
65
+ await page1.close();
66
+
67
+ // Opens a second page in the same browser.
68
+ const page2 = await browserPool.newPage();
69
+ // When everything's finished, tear down the pool.
70
+ await browserPool.destroy();
71
+ })();
72
+ ```
28
73
 
29
- ## Installation
74
+ > Browser Pool uses the same asynchronous API as the underlying automation libraries which means
75
+ extensive use of Promises and the `async` / `await` pattern. [Visit MDN to learn more](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await).
30
76
 
31
- We recommend visiting the [Introduction tutorial](https://crawlee.dev/docs/introduction) in Crawlee documentation for more information.
77
+ ## Launching multiple browsers
78
+ The basic example shows how to launch a single browser, but the purpose
79
+ of Browser Pool is to launch many browsers. This is done automatically
80
+ in the background. You only need to provide the relevant plugins and call
81
+ `browserPool.newPage()`.
32
82
 
33
- > Crawlee requires **Node.js 16 or higher**.
83
+ ```js
84
+ import { BrowserPool, PlaywrightPlugin } from '@crawlee/browser-pool';
85
+ import playwright from 'playwright';
86
+
87
+ const browserPool = new BrowserPool({
88
+ browserPlugins: [
89
+ new PlaywrightPlugin(playwright.chromium),
90
+ new PlaywrightPlugin(playwright.firefox),
91
+ new PlaywrightPlugin(playwright.webkit),
92
+ ],
93
+ });
94
+
95
+ (async () => {
96
+ // Open 4 pages in 3 browsers. The browsers are launched
97
+ // in a round-robin fashion based on the plugin order.
98
+ const chromiumPage = await browserPool.newPage();
99
+ const firefoxPage = await browserPool.newPage();
100
+ const webkitPage = await browserPool.newPage();
101
+ const chromiumPage2 = await browserPool.newPage();
34
102
 
35
- ### With Crawlee CLI
103
+ // Don't forget to close pages / destroy pool when you're done.
104
+ })();
105
+ ```
36
106
 
37
- The fastest way to try Crawlee out is to use the **Crawlee CLI** and choose the **Getting started example**. The CLI will install all the necessary dependencies and add boilerplate code for you to play with.
107
+ This round-robin way of opening pages may not be useful for you,
108
+ if you need to consistently run tasks in multiple environments.
109
+ For that, there's the `newPageWithEachPlugin` function.
38
110
 
39
- ```bash
40
- npx crawlee create my-crawler
111
+ ```js
112
+ import { BrowserPool, PlaywrightPlugin, PuppeteerPlugin } from '@crawlee/browser-pool';
113
+ import playwright from 'playwright';
114
+ import puppeteer from 'puppeteer';
115
+
116
+ const browserPool = new BrowserPool({
117
+ browserPlugins: [
118
+ new PlaywrightPlugin(playwright.chromium),
119
+ new PuppeteerPlugin(puppeteer),
120
+ ],
121
+ });
122
+
123
+ (async () => {
124
+ const pages = await browserPool.newPageWithEachPlugin();
125
+ const promises = pages.map(async page => {
126
+ // Run some task with each page
127
+ // pages are in order of plugins:
128
+ // [playwrightPage, puppeteerPage]
129
+ await page.close();
130
+ });
131
+ await Promise.all(promises);
132
+
133
+ // Continue with some more work.
134
+ })();
41
135
  ```
42
136
 
43
- ```bash
44
- cd my-crawler
45
- npm start
137
+ ## Features
138
+ Besides a simple interface for launching browsers, Browser Pool includes
139
+ other helpful features that make browser management more convenient.
140
+
141
+ ### Simple configuration
142
+ You can easily set the maximum number of pages that can be open in a given
143
+ browser and also the maximum number of pages to process before a browser
144
+ [is retired](#graceful-browser-closing).
145
+
146
+ ```js
147
+ const browserPool = new BrowserPool({
148
+ maxOpenPagesPerBrowser: 20,
149
+ retireBrowserAfterPageCount: 100,
150
+ });
46
151
  ```
47
152
 
48
- ### Manual installation
49
- If you prefer adding Crawlee **into your own project**, try the example below. Because it uses `PlaywrightCrawler` we also need to install [Playwright](https://playwright.dev). It's not bundled with Crawlee to reduce install size.
153
+ You can configure the browser launch options either right in the plugins:
50
154
 
51
- ```bash
52
- npm install crawlee playwright
155
+ ```js
156
+ const playwrightPlugin = new PlaywrightPlugin(playwright.chromium, {
157
+ launchOptions: {
158
+ headless: true,
159
+ }
160
+ })
53
161
  ```
54
162
 
163
+ Or dynamically in [pre-launch hooks](#lifecycle-management-with-hooks):
164
+
165
+ ```js
166
+ const browserPool = new BrowserPool({
167
+ preLaunchHooks: [(pageId, launchContext) => {
168
+ if (pageId === 'headful') {
169
+ launchContext.launchOptions.headless = false;
170
+ }
171
+ }]
172
+ });
173
+ ```
174
+
175
+ ### Proxy management
176
+ When scraping at scale or testing websites from multiple geolocations,
177
+ one often needs to use proxy servers. Setting up an authenticated proxy
178
+ in Puppeteer can be cumbersome, so we created a helper that does all
179
+ the heavy lifting for you. Simply provide a proxy URL with authentication
180
+ credentials, and you're done. It works the same for Playwright too.
181
+
182
+ ```js
183
+ const puppeteerPlugin = new PuppeteerPlugin(puppeteer, {
184
+ proxyUrl: 'http://<username>:<password>@proxy.com:8000'
185
+ });
186
+ ```
187
+
188
+ > We plan to extend this by adding a proxy-per-page functionality,
189
+ > allowing you to rotate proxies per page, rather than per browser.
190
+
191
+ ### Lifecycle management with hooks
192
+ Browser Pool allows you to manage the full browser / page lifecycle
193
+ by attaching hooks to the most important events. Asynchronous hooks
194
+ are supported, and their execution order is guaranteed.
195
+
196
+ The first parameter of each hook is either a `pageId` for the hooks
197
+ executed before a `page` is created or a `page` afterwards. This is
198
+ useful to keep track of which hook was triggered by which `newPage()`
199
+ call.
200
+
201
+ ```js
202
+ const browserPool = new BrowserPool({
203
+ browserPlugins: [
204
+ new PlaywrightPlugin(playwright.chromium),
205
+ ],
206
+ preLaunchHooks: [(pageId, launchContext) => {
207
+ // You can use pre-launch hooks to make dynamic changes
208
+ // to the launchContext, such as changing a proxyUrl
209
+ // or updating the browser launchOptions
210
+
211
+ pageId === 'my-page' // true
212
+ }],
213
+ postPageCreateHooks: [(page, browserController) => {
214
+ // It makes sense to make global changes to pages
215
+ // in post-page-create hooks. For example, you can
216
+ // inject some JavaScript library, such as jQuery.
217
+
218
+ browserPool.getPageId(page) === 'my-page' // true
219
+ }]
220
+ });
221
+
222
+ await browserPool.newPage({ id: 'my-page' });
223
+ ```
224
+
225
+ > See the API Documentation for all hooks and their arguments.
226
+ ### Manipulating playwright context using `pageOptions` or `launchOptions`
227
+ Playwright allows customizing multiple browser attributes by browser context.
228
+ You can customize some of them once the context is created, but some need to be customized within its creation.
229
+ This part of the documentation should explain how you can effectively customize the browser context.
230
+
231
+ First of all, let's take a look at what kind of context strategy you chose. You can choose between two strategies by `useIncognitoPages` `LaunchContext` option.
232
+
233
+ Suppose you decide to keep `useIncognitoPages` default `false` and create a shared context across all pages launched by one browser. In this case, you should pass the `contextOptions` as a `launchOptions` since the context is created within the new browser launch. The `launchOptions` corresponds to these [playwright options](https://playwright.dev/docs/api/class-browsertype#browsertypelaunchpersistentcontextuserdatadir-options). As you can see, these options contain not only ordinary playwright launch options but also the context options.
234
+
235
+ If you set `useIncognitoPages` to `true`, you will create a new context within each new page, which allows you to handle each page its cookies and application data. This approach allows you to pass the context options as `pageOptions` because a new context is created once you create a new page. In this case, the `pageOptions` corresponds to these [playwright options](https://playwright.dev/docs/api/class-browser#browsernewpageoptions).
236
+
237
+ **Changing context options with `LaunchContext`:**
238
+
239
+ This will only work if you keep the default value for `useIncognitoPages` (`false`).
240
+ ```javascript
241
+ const browserPool = new BrowserPool({
242
+ browserPlugins: [
243
+ new PlaywrightPlugin(
244
+ playwright.chromium,
245
+ {
246
+ launchOptions: {
247
+ deviceScaleFactor: 2,
248
+ },
249
+ },
250
+ ),
251
+ ],
252
+
253
+ });
254
+ ```
255
+ **Changing context options with `browserPool.newPage` options:**
256
+
257
+ ```javascript
258
+ const browserPool = new BrowserPool({
259
+ browserPlugins: [
260
+ new PlaywrightPlugin(
261
+ playwright.chromium,
262
+ {
263
+ useIncognitoPages: true, // You must turn on incognito pages.
264
+ launchOptions: {
265
+ // launch options
266
+ headless: false,
267
+ devtools: true,
268
+ },
269
+ },
270
+ ),
271
+ ],
272
+ });
273
+
274
+ (async () => {
275
+ // Launches Chromium with Playwright and returns a Playwright Page.
276
+ const page = await browserPool.newPage({
277
+ pageOptions: {
278
+ // context options
279
+ deviceScaleFactor: 2,
280
+ colorScheme: 'light',
281
+ locale: 'de-DE',
282
+ },
283
+ });
284
+ })();
285
+
286
+ ```
287
+ **Changing context options with `prePageCreateHooks` options:**
288
+ ```javascript
289
+ const browserPool = new BrowserPool({
290
+ browserPlugins: [
291
+ new PlaywrightPlugin(
292
+ playwright.chromium,
293
+ {
294
+ useIncognitoPages: true,
295
+ launchOptions: {
296
+ // launch options
297
+ headless: false,
298
+ devtools: true,
299
+ },
300
+ },
301
+ ),
302
+ ],
303
+ prePageCreateHooks: [
304
+ (pageId, browserController, pageOptions) => {
305
+ pageOptions.deviceScaleFactor = 2;
306
+ pageOptions.colorScheme = 'dark';
307
+ pageOptions.locale = 'de-DE';
308
+
309
+ // You must modify the 'pageOptions' object, not assign to the variable.
310
+ // pageOptions = {deviceScaleFactor: 2, ...etc} => This will not work!
311
+ },
312
+ ],
313
+ });
314
+
315
+ (async () => {
316
+ // Launches Chromium with Playwright and returns a Playwright Page.
317
+ const page = await browserPool.newPage();
318
+ })();
319
+ ```
320
+ ### Single API for common operations
321
+ Puppeteer and Playwright handle some things differently. Browser Pool
322
+ attempts to remove those differences for the most common use-cases.
323
+
324
+ ```js
325
+ // Playwright
326
+ const cookies = await context.cookies();
327
+ await context.addCookies(cookies);
328
+
329
+ // Puppeteer
330
+ const cookies = await page.cookies();
331
+ await page.setCookie(...cookies);
332
+
333
+ // BrowserPool uses the same API for all plugins
334
+ const cookies = await browserController.getCookies(page);
335
+ await browserController.setCookies(page, cookies);
336
+ ```
337
+
338
+ ### Graceful browser closing
339
+ With Browser Pool, browsers are not closed, but retired. A retired browser
340
+ will no longer open new pages, but it will wait until the open pages are closed,
341
+ allowing your running tasks to finish. If a browser gets stuck in limbo,
342
+ it will be killed after a timeout to prevent hanging browser processes.
343
+
344
+ ### Changing browser fingerprints a.k.a. browser signatures
345
+
346
+ > Fingerprints are enabled by default since v3.
347
+
348
+ Changing browser fingerprints is beneficial for avoiding getting blocked and simulating real user browsers.
349
+ With Browser Pool, you can do this otherwise complicated technique by enabling the `useFingerprints` option.
350
+ The fingerprints are by default tied to the respective proxy urls to not use the same unique fingerprint from various IP addresses.
351
+ You can disable this behavior in the [`fingerprintOptions`](#new_BrowserPool_new). In the `fingerprintOptions`, You can also control which fingerprints are generated.
352
+ You can control parameters as browser, operating system, and browser versions.
353
+
354
+ ### (UNSTABLE) Extensibility with plugins
355
+ A new super cool browser automation library appears? No problem, we add
356
+ a simple plugin to Browser Pool, and it automagically works.
357
+
358
+ > The BrowserPlugin and BrowserController interfaces are unstable and may
359
+ > change if we find some implementation to be suboptimal.
360
+
361
+ ## API Reference
362
+ All public classes, methods and their parameters can be inspected in this API reference.
363
+
364
+ <a name="module_browser-pool"></a>
365
+
366
+ ### @crawlee/browser-pool
367
+ The `@crawlee/browser-pool` module exports three constructors. One for `BrowserPool`
368
+ itself and two for the included Puppeteer and Playwright plugins.
369
+
370
+ **Example:**
371
+ ```js
372
+ import { BrowserPool, PuppeteerPlugin, PlaywrightPlugin } from '@crawlee/browser-pool';
373
+ import puppeteer from 'puppeteer';
374
+ import playwright from 'playwright';
375
+
376
+ const browserPool = new BrowserPool({
377
+ browserPlugins: [
378
+ new PuppeteerPlugin(puppeteer),
379
+ new PlaywrightPlugin(playwright.chromium),
380
+ ]
381
+ });
382
+ ```
383
+
384
+ **Properties**
385
+
386
+ | Name | Type |
387
+ | --- | --- |
388
+ | BrowserPool | [<code>BrowserPool</code>](#BrowserPool) |
389
+ | PuppeteerPlugin | <code>PuppeteerPlugin</code> |
390
+ | PlaywrightPlugin | <code>PlaywrightPlugin</code> |
391
+
392
+
393
+ * * *
394
+
395
+ <a name="BrowserPool"></a>
396
+
397
+ ### BrowserPool
398
+ The `BrowserPool` class is the most important class of the `@crawlee/browser-pool` module.
399
+ It manages opening and closing of browsers and their pages and its constructor
400
+ options allow easy configuration of the browsers' and pages' lifecycle.
401
+
402
+ The most important and useful constructor options are the various lifecycle hooks.
403
+ Those allow you to sequentially call a list of (asynchronous) functions at each
404
+ stage of the browser / page lifecycle.
405
+
406
+ **Example:**
407
+ ```js
408
+ import { BrowserPool, PlaywrightPlugin } from '@crawlee/browser-pool';
409
+ import playwright from 'playwright';
410
+
411
+ const browserPool = new BrowserPool({
412
+ browserPlugins: [ new PlaywrightPlugin(playwright.chromium)],
413
+ preLaunchHooks: [(pageId, launchContext) => {
414
+ // do something before a browser gets launched
415
+ launchContext.launchOptions.headless = false;
416
+ }],
417
+ postLaunchHooks: [(pageId, browserController) => {
418
+ // manipulate the browser right after launch
419
+ console.dir(browserController.browser.contexts());
420
+ }],
421
+ prePageCreateHooks: [(pageId, browserController) => {
422
+ if (pageId === 'my-page') {
423
+ // make changes right before a specific page is created
424
+ }
425
+ }],
426
+ postPageCreateHooks: [async (page, browserController) => {
427
+ // update some or all new pages
428
+ await page.evaluate(() => {
429
+ // now all pages will have 'foo'
430
+ window.foo = 'bar'
431
+ })
432
+ }],
433
+ prePageCloseHooks: [async (page, browserController) => {
434
+ // collect information just before a page closes
435
+ await page.screenshot();
436
+ }],
437
+ postPageCloseHooks: [(pageId, browserController) => {
438
+ // clean up or log after a job is done
439
+ console.log('Page closed: ', pageId)
440
+ }]
441
+ });
442
+ ```
443
+
444
+
445
+ * [BrowserPool](#BrowserPool)
446
+ * [`new BrowserPool(options)`](#new_BrowserPool_new)
447
+ * [`.newPage(options)`](#BrowserPool+newPage) ⇒ <code>Promise.&lt;Page&gt;</code>
448
+ * [`.newPageInNewBrowser(options)`](#BrowserPool+newPageInNewBrowser) ⇒ <code>Promise.&lt;Page&gt;</code>
449
+ * [`.newPageWithEachPlugin(optionsList)`](#BrowserPool+newPageWithEachPlugin) ⇒ <code>Promise.&lt;Array.&lt;Page&gt;&gt;</code>
450
+ * [`.getBrowserControllerByPage(page)`](#BrowserPool+getBrowserControllerByPage) ⇒ [<code>BrowserController</code>](#BrowserController)
451
+ * [`.getPage(id)`](#BrowserPool+getPage) ⇒ <code>Page</code>
452
+ * [`.getPageId(page)`](#BrowserPool+getPageId) ⇒ <code>string</code>
453
+ * [`.retireBrowserController(browserController)`](#BrowserPool+retireBrowserController)
454
+ * [`.retireBrowserByPage(page)`](#BrowserPool+retireBrowserByPage)
455
+ * [`.retireAllBrowsers()`](#BrowserPool+retireAllBrowsers)
456
+ * [`.closeAllBrowsers()`](#BrowserPool+closeAllBrowsers) ⇒ <code>Promise.&lt;void&gt;</code>
457
+ * [`.destroy()`](#BrowserPool+destroy) ⇒ <code>Promise.&lt;void&gt;</code>
458
+
459
+
460
+ * * *
461
+
462
+ <a name="new_BrowserPool_new"></a>
463
+
464
+ #### `new BrowserPool(options)`
465
+
466
+ | Param | Type | Default | Description |
467
+ | --- | --- | --- | --- |
468
+ | options | <code>object</code> | | |
469
+ | options.browserPlugins | [<code>Array.&lt;BrowserPlugin&gt;</code>](#BrowserPlugin) | | Browser plugins are wrappers of browser automation libraries that allow `BrowserPool` to control browsers with those libraries. `@crawlee/browser-pool` comes with a `PuppeteerPlugin` and a `PlaywrightPlugin`. |
470
+ | [options.maxOpenPagesPerBrowser] | <code>number</code> | <code>20</code> | Sets the maximum number of pages that can be open in a browser at the same time. Once reached, a new browser will be launched to handle the excess. |
471
+ | [options.retireBrowserAfterPageCount] | <code>number</code> | <code>100</code> | Browsers tend to get bloated after processing a lot of pages. This option configures the number of processed pages after which the browser will automatically retire and close. A new browser will launch in its place. |
472
+ | [options.operationTimeoutSecs] | <code>number</code> | <code>15</code> | As we know from experience, async operations of the underlying libraries, such as launching a browser or opening a new page, can get stuck. To prevent `BrowserPool` from getting stuck, we add a timeout to those operations and you can configure it with this option. |
473
+ | [options.closeInactiveBrowserAfterSecs] | <code>number</code> | <code>300</code> | Browsers normally close immediately after their last page is processed. However, there could be situations where this does not happen. Browser Pool makes sure all inactive browsers are closed regularly, to free resources. |
474
+ | [options.preLaunchHooks] | <code>Array.&lt;function()&gt;</code> | | Pre-launch hooks are executed just before a browser is launched and provide a good opportunity to dynamically change the launch options. The hooks are called with two arguments: `pageId`: `string` and `launchContext`: [LaunchContext](#LaunchContext) |
475
+ | [options.postLaunchHooks] | <code>Array.&lt;function()&gt;</code> | | Post-launch hooks are executed as soon as a browser is launched. The hooks are called with two arguments: `pageId`: `string` and `browserController`: [BrowserController](#BrowserController) To guarantee order of execution before other hooks in the same browser, the [BrowserController](#BrowserController) methods cannot be used until the post-launch hooks complete. If you attempt to call `await browserController.close()` from a post-launch hook, it will deadlock the process. This API is subject to change. |
476
+ | [options.prePageCreateHooks] | <code>Array.&lt;function()&gt;</code> | | Pre-page-create hooks are executed just before a new page is created. They are useful to make dynamic changes to the browser before opening a page. The hooks are called with two arguments: `pageId`: `string`, `browserController`: [BrowserController](#BrowserController) and `pageOptions`: `object|undefined` - This only works if the underlying `BrowserController` supports new page options. So far, new page options are only supported by `PlaywrightController`. If the page options are not supported by `BrowserController` the `pageOptions` argument is `undefined`. |
477
+ | [options.postPageCreateHooks] | <code>Array.&lt;function()&gt;</code> | | Post-page-create hooks are called right after a new page is created and all internal actions of Browser Pool are completed. This is the place to make changes to a page that you would like to apply to all pages. Such as injecting a JavaScript library into all pages. The hooks are called with two arguments: `page`: `Page` and `browserController`: [BrowserController](#BrowserController) |
478
+ | [options.prePageCloseHooks] | <code>Array.&lt;function()&gt;</code> | | Pre-page-close hooks give you the opportunity to make last second changes in a page that's about to be closed, such as saving a snapshot or updating state. The hooks are called with two arguments: `page`: `Page` and `browserController`: [BrowserController](#BrowserController) |
479
+ | [options.postPageCloseHooks] | <code>Array.&lt;function()&gt;</code> | | Post-page-close hooks allow you to do page related clean up. The hooks are called with two arguments: `pageId`: `string` and `browserController`: [BrowserController](#BrowserController) |
480
+ | [options.useFingerprints] | <code>boolean</code> | <code>false</code> | If true the Browser pool will automatically generate and inject fingerprints to browsers. |
481
+ | [options.fingerprintOptions] | <code>FingerprintOptions </code> | | Fingerprints options that allows customizing the fingerprinting behavior. |
482
+ | [options.fingerprintOptions.fingerprintGeneratorOptions] | | | See the [Fingerprint generator](https://github.com/apify/fingerprint-generator#headergeneratoroptions) documentation. |
483
+ | [options.fingerprintOptions.useFingerprintCache] | <code>boolean</code> | <code>true</code> | Fingerprints are automatically assigned to an instance of a Session or proxy URL. You can disable this behavior by setting this property to `false`. |
484
+ | [options.fingerprintOptions.fingerprintCacheSize] | <code>number</code> | <code>10000</code> | Maximum number of cached browser fingerprints. |
485
+ * * *
486
+
487
+ <a name="BrowserPool+newPage"></a>
488
+
489
+ #### `browserPool.newPage(options)` ⇒ <code>Promise.&lt;Page&gt;</code>
490
+ Opens a new page in one of the running browsers or launches
491
+ a new browser and opens a page there, if no browsers are active,
492
+ or their page limits have been exceeded.
493
+
494
+
495
+ | Param | Type | Description |
496
+ | --- | --- | --- |
497
+ | options | <code>object</code> | |
498
+ | [options.id] | <code>string</code> | Assign a custom ID to the page. If you don't a random string ID will be generated. |
499
+ | [options.pageOptions] | <code>object</code> | Some libraries (Playwright) allow you to open new pages with specific options. Use this property to set those options. |
500
+ | [options.browserPlugin] | [<code>BrowserPlugin</code>](#BrowserPlugin) | Choose a plugin to open the page with. If none is provided, one of the pool's available plugins will be used. It must be one of the plugins browser pool was created with. If you wish to start a browser with a different configuration, see the `newPageInNewBrowser` function. |
501
+
502
+
503
+ * * *
504
+
505
+ <a name="BrowserPool+newPageInNewBrowser"></a>
506
+
507
+ #### `browserPool.newPageInNewBrowser(options)` ⇒ <code>Promise.&lt;Page&gt;</code>
508
+ Unlike [newPage](#BrowserPool+newPage), `newPageInNewBrowser` always launches a new
509
+ browser to open the page in. Use the `launchOptions` option to
510
+ configure the new browser.
511
+
512
+
513
+ | Param | Type | Description |
514
+ | --- | --- | --- |
515
+ | options | <code>object</code> | |
516
+ | [options.id] | <code>string</code> | Assign a custom ID to the page. If you don't a random string ID will be generated. |
517
+ | [options.pageOptions] | <code>object</code> | Some libraries (Playwright) allow you to open new pages with specific options. Use this property to set those options. |
518
+ | [options.launchOptions] | <code>object</code> | Options that will be used to launch the new browser. |
519
+ | [options.browserPlugin] | [<code>BrowserPlugin</code>](#BrowserPlugin) | Provide a plugin to launch the browser. If none is provided, one of the pool's available plugins will be used. If you configured `BrowserPool` to rotate multiple libraries, such as both Puppeteer and Playwright, you should always set the `browserPlugin` when using the `launchOptions` option. The plugin will not be added to the list of plugins used by the pool. You can either use one of those, to launch a specific browser, or provide a completely new configuration. |
520
+
521
+
522
+ * * *
523
+
524
+ <a name="BrowserPool+newPageWithEachPlugin"></a>
525
+
526
+ #### `browserPool.newPageWithEachPlugin(optionsList)` ⇒ <code>Promise.&lt;Array.&lt;Page&gt;&gt;</code>
527
+ Opens new pages with all available plugins and returns an array
528
+ of pages in the same order as the plugins were provided to `BrowserPool`.
529
+ This is useful when you want to run a script in multiple environments
530
+ at the same time, typically in testing or website analysis.
531
+
532
+ **Example:**
55
533
  ```js
56
- import { PlaywrightCrawler, Dataset } from 'crawlee';
57
-
58
- // PlaywrightCrawler crawls the web using a headless
59
- // browser controlled by the Playwright library.
60
- const crawler = new PlaywrightCrawler({
61
- // Use the requestHandler to process each of the crawled pages.
62
- async requestHandler({ request, page, enqueueLinks, log }) {
63
- const title = await page.title();
64
- log.info(`Title of ${request.loadedUrl} is '${title}'`);
65
-
66
- // Save results as JSON to ./storage/datasets/default
67
- await Dataset.pushData({ title, url: request.loadedUrl });
68
-
69
- // Extract links from the current page
70
- // and add them to the crawling queue.
71
- await enqueueLinks();
72
- },
73
- // Uncomment this option to see the browser window.
74
- // headless: false,
534
+ const browserPool = new BrowserPool({
535
+ browserPlugins: [
536
+ new PlaywrightPlugin(playwright.chromium),
537
+ new PlaywrightPlugin(playwright.firefox),
538
+ new PlaywrightPlugin(playwright.webkit),
539
+ new PuppeteerPlugin(puppeteer),
540
+ ]
75
541
  });
76
542
 
77
- // Add first URL to the queue and start the crawl.
78
- await crawler.run(['https://crawlee.dev']);
543
+ const pages = await browserPool.newPageWithEachPlugin();
544
+ const [chromiumPage, firefoxPage, webkitPage, puppeteerPage] = pages;
79
545
  ```
80
546
 
81
- By default, Crawlee stores data to `./storage` in the current working directory. You can override this directory via Crawlee configuration. For details, see [Configuration guide](https://crawlee.dev/docs/guides/configuration), [Request storage](https://crawlee.dev/docs/guides/request-storage) and [Result storage](https://crawlee.dev/docs/guides/result-storage).
82
547
 
83
- ## 🛠 Features
548
+ | Param | Type |
549
+ | --- | --- |
550
+ | optionsList | <code>Array.&lt;object&gt;</code> |
551
+
552
+
553
+ * * *
554
+
555
+ <a name="BrowserPool+getBrowserControllerByPage"></a>
556
+
557
+ #### `browserPool.getBrowserControllerByPage(page)` ⇒ [<code>BrowserController</code>](#BrowserController)
558
+ Retrieves a [BrowserController](#BrowserController) for a given page. This is useful
559
+ when you're working only with pages and need to access the browser
560
+ manipulation functionality.
561
+
562
+ You could access the browser directly from the page,
563
+ but that would circumvent `BrowserPool` and most likely
564
+ cause weird things to happen, so please always use `BrowserController`
565
+ to control your browsers. The function returns `undefined` if the
566
+ browser is closed.
567
+
568
+
569
+ | Param | Type | Description |
570
+ | --- | --- | --- |
571
+ | page | <code>Page</code> | Browser plugin page |
572
+
573
+
574
+ * * *
575
+
576
+ <a name="BrowserPool+getPage"></a>
577
+
578
+ #### `browserPool.getPage(id)` ⇒ <code>Page</code>
579
+ If you provided a custom ID to one of your pages or saved the
580
+ randomly generated one, you can use this function to retrieve
581
+ the page. If the page is no longer open, the function will
582
+ return `undefined`.
583
+
584
+
585
+ | Param | Type |
586
+ | --- | --- |
587
+ | id | <code>string</code> |
588
+
589
+
590
+ * * *
591
+
592
+ <a name="BrowserPool+getPageId"></a>
593
+
594
+ #### `browserPool.getPageId(page)` ⇒ <code>string</code>
595
+ Page IDs are used throughout `BrowserPool` as a method of linking
596
+ events. You can use a page ID to track the full lifecycle of the page.
597
+ It is created even before a browser is launched and stays with the page
598
+ until it's closed.
599
+
600
+
601
+ | Param | Type |
602
+ | --- | --- |
603
+ | page | <code>Page</code> |
604
+
605
+
606
+ * * *
607
+
608
+ <a name="BrowserPool+retireBrowserController"></a>
609
+
610
+ #### `browserPool.retireBrowserController(browserController)`
611
+ Removes a browser controller from the pool. The underlying
612
+ browser will be closed after all its pages are closed.
613
+
614
+
615
+ | Param | Type |
616
+ | --- | --- |
617
+ | browserController | [<code>BrowserController</code>](#BrowserController) |
618
+
619
+
620
+ * * *
621
+
622
+ <a name="BrowserPool+retireBrowserByPage"></a>
623
+
624
+ #### `browserPool.retireBrowserByPage(page)`
625
+ Removes a browser from the pool. It will be
626
+ closed after all its pages are closed.
627
+
628
+
629
+ | Param | Type |
630
+ | --- | --- |
631
+ | page | <code>Page</code> |
632
+
633
+
634
+ * * *
635
+
636
+ <a name="BrowserPool+retireAllBrowsers"></a>
637
+
638
+ #### `browserPool.retireAllBrowsers()`
639
+ Removes all active browsers from the pool. The browsers will be
640
+ closed after all their pages are closed.
641
+
642
+
643
+ * * *
644
+
645
+ <a name="BrowserPool+closeAllBrowsers"></a>
646
+
647
+ #### `browserPool.closeAllBrowsers()` ⇒ <code>Promise.&lt;void&gt;</code>
648
+ Closes all managed browsers without waiting for pages to close.
649
+
650
+
651
+ * * *
652
+
653
+ <a name="BrowserPool+destroy"></a>
654
+
655
+ #### `browserPool.destroy()` ⇒ <code>Promise.&lt;void&gt;</code>
656
+ Closes all managed browsers and tears down the pool.
657
+
658
+
659
+ * * *
660
+
661
+ <a name="BrowserController"></a>
662
+
663
+ ### BrowserController
664
+ The `BrowserController` serves two purposes. First, it is the base class that
665
+ specialized controllers like `PuppeteerController` or `PlaywrightController`
666
+ extend. Second, it defines the public interface of the specialized classes
667
+ which provide only private methods. Therefore, we do not keep documentation
668
+ for the specialized classes, because it's the same for all of them.
669
+
670
+ **Properties**
671
+
672
+ | Name | Type | Description |
673
+ | --- | --- | --- |
674
+ | id | <code>string</code> | |
675
+ | browserPlugin | [<code>BrowserPlugin</code>](#BrowserPlugin) | The `BrowserPlugin` instance used to launch the browser. |
676
+ | browser | <code>Browser</code> | Browser representation of the underlying automation library. |
677
+ | launchContext | [<code>LaunchContext</code>](#LaunchContext) | The configuration the browser was launched with. |
678
+
679
+
680
+ * [BrowserController](#BrowserController)
681
+ * [`.close()`](#BrowserController+close) ⇒ <code>Promise.&lt;void&gt;</code>
682
+ * [`.kill()`](#BrowserController+kill) ⇒ <code>Promise.&lt;void&gt;</code>
683
+ * [`.setCookies(page, cookies)`](#BrowserController+setCookies) ⇒ <code>Promise.&lt;void&gt;</code>
684
+ * [`.getCookies(page)`](#BrowserController+getCookies) ⇒ <code>Promise.&lt;Array.&lt;object&gt;&gt;</code>
685
+
686
+
687
+ * * *
688
+
689
+ <a name="BrowserController+close"></a>
690
+
691
+ #### `browserController.close()` ⇒ <code>Promise.&lt;void&gt;</code>
692
+ Gracefully closes the browser and makes sure
693
+ there will be no lingering browser processes.
694
+
695
+ Emits 'browserClosed' event.
696
+
697
+
698
+ * * *
699
+
700
+ <a name="BrowserController+kill"></a>
701
+
702
+ #### `browserController.kill()` ⇒ <code>Promise.&lt;void&gt;</code>
703
+ Immediately kills the browser process.
704
+
705
+ Emits 'browserClosed' event.
706
+
707
+
708
+ * * *
709
+
710
+ <a name="BrowserController+setCookies"></a>
711
+
712
+ #### `browserController.setCookies(page, cookies)` ⇒ <code>Promise.&lt;void&gt;</code>
713
+
714
+ | Param | Type |
715
+ | --- | --- |
716
+ | page | <code>Object</code> |
717
+ | cookies | <code>Array.&lt;object&gt;</code> |
718
+
719
+
720
+ * * *
721
+
722
+ <a name="BrowserController+getCookies"></a>
723
+
724
+ #### `browserController.getCookies(page)` ⇒ <code>Promise.&lt;Array.&lt;object&gt;&gt;</code>
725
+
726
+ | Param | Type |
727
+ | --- | --- |
728
+ | page | <code>Object</code> |
729
+
730
+
731
+ * * *
732
+
733
+ <a name="BrowserPlugin"></a>
734
+
735
+ ### BrowserPlugin
736
+ The `BrowserPlugin` serves two purposes. First, it is the base class that
737
+ specialized controllers like `PuppeteerPlugin` or `PlaywrightPlugin` extend.
738
+ Second, it allows the user to configure the automation libraries and
739
+ feed them to [BrowserPool](#BrowserPool) for use.
740
+
741
+ **Properties**
742
+
743
+ | Name | Type | Default | Description |
744
+ | --- | --- | --- | --- |
745
+ | [useIncognitoPages] | <code>boolean</code> | <code>false</code> | By default pages share the same browser context. If set to true each page uses its own context that is destroyed once the page is closed or crashes. |
746
+ | [userDataDir] | <code>object</code> | | Path to a User Data Directory, which stores browser session data like cookies and local storage. |
747
+
748
+
749
+ * * *
750
+
751
+ <a name="new_BrowserPlugin_new"></a>
752
+
753
+ #### `new BrowserPlugin(library, [options])`
754
+
755
+ | Param | Type | Description |
756
+ | --- | --- | --- |
757
+ | library | <code>object</code> | Each plugin expects an instance of the object with the `.launch()` property. For Puppeteer, it is the `puppeteer` module itself, whereas for Playwright it is one of the browser types, such as `puppeteer.chromium`. `BrowserPlugin` does not include the library. You can choose any version or fork of the library. It also keeps `@crawlee/browser-pool` installation small. |
758
+ | [options] | <code>object</code> | |
759
+ | [options.launchOptions] | <code>object</code> | Options that will be passed down to the automation library. E.g. `puppeteer.launch(launchOptions);`. This is a good place to set options that you want to apply as defaults. To dynamically override those options per-browser, see the `preLaunchHooks` of [BrowserPool](#BrowserPool). |
760
+ | [options.proxyUrl] | <code>string</code> | Automation libraries configure proxies differently. This helper allows you to set a proxy URL without worrying about specific implementations. It also allows you use an authenticated proxy without extra code. |
761
+
762
+
763
+ * * *
764
+
765
+ <a name="LaunchContext"></a>
766
+
767
+ ### LaunchContext
768
+ `LaunchContext` holds information about the launched browser. It's useful
769
+ to retrieve the `launchOptions`, the proxy the browser was launched with
770
+ or any other information user chose to add to the `LaunchContext` by calling
771
+ its `extend` function. This is very useful to keep track of browser-scoped
772
+ values, such as session IDs.
773
+
774
+ **Properties**
775
+
776
+ | Name | Type | Description |
777
+ | --- | --- | --- |
778
+ | id | <code>string</code> | To make identification of `LaunchContext` easier, `BrowserPool` assigns the `LaunchContext` an `id` that's equal to the `id` of the page that triggered the browser launch. This is useful, because many pages share a single launch context (single browser). |
779
+ | browserPlugin | [<code>BrowserPlugin</code>](#BrowserPlugin) | The `BrowserPlugin` instance used to launch the browser. |
780
+ | launchOptions | <code>object</code> | The actual options the browser was launched with, after changes. Those changes would be typically made in pre-launch hooks. |
781
+ | [useIncognitoPages] | <code>boolean</code> | By default pages share the same browser context. If set to true each page uses its own context that is destroyed once the page is closed or crashes. |
782
+ | [userDataDir] | <code>object</code> | Path to a User Data Directory, which stores browser session data like cookies and local storage. |
783
+
784
+
785
+ * [LaunchContext](#LaunchContext)
786
+ * [`.proxyUrl`](#LaunchContext+proxyUrl)
787
+ * [`.proxyUrl`](#LaunchContext+proxyUrl) ⇒ <code>string</code>
788
+ * [`.extend(fields)`](#LaunchContext+extend)
789
+
790
+
791
+ * * *
792
+
793
+ <a name="LaunchContext+proxyUrl"></a>
794
+
795
+ #### `launchContext.proxyUrl`
796
+ Sets a proxy URL for the browser.
797
+ Use `undefined` to unset existing proxy URL.
798
+
799
+
800
+ | Param | Type |
801
+ | --- | --- |
802
+ | url | <code>string</code> |
84
803
 
85
- - Single interface for **HTTP and headless browser** crawling
86
- - Persistent **queue** for URLs to crawl (breadth & depth first)
87
- - Pluggable **storage** of both tabular data and files
88
- - Automatic **scaling** with available system resources
89
- - Integrated **proxy rotation** and session management
90
- - Lifecycles customizable with **hooks**
91
- - **CLI** to bootstrap your projects
92
- - Configurable **routing**, **error handling** and **retries**
93
- - **Dockerfiles** ready to deploy
94
- - Written in **TypeScript** with generics
95
804
 
96
- ### 👾 HTTP crawling
805
+ * * *
97
806
 
98
- - Zero config **HTTP2 support**, even for proxies
99
- - Automatic generation of **browser-like headers**
100
- - Replication of browser **TLS fingerprints**
101
- - Integrated fast **HTML parsers**. Cheerio and JSDOM
102
- - Yes, you can scrape **JSON APIs** as well
807
+ <a name="LaunchContext+proxyUrl"></a>
103
808
 
104
- ### 💻 Real browser crawling
809
+ #### `launchContext.proxyUrl` ⇒ <code>string</code>
810
+ Returns the proxy URL of the browser.
105
811
 
106
- - JavaScript **rendering** and **screenshots**
107
- - **Headless** and **headful** support
108
- - Zero-config generation of **human-like fingerprints**
109
- - Automatic **browser management**
110
- - Use **Playwright** and **Puppeteer** with the same interface
111
- - **Chrome**, **Firefox**, **Webkit** and many others
112
812
 
113
- ## Usage on the Apify platform
813
+ * * *
114
814
 
115
- Crawlee is open-source and runs anywhere, but since it's developed by [Apify](https://apify.com), it's easy to set up on the Apify platform and run in the cloud. Visit the [Apify SDK website](https://sdk.apify.com) to learn more about deploying Crawlee to the Apify platform.
815
+ <a name="LaunchContext+extend"></a>
116
816
 
117
- ## Support
817
+ #### `launchContext.extend(fields)`
818
+ Extend the launch context with any extra fields.
819
+ This is useful to keep state information relevant
820
+ to the browser being launched. It ensures that
821
+ no internal fields are overridden and should be
822
+ used instead of property assignment.
118
823
 
119
- If you find any bug or issue with Crawlee, please [submit an issue on GitHub](https://github.com/apify/crawlee/issues). For questions, you can ask on [Stack Overflow](https://stackoverflow.com/questions/tagged/apify), in GitHub Discussions or you can join our [Discord server](https://discord.com/invite/jyEM2PRvMU).
120
824
 
121
- ## Contributing
825
+ | Param | Type |
826
+ | --- | --- |
827
+ | fields | <code>object</code> |
122
828
 
123
- Your code contributions are welcome, and you'll be praised to eternity! If you have any ideas for improvements, either submit an issue or create a pull request. For contribution guidelines and the code of conduct, see [CONTRIBUTING.md](https://github.com/apify/crawlee/blob/master/CONTRIBUTING.md).
124
829
 
125
- ## License
830
+ * * *
126
831
 
127
- This project is licensed under the Apache License 2.0 - see the [LICENSE.md](https://github.com/apify/crawlee/blob/master/LICENSE.md) file for details.