@crawlee/basic 4.0.0-beta.87 → 4.0.0-beta.89
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 +66 -19
- package/internals/basic-crawler.js +74 -12
- package/package.json +7 -7
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AddRequestsBatchedOptions, AddRequestsBatchedResult,
|
|
2
|
-
import { AutoscaledPool, ContextPipeline, Dataset, RequestQueue, Statistics } from '@crawlee/core';
|
|
1
|
+
import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, AutoscaledPoolPredicateOptions, ConcurrencySystemOptions, Configuration, CrawleeLogger, CrawlingContext, DatasetExportOptions, EnqueueLinksOptions, EventManager, FinalStatistics, GetUserDataFromRequest, IConcurrencySystem, IProxyConfiguration, IRequestLoader, IRequestManager, Request, RequestsLike, RouterHandler, RouterRoutes, SkippedRequestCallback, Source, StatisticsOptions, StatisticState, StorageIdentifier, TypedRequestsLike } from '@crawlee/core';
|
|
2
|
+
import { AutoscaledPool, ConcurrencySystem, 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';
|
|
5
5
|
import type { ReadonlyDeep, SetRequired } from 'type-fest';
|
|
@@ -151,29 +151,49 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
|
|
|
151
151
|
*/
|
|
152
152
|
maxCrawlDepth?: number;
|
|
153
153
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
154
|
+
* Lets you override the task-loop predicates (`isFinishedFunction`, `isTaskReadyFunction`) of the crawler's
|
|
155
|
+
* underlying {@link AutoscaledPool}. The `runTaskFunction` is owned by the crawler and cannot be overridden.
|
|
156
|
+
*
|
|
157
|
+
* Concurrency is configured elsewhere — through the `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute`
|
|
158
|
+
* shortcuts, or a {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} for finer control.
|
|
159
|
+
*/
|
|
160
|
+
autoscaledPoolOptions?: AutoscaledPoolPredicateOptions;
|
|
161
|
+
/**
|
|
162
|
+
* A pre-configured concurrency governor — the component that decides whether there is free compute for one more
|
|
163
|
+
* task. Typically a {@link ConcurrencySystem}, though any {@link IConcurrencySystem} is accepted. All
|
|
164
|
+
* scaling configuration (min/max/desired concurrency, scaling ratios, `maxTasksPerMinute`, snapshotter tuning)
|
|
165
|
+
* lives on the instance itself.
|
|
166
|
+
*
|
|
167
|
+
* Inject the *same* instance into several concurrent crawlers to cap their **combined** concurrency against a
|
|
168
|
+
* single budget. Each crawler still builds and drives its own {@link AutoscaledPool}; only the load/scaling
|
|
169
|
+
* accounting is shared.
|
|
170
|
+
*
|
|
171
|
+
* Mutually exclusive with the `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts, which configure
|
|
172
|
+
* the default system this one replaces — combining the two throws.
|
|
173
|
+
*
|
|
174
|
+
* You own a supplied system's lifecycle: `start()` it before `run()` (which throws otherwise) and `stop()` it once
|
|
175
|
+
* every crawler borrowing it has finished. The crawler does neither on your behalf.
|
|
159
176
|
*/
|
|
160
|
-
|
|
177
|
+
concurrencySystem?: IConcurrencySystem;
|
|
161
178
|
/**
|
|
162
179
|
* Sets the minimum concurrency (parallelism) for the crawl. Shortcut for the
|
|
163
|
-
*
|
|
180
|
+
* {@link ConcurrencySystemOptions.minConcurrency|`minConcurrency`} option of the crawler's default
|
|
181
|
+
* {@link ConcurrencySystem}.
|
|
164
182
|
* > *WARNING:* If we set this value too high with respect to the available system memory and CPU, our crawler will run extremely slow or crash.
|
|
165
183
|
* If not sure, it's better to keep the default value and the concurrency will scale up automatically.
|
|
166
184
|
*/
|
|
167
185
|
minConcurrency?: number;
|
|
168
186
|
/**
|
|
169
187
|
* Sets the maximum concurrency (parallelism) for the crawl. Shortcut for the
|
|
170
|
-
*
|
|
188
|
+
* {@link ConcurrencySystemOptions.maxConcurrency|`maxConcurrency`} option of the crawler's default
|
|
189
|
+
* {@link ConcurrencySystem}.
|
|
171
190
|
*/
|
|
172
191
|
maxConcurrency?: number;
|
|
173
192
|
/**
|
|
174
193
|
* The maximum number of requests per minute the crawler should run.
|
|
175
194
|
* By default, this is set to `Infinity`, but we can pass any positive, non-zero integer.
|
|
176
|
-
* Shortcut for the
|
|
195
|
+
* Shortcut for the {@link ConcurrencySystemOptions.maxTasksPerMinute|`maxTasksPerMinute`} option of the
|
|
196
|
+
* crawler's default {@link ConcurrencySystem}.
|
|
177
197
|
*/
|
|
178
198
|
maxRequestsPerMinute?: number;
|
|
179
199
|
/**
|
|
@@ -335,11 +355,10 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
|
|
|
335
355
|
*
|
|
336
356
|
* New requests are only dispatched when there is enough free CPU and memory available,
|
|
337
357
|
* using the functionality provided by the {@link AutoscaledPool} class.
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
* {@link
|
|
342
|
-
* underlying {@link AutoscaledPool} constructor are available directly in the `BasicCrawler` constructor.
|
|
358
|
+
* Concurrency is tuned via the {@link BasicCrawlerOptions.minConcurrency|`minConcurrency`},
|
|
359
|
+
* {@link BasicCrawlerOptions.maxConcurrency|`maxConcurrency`} and
|
|
360
|
+
* {@link BasicCrawlerOptions.maxRequestsPerMinute|`maxRequestsPerMinute`} shortcuts, or, for finer control, by
|
|
361
|
+
* injecting a pre-configured {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`}.
|
|
343
362
|
*
|
|
344
363
|
* **Example usage:**
|
|
345
364
|
*
|
|
@@ -431,11 +450,22 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
431
450
|
*/
|
|
432
451
|
private requestManagerTimeoutsApplied;
|
|
433
452
|
/**
|
|
434
|
-
*
|
|
453
|
+
* Resolves the governor for one run: either the injected
|
|
454
|
+
* {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} (borrowed) or a freshly built default with
|
|
455
|
+
* the concurrency shortcuts folded in (owned, so the crawler starts and stops it).
|
|
456
|
+
*/
|
|
457
|
+
private readonly resolveConcurrencySystem;
|
|
458
|
+
/** As resolved by `_init()`. Absent until the first run, so a `teardown()` before it is a no-op. */
|
|
459
|
+
private concurrencySystemDep?;
|
|
460
|
+
/**
|
|
461
|
+
* A reference to the underlying {@link AutoscaledPool} class that runs the crawler's task loop.
|
|
435
462
|
* > *NOTE:* This property is only initialized after calling the {@link BasicCrawler.run|`crawler.run()`} function.
|
|
436
|
-
* We can use it to
|
|
437
|
-
* to pause the crawler by calling {@link AutoscaledPool.pause|`autoscaledPool.pause()`}
|
|
463
|
+
* We can use it to pause the crawler by calling {@link AutoscaledPool.pause|`autoscaledPool.pause()`}
|
|
438
464
|
* or to abort it by calling {@link AutoscaledPool.abort|`autoscaledPool.abort()`}.
|
|
465
|
+
*
|
|
466
|
+
* The pool only exposes read-only concurrency telemetry. To tune concurrency at runtime, keep a reference to a
|
|
467
|
+
* {@link ConcurrencySystem} and inject it via
|
|
468
|
+
* {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`}.
|
|
439
469
|
*/
|
|
440
470
|
autoscaledPool?: AutoscaledPool;
|
|
441
471
|
/**
|
|
@@ -482,6 +512,12 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
482
512
|
protected blockedStatusCodes: Set<number>;
|
|
483
513
|
protected readonly additionalHttpErrorStatusCodes: Set<number>;
|
|
484
514
|
private ignoreHttpErrorStatusCodes;
|
|
515
|
+
/**
|
|
516
|
+
* The resolved task-loop options for the crawler's own {@link AutoscaledPool} — the crawler-owned
|
|
517
|
+
* `runTaskFunction`, the (possibly user-overridden) ready/finished predicates and cadence/logging. Concurrency
|
|
518
|
+
* configuration lives on the {@link ConcurrencySystem} instead, and the pool's `consumer` identity is the
|
|
519
|
+
* crawler's own, so neither is settable here.
|
|
520
|
+
*/
|
|
485
521
|
private autoscaledPoolOptions;
|
|
486
522
|
protected readonly httpClient: BaseHttpClient;
|
|
487
523
|
protected readonly retryOnBlocked: boolean;
|
|
@@ -519,6 +555,8 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
519
555
|
maxCrawlDepth: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
|
|
520
556
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
521
557
|
autoscaledPoolOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
558
|
+
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
559
|
+
concurrencySystem: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
522
560
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
523
561
|
sessionPool: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
524
562
|
// @ts-ignore optional peer dependency or compatibility with es2022
|
|
@@ -566,6 +604,15 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
|
|
|
566
604
|
* All `BasicCrawler` parameters are passed via an options object.
|
|
567
605
|
*/
|
|
568
606
|
constructor(options?: BasicCrawlerOptions<Context, ContextExtension, ExtendedContext, Routes> & RequireContextPipeline<CrawlingContext, Context>);
|
|
607
|
+
/**
|
|
608
|
+
* Builds the crawler-owned default {@link ConcurrencySystem} from the resolved
|
|
609
|
+
* `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts. Not called when a
|
|
610
|
+
* {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} was injected.
|
|
611
|
+
*
|
|
612
|
+
* Subclasses may override this to tune the default system (e.g. {@link HttpCrawler} raises the starting
|
|
613
|
+
* concurrency and relaxes the event loop signal) while still honouring the user's shortcuts.
|
|
614
|
+
*/
|
|
615
|
+
protected createDefaultConcurrencySystem(options: ConcurrencySystemOptions): ConcurrencySystem;
|
|
569
616
|
/**
|
|
570
617
|
* Determines if the given HTTP status code is an error status code given
|
|
571
618
|
* the default behaviour and user-set preferences.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
|
-
import { AutoscaledPool, bindMethodsToServiceLocator, BLOCKED_STATUS_CODES, ContextPipeline, ContextPipelineCleanupError, ContextPipelineInitializationError, ContextPipelineInterruptedError, CriticalError, Dataset, enqueueLinks, EnqueueStrategy, getObjectType, KeyValueStore, log, LogLevel, mergeCookies, MissingSessionError, NavigationSkippedError, NonRetryableError, OwnedOrInjected, purgeDefaultStorages, RequestHandlerError, RequestManagerTandem, RequestQueue, RequestState, RetryRequestError, Router, ServiceLocator, serviceLocator, Session, SessionError, SessionPool, Statistics, validateUserData, validators, } from '@crawlee/core';
|
|
3
|
+
import { AutoscaledPool, bindMethodsToServiceLocator, BLOCKED_STATUS_CODES, ConcurrencySystem, ContextPipeline, ContextPipelineCleanupError, ContextPipelineInitializationError, ContextPipelineInterruptedError, CriticalError, Dataset, enqueueLinks, EnqueueStrategy, getObjectType, KeyValueStore, log, LogLevel, mergeCookies, MissingSessionError, NavigationSkippedError, NonRetryableError, OwnedOrInjected, purgeDefaultStorages, RequestHandlerError, RequestManagerTandem, RequestQueue, RequestState, RetryRequestError, Router, ServiceLocator, serviceLocator, Session, SessionError, SessionPool, Statistics, validateUserData, validators, } from '@crawlee/core';
|
|
4
4
|
import { FetchHttpClient } from '@crawlee/http-client';
|
|
5
5
|
import { isAsyncIterable, isIterable, RobotsTxtFile, ROTATE_PROXY_ERRORS } from '@crawlee/utils';
|
|
6
6
|
import { stringify } from 'csv-stringify/sync';
|
|
7
7
|
import { ensureDir, writeJSON } from 'fs-extra/esm';
|
|
8
|
-
import ow from 'ow';
|
|
8
|
+
import ow, { ArgumentError } from 'ow';
|
|
9
9
|
import { getDomain } from 'tldts';
|
|
10
10
|
import { LruCache } from '@apify/datastructures';
|
|
11
11
|
import { addTimeoutToPromise, TimeoutError } from '@apify/timeout';
|
|
@@ -82,11 +82,22 @@ export class BasicCrawler {
|
|
|
82
82
|
*/
|
|
83
83
|
requestManagerTimeoutsApplied = false;
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
85
|
+
* Resolves the governor for one run: either the injected
|
|
86
|
+
* {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} (borrowed) or a freshly built default with
|
|
87
|
+
* the concurrency shortcuts folded in (owned, so the crawler starts and stops it).
|
|
88
|
+
*/
|
|
89
|
+
resolveConcurrencySystem;
|
|
90
|
+
/** As resolved by `_init()`. Absent until the first run, so a `teardown()` before it is a no-op. */
|
|
91
|
+
concurrencySystemDep;
|
|
92
|
+
/**
|
|
93
|
+
* A reference to the underlying {@link AutoscaledPool} class that runs the crawler's task loop.
|
|
86
94
|
* > *NOTE:* This property is only initialized after calling the {@link BasicCrawler.run|`crawler.run()`} function.
|
|
87
|
-
* We can use it to
|
|
88
|
-
* to pause the crawler by calling {@link AutoscaledPool.pause|`autoscaledPool.pause()`}
|
|
95
|
+
* We can use it to pause the crawler by calling {@link AutoscaledPool.pause|`autoscaledPool.pause()`}
|
|
89
96
|
* or to abort it by calling {@link AutoscaledPool.abort|`autoscaledPool.abort()`}.
|
|
97
|
+
*
|
|
98
|
+
* The pool only exposes read-only concurrency telemetry. To tune concurrency at runtime, keep a reference to a
|
|
99
|
+
* {@link ConcurrencySystem} and inject it via
|
|
100
|
+
* {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`}.
|
|
90
101
|
*/
|
|
91
102
|
autoscaledPool;
|
|
92
103
|
/**
|
|
@@ -146,6 +157,12 @@ export class BasicCrawler {
|
|
|
146
157
|
blockedStatusCodes = new Set();
|
|
147
158
|
additionalHttpErrorStatusCodes;
|
|
148
159
|
ignoreHttpErrorStatusCodes;
|
|
160
|
+
/**
|
|
161
|
+
* The resolved task-loop options for the crawler's own {@link AutoscaledPool} — the crawler-owned
|
|
162
|
+
* `runTaskFunction`, the (possibly user-overridden) ready/finished predicates and cadence/logging. Concurrency
|
|
163
|
+
* configuration lives on the {@link ConcurrencySystem} instead, and the pool's `consumer` identity is the
|
|
164
|
+
* crawler's own, so neither is settable here.
|
|
165
|
+
*/
|
|
149
166
|
autoscaledPoolOptions;
|
|
150
167
|
httpClient;
|
|
151
168
|
retryOnBlocked;
|
|
@@ -173,6 +190,7 @@ export class BasicCrawler {
|
|
|
173
190
|
maxRequestsPerCrawl: ow.optional.number,
|
|
174
191
|
maxCrawlDepth: ow.optional.number,
|
|
175
192
|
autoscaledPoolOptions: ow.optional.object,
|
|
193
|
+
concurrencySystem: ow.optional.object,
|
|
176
194
|
sessionPool: ow.optional.object.validate(validators.sessionPool),
|
|
177
195
|
proxyConfiguration: ow.optional.object.validate(validators.proxyConfiguration),
|
|
178
196
|
statusMessageLoggingInterval: ow.optional.number,
|
|
@@ -205,11 +223,20 @@ export class BasicCrawler {
|
|
|
205
223
|
// oxlint-disable-next-line typescript/no-deprecated -- still accepted and folded into `requestManager` for back-compat
|
|
206
224
|
requestList,
|
|
207
225
|
// oxlint-disable-next-line typescript/no-deprecated -- still accepted and folded into `requestManager` for back-compat
|
|
208
|
-
requestQueue, requestManager, maxRequestRetries = 3, sameDomainDelaySecs = 0, maxRequestsPerCrawl, maxCrawlDepth, autoscaledPoolOptions = {}, keepAlive, sessionPool, proxyConfiguration, additionalHttpErrorStatusCodes = [], ignoreHttpErrorStatusCodes = [],
|
|
226
|
+
requestQueue, requestManager, maxRequestRetries = 3, sameDomainDelaySecs = 0, maxRequestsPerCrawl, maxCrawlDepth, autoscaledPoolOptions = {}, concurrencySystem, keepAlive, sessionPool, proxyConfiguration, additionalHttpErrorStatusCodes = [], ignoreHttpErrorStatusCodes = [],
|
|
209
227
|
// Service locator options
|
|
210
228
|
configuration, storageBackend, eventManager, logger,
|
|
211
229
|
// AutoscaledPool shorthands
|
|
212
230
|
minConcurrency, maxConcurrency, maxRequestsPerMinute, blockedStatusCodes: blockedStatusCodesInput, retryOnBlocked = false, respectRobotsTxtFile = false, onSkippedRequest, requestHandler, requestHandlerTimeoutSecs, errorHandler, failedRequestHandler, statusMessageLoggingInterval = 10, statusMessageCallback, statisticsOptions, httpClient, id, } = options;
|
|
231
|
+
// All concurrency configuration lives on the `ConcurrencySystem`, so the shortcuts have nowhere to go once
|
|
232
|
+
// one is supplied - and silently dropping a `maxConcurrency` the user asked for is how crawls end up
|
|
233
|
+
// hammering a site.
|
|
234
|
+
if (concurrencySystem !== undefined &&
|
|
235
|
+
(minConcurrency !== undefined || maxConcurrency !== undefined || maxRequestsPerMinute !== undefined)) {
|
|
236
|
+
throw new ArgumentError('The `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts cannot be combined with ' +
|
|
237
|
+
'`concurrencySystem` - they configure the default `ConcurrencySystem` that a supplied one ' +
|
|
238
|
+
'replaces. Pass them to the `ConcurrencySystem` constructor instead.', this.constructor);
|
|
239
|
+
}
|
|
213
240
|
// Create per-crawler service locator if custom services were provided.
|
|
214
241
|
// This wraps every method on the crawler instance so that calls to the global `serviceLocator`
|
|
215
242
|
// (via AsyncLocalStorage) resolve to this scoped instance instead.
|
|
@@ -317,9 +344,6 @@ export class BasicCrawler {
|
|
|
317
344
|
isFinishedFunction = async () => false;
|
|
318
345
|
}
|
|
319
346
|
const basicCrawlerAutoscaledPoolConfiguration = {
|
|
320
|
-
minConcurrency: minConcurrency ?? autoscaledPoolOptions?.minConcurrency,
|
|
321
|
-
maxConcurrency: maxConcurrency ?? autoscaledPoolOptions?.maxConcurrency,
|
|
322
|
-
maxTasksPerMinute: maxRequestsPerMinute ?? autoscaledPoolOptions?.maxTasksPerMinute,
|
|
323
347
|
runTaskFunction: async () => {
|
|
324
348
|
const source = this.requestManager;
|
|
325
349
|
if (!source)
|
|
@@ -405,11 +429,28 @@ export class BasicCrawler {
|
|
|
405
429
|
log: this.log,
|
|
406
430
|
};
|
|
407
431
|
this.autoscaledPoolOptions = { ...autoscaledPoolOptions, ...basicCrawlerAutoscaledPoolConfiguration };
|
|
432
|
+
this.resolveConcurrencySystem = () => OwnedOrInjected.resolve(concurrencySystem, () => this.createDefaultConcurrencySystem({
|
|
433
|
+
minConcurrency,
|
|
434
|
+
maxConcurrency,
|
|
435
|
+
maxTasksPerMinute: maxRequestsPerMinute,
|
|
436
|
+
log: this.log,
|
|
437
|
+
}));
|
|
408
438
|
}
|
|
409
439
|
finally {
|
|
410
440
|
serviceLocatorScope.exitScope();
|
|
411
441
|
}
|
|
412
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* Builds the crawler-owned default {@link ConcurrencySystem} from the resolved
|
|
445
|
+
* `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts. Not called when a
|
|
446
|
+
* {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} was injected.
|
|
447
|
+
*
|
|
448
|
+
* Subclasses may override this to tune the default system (e.g. {@link HttpCrawler} raises the starting
|
|
449
|
+
* concurrency and relaxes the event loop signal) while still honouring the user's shortcuts.
|
|
450
|
+
*/
|
|
451
|
+
createDefaultConcurrencySystem(options) {
|
|
452
|
+
return new ConcurrencySystem(options);
|
|
453
|
+
}
|
|
413
454
|
/**
|
|
414
455
|
* Determines if the given HTTP status code is an error status code given
|
|
415
456
|
* the default behaviour and user-set preferences.
|
|
@@ -648,8 +689,19 @@ export class BasicCrawler {
|
|
|
648
689
|
if (requests) {
|
|
649
690
|
await this.addRequests(requests, addRequestsOptions);
|
|
650
691
|
}
|
|
651
|
-
|
|
652
|
-
|
|
692
|
+
try {
|
|
693
|
+
await this._init();
|
|
694
|
+
await this.stats.startCapturing();
|
|
695
|
+
}
|
|
696
|
+
catch (error) {
|
|
697
|
+
// Clean up here before propagating, otherwise a failed startup would leave the process hanging.
|
|
698
|
+
await this.teardown().catch((teardownError) => {
|
|
699
|
+
this.log.exception(teardownError, 'Cleaning up after a failed crawler startup failed.');
|
|
700
|
+
});
|
|
701
|
+
// The run never began, so let the instance be run again instead of leaving it wedged as `running`.
|
|
702
|
+
this.running = false;
|
|
703
|
+
throw error;
|
|
704
|
+
}
|
|
653
705
|
const periodicLogger = this.getPeriodicLogger();
|
|
654
706
|
this.setStatusMessage('Starting the crawler.', { level: 'INFO' });
|
|
655
707
|
const sigintHandler = async () => {
|
|
@@ -977,7 +1029,16 @@ export class BasicCrawler {
|
|
|
977
1029
|
await eventManager.init();
|
|
978
1030
|
this._closeEvents = true;
|
|
979
1031
|
}
|
|
980
|
-
|
|
1032
|
+
// An owned governor is rebuilt (and started) for every run, so it always starts from a clean slate — stale
|
|
1033
|
+
// resource snapshots or a previous run's scaled desired concurrency would otherwise distort this run's
|
|
1034
|
+
// scaling. An injected one is long-lived and its lifecycle belongs to the caller.
|
|
1035
|
+
this.concurrencySystemDep = this.resolveConcurrencySystem();
|
|
1036
|
+
await this.concurrencySystemDep.ifOwned((system) => system.start());
|
|
1037
|
+
this.autoscaledPool = new AutoscaledPool({
|
|
1038
|
+
...this.autoscaledPoolOptions,
|
|
1039
|
+
concurrencySystem: this.concurrencySystemDep.value,
|
|
1040
|
+
consumer: this.identity,
|
|
1041
|
+
});
|
|
981
1042
|
await this.getRequestManager();
|
|
982
1043
|
}
|
|
983
1044
|
async runRequestHandler(crawlingContext) {
|
|
@@ -1356,6 +1417,7 @@ export class BasicCrawler {
|
|
|
1356
1417
|
}
|
|
1357
1418
|
await this.sessionPoolDep.ifOwned((pool) => pool.teardown());
|
|
1358
1419
|
await this.autoscaledPool?.abort();
|
|
1420
|
+
await this.concurrencySystemDep?.ifOwned((system) => system.stop());
|
|
1359
1421
|
}
|
|
1360
1422
|
_getCookieHeaderFromRequest(request) {
|
|
1361
1423
|
if (request.headers?.Cookie && request.headers?.cookie) {
|
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.89",
|
|
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.89",
|
|
46
|
+
"@crawlee/http-client": "4.0.0-beta.89",
|
|
47
|
+
"@crawlee/types": "4.0.0-beta.89",
|
|
48
|
+
"@crawlee/utils": "4.0.0-beta.89",
|
|
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.89"
|
|
58
58
|
},
|
|
59
59
|
"lerna": {
|
|
60
60
|
"command": {
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "9279941162eb4be0a9113f768b5bd79d27e66eba"
|
|
67
67
|
}
|