@crawlee/puppeteer 4.0.0-beta.13 → 4.0.0-beta.131
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 +17 -13
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/internals/enqueue-links/click-elements.d.ts +40 -56
- package/internals/enqueue-links/click-elements.js +57 -52
- package/internals/puppeteer-browser-pool.d.ts +55 -0
- package/internals/puppeteer-browser-pool.js +48 -0
- package/internals/puppeteer-crawler.d.ts +176 -114
- package/internals/puppeteer-crawler.js +47 -66
- package/internals/puppeteer-launcher.d.ts +27 -19
- package/internals/puppeteer-launcher.js +14 -15
- package/internals/utils/puppeteer_request_interception.d.ts +0 -1
- package/internals/utils/puppeteer_request_interception.js +7 -8
- package/internals/utils/puppeteer_utils.d.ts +17 -11
- package/internals/utils/puppeteer_utils.js +118 -64
- package/package.json +14 -11
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/internals/enqueue-links/click-elements.d.ts.map +0 -1
- package/internals/enqueue-links/click-elements.js.map +0 -1
- package/internals/puppeteer-crawler.d.ts.map +0 -1
- package/internals/puppeteer-crawler.js.map +0 -1
- package/internals/puppeteer-launcher.d.ts.map +0 -1
- package/internals/puppeteer-launcher.js.map +0 -1
- package/internals/utils/puppeteer_request_interception.d.ts.map +0 -1
- package/internals/utils/puppeteer_request_interception.js.map +0 -1
- package/internals/utils/puppeteer_utils.d.ts.map +0 -1
- package/internals/utils/puppeteer_utils.js.map +0 -1
- package/tsconfig.build.tsbuildinfo +0 -1
|
@@ -3,6 +3,7 @@ import { BrowserLauncher, Configuration } from '@crawlee/browser';
|
|
|
3
3
|
import { PuppeteerPlugin } from '@crawlee/browser-pool';
|
|
4
4
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
5
5
|
import type { Browser } from 'puppeteer';
|
|
6
|
+
import { z } from 'zod';
|
|
6
7
|
/**
|
|
7
8
|
* Apify extends the launch options of Puppeteer.
|
|
8
9
|
* You can use any of the Puppeteer compatible
|
|
@@ -66,30 +67,38 @@ export interface PuppeteerLaunchContext extends BrowserLaunchContext<PuppeteerPl
|
|
|
66
67
|
* @ignore
|
|
67
68
|
*/
|
|
68
69
|
export declare class PuppeteerLauncher extends BrowserLauncher<PuppeteerPlugin, unknown> {
|
|
69
|
-
readonly
|
|
70
|
+
readonly configuration: Configuration;
|
|
70
71
|
protected static optionsShape: {
|
|
71
72
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
72
|
-
launcher: import("
|
|
73
|
+
launcher: z.ZodOptional<z.ZodCustom<import("@crawlee/types").Dictionary, import("@crawlee/types").Dictionary>>;
|
|
74
|
+
proxyUrl: z.ZodOptional<z.ZodURL>;
|
|
75
|
+
useChrome: z.ZodOptional<z.ZodBoolean>;
|
|
76
|
+
useIncognitoPages: z.ZodOptional<z.ZodBoolean>;
|
|
77
|
+
browserPerProxy: z.ZodOptional<z.ZodBoolean>;
|
|
78
|
+
ignoreProxyCertificate: z.ZodOptional<z.ZodBoolean>;
|
|
79
|
+
userDataDir: z.ZodOptional<z.ZodString>;
|
|
73
80
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
useIncognitoPages: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
|
|
79
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
80
|
-
browserPerProxy: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
|
|
81
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
82
|
-
userDataDir: import("ow").StringPredicate & import("ow").BasePredicate<string | undefined>;
|
|
81
|
+
launchOptions: z.ZodOptional<z.ZodCustom<import("@crawlee/types").Dictionary, import("@crawlee/types").Dictionary>>;
|
|
82
|
+
userAgent: z.ZodOptional<z.ZodString>;
|
|
83
|
+
};
|
|
84
|
+
protected static optionsSchema: z.ZodObject<{
|
|
83
85
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
84
|
-
|
|
86
|
+
launcher: z.ZodOptional<z.ZodCustom<import("@crawlee/types").Dictionary, import("@crawlee/types").Dictionary>>;
|
|
87
|
+
proxyUrl: z.ZodOptional<z.ZodURL>;
|
|
88
|
+
useChrome: z.ZodOptional<z.ZodBoolean>;
|
|
89
|
+
useIncognitoPages: z.ZodOptional<z.ZodBoolean>;
|
|
90
|
+
browserPerProxy: z.ZodOptional<z.ZodBoolean>;
|
|
91
|
+
ignoreProxyCertificate: z.ZodOptional<z.ZodBoolean>;
|
|
92
|
+
userDataDir: z.ZodOptional<z.ZodString>;
|
|
85
93
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
86
|
-
|
|
87
|
-
|
|
94
|
+
launchOptions: z.ZodOptional<z.ZodCustom<import("@crawlee/types").Dictionary, import("@crawlee/types").Dictionary>>;
|
|
95
|
+
userAgent: z.ZodOptional<z.ZodString>;
|
|
96
|
+
}, z.core.$strict>;
|
|
88
97
|
/**
|
|
89
98
|
* All `PuppeteerLauncher` parameters are passed via an launchContext object.
|
|
90
99
|
*/
|
|
91
|
-
constructor(launchContext?: PuppeteerLaunchContext,
|
|
92
|
-
protected
|
|
100
|
+
constructor(launchContext?: PuppeteerLaunchContext, configuration?: Configuration);
|
|
101
|
+
protected getDefaultHeadlessOption(): boolean;
|
|
93
102
|
}
|
|
94
103
|
/**
|
|
95
104
|
* Launches headless Chrome using Puppeteer pre-configured to work within the Apify platform.
|
|
@@ -119,9 +128,8 @@ export declare class PuppeteerLauncher extends BrowserLauncher<PuppeteerPlugin,
|
|
|
119
128
|
* @param [launchContext]
|
|
120
129
|
* All `PuppeteerLauncher` parameters are passed via an launchContext object.
|
|
121
130
|
* If you want to pass custom `puppeteer.launch(options)` options you can use the `PuppeteerLaunchContext.launchOptions` property.
|
|
122
|
-
* @param [
|
|
131
|
+
* @param [configuration]
|
|
123
132
|
* @returns
|
|
124
133
|
* Promise that resolves to Puppeteer's `Browser` instance.
|
|
125
134
|
*/
|
|
126
|
-
export declare function launchPuppeteer(launchContext?: PuppeteerLaunchContext,
|
|
127
|
-
//# sourceMappingURL=puppeteer-launcher.d.ts.map
|
|
135
|
+
export declare function launchPuppeteer(launchContext?: PuppeteerLaunchContext, configuration?: Configuration): Promise<Browser>;
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { BrowserLauncher, Configuration } from '@crawlee/browser';
|
|
1
|
+
import { BrowserLauncher, Configuration, parseArgument, schemas } from '@crawlee/browser';
|
|
2
2
|
import { PuppeteerPlugin } from '@crawlee/browser-pool';
|
|
3
|
-
import
|
|
3
|
+
import { z } from 'zod';
|
|
4
4
|
/**
|
|
5
5
|
* `PuppeteerLauncher` is based on the `BrowserLauncher`. It launches `puppeteer` browser instance.
|
|
6
6
|
* @ignore
|
|
7
7
|
*/
|
|
8
8
|
export class PuppeteerLauncher extends BrowserLauncher {
|
|
9
|
-
|
|
9
|
+
configuration;
|
|
10
10
|
static optionsShape = {
|
|
11
11
|
...BrowserLauncher.optionsShape,
|
|
12
|
-
launcher:
|
|
12
|
+
launcher: schemas.anyObject.optional(),
|
|
13
13
|
};
|
|
14
|
+
static optionsSchema = z.strictObject(PuppeteerLauncher.optionsShape);
|
|
14
15
|
/**
|
|
15
16
|
* All `PuppeteerLauncher` parameters are passed via an launchContext object.
|
|
16
17
|
*/
|
|
17
|
-
constructor(launchContext = {},
|
|
18
|
-
|
|
19
|
-
const { launcher = BrowserLauncher.requireLauncherOrThrow('puppeteer', 'apify/actor-node-puppeteer-chrome'), ...browserLauncherOptions } = launchContext;
|
|
18
|
+
constructor(launchContext = {}, configuration = Configuration.getGlobalConfiguration()) {
|
|
19
|
+
const { launcher = BrowserLauncher.requireLauncherOrThrow('puppeteer', 'apify/actor-node-puppeteer-chrome'), ...browserLauncherOptions } = parseArgument(launchContext, PuppeteerLauncher.optionsSchema, 'PuppeteerLaunchContext');
|
|
20
20
|
super({
|
|
21
21
|
...browserLauncherOptions,
|
|
22
22
|
launcher,
|
|
23
|
-
},
|
|
24
|
-
this.
|
|
23
|
+
}, configuration);
|
|
24
|
+
this.configuration = configuration;
|
|
25
25
|
this.Plugin = PuppeteerPlugin;
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
const headless = super.
|
|
27
|
+
getDefaultHeadlessOption() {
|
|
28
|
+
const headless = super.getDefaultHeadlessOption();
|
|
29
29
|
return headless ? 'new' : headless;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -57,12 +57,11 @@ export class PuppeteerLauncher extends BrowserLauncher {
|
|
|
57
57
|
* @param [launchContext]
|
|
58
58
|
* All `PuppeteerLauncher` parameters are passed via an launchContext object.
|
|
59
59
|
* If you want to pass custom `puppeteer.launch(options)` options you can use the `PuppeteerLaunchContext.launchOptions` property.
|
|
60
|
-
* @param [
|
|
60
|
+
* @param [configuration]
|
|
61
61
|
* @returns
|
|
62
62
|
* Promise that resolves to Puppeteer's `Browser` instance.
|
|
63
63
|
*/
|
|
64
|
-
export async function launchPuppeteer(launchContext,
|
|
65
|
-
const puppeteerLauncher = new PuppeteerLauncher(launchContext,
|
|
64
|
+
export async function launchPuppeteer(launchContext, configuration = Configuration.getGlobalConfiguration()) {
|
|
65
|
+
const puppeteerLauncher = new PuppeteerLauncher(launchContext, configuration);
|
|
66
66
|
return puppeteerLauncher.launch();
|
|
67
67
|
}
|
|
68
|
-
//# sourceMappingURL=puppeteer-launcher.js.map
|
|
@@ -58,4 +58,3 @@ export declare function addInterceptRequestHandler(page: Page, handler: Intercep
|
|
|
58
58
|
* @param handler Request interception handler.
|
|
59
59
|
*/
|
|
60
60
|
export declare function removeInterceptRequestHandler(page: Page, handler: InterceptHandler): Promise<void>;
|
|
61
|
-
//# sourceMappingURL=puppeteer_request_interception.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
import { parseArgument, schemas, serviceLocator } from '@crawlee/browser';
|
|
3
|
+
const pageSchema = schemas.objectWithKeys(['goto', 'evaluate']);
|
|
4
4
|
// We use weak maps here so that the content gets discarded after page gets closed.
|
|
5
5
|
const pageInterceptRequestHandlersMap = new WeakMap(); // Maps page to an array of request interception handlers.
|
|
6
6
|
const pageInterceptRequestMasterHandlerMap = new WeakMap(); // Maps page to master request interception handler.
|
|
@@ -137,8 +137,8 @@ async function handleRequest(request, interceptRequestHandlers) {
|
|
|
137
137
|
* @param handler Request interception handler.
|
|
138
138
|
*/
|
|
139
139
|
export async function addInterceptRequestHandler(page, handler) {
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
parseArgument(page, pageSchema);
|
|
141
|
+
parseArgument(handler, schemas.anyFunction);
|
|
142
142
|
if (!pageInterceptRequestHandlersMap.has(page)) {
|
|
143
143
|
pageInterceptRequestHandlersMap.set(page, []);
|
|
144
144
|
}
|
|
@@ -174,8 +174,8 @@ export async function addInterceptRequestHandler(page, handler) {
|
|
|
174
174
|
* @param handler Request interception handler.
|
|
175
175
|
*/
|
|
176
176
|
export async function removeInterceptRequestHandler(page, handler) {
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
parseArgument(page, pageSchema);
|
|
178
|
+
parseArgument(handler, schemas.anyFunction);
|
|
179
179
|
const handlersArray = pageInterceptRequestHandlersMap.get(page).filter((item) => item !== handler);
|
|
180
180
|
pageInterceptRequestHandlersMap.set(page, handlersArray);
|
|
181
181
|
if (handlersArray.length === 0) {
|
|
@@ -194,7 +194,7 @@ export async function removeInterceptRequestHandler(page, handler) {
|
|
|
194
194
|
interceptedRequestsInProgress.removeListener('delete', onDelete);
|
|
195
195
|
}
|
|
196
196
|
catch (error) {
|
|
197
|
-
|
|
197
|
+
serviceLocator.getLogger().debug('Error while disabling request interception', { error });
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
};
|
|
@@ -207,4 +207,3 @@ async function disableRequestInterception(page) {
|
|
|
207
207
|
const requestHandler = pageInterceptRequestMasterHandlerMap.get(page);
|
|
208
208
|
page.off('request', requestHandler);
|
|
209
209
|
}
|
|
210
|
-
//# sourceMappingURL=puppeteer_request_interception.js.map
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import type { Request } from '@crawlee/browser';
|
|
21
21
|
import { Configuration } from '@crawlee/browser';
|
|
22
22
|
import type { BatchAddRequestsResult, Dictionary } from '@crawlee/types';
|
|
23
|
-
import { type CheerioRoot } from '@crawlee/utils';
|
|
23
|
+
import { type CheerioRoot } from '@crawlee/utils/internal';
|
|
24
24
|
import type { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping.js';
|
|
25
25
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
26
26
|
import type { HTTPResponse, Page, ResponseForRequest } from 'puppeteer';
|
|
@@ -305,9 +305,9 @@ export interface SaveSnapshotOptions {
|
|
|
305
305
|
keyValueStoreName?: string | null;
|
|
306
306
|
/**
|
|
307
307
|
* Configuration of the crawler that will be used to save the snapshot.
|
|
308
|
-
* @default Configuration.
|
|
308
|
+
* @default Configuration.getGlobalConfiguration()
|
|
309
309
|
*/
|
|
310
|
-
|
|
310
|
+
configuration?: Configuration;
|
|
311
311
|
}
|
|
312
312
|
/**
|
|
313
313
|
* Saves a full screenshot and HTML of the current page into a Key-Value store.
|
|
@@ -316,7 +316,6 @@ export interface SaveSnapshotOptions {
|
|
|
316
316
|
*/
|
|
317
317
|
export declare function saveSnapshot(page: Page, options?: SaveSnapshotOptions): Promise<void>;
|
|
318
318
|
export declare function closeCookieModals(page: Page): Promise<void>;
|
|
319
|
-
/** @internal */
|
|
320
319
|
export interface PuppeteerContextUtils {
|
|
321
320
|
/**
|
|
322
321
|
* Injects a JavaScript file into current `page`.
|
|
@@ -388,8 +387,7 @@ export interface PuppeteerContextUtils {
|
|
|
388
387
|
* in `href` elements, but rather navigations are triggered in click handlers.
|
|
389
388
|
* If you're looking to find URLs in `href` attributes of the page, see {@link enqueueLinks}.
|
|
390
389
|
*
|
|
391
|
-
* Optionally, the function allows you to filter the target links' URLs using an array of
|
|
392
|
-
* and override settings of the enqueued {@link Request} objects.
|
|
390
|
+
* Optionally, the function allows you to filter the target links' URLs using an array of glob or regexp patterns.
|
|
393
391
|
*
|
|
394
392
|
* **IMPORTANT**: To be able to do this, this function uses various mutations on the page,
|
|
395
393
|
* such as changing the Z-index of elements being clicked and their visibility. Therefore,
|
|
@@ -410,9 +408,9 @@ export interface PuppeteerContextUtils {
|
|
|
410
408
|
* async requestHandler({ enqueueLinksByClickingElements }) {
|
|
411
409
|
* await enqueueLinksByClickingElements({
|
|
412
410
|
* selector: 'a.product-detail',
|
|
413
|
-
*
|
|
414
|
-
* 'https://www.example.com/handbags/**'
|
|
415
|
-
* 'https://www.example.com/purses/**'
|
|
411
|
+
* include: [
|
|
412
|
+
* 'https://www.example.com/handbags/**',
|
|
413
|
+
* 'https://www.example.com/purses/**',
|
|
416
414
|
* ],
|
|
417
415
|
* });
|
|
418
416
|
* });
|
|
@@ -420,7 +418,7 @@ export interface PuppeteerContextUtils {
|
|
|
420
418
|
*
|
|
421
419
|
* @returns Promise that resolves to {@link BatchAddRequestsResult} object.
|
|
422
420
|
*/
|
|
423
|
-
enqueueLinksByClickingElements(options: Omit<EnqueueLinksByClickingElementsOptions, 'page' | '
|
|
421
|
+
enqueueLinksByClickingElements(options: Omit<EnqueueLinksByClickingElementsOptions, 'page' | 'requestManager'>): Promise<BatchAddRequestsResult>;
|
|
424
422
|
/**
|
|
425
423
|
* Forces the Puppeteer browser tab to block loading URLs that match a provided pattern.
|
|
426
424
|
* This is useful to speed up crawling of websites, since it reduces the amount
|
|
@@ -552,6 +550,15 @@ export interface PuppeteerContextUtils {
|
|
|
552
550
|
saveSnapshot(options?: SaveSnapshotOptions): Promise<void>;
|
|
553
551
|
/**
|
|
554
552
|
* Tries to close cookie consent modals on the page. Based on the I Don't Care About Cookies browser extension.
|
|
553
|
+
*
|
|
554
|
+
* Note that this method requires the idcac-playwright package to be installed.
|
|
555
|
+
* Crawlee does not include it by default due to licensing issues.
|
|
556
|
+
*
|
|
557
|
+
* To use this method, please install the package manually by running:
|
|
558
|
+
*
|
|
559
|
+
* ```bash
|
|
560
|
+
* npm install idcac-playwright
|
|
561
|
+
* ```
|
|
555
562
|
*/
|
|
556
563
|
closeCookieModals(): Promise<void>;
|
|
557
564
|
}
|
|
@@ -571,4 +578,3 @@ export declare const puppeteerUtils: {
|
|
|
571
578
|
parseWithCheerio: typeof parseWithCheerio;
|
|
572
579
|
closeCookieModals: typeof closeCookieModals;
|
|
573
580
|
};
|
|
574
|
-
//# sourceMappingURL=puppeteer_utils.d.ts.map
|
|
@@ -20,20 +20,48 @@
|
|
|
20
20
|
import { readFile } from 'node:fs/promises';
|
|
21
21
|
import { createRequire } from 'node:module';
|
|
22
22
|
import vm from 'node:vm';
|
|
23
|
-
import { Configuration, KeyValueStore, validators } from '@crawlee/browser';
|
|
23
|
+
import { Configuration, KeyValueStore, parseArgument, schemas, serviceLocator, validators } from '@crawlee/browser';
|
|
24
24
|
import { expandShadowRoots, sleep } from '@crawlee/utils';
|
|
25
|
-
import
|
|
26
|
-
import { getInjectableScript } from 'idcac-playwright';
|
|
27
|
-
import ow from 'ow';
|
|
25
|
+
import { z } from 'zod';
|
|
28
26
|
import { LruCache } from '@apify/datastructures';
|
|
29
|
-
import log_ from '@apify/log';
|
|
30
27
|
import { enqueueLinksByClickingElements } from '../enqueue-links/click-elements.js';
|
|
31
28
|
import { addInterceptRequestHandler, removeInterceptRequestHandler } from './puppeteer_request_interception.js';
|
|
32
29
|
const require = createRequire(import.meta.url);
|
|
33
30
|
const jqueryPath = require.resolve('jquery');
|
|
34
31
|
const MAX_INJECT_FILE_CACHE_SIZE = 10;
|
|
35
32
|
const DEFAULT_BLOCK_REQUEST_URL_PATTERNS = ['.css', '.jpg', '.jpeg', '.png', '.svg', '.gif', '.woff', '.pdf', '.zip'];
|
|
36
|
-
const
|
|
33
|
+
const filePathSchema = z.string();
|
|
34
|
+
const injectFileOptionsSchema = z.strictObject({
|
|
35
|
+
surviveNavigations: z.boolean().optional(),
|
|
36
|
+
});
|
|
37
|
+
const responseUrlRulesSchema = schemas.arrayOf(z.union([z.string(), z.instanceof(RegExp)]), 'strings or RegExps');
|
|
38
|
+
const gotoExtendedRequestSchema = z.looseObject({
|
|
39
|
+
url: z.url(),
|
|
40
|
+
method: z.string().optional(),
|
|
41
|
+
headers: schemas.anyObject.optional(),
|
|
42
|
+
payload: z.union([z.string(), z.instanceof(Uint8Array)]).optional(),
|
|
43
|
+
});
|
|
44
|
+
const blockRequestsOptionsSchema = z.strictObject({
|
|
45
|
+
urlPatterns: schemas.arrayOf(z.string(), 'strings').default(DEFAULT_BLOCK_REQUEST_URL_PATTERNS),
|
|
46
|
+
extraUrlPatterns: schemas.arrayOf(z.string(), 'strings').default(() => []),
|
|
47
|
+
});
|
|
48
|
+
const infiniteScrollOptionsSchema = z.strictObject({
|
|
49
|
+
timeoutSecs: schemas.anyNumber.default(0),
|
|
50
|
+
maxScrollHeight: schemas.anyNumber.default(0),
|
|
51
|
+
waitForSecs: schemas.anyNumber.default(4),
|
|
52
|
+
scrollDownAndUp: z.boolean().default(false),
|
|
53
|
+
buttonSelector: z.string().optional(),
|
|
54
|
+
stopScrollCallback: schemas.anyFunction.optional(),
|
|
55
|
+
});
|
|
56
|
+
const saveSnapshotOptionsSchema = z.strictObject({
|
|
57
|
+
key: z.string().min(1).default('SNAPSHOT'),
|
|
58
|
+
screenshotQuality: schemas.anyNumber.default(50),
|
|
59
|
+
saveScreenshot: z.boolean().default(true),
|
|
60
|
+
saveHtml: z.boolean().default(true),
|
|
61
|
+
keyValueStoreName: z.string().optional(),
|
|
62
|
+
configuration: schemas.anyObject.optional(),
|
|
63
|
+
});
|
|
64
|
+
const getLog = () => serviceLocator.getChildLog('Puppeteer Utils');
|
|
37
65
|
/**
|
|
38
66
|
* Cache contents of previously injected files to limit file system access.
|
|
39
67
|
*/
|
|
@@ -50,21 +78,19 @@ const injectedFilesCache = new LruCache({ maxLength: MAX_INJECT_FILE_CACHE_SIZE
|
|
|
50
78
|
* @param [options]
|
|
51
79
|
*/
|
|
52
80
|
export async function injectFile(page, filePath, options = {}) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
surviveNavigations: ow.optional.boolean,
|
|
57
|
-
}));
|
|
81
|
+
parseArgument(page, validators.browserPage);
|
|
82
|
+
parseArgument(filePath, filePathSchema);
|
|
83
|
+
const { surviveNavigations } = parseArgument(options, injectFileOptionsSchema);
|
|
58
84
|
let contents = injectedFilesCache.get(filePath);
|
|
59
85
|
if (!contents) {
|
|
60
86
|
contents = await readFile(filePath, 'utf8');
|
|
61
87
|
injectedFilesCache.add(filePath, contents);
|
|
62
88
|
}
|
|
63
89
|
const evalP = page.evaluate(contents);
|
|
64
|
-
if (
|
|
90
|
+
if (surviveNavigations) {
|
|
65
91
|
page.on('framenavigated', async () => page
|
|
66
92
|
.evaluate(contents)
|
|
67
|
-
.catch((error) =>
|
|
93
|
+
.catch((error) => getLog().warning('An error occurred during the script injection!', { error })));
|
|
68
94
|
}
|
|
69
95
|
return evalP;
|
|
70
96
|
}
|
|
@@ -95,7 +121,7 @@ export async function injectFile(page, filePath, options = {}) {
|
|
|
95
121
|
* @param [options.surviveNavigations] Opt-out option to disable the JQuery reinjection after navigation.
|
|
96
122
|
*/
|
|
97
123
|
export async function injectJQuery(page, options) {
|
|
98
|
-
|
|
124
|
+
parseArgument(page, validators.browserPage);
|
|
99
125
|
return injectFile(page, jqueryPath, { surviveNavigations: options?.surviveNavigations ?? true });
|
|
100
126
|
}
|
|
101
127
|
/**
|
|
@@ -111,14 +137,22 @@ export async function injectJQuery(page, options) {
|
|
|
111
137
|
* @param ignoreShadowRoots
|
|
112
138
|
*/
|
|
113
139
|
export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIframes = false) {
|
|
114
|
-
|
|
140
|
+
parseArgument(page, validators.browserPage);
|
|
115
141
|
if (page.frames().length > 1 && !ignoreIframes) {
|
|
116
142
|
const frames = await page.$$('iframe');
|
|
117
143
|
await Promise.all(frames.map(async (frame) => {
|
|
118
144
|
try {
|
|
119
145
|
const iframe = await frame.contentFrame();
|
|
120
146
|
if (iframe) {
|
|
121
|
-
const
|
|
147
|
+
const getIframeHTML = async () => {
|
|
148
|
+
try {
|
|
149
|
+
return iframe.$eval('body', (el) => el.innerHTML);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return iframe.content();
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const contents = await getIframeHTML();
|
|
122
156
|
await frame.evaluate((f, c) => {
|
|
123
157
|
const replacementNode = document.createElement('div');
|
|
124
158
|
replacementNode.innerHTML = c;
|
|
@@ -128,7 +162,7 @@ export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIf
|
|
|
128
162
|
}
|
|
129
163
|
}
|
|
130
164
|
catch (error) {
|
|
131
|
-
|
|
165
|
+
getLog().warning(`Failed to extract iframe content: ${error}`);
|
|
132
166
|
}
|
|
133
167
|
}));
|
|
134
168
|
}
|
|
@@ -136,7 +170,8 @@ export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIf
|
|
|
136
170
|
? null
|
|
137
171
|
: (await page.evaluate(`(${expandShadowRoots.toString()})(document)`));
|
|
138
172
|
const pageContent = html || (await page.content());
|
|
139
|
-
|
|
173
|
+
const { load } = await import('cheerio');
|
|
174
|
+
return load(pageContent);
|
|
140
175
|
}
|
|
141
176
|
/**
|
|
142
177
|
* Forces the Puppeteer browser tab to block loading URLs that match a provided pattern.
|
|
@@ -181,12 +216,8 @@ export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIf
|
|
|
181
216
|
* @param [options]
|
|
182
217
|
*/
|
|
183
218
|
export async function blockRequests(page, options = {}) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
urlPatterns: ow.optional.array.ofType(ow.string),
|
|
187
|
-
extraUrlPatterns: ow.optional.array.ofType(ow.string),
|
|
188
|
-
}));
|
|
189
|
-
const { urlPatterns = DEFAULT_BLOCK_REQUEST_URL_PATTERNS, extraUrlPatterns = [] } = options;
|
|
219
|
+
parseArgument(page, validators.browserPage);
|
|
220
|
+
const { urlPatterns, extraUrlPatterns } = parseArgument(options, blockRequestsOptionsSchema);
|
|
190
221
|
const patternsToBlock = [...urlPatterns, ...extraUrlPatterns];
|
|
191
222
|
// We use CDP commands instead of request interception as the latter disables caching, which is not ideal
|
|
192
223
|
await sendCDPCommand(page, 'Network.setBlockedURLs', { urls: patternsToBlock });
|
|
@@ -214,7 +245,9 @@ export async function sendCDPCommand(page, command, ...args) {
|
|
|
214
245
|
* @deprecated
|
|
215
246
|
*/
|
|
216
247
|
export const blockResources = async (page, resourceTypes = ['stylesheet', 'font', 'image', 'media']) => {
|
|
217
|
-
|
|
248
|
+
serviceLocator
|
|
249
|
+
.getLogger()
|
|
250
|
+
.deprecated('utils.puppeteer.blockResources() has a high impact on performance in recent versions of Puppeteer. ' +
|
|
218
251
|
'Until this resolves, please use utils.puppeteer.blockRequests()');
|
|
219
252
|
await addInterceptRequestHandler(page, async (request) => {
|
|
220
253
|
const type = request.resourceType();
|
|
@@ -241,10 +274,12 @@ export const blockResources = async (page, resourceTypes = ['stylesheet', 'font'
|
|
|
241
274
|
* @deprecated
|
|
242
275
|
*/
|
|
243
276
|
export async function cacheResponses(page, cache, responseUrlRules) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
277
|
+
parseArgument(page, validators.browserPage);
|
|
278
|
+
parseArgument(cache, schemas.anyObject);
|
|
279
|
+
parseArgument(responseUrlRules, responseUrlRulesSchema);
|
|
280
|
+
serviceLocator
|
|
281
|
+
.getLogger()
|
|
282
|
+
.deprecated('utils.puppeteer.cacheResponses() has a high impact on performance ' +
|
|
248
283
|
"in recent versions of Puppeteer so it's use is discouraged until this issue resolves.");
|
|
249
284
|
await addInterceptRequestHandler(page, async (request) => {
|
|
250
285
|
const url = request.url();
|
|
@@ -314,7 +349,7 @@ export function compileScript(scriptString, context = Object.create(null)) {
|
|
|
314
349
|
func = vm.runInNewContext(funcString, context); // "Secure" the context by removing prototypes, unless custom context is provided.
|
|
315
350
|
}
|
|
316
351
|
catch (err) {
|
|
317
|
-
|
|
352
|
+
getLog().exception(err, 'Cannot compile script!');
|
|
318
353
|
throw err;
|
|
319
354
|
}
|
|
320
355
|
if (typeof func !== 'function')
|
|
@@ -334,23 +369,20 @@ export function compileScript(scriptString, context = Object.create(null)) {
|
|
|
334
369
|
* @param [gotoOptions] Custom options for `page.goto()`.
|
|
335
370
|
*/
|
|
336
371
|
export async function gotoExtended(page, request, gotoOptions = {}) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
method: ow.optional.string,
|
|
341
|
-
headers: ow.optional.object,
|
|
342
|
-
payload: ow.optional.any(ow.string, ow.uint8Array),
|
|
343
|
-
}));
|
|
344
|
-
ow(gotoOptions, ow.object);
|
|
372
|
+
parseArgument(page, validators.browserPage);
|
|
373
|
+
parseArgument(request, gotoExtendedRequestSchema);
|
|
374
|
+
parseArgument(gotoOptions, schemas.anyObject);
|
|
345
375
|
gotoOptions = { ...gotoOptions };
|
|
346
376
|
if (gotoOptions.waitUntil === 'networkidle') {
|
|
347
377
|
gotoOptions.waitUntil = 'networkidle0';
|
|
348
378
|
}
|
|
349
379
|
const { url, method, headers, payload } = request;
|
|
350
380
|
const isEmpty = (o) => !o || Object.keys(o).length === 0;
|
|
351
|
-
if (method !== 'GET' || payload
|
|
381
|
+
if (method !== 'GET' || payload) {
|
|
352
382
|
// This is not deprecated, we use it to log only once.
|
|
353
|
-
|
|
383
|
+
serviceLocator
|
|
384
|
+
.getLogger()
|
|
385
|
+
.deprecated('Using other request methods than GET, rewriting headers and adding payloads has a high impact on performance ' +
|
|
354
386
|
'in recent versions of Puppeteer. Use only when necessary.');
|
|
355
387
|
let wasCalled = false;
|
|
356
388
|
const interceptRequestHandler = async (interceptedRequest) => {
|
|
@@ -373,6 +405,19 @@ export async function gotoExtended(page, request, gotoOptions = {}) {
|
|
|
373
405
|
};
|
|
374
406
|
await addInterceptRequestHandler(page, interceptRequestHandler);
|
|
375
407
|
}
|
|
408
|
+
else if (!isEmpty(headers)) {
|
|
409
|
+
const extraHeaders = { ...headers };
|
|
410
|
+
// Chrome bundled with Puppeteer 25+ ignores a `User-Agent` passed via `setExtraHTTPHeaders()`, it has to be set explicitly.
|
|
411
|
+
for (const name of Object.keys(extraHeaders)) {
|
|
412
|
+
if (name.toLowerCase() === 'user-agent') {
|
|
413
|
+
await page.setUserAgent(extraHeaders[name]);
|
|
414
|
+
delete extraHeaders[name];
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (!isEmpty(extraHeaders)) {
|
|
418
|
+
await page.setExtraHTTPHeaders(extraHeaders);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
376
421
|
return page.goto(url, gotoOptions);
|
|
377
422
|
}
|
|
378
423
|
/**
|
|
@@ -382,16 +427,8 @@ export async function gotoExtended(page, request, gotoOptions = {}) {
|
|
|
382
427
|
* @param [options]
|
|
383
428
|
*/
|
|
384
429
|
export async function infiniteScroll(page, options = {}) {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
timeoutSecs: ow.optional.number,
|
|
388
|
-
maxScrollHeight: ow.optional.number,
|
|
389
|
-
waitForSecs: ow.optional.number,
|
|
390
|
-
scrollDownAndUp: ow.optional.boolean,
|
|
391
|
-
buttonSelector: ow.optional.string,
|
|
392
|
-
stopScrollCallback: ow.optional.function,
|
|
393
|
-
}));
|
|
394
|
-
const { timeoutSecs = 0, maxScrollHeight = 0, waitForSecs = 4, scrollDownAndUp = false, buttonSelector, stopScrollCallback, } = options;
|
|
430
|
+
parseArgument(page, validators.browserPage);
|
|
431
|
+
const { timeoutSecs, maxScrollHeight, waitForSecs, scrollDownAndUp, buttonSelector, stopScrollCallback } = parseArgument(options, infiniteScrollOptionsSchema);
|
|
395
432
|
let finished;
|
|
396
433
|
const startTime = Date.now();
|
|
397
434
|
const CHECK_INTERVAL_MILLIS = 1000;
|
|
@@ -483,19 +520,11 @@ export async function infiniteScroll(page, options = {}) {
|
|
|
483
520
|
* @param [options]
|
|
484
521
|
*/
|
|
485
522
|
export async function saveSnapshot(page, options = {}) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
key: ow.optional.string.nonEmpty,
|
|
489
|
-
screenshotQuality: ow.optional.number,
|
|
490
|
-
saveScreenshot: ow.optional.boolean,
|
|
491
|
-
saveHtml: ow.optional.boolean,
|
|
492
|
-
keyValueStoreName: ow.optional.string,
|
|
493
|
-
config: ow.optional.object,
|
|
494
|
-
}));
|
|
495
|
-
const { key = 'SNAPSHOT', screenshotQuality = 50, saveScreenshot = true, saveHtml = true, keyValueStoreName, config, } = options;
|
|
523
|
+
parseArgument(page, validators.browserPage);
|
|
524
|
+
const { key, screenshotQuality, saveScreenshot, saveHtml, keyValueStoreName, configuration } = parseArgument(options, saveSnapshotOptionsSchema);
|
|
496
525
|
try {
|
|
497
|
-
const store = await KeyValueStore.open(keyValueStoreName, {
|
|
498
|
-
|
|
526
|
+
const store = await KeyValueStore.open(keyValueStoreName ? { name: keyValueStoreName } : null, {
|
|
527
|
+
configuration: configuration ?? Configuration.getGlobalConfiguration(),
|
|
499
528
|
});
|
|
500
529
|
if (saveScreenshot) {
|
|
501
530
|
const screenshotName = `${key}.jpg`;
|
|
@@ -516,8 +545,34 @@ export async function saveSnapshot(page, options = {}) {
|
|
|
516
545
|
throw new Error(`saveSnapshot with key ${key} failed.\nCause:${err.message}`);
|
|
517
546
|
}
|
|
518
547
|
}
|
|
548
|
+
let idcacPlaywright = null;
|
|
549
|
+
async function getIdcacPlaywright() {
|
|
550
|
+
if (idcacPlaywright)
|
|
551
|
+
return idcacPlaywright;
|
|
552
|
+
try {
|
|
553
|
+
idcacPlaywright = await import('idcac-playwright');
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
getLog().warning(`Failed to import 'idcac-playwright'.
|
|
557
|
+
|
|
558
|
+
We recently made idcac-playwright an optional dependency due to licensing issues.
|
|
559
|
+
To use this feature, please install it manually by running
|
|
560
|
+
|
|
561
|
+
npm install idcac-playwright
|
|
562
|
+
|
|
563
|
+
Original error message follows:
|
|
564
|
+
|
|
565
|
+
${error.message}
|
|
566
|
+
`);
|
|
567
|
+
}
|
|
568
|
+
return idcacPlaywright;
|
|
569
|
+
}
|
|
519
570
|
export async function closeCookieModals(page) {
|
|
520
|
-
|
|
571
|
+
parseArgument(page, validators.browserPage);
|
|
572
|
+
const idcac = await getIdcacPlaywright();
|
|
573
|
+
if (idcac?.getInjectableScript()) {
|
|
574
|
+
await page.evaluate(idcac.getInjectableScript());
|
|
575
|
+
}
|
|
521
576
|
}
|
|
522
577
|
export { enqueueLinksByClickingElements, addInterceptRequestHandler, removeInterceptRequestHandler };
|
|
523
578
|
/** @internal */
|
|
@@ -535,4 +590,3 @@ export const puppeteerUtils = {
|
|
|
535
590
|
parseWithCheerio,
|
|
536
591
|
closeCookieModals,
|
|
537
592
|
};
|
|
538
|
-
//# sourceMappingURL=puppeteer_utils.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/puppeteer",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.131",
|
|
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"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://crawlee.dev",
|
|
40
40
|
"scripts": {
|
|
41
|
-
"build": "
|
|
41
|
+
"build": "pnpm clean && pnpm compile && pnpm copy",
|
|
42
42
|
"clean": "rimraf ./dist",
|
|
43
43
|
"compile": "tsc -p tsconfig.build.json",
|
|
44
44
|
"copy": "tsx ../../scripts/copy.ts"
|
|
@@ -48,22 +48,25 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@apify/datastructures": "^2.0.3",
|
|
51
|
-
"@
|
|
52
|
-
"@crawlee/browser": "4.0.0-beta.
|
|
53
|
-
"@crawlee/
|
|
54
|
-
"@crawlee/types": "4.0.0-beta.
|
|
55
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
51
|
+
"@crawlee/browser": "4.0.0-beta.131",
|
|
52
|
+
"@crawlee/browser-pool": "4.0.0-beta.131",
|
|
53
|
+
"@crawlee/core": "4.0.0-beta.131",
|
|
54
|
+
"@crawlee/types": "4.0.0-beta.131",
|
|
55
|
+
"@crawlee/utils": "4.0.0-beta.131",
|
|
56
56
|
"cheerio": "^1.0.0",
|
|
57
57
|
"devtools-protocol": "*",
|
|
58
|
-
"idcac-playwright": "^0.1.3",
|
|
59
58
|
"jquery": "^3.7.1",
|
|
60
|
-
"
|
|
61
|
-
"
|
|
59
|
+
"tslib": "^2.8.1",
|
|
60
|
+
"zod": "^4.4.3"
|
|
62
61
|
},
|
|
63
62
|
"peerDependencies": {
|
|
63
|
+
"idcac-playwright": "^0.2.0",
|
|
64
64
|
"puppeteer": "*"
|
|
65
65
|
},
|
|
66
66
|
"peerDependenciesMeta": {
|
|
67
|
+
"idcac-playwright": {
|
|
68
|
+
"optional": true
|
|
69
|
+
},
|
|
67
70
|
"puppeteer": {
|
|
68
71
|
"optional": true
|
|
69
72
|
}
|
|
@@ -75,5 +78,5 @@
|
|
|
75
78
|
}
|
|
76
79
|
}
|
|
77
80
|
},
|
|
78
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "de741c0b63d0da061e4ffcd6ce6fdc0c92401399"
|
|
79
82
|
}
|
package/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAElD,OAAO,KAAK,4BAA4B,MAAM,qDAAqD,CAAC;AACpG,YAAY,EAAE,gBAAgB,EAAE,MAAM,qDAAqD,CAAC;AAE5F,OAAO,KAAK,cAAc,MAAM,sCAAsC,CAAC;AACvE,YAAY,EACR,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,IAAI,gCAAgC,EAC3D,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,GACtB,MAAM,sCAAsC,CAAC;AAE9C,OAAO,KAAK,sBAAsB,MAAM,6CAA6C,CAAC;AACtF,YAAY,EAAE,qCAAqC,EAAE,MAAM,6CAA6C,CAAC"}
|
package/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAElD,OAAO,KAAK,4BAA4B,MAAM,qDAAqD,CAAC;AAGpG,OAAO,KAAK,cAAc,MAAM,sCAAsC,CAAC;AAWvE,OAAO,KAAK,sBAAsB,MAAM,6CAA6C,CAAC"}
|