@crawlee/basic 4.0.0-beta.120 → 4.0.0-beta.122
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, ConcurrencySystemOptions,
|
|
2
|
-
import { ConcurrencySystem, ContextPipeline, Dataset, RequestQueue } from '@crawlee/core';
|
|
3
|
-
import
|
|
1
|
+
import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, ConcurrencySystemOptions, CrawleeLogger, CrawlingContext, DatasetExportOptions, EnqueueLinksOptions, FinalStatistics, GetUserDataFromRequest, IConcurrencySystem, IProxyConfiguration, IRequestLoader, IRequestManager, IStatistics, Request, RequestsLike, RouterHandler, RouterRoutes, SkippedRequestCallback, Source, StatisticState, StorageIdentifier, StorageWritePolicy, TaskLoopPredicates, TypedRequestsLike } from '@crawlee/core';
|
|
2
|
+
import { ConcurrencySystem, Configuration, ContextPipeline, Dataset, EventManager, RequestQueue } from '@crawlee/core';
|
|
3
|
+
import { BaseHttpClient } from '@crawlee/http-client';
|
|
4
|
+
import type { Awaitable, BatchAddRequestsResult, Dictionary, ISession, ISessionPool, ProxyInfo, SetStatusMessageOptions, StorageBackend } from '@crawlee/types';
|
|
4
5
|
import { RobotsTxtFile } from '@crawlee/utils';
|
|
5
|
-
import { type BasePredicate } from 'ow';
|
|
6
6
|
import type { ReadonlyDeep, SetRequired } from 'type-fest';
|
|
7
|
+
import { z } from 'zod';
|
|
7
8
|
import { TimeoutError } from '@apify/timeout';
|
|
8
9
|
export interface BasicCrawlingContext<UserData extends Dictionary = Dictionary> extends CrawlingContext<UserData> {
|
|
9
10
|
}
|
|
@@ -514,80 +515,93 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
514
515
|
protected readonly onSkippedRequest?: SkippedRequestCallback;
|
|
515
516
|
protected readonly identity: CrawlerIdentity;
|
|
516
517
|
protected static optionsShape: {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
560
|
-
blockedStatusCodes: import("ow").ArrayPredicate<number>;
|
|
561
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
562
|
-
retryOnBlocked: import("ow").BooleanPredicate & BasePredicate<boolean | undefined>;
|
|
563
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
564
|
-
respectRobotsTxtFile: import("ow").AnyPredicate<boolean | object>;
|
|
565
|
-
transactionalStorage: BasePredicate<boolean | Partial<StorageWritePolicy> | undefined>;
|
|
566
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
567
|
-
onSkippedRequest: import("ow").Predicate<Function> & BasePredicate<Function | undefined>;
|
|
568
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
569
|
-
httpClient: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
|
|
570
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
571
|
-
configuration: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
|
|
572
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
573
|
-
storageBackend: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
|
|
574
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
575
|
-
eventManager: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
|
|
576
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
577
|
-
logger: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
|
|
578
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
579
|
-
minConcurrency: import("ow").NumberPredicate & BasePredicate<number | undefined>;
|
|
580
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
581
|
-
maxConcurrency: import("ow").NumberPredicate & BasePredicate<number | undefined>;
|
|
582
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
583
|
-
maxRequestsPerMinute: import("ow").NumberPredicate & BasePredicate<number | undefined>;
|
|
584
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
585
|
-
keepAlive: import("ow").BooleanPredicate & BasePredicate<boolean | undefined>;
|
|
586
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
587
|
-
statistics: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
|
|
588
|
-
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
589
|
-
id: import("ow").StringPredicate & BasePredicate<string | undefined>;
|
|
518
|
+
contextPipelineBuilder: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
519
|
+
extendContext: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
520
|
+
requestList: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
521
|
+
requestQueue: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
522
|
+
requestManager: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
523
|
+
requestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
524
|
+
requestHandlerTimeoutSecs: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
525
|
+
errorHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
526
|
+
failedRequestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
527
|
+
maxRequestRetries: z.ZodDefault<z.ZodCustom<number, number>>;
|
|
528
|
+
sameDomainDelaySecs: z.ZodDefault<z.ZodCustom<number, number>>;
|
|
529
|
+
maxRequestsPerCrawl: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
530
|
+
maxCrawlDepth: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
531
|
+
taskLoopOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
532
|
+
concurrencySystem: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
533
|
+
sessionPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
534
|
+
proxyConfiguration: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
535
|
+
statusMessageLoggingInterval: z.ZodDefault<z.ZodCustom<number, number>>;
|
|
536
|
+
statusMessageCallback: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
537
|
+
additionalHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
|
|
538
|
+
ignoreHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
|
|
539
|
+
blockedStatusCodes: z.ZodOptional<z.ZodArray<z.ZodCustom<number, number>>>;
|
|
540
|
+
retryOnBlocked: z.ZodDefault<z.ZodBoolean>;
|
|
541
|
+
respectRobotsTxtFile: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCustom<Dictionary, Dictionary>]>>;
|
|
542
|
+
transactionalStorage: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
543
|
+
requestQueue: z.ZodOptional<z.ZodEnum<{
|
|
544
|
+
deferred: "deferred";
|
|
545
|
+
writeThrough: "writeThrough";
|
|
546
|
+
}>>;
|
|
547
|
+
}, z.core.$strict>]>>;
|
|
548
|
+
onSkippedRequest: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
549
|
+
httpClient: z.ZodOptional<z.ZodCustom<BaseHttpClient, BaseHttpClient>>;
|
|
550
|
+
configuration: z.ZodOptional<z.ZodCustom<Configuration, Configuration>>;
|
|
551
|
+
storageBackend: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
552
|
+
eventManager: z.ZodOptional<z.ZodCustom<EventManager, EventManager>>;
|
|
553
|
+
logger: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
554
|
+
minConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
555
|
+
maxConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
556
|
+
maxRequestsPerMinute: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
557
|
+
keepAlive: z.ZodOptional<z.ZodBoolean>;
|
|
558
|
+
statistics: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
559
|
+
id: z.ZodOptional<z.ZodString>;
|
|
590
560
|
};
|
|
561
|
+
protected static optionsSchema: z.ZodObject<{
|
|
562
|
+
contextPipelineBuilder: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
563
|
+
extendContext: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
564
|
+
requestList: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
565
|
+
requestQueue: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
566
|
+
requestManager: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
567
|
+
requestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
568
|
+
requestHandlerTimeoutSecs: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
569
|
+
errorHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
570
|
+
failedRequestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
571
|
+
maxRequestRetries: z.ZodDefault<z.ZodCustom<number, number>>;
|
|
572
|
+
sameDomainDelaySecs: z.ZodDefault<z.ZodCustom<number, number>>;
|
|
573
|
+
maxRequestsPerCrawl: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
574
|
+
maxCrawlDepth: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
575
|
+
taskLoopOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
576
|
+
concurrencySystem: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
577
|
+
sessionPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
578
|
+
proxyConfiguration: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
579
|
+
statusMessageLoggingInterval: z.ZodDefault<z.ZodCustom<number, number>>;
|
|
580
|
+
statusMessageCallback: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
581
|
+
additionalHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
|
|
582
|
+
ignoreHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
|
|
583
|
+
blockedStatusCodes: z.ZodOptional<z.ZodArray<z.ZodCustom<number, number>>>;
|
|
584
|
+
retryOnBlocked: z.ZodDefault<z.ZodBoolean>;
|
|
585
|
+
respectRobotsTxtFile: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCustom<Dictionary, Dictionary>]>>;
|
|
586
|
+
transactionalStorage: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
587
|
+
requestQueue: z.ZodOptional<z.ZodEnum<{
|
|
588
|
+
deferred: "deferred";
|
|
589
|
+
writeThrough: "writeThrough";
|
|
590
|
+
}>>;
|
|
591
|
+
}, z.core.$strict>]>>;
|
|
592
|
+
onSkippedRequest: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
|
|
593
|
+
httpClient: z.ZodOptional<z.ZodCustom<BaseHttpClient, BaseHttpClient>>;
|
|
594
|
+
configuration: z.ZodOptional<z.ZodCustom<Configuration, Configuration>>;
|
|
595
|
+
storageBackend: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
596
|
+
eventManager: z.ZodOptional<z.ZodCustom<EventManager, EventManager>>;
|
|
597
|
+
logger: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
|
|
598
|
+
minConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
599
|
+
maxConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
600
|
+
maxRequestsPerMinute: z.ZodOptional<z.ZodCustom<number, number>>;
|
|
601
|
+
keepAlive: z.ZodOptional<z.ZodBoolean>;
|
|
602
|
+
statistics: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
|
|
603
|
+
id: z.ZodOptional<z.ZodString>;
|
|
604
|
+
}, z.core.$strict>;
|
|
591
605
|
/**
|
|
592
606
|
* All `BasicCrawler` parameters are passed via an options object.
|
|
593
607
|
*/
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
|
-
import { AutoscaledPool, bindMethodsToServiceLocator, BLOCKED_STATUS_CODES, ConcurrencySystem, ContextPipeline, ContextPipelineCleanupError, ContextPipelineInitializationError, ContextPipelineInterruptedError, createStorageTransaction, CriticalError, currentStorageTransaction, Dataset, enqueueLinks, EnqueueStrategy, EventType, getObjectType, KeyValueStore, log, LogLevel, mergeCookies, MissingSessionError, NavigationSkippedError, NonRetryableError, OwnedOrInjected, purgeDefaultStorages, RequestHandlerError, parseRetryAfterHeader, RequestThrottledError, RequestManagerTandem, RequestQueue, RequestState, RetryRequestError, supportsDomainThrottling, Router, ServiceLocator, serviceLocator, Session, SessionError, SessionPool, Statistics, validateUserData, validators, withDirectStorageAccess, } from '@crawlee/core';
|
|
4
|
-
import { FetchHttpClient } from '@crawlee/http-client';
|
|
3
|
+
import { AutoscaledPool, bindMethodsToServiceLocator, BLOCKED_STATUS_CODES, ConcurrencySystem, Configuration, ContextPipeline, ContextPipelineCleanupError, ContextPipelineInitializationError, ContextPipelineInterruptedError, createStorageTransaction, CriticalError, currentStorageTransaction, Dataset, enqueueLinks, EnqueueStrategy, EventManager, EventType, getObjectType, KeyValueStore, log, LogLevel, mergeCookies, MissingSessionError, NavigationSkippedError, NonRetryableError, OwnedOrInjected, parseArgument, purgeDefaultStorages, RequestHandlerError, parseRetryAfterHeader, RequestThrottledError, RequestManagerTandem, RequestQueue, RequestState, RetryRequestError, supportsDomainThrottling, Router, schemas, ServiceLocator, serviceLocator, Session, SessionError, SessionPool, Statistics, validateUserData, validators, withDirectStorageAccess, } from '@crawlee/core';
|
|
4
|
+
import { BaseHttpClient, FetchHttpClient } from '@crawlee/http-client';
|
|
5
5
|
import { isAsyncIterable, isIterable, ROTATE_PROXY_ERRORS } from '@crawlee/utils/internal';
|
|
6
6
|
import { RobotsTxtFile } from '@crawlee/utils';
|
|
7
|
-
import ow, { ArgumentError } from 'ow';
|
|
8
7
|
import { getDomain } from 'tldts';
|
|
8
|
+
import { z } from 'zod';
|
|
9
9
|
import { LruCache } from '@apify/datastructures';
|
|
10
10
|
import { addTimeoutToPromise, extendTimeout, TimeoutError } from '@apify/timeout';
|
|
11
11
|
import { cryptoRandomObjectId } from '@apify/utilities';
|
|
12
12
|
import { extendTimeoutKey, navigationDeadlineKey, raceWithTimeout, timeoutExpiredKey, } from './request-timeout.js';
|
|
13
13
|
import { createSendRequest } from './send-request.js';
|
|
14
|
-
class LazyDefaultHttpClient {
|
|
14
|
+
class LazyDefaultHttpClient extends BaseHttpClient {
|
|
15
15
|
#delegatePromise;
|
|
16
16
|
constructor(options) {
|
|
17
|
+
super(options);
|
|
17
18
|
this.#delegatePromise = import('@crawlee/impit-client')
|
|
18
19
|
.then(({ ImpitHttpClient }) => new ImpitHttpClient(options))
|
|
19
20
|
.catch(() => {
|
|
@@ -22,6 +23,9 @@ class LazyDefaultHttpClient {
|
|
|
22
23
|
return new FetchHttpClient(options);
|
|
23
24
|
});
|
|
24
25
|
}
|
|
26
|
+
fetch() {
|
|
27
|
+
throw new Error('LazyDefaultHttpClient delegates `sendRequest` entirely; `fetch` is never called.');
|
|
28
|
+
}
|
|
25
29
|
async sendRequest(...args) {
|
|
26
30
|
return (await this.#delegatePromise).sendRequest(...args);
|
|
27
31
|
}
|
|
@@ -200,72 +204,77 @@ export class BasicCrawler {
|
|
|
200
204
|
identity;
|
|
201
205
|
#contextPipelineOptions;
|
|
202
206
|
static optionsShape = {
|
|
203
|
-
contextPipelineBuilder:
|
|
204
|
-
extendContext:
|
|
205
|
-
requestList:
|
|
206
|
-
requestQueue:
|
|
207
|
-
requestManager:
|
|
207
|
+
contextPipelineBuilder: schemas.anyObject.optional(),
|
|
208
|
+
extendContext: schemas.anyFunction.optional(),
|
|
209
|
+
requestList: validators.requestList.optional(),
|
|
210
|
+
requestQueue: validators.requestQueue.optional(),
|
|
211
|
+
requestManager: validators.requestManager.optional(),
|
|
208
212
|
// Subclasses override this function instead of passing it
|
|
209
213
|
// in constructor, so this validation needs to apply only
|
|
210
214
|
// if the user creates an instance of BasicCrawler directly.
|
|
211
|
-
requestHandler:
|
|
212
|
-
requestHandlerTimeoutSecs:
|
|
213
|
-
errorHandler:
|
|
214
|
-
failedRequestHandler:
|
|
215
|
-
maxRequestRetries:
|
|
216
|
-
sameDomainDelaySecs:
|
|
217
|
-
maxRequestsPerCrawl:
|
|
218
|
-
maxCrawlDepth:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
215
|
+
requestHandler: schemas.anyFunction.optional(),
|
|
216
|
+
requestHandlerTimeoutSecs: schemas.anyNumber.optional(),
|
|
217
|
+
errorHandler: schemas.anyFunction.optional(),
|
|
218
|
+
failedRequestHandler: schemas.anyFunction.optional(),
|
|
219
|
+
maxRequestRetries: schemas.anyNumber.default(3),
|
|
220
|
+
sameDomainDelaySecs: schemas.anyNumber.default(0),
|
|
221
|
+
maxRequestsPerCrawl: schemas.anyNumber.optional(),
|
|
222
|
+
maxCrawlDepth: schemas.anyNumber.optional(),
|
|
223
|
+
// No zod default — subclasses provide their own fallback (e.g. HTTP-optimized pool options).
|
|
224
|
+
taskLoopOptions: schemas.anyObject.optional(),
|
|
225
|
+
concurrencySystem: schemas.anyObject.optional(),
|
|
226
|
+
sessionPool: validators.sessionPool.optional(),
|
|
227
|
+
proxyConfiguration: validators.proxyConfiguration.optional(),
|
|
228
|
+
statusMessageLoggingInterval: schemas.anyNumber.default(10),
|
|
229
|
+
statusMessageCallback: schemas.anyFunction.optional(),
|
|
230
|
+
additionalHttpErrorStatusCodes: schemas.arrayOf(schemas.anyNumber, 'numbers').default(() => []),
|
|
231
|
+
ignoreHttpErrorStatusCodes: schemas.arrayOf(schemas.anyNumber, 'numbers').default(() => []),
|
|
232
|
+
blockedStatusCodes: schemas.arrayOf(schemas.anyNumber, 'numbers').optional(),
|
|
233
|
+
retryOnBlocked: z.boolean().default(false),
|
|
234
|
+
respectRobotsTxtFile: z.union([z.boolean(), schemas.anyObject]).default(false),
|
|
235
|
+
transactionalStorage: z
|
|
236
|
+
.union([z.boolean(), z.strictObject({ requestQueue: z.enum(['deferred', 'writeThrough']).optional() })])
|
|
237
|
+
.optional(),
|
|
238
|
+
onSkippedRequest: schemas.anyFunction.optional(),
|
|
239
|
+
httpClient: schemas.httpClient.optional(),
|
|
240
|
+
configuration: z.instanceof(Configuration).optional(),
|
|
241
|
+
storageBackend: validators.storageBackend.optional(),
|
|
242
|
+
eventManager: z.instanceof(EventManager).optional(),
|
|
243
|
+
logger: validators.logger.optional(),
|
|
239
244
|
// AutoscaledPool shorthands
|
|
240
|
-
minConcurrency:
|
|
241
|
-
maxConcurrency:
|
|
242
|
-
maxRequestsPerMinute:
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
245
|
+
minConcurrency: schemas.anyNumber.optional(),
|
|
246
|
+
maxConcurrency: schemas.anyNumber.optional(),
|
|
247
|
+
maxRequestsPerMinute: schemas.anyNumber
|
|
248
|
+
.refine((value) => Number.isInteger(value) || value === Infinity, 'Expected an integer or infinite number')
|
|
249
|
+
.refine((value) => value >= 1, 'Expected a number greater than or equal to 1')
|
|
250
|
+
.optional(),
|
|
251
|
+
keepAlive: z.boolean().optional(),
|
|
252
|
+
statistics: schemas.anyObject.optional(),
|
|
253
|
+
id: z.string().optional(),
|
|
246
254
|
};
|
|
255
|
+
static optionsSchema = z.strictObject(BasicCrawler.optionsShape);
|
|
247
256
|
/**
|
|
248
257
|
* All `BasicCrawler` parameters are passed via an options object.
|
|
249
258
|
*/
|
|
250
259
|
constructor(options = {}) {
|
|
251
|
-
|
|
260
|
+
const parsedOptions = parseArgument(options, BasicCrawler.optionsSchema, 'BasicCrawlerOptions');
|
|
252
261
|
const {
|
|
253
262
|
// oxlint-disable-next-line typescript/no-deprecated -- still accepted and folded into `requestManager` for back-compat
|
|
254
263
|
requestList,
|
|
255
264
|
// oxlint-disable-next-line typescript/no-deprecated -- still accepted and folded into `requestManager` for back-compat
|
|
256
|
-
requestQueue, requestManager, maxRequestRetries
|
|
265
|
+
requestQueue, requestManager, maxRequestRetries, sameDomainDelaySecs, maxRequestsPerCrawl, maxCrawlDepth, taskLoopOptions = {}, concurrencySystem, keepAlive, sessionPool, proxyConfiguration, additionalHttpErrorStatusCodes, ignoreHttpErrorStatusCodes,
|
|
257
266
|
// Service locator options
|
|
258
267
|
configuration, storageBackend, eventManager, logger,
|
|
259
268
|
// AutoscaledPool shorthands
|
|
260
|
-
minConcurrency, maxConcurrency, maxRequestsPerMinute, blockedStatusCodes: blockedStatusCodesInput, retryOnBlocked
|
|
269
|
+
minConcurrency, maxConcurrency, maxRequestsPerMinute, blockedStatusCodes: blockedStatusCodesInput, retryOnBlocked, respectRobotsTxtFile, transactionalStorage, onSkippedRequest, requestHandler, requestHandlerTimeoutSecs, errorHandler, failedRequestHandler, statusMessageLoggingInterval, statusMessageCallback, statistics, httpClient, id, } = parsedOptions;
|
|
261
270
|
// All concurrency configuration lives on the `ConcurrencySystem`, so the shortcuts have nowhere to go once
|
|
262
271
|
// one is supplied - and silently dropping a `maxConcurrency` the user asked for is how crawls end up
|
|
263
272
|
// hammering a site.
|
|
264
273
|
if (concurrencySystem !== undefined &&
|
|
265
274
|
(minConcurrency !== undefined || maxConcurrency !== undefined || maxRequestsPerMinute !== undefined)) {
|
|
266
|
-
throw new
|
|
275
|
+
throw new Error('The `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts cannot be combined with ' +
|
|
267
276
|
'`concurrencySystem` - they configure the default `ConcurrencySystem` that a supplied one ' +
|
|
268
|
-
'replaces. Pass them to the `ConcurrencySystem` constructor instead.'
|
|
277
|
+
'replaces. Pass them to the `ConcurrencySystem` constructor instead.');
|
|
269
278
|
}
|
|
270
279
|
// Create per-crawler service locator if custom services were provided.
|
|
271
280
|
// This wraps every method on the crawler instance so that calls to the global `serviceLocator`
|
|
@@ -277,14 +286,18 @@ export class BasicCrawler {
|
|
|
277
286
|
eventManager ||
|
|
278
287
|
logger ||
|
|
279
288
|
(configuration !== undefined && configuration !== serviceLocator.getConfiguration())) {
|
|
280
|
-
|
|
289
|
+
// Inherit the ambient locator's already-set services for anything not explicitly
|
|
290
|
+
// provided - e.g. only passing a `logger` must not detach the crawler from a globally
|
|
291
|
+
// configured storage backend.
|
|
292
|
+
const ambientServices = serviceLocator.getServicesIfSet();
|
|
293
|
+
const scopedServiceLocator = new ServiceLocator(configuration ?? ambientServices.configuration, eventManager ?? ambientServices.eventManager, storageBackend ?? ambientServices.storageBackend, logger ?? ambientServices.logger);
|
|
281
294
|
serviceLocatorScope = bindMethodsToServiceLocator(scopedServiceLocator, this);
|
|
282
295
|
}
|
|
283
296
|
try {
|
|
284
297
|
serviceLocatorScope.enterScope();
|
|
285
298
|
this.#contextPipelineOptions = {
|
|
286
|
-
contextPipelineBuilder:
|
|
287
|
-
extendContext:
|
|
299
|
+
contextPipelineBuilder: parsedOptions.contextPipelineBuilder,
|
|
300
|
+
extendContext: parsedOptions.extendContext,
|
|
288
301
|
};
|
|
289
302
|
this.#log = serviceLocator.getLogger().child({ prefix: this.constructor.name });
|
|
290
303
|
// Initialize the Configuration instance to avoid lazy loading in the components
|
|
@@ -1005,9 +1018,9 @@ export class BasicCrawler {
|
|
|
1005
1018
|
const isAllowedBasedOnRobotsTxtFile = this.isAllowedBasedOnRobotsTxtFile.bind(this);
|
|
1006
1019
|
const maxCrawlDepth = this.maxCrawlDepth;
|
|
1007
1020
|
const validateRequestUserData = this.validateRequestUserData.bind(this);
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1021
|
+
if (!isIterable(requests) && !isAsyncIterable(requests)) {
|
|
1022
|
+
throw new Error(`Expected an iterable or async iterable, got ${getObjectType(requests)}`);
|
|
1023
|
+
}
|
|
1011
1024
|
async function* filteredRequests() {
|
|
1012
1025
|
for await (const request of requests) {
|
|
1013
1026
|
const url = typeof request === 'string' ? request : request.url;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Request as CrawleeRequest } from '@crawlee/core';
|
|
2
|
-
import type { BaseHttpClient
|
|
2
|
+
import type { BaseHttpClient } from '@crawlee/http-client';
|
|
3
|
+
import type { HttpRequestOptions, ISession, SendRequestOptions } from '@crawlee/types';
|
|
3
4
|
/**
|
|
4
5
|
* Prepares a function to be used as the `sendRequest` context helper.
|
|
5
6
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/basic",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.122",
|
|
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"
|
|
@@ -42,18 +42,18 @@
|
|
|
42
42
|
"@apify/datastructures": "^2.0.0",
|
|
43
43
|
"@apify/timeout": "^0.4.4",
|
|
44
44
|
"@apify/utilities": "^2.15.5",
|
|
45
|
-
"@crawlee/core": "4.0.0-beta.
|
|
46
|
-
"@crawlee/http-client": "4.0.0-beta.
|
|
47
|
-
"@crawlee/types": "4.0.0-beta.
|
|
48
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
45
|
+
"@crawlee/core": "4.0.0-beta.122",
|
|
46
|
+
"@crawlee/http-client": "4.0.0-beta.122",
|
|
47
|
+
"@crawlee/types": "4.0.0-beta.122",
|
|
48
|
+
"@crawlee/utils": "4.0.0-beta.122",
|
|
49
49
|
"csv-stringify": "^6.5.2",
|
|
50
|
-
"ow": "^2.0.0",
|
|
51
50
|
"tldts": "^7.0.6",
|
|
52
51
|
"tslib": "^2.8.1",
|
|
53
|
-
"type-fest": "^4.41.0"
|
|
52
|
+
"type-fest": "^4.41.0",
|
|
53
|
+
"zod": "^4.4.3"
|
|
54
54
|
},
|
|
55
55
|
"optionalDependencies": {
|
|
56
|
-
"@crawlee/impit-client": "^4.0.0-beta.
|
|
56
|
+
"@crawlee/impit-client": "^4.0.0-beta.122"
|
|
57
57
|
},
|
|
58
58
|
"lerna": {
|
|
59
59
|
"command": {
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "2c3e87fefdb9e1fca4c144f03167d8524cfdc2e5"
|
|
66
66
|
}
|