@crawlee/basic 4.0.0-beta.80 → 4.0.0-beta.81
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/internals/basic-crawler.d.ts +38 -45
- package/internals/basic-crawler.js +25 -39
- package/package.json +7 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, AutoscaledPoolOptions, Configuration, CrawleeLogger, CrawlingContext, DatasetExportOptions, EnqueueLinksOptions, EventManager, FinalStatistics, GetUserDataFromRequest, IRequestLoader, IRequestManager,
|
|
1
|
+
import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, AutoscaledPoolOptions, Configuration, CrawleeLogger, CrawlingContext, DatasetExportOptions, EnqueueLinksOptions, EventManager, FinalStatistics, GetUserDataFromRequest, IProxyConfiguration, IRequestLoader, IRequestManager, Request, RequestsLike, RouterHandler, RouterRoutes, SkippedRequestCallback, Source, StatisticsOptions, StatisticState, StorageIdentifier } from '@crawlee/core';
|
|
2
2
|
import { AutoscaledPool, ContextPipeline, Dataset, RequestQueue, Statistics } from '@crawlee/core';
|
|
3
3
|
import type { Awaitable, BaseHttpClient, BatchAddRequestsResult, Dictionary, ISession, ISessionPool, ProxyInfo, SetStatusMessageOptions, StorageBackend } from '@crawlee/types';
|
|
4
4
|
import { RobotsTxtFile } from '@crawlee/utils';
|
|
@@ -252,7 +252,7 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
|
|
|
252
252
|
* If set, the crawler will be configured for all connections to use
|
|
253
253
|
* the Proxy URLs provided and rotated according to the configuration.
|
|
254
254
|
*/
|
|
255
|
-
proxyConfiguration?:
|
|
255
|
+
proxyConfiguration?: IProxyConfiguration;
|
|
256
256
|
/**
|
|
257
257
|
* Custom configuration to use for this crawler.
|
|
258
258
|
* If provided, the crawler will use its own ServiceLocator instance instead of the global one.
|
|
@@ -409,7 +409,7 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
409
409
|
* A reference to the underlying session pool that manages the crawler's {@link Session|sessions}. Typed as
|
|
410
410
|
* {@link ISessionPool} so custom implementations can be plugged in via the `sessionPool` constructor option.
|
|
411
411
|
*/
|
|
412
|
-
sessionPool: ISessionPool;
|
|
412
|
+
readonly sessionPool: ISessionPool;
|
|
413
413
|
/**
|
|
414
414
|
* Set when the crawler constructed its own {@link SessionPool} (no `sessionPool` option was provided).
|
|
415
415
|
* Holds the same instance as `sessionPool`, but typed as the concrete class so the crawler can call
|
|
@@ -438,10 +438,10 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
438
438
|
*/
|
|
439
439
|
autoscaledPool?: AutoscaledPool;
|
|
440
440
|
/**
|
|
441
|
-
* A reference to the underlying {@link
|
|
441
|
+
* A reference to the underlying {@link IProxyConfiguration} instance that manages the crawler's proxies.
|
|
442
442
|
* Only available if used by the crawler.
|
|
443
443
|
*/
|
|
444
|
-
proxyConfiguration?:
|
|
444
|
+
readonly proxyConfiguration?: IProxyConfiguration;
|
|
445
445
|
/**
|
|
446
446
|
* Default {@link Router} instance that will be used if we don't specify any {@link BasicCrawlerOptions.requestHandler|`requestHandler`}.
|
|
447
447
|
* See {@link Router.addHandler|`router.addHandler()`} and {@link Router.addDefaultHandler|`router.addDefaultHandler()`}.
|
|
@@ -463,33 +463,29 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
463
463
|
get contextPipeline(): ContextPipeline<CrawlingContext, ExtendedContext>;
|
|
464
464
|
running: boolean;
|
|
465
465
|
hasFinishedBefore: boolean;
|
|
466
|
-
|
|
466
|
+
private unexpectedStop;
|
|
467
467
|
get log(): CrawleeLogger;
|
|
468
|
-
protected requestHandler: RequestHandler<ExtendedContext>;
|
|
469
|
-
protected errorHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
|
|
470
|
-
protected failedRequestHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
|
|
471
|
-
|
|
472
|
-
protected internalTimeoutMillis: number;
|
|
473
|
-
protected maxRequestRetries: number;
|
|
474
|
-
protected maxCrawlDepth?: number;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
protected maxRequestsPerCrawl?: number;
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
protected statusMessageLoggingInterval: number;
|
|
482
|
-
protected statusMessageCallback?: StatusMessageCallback;
|
|
468
|
+
protected readonly requestHandler: RequestHandler<ExtendedContext>;
|
|
469
|
+
protected readonly errorHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
|
|
470
|
+
protected readonly failedRequestHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
|
|
471
|
+
private requestHandlerTimeoutMillis;
|
|
472
|
+
protected readonly internalTimeoutMillis: number;
|
|
473
|
+
protected readonly maxRequestRetries: number;
|
|
474
|
+
protected readonly maxCrawlDepth?: number;
|
|
475
|
+
private sameDomainDelayMillis;
|
|
476
|
+
private domainAccessedTime;
|
|
477
|
+
protected readonly maxRequestsPerCrawl?: number;
|
|
478
|
+
private get handledRequestsCount();
|
|
479
|
+
private statusMessageLoggingInterval;
|
|
480
|
+
private statusMessageCallback?;
|
|
483
481
|
protected blockedStatusCodes: Set<number>;
|
|
484
|
-
protected additionalHttpErrorStatusCodes: Set<number>;
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
protected httpClient: BaseHttpClient;
|
|
488
|
-
protected retryOnBlocked: boolean;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
};
|
|
492
|
-
protected onSkippedRequest?: SkippedRequestCallback;
|
|
482
|
+
protected readonly additionalHttpErrorStatusCodes: Set<number>;
|
|
483
|
+
private ignoreHttpErrorStatusCodes;
|
|
484
|
+
private autoscaledPoolOptions;
|
|
485
|
+
protected readonly httpClient: BaseHttpClient;
|
|
486
|
+
protected readonly retryOnBlocked: boolean;
|
|
487
|
+
private respectRobotsTxtFile;
|
|
488
|
+
protected readonly onSkippedRequest?: SkippedRequestCallback;
|
|
493
489
|
private _closeEvents?;
|
|
494
490
|
private loggedPerRun;
|
|
495
491
|
private readonly robotsTxtFileCache;
|
|
@@ -580,9 +576,7 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
580
576
|
* Builds the basic context pipeline that transforms `{ request }` into a full `CrawlingContext`.
|
|
581
577
|
* This handles base context creation, session resolution, and context helpers.
|
|
582
578
|
*/
|
|
583
|
-
|
|
584
|
-
request: Request;
|
|
585
|
-
}, CrawlingContext>;
|
|
579
|
+
private buildBasicContextPipeline;
|
|
586
580
|
private checkRobotsTxt;
|
|
587
581
|
/**
|
|
588
582
|
* Builds the subclass-specific context pipeline that transforms a `CrawlingContext` into the crawler's target context type.
|
|
@@ -662,7 +656,7 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
662
656
|
* the request's label. Applied by the crawler on the add paths it owns — `crawler.addRequests`,
|
|
663
657
|
* `crawler.run`, `context.addRequests` and `context.enqueueLinks`.
|
|
664
658
|
*/
|
|
665
|
-
|
|
659
|
+
private validateRequestUserData;
|
|
666
660
|
useState<State extends Dictionary = Dictionary>(defaultValue?: State): Promise<State>;
|
|
667
661
|
protected getPendingRequestCountApproximation(): Promise<number>;
|
|
668
662
|
protected calculateEnqueuedRequestLimit(explicitLimit?: number): Promise<number | undefined>;
|
|
@@ -708,19 +702,19 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
708
702
|
protected _throwOnBlockedRequest(statusCode: number): void;
|
|
709
703
|
private isAllowedBasedOnRobotsTxtFile;
|
|
710
704
|
protected getRobotsTxtFileForUrl(url: string): Promise<RobotsTxtFile | undefined>;
|
|
711
|
-
|
|
705
|
+
private pauseOnMigration;
|
|
712
706
|
/**
|
|
713
707
|
* Fetches the next request to process from the underlying request provider.
|
|
714
708
|
*/
|
|
715
|
-
|
|
709
|
+
private fetchNextRequest;
|
|
716
710
|
/**
|
|
717
711
|
* Delays processing of the request based on the `sameDomainDelaySecs` option,
|
|
718
712
|
* adding it back to the queue after the timeout passes. Returns `true` if the request
|
|
719
713
|
* should be ignored and will be reclaimed to the queue once ready.
|
|
720
714
|
*/
|
|
721
|
-
|
|
715
|
+
private delayRequest;
|
|
722
716
|
/** Handles a single request - runs the request handler with retries, error handling, and lifecycle management. */
|
|
723
|
-
|
|
717
|
+
private handleRequest;
|
|
724
718
|
/**
|
|
725
719
|
* Wrapper around the crawling context's `enqueueLinks` method:
|
|
726
720
|
* - Injects `crawlDepth` to each request being added based on the crawling context request.
|
|
@@ -738,15 +732,15 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
738
732
|
* Run async callback with given timeout and retry. Returns the result of the callback.
|
|
739
733
|
* @ignore
|
|
740
734
|
*/
|
|
741
|
-
|
|
735
|
+
private timeoutAndRetry;
|
|
742
736
|
/**
|
|
743
737
|
* Returns true if either RequestList or RequestQueue have a request ready for processing.
|
|
744
738
|
*/
|
|
745
|
-
|
|
739
|
+
private isTaskReadyFunction;
|
|
746
740
|
/**
|
|
747
741
|
* Returns true if both RequestList and RequestQueue have all requests finished.
|
|
748
742
|
*/
|
|
749
|
-
|
|
743
|
+
private defaultIsFinishedFunction;
|
|
750
744
|
/**
|
|
751
745
|
* Unwraps errors thrown by the context pipeline to get the actual user error.
|
|
752
746
|
* RequestHandlerError and ContextPipelineInitializationError wrap the actual error.
|
|
@@ -757,16 +751,15 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
757
751
|
*
|
|
758
752
|
* @param request The request object, passed separately to circumvent potential dynamic logic in crawlingContext.request
|
|
759
753
|
*/
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
protected _handleFailedRequestHandler(crawlingContext: CrawlingContext, error: Error): Promise<void>;
|
|
754
|
+
private requestFunctionErrorHandler;
|
|
755
|
+
private handleFailedRequestHandler;
|
|
763
756
|
/**
|
|
764
757
|
* Resolves the most verbose error message from a thrown error
|
|
765
758
|
* @param error The error received
|
|
766
759
|
* @returns The message to be logged
|
|
767
760
|
*/
|
|
768
761
|
protected _getMessageFromError(error: Error, forceStack?: boolean): string | TimeoutError | undefined;
|
|
769
|
-
|
|
762
|
+
private canRequestBeRetried;
|
|
770
763
|
/**
|
|
771
764
|
* Stops the crawler immediately.
|
|
772
765
|
*
|
|
@@ -93,7 +93,7 @@ export class BasicCrawler {
|
|
|
93
93
|
*/
|
|
94
94
|
autoscaledPool;
|
|
95
95
|
/**
|
|
96
|
-
* A reference to the underlying {@link
|
|
96
|
+
* A reference to the underlying {@link IProxyConfiguration} instance that manages the crawler's proxies.
|
|
97
97
|
* Only available if used by the crawler.
|
|
98
98
|
*/
|
|
99
99
|
proxyConfiguration;
|
|
@@ -144,11 +144,6 @@ export class BasicCrawler {
|
|
|
144
144
|
get handledRequestsCount() {
|
|
145
145
|
return this.stats.state.requestsFinished + this.stats.state.requestsFailed;
|
|
146
146
|
}
|
|
147
|
-
/** @deprecated Setting `handledRequestsCount` directly is no longer supported. The count is now derived from `this.stats`. */
|
|
148
|
-
set handledRequestsCount(_value) {
|
|
149
|
-
throw new Error('Setting `handledRequestsCount` directly is no longer supported. ' +
|
|
150
|
-
'The count is now derived from `this.stats.state.requestsFinished` and `this.stats.state.requestsFailed`.');
|
|
151
|
-
}
|
|
152
147
|
statusMessageLoggingInterval;
|
|
153
148
|
statusMessageCallback;
|
|
154
149
|
blockedStatusCodes = new Set();
|
|
@@ -357,7 +352,7 @@ export class BasicCrawler {
|
|
|
357
352
|
// (e.g., doesn't match enqueue strategy after redirect). Just return gracefully.
|
|
358
353
|
if (error instanceof ContextPipelineInterruptedError) {
|
|
359
354
|
this.stats.discardJob(request.id || request.uniqueKey);
|
|
360
|
-
await this.
|
|
355
|
+
await this.timeoutAndRetry(async () => this.requestManager?.markRequestAsHandled(request), this.internalTimeoutMillis, `Marking request ${crawlingContext.request.url} (${crawlingContext.request.id}) as handled timed out after ${this.internalTimeoutMillis / 1e3} seconds.`);
|
|
361
356
|
return;
|
|
362
357
|
}
|
|
363
358
|
// If the error happened during pipeline initialization (e.g., navigation timeout, session/proxy error,
|
|
@@ -365,8 +360,8 @@ export class BasicCrawler {
|
|
|
365
360
|
const isPipelineError = error instanceof ContextPipelineInitializationError || error instanceof SessionError;
|
|
366
361
|
if (isPipelineError) {
|
|
367
362
|
const unwrappedError = this.unwrapError(error);
|
|
368
|
-
await this.
|
|
369
|
-
// SessionError already retired the session in `
|
|
363
|
+
await this.requestFunctionErrorHandler(unwrappedError, crawlingContext, request, this.requestManager);
|
|
364
|
+
// SessionError already retired the session in `requestFunctionErrorHandler`;
|
|
370
365
|
// skip `markBad` to avoid double-counting usage/error score.
|
|
371
366
|
if (!(unwrappedError instanceof SessionError)) {
|
|
372
367
|
crawlingContext.session?.markBad();
|
|
@@ -392,7 +387,7 @@ export class BasicCrawler {
|
|
|
392
387
|
'Ongoing requests will be allowed to complete.');
|
|
393
388
|
return false;
|
|
394
389
|
}
|
|
395
|
-
return isTaskReadyFunction ? await isTaskReadyFunction() : await this.
|
|
390
|
+
return isTaskReadyFunction ? await isTaskReadyFunction() : await this.isTaskReadyFunction();
|
|
396
391
|
},
|
|
397
392
|
isFinishedFunction: async () => {
|
|
398
393
|
if (isMaxPagesExceeded()) {
|
|
@@ -407,7 +402,7 @@ export class BasicCrawler {
|
|
|
407
402
|
}
|
|
408
403
|
const isFinished = isFinishedFunction
|
|
409
404
|
? await isFinishedFunction()
|
|
410
|
-
: await this.
|
|
405
|
+
: await this.defaultIsFinishedFunction();
|
|
411
406
|
if (isFinished) {
|
|
412
407
|
const reason = isFinishedFunction
|
|
413
408
|
? "Crawler's custom isFinishedFunction() returned true, the crawler will shut down."
|
|
@@ -481,7 +476,7 @@ export class BasicCrawler {
|
|
|
481
476
|
};
|
|
482
477
|
}
|
|
483
478
|
async resolveRequest() {
|
|
484
|
-
const request = await this.
|
|
479
|
+
const request = await this.timeoutAndRetry(this.fetchNextRequest.bind(this), this.internalTimeoutMillis, `Fetching next request timed out after ${this.internalTimeoutMillis / 1e3} seconds.`);
|
|
485
480
|
// Reset loadedUrl so an old one is not carried over to retries.
|
|
486
481
|
if (request) {
|
|
487
482
|
request.loadedUrl = undefined;
|
|
@@ -489,7 +484,7 @@ export class BasicCrawler {
|
|
|
489
484
|
return request;
|
|
490
485
|
}
|
|
491
486
|
async resolveSession({ request }) {
|
|
492
|
-
const session = await this.
|
|
487
|
+
const session = await this.timeoutAndRetry(async () => {
|
|
493
488
|
const existingSession = await this.sessionPool.getSession(request.sessionId);
|
|
494
489
|
if (!existingSession) {
|
|
495
490
|
throw new ContextPipelineInitializationError(new MissingSessionError(request.sessionId));
|
|
@@ -654,11 +649,11 @@ export class BasicCrawler {
|
|
|
654
649
|
this.setStatusMessage('Starting the crawler.', { level: 'INFO' });
|
|
655
650
|
const sigintHandler = async () => {
|
|
656
651
|
this.log.warning('Pausing... Press CTRL+C again to force exit. To resume, do: CRAWLEE_PURGE_ON_START=0 npm start');
|
|
657
|
-
await this.
|
|
652
|
+
await this.pauseOnMigration();
|
|
658
653
|
await this.autoscaledPool.abort();
|
|
659
654
|
};
|
|
660
655
|
// Attach a listener to handle migration and aborting events gracefully.
|
|
661
|
-
const boundPauseOnMigration = this.
|
|
656
|
+
const boundPauseOnMigration = this.pauseOnMigration.bind(this);
|
|
662
657
|
process.once('SIGINT', sigintHandler);
|
|
663
658
|
const eventManager = serviceLocator.getEventManager();
|
|
664
659
|
eventManager.on("migrating" /* EventType.MIGRATING */, boundPauseOnMigration);
|
|
@@ -1021,7 +1016,7 @@ export class BasicCrawler {
|
|
|
1021
1016
|
return undefined;
|
|
1022
1017
|
}
|
|
1023
1018
|
}
|
|
1024
|
-
async
|
|
1019
|
+
async pauseOnMigration() {
|
|
1025
1020
|
if (this.autoscaledPool) {
|
|
1026
1021
|
// if run wasn't called, this is going to crash
|
|
1027
1022
|
await this.autoscaledPool.pause(SAFE_MIGRATION_WAIT_MILLIS).catch((err) => {
|
|
@@ -1058,9 +1053,9 @@ export class BasicCrawler {
|
|
|
1058
1053
|
/**
|
|
1059
1054
|
* Fetches the next request to process from the underlying request provider.
|
|
1060
1055
|
*/
|
|
1061
|
-
async
|
|
1056
|
+
async fetchNextRequest() {
|
|
1062
1057
|
if (this.requestManager === undefined) {
|
|
1063
|
-
throw new Error(`
|
|
1058
|
+
throw new Error(`fetchNextRequest called on an uninitialized crawler`);
|
|
1064
1059
|
}
|
|
1065
1060
|
return this.requestManager.fetchNextRequest();
|
|
1066
1061
|
}
|
|
@@ -1095,7 +1090,7 @@ export class BasicCrawler {
|
|
|
1095
1090
|
try {
|
|
1096
1091
|
request.state = RequestState.REQUEST_HANDLER;
|
|
1097
1092
|
await this.runRequestHandler(crawlingContext);
|
|
1098
|
-
await this.
|
|
1093
|
+
await this.timeoutAndRetry(async () => requestSource.markRequestAsHandled(request), this.internalTimeoutMillis, `Marking request ${request.url} (${request.id}) as handled timed out after ${this.internalTimeoutMillis / 1e3} seconds.`);
|
|
1099
1094
|
isRequestLocked = false; // markRequestAsHandled succeeded and unlocked the request
|
|
1100
1095
|
this.stats.finishJob(statisticsId, request.retryCount);
|
|
1101
1096
|
// reclaim session if request finishes successfully
|
|
@@ -1106,9 +1101,9 @@ export class BasicCrawler {
|
|
|
1106
1101
|
const err = this.unwrapError(rawError);
|
|
1107
1102
|
try {
|
|
1108
1103
|
request.state = RequestState.ERROR_HANDLER;
|
|
1109
|
-
await addTimeoutToPromise(async () => this.
|
|
1104
|
+
await addTimeoutToPromise(async () => this.requestFunctionErrorHandler(err, crawlingContext, request, requestSource), this.internalTimeoutMillis, `Handling request failure of ${request.url} (${request.id}) timed out after ${this.internalTimeoutMillis / 1e3} seconds.`);
|
|
1110
1105
|
if (!(err instanceof CriticalError)) {
|
|
1111
|
-
isRequestLocked = false; //
|
|
1106
|
+
isRequestLocked = false; // requestFunctionErrorHandler calls either markRequestAsHandled or reclaimRequest
|
|
1112
1107
|
}
|
|
1113
1108
|
request.state = RequestState.DONE;
|
|
1114
1109
|
}
|
|
@@ -1211,7 +1206,7 @@ export class BasicCrawler {
|
|
|
1211
1206
|
* Run async callback with given timeout and retry. Returns the result of the callback.
|
|
1212
1207
|
* @ignore
|
|
1213
1208
|
*/
|
|
1214
|
-
async
|
|
1209
|
+
async timeoutAndRetry(handler, timeout, error, maxRetries = 3, retried = 1) {
|
|
1215
1210
|
try {
|
|
1216
1211
|
return await addTimeoutToPromise(handler, timeout, error);
|
|
1217
1212
|
}
|
|
@@ -1219,7 +1214,7 @@ export class BasicCrawler {
|
|
|
1219
1214
|
if (retried <= maxRetries) {
|
|
1220
1215
|
// we retry on any error, not just timeout
|
|
1221
1216
|
this.log.warning(`${e.message} (retrying ${retried}/${maxRetries})`);
|
|
1222
|
-
return this.
|
|
1217
|
+
return this.timeoutAndRetry(handler, timeout, error, maxRetries, retried + 1);
|
|
1223
1218
|
}
|
|
1224
1219
|
throw e;
|
|
1225
1220
|
}
|
|
@@ -1227,13 +1222,13 @@ export class BasicCrawler {
|
|
|
1227
1222
|
/**
|
|
1228
1223
|
* Returns true if either RequestList or RequestQueue have a request ready for processing.
|
|
1229
1224
|
*/
|
|
1230
|
-
async
|
|
1225
|
+
async isTaskReadyFunction() {
|
|
1231
1226
|
return this.requestManager !== undefined && !(await this.requestManager.isEmpty());
|
|
1232
1227
|
}
|
|
1233
1228
|
/**
|
|
1234
1229
|
* Returns true if both RequestList and RequestQueue have all requests finished.
|
|
1235
1230
|
*/
|
|
1236
|
-
async
|
|
1231
|
+
async defaultIsFinishedFunction() {
|
|
1237
1232
|
return !this.requestManager || (await this.requestManager.isFinished());
|
|
1238
1233
|
}
|
|
1239
1234
|
/**
|
|
@@ -1253,12 +1248,12 @@ export class BasicCrawler {
|
|
|
1253
1248
|
*
|
|
1254
1249
|
* @param request The request object, passed separately to circumvent potential dynamic logic in crawlingContext.request
|
|
1255
1250
|
*/
|
|
1256
|
-
async
|
|
1251
|
+
async requestFunctionErrorHandler(error, crawlingContext, request, source) {
|
|
1257
1252
|
request.pushErrorMessage(error);
|
|
1258
1253
|
if (error instanceof CriticalError) {
|
|
1259
1254
|
throw error;
|
|
1260
1255
|
}
|
|
1261
|
-
const shouldRetryRequest = this.
|
|
1256
|
+
const shouldRetryRequest = this.canRequestBeRetried(request, error);
|
|
1262
1257
|
if (shouldRetryRequest) {
|
|
1263
1258
|
await this.stats.errorTrackerRetry.addAsync(error, crawlingContext);
|
|
1264
1259
|
await this.errorHandler?.(crawlingContext, // valid cast - ExtendedContext transitively extends CrawlingContext
|
|
@@ -1299,18 +1294,9 @@ export class BasicCrawler {
|
|
|
1299
1294
|
// Mark the request as failed and do not retry.
|
|
1300
1295
|
await source.markRequestAsHandled(request);
|
|
1301
1296
|
this.stats.failJob(request.id || request.uniqueKey, request.retryCount);
|
|
1302
|
-
await this.
|
|
1303
|
-
}
|
|
1304
|
-
async _tagUserHandlerError(cb) {
|
|
1305
|
-
try {
|
|
1306
|
-
return (await cb());
|
|
1307
|
-
}
|
|
1308
|
-
catch (e) {
|
|
1309
|
-
Object.defineProperty(e, 'triggeredFromUserHandler', { value: true });
|
|
1310
|
-
throw e;
|
|
1311
|
-
}
|
|
1297
|
+
await this.handleFailedRequestHandler(crawlingContext, error); // This function prints an error message.
|
|
1312
1298
|
}
|
|
1313
|
-
async
|
|
1299
|
+
async handleFailedRequestHandler(crawlingContext, error) {
|
|
1314
1300
|
// Always log the last error regardless if the user provided a failedRequestHandler
|
|
1315
1301
|
const { id, url, method, uniqueKey } = crawlingContext.request;
|
|
1316
1302
|
const message = this._getMessageFromError(error, true);
|
|
@@ -1339,7 +1325,7 @@ export class BasicCrawler {
|
|
|
1339
1325
|
? (error.stack ?? [error.message || error, ...stackLines].join('\n'))
|
|
1340
1326
|
: [error.message || error, userLine].join('\n');
|
|
1341
1327
|
}
|
|
1342
|
-
|
|
1328
|
+
canRequestBeRetried(request, error) {
|
|
1343
1329
|
// Request should never be retried, or the error encountered makes it not able to be retried.
|
|
1344
1330
|
if (request.noRetry || error instanceof NonRetryableError) {
|
|
1345
1331
|
return false;
|
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.81",
|
|
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,10 +42,10 @@
|
|
|
42
42
|
"@apify/datastructures": "^2.0.0",
|
|
43
43
|
"@apify/timeout": "^0.3.2",
|
|
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.81",
|
|
46
|
+
"@crawlee/http-client": "4.0.0-beta.81",
|
|
47
|
+
"@crawlee/types": "4.0.0-beta.81",
|
|
48
|
+
"@crawlee/utils": "4.0.0-beta.81",
|
|
49
49
|
"csv-stringify": "^6.5.2",
|
|
50
50
|
"fs-extra": "^11.3.0",
|
|
51
51
|
"ow": "^2.0.0",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"type-fest": "^4.41.0"
|
|
55
55
|
},
|
|
56
56
|
"optionalDependencies": {
|
|
57
|
-
"@crawlee/impit-client": "^4.0.0-beta.
|
|
57
|
+
"@crawlee/impit-client": "^4.0.0-beta.81"
|
|
58
58
|
},
|
|
59
59
|
"lerna": {
|
|
60
60
|
"command": {
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "80dc6b4fc82237e63a51a71153809ec8dfd0cc50"
|
|
67
67
|
}
|