@crawlee/http 4.0.0-beta.210 → 4.0.0-beta.212
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.
|
@@ -49,7 +49,6 @@ export type FileDownloadRequestHandler<UserData extends Dictionary = any> = Requ
|
|
|
49
49
|
export declare class FileDownload extends BasicCrawler<FileDownloadCrawlingContext> {
|
|
50
50
|
#private;
|
|
51
51
|
constructor(options?: Omit<BasicCrawlerOptions<FileDownloadCrawlingContext>, 'contextPipelineBuilder'>);
|
|
52
|
-
private initiateDownload;
|
|
53
52
|
}
|
|
54
53
|
/**
|
|
55
54
|
* Creates new {@link Router} instance that works based on request labels.
|
|
@@ -44,9 +44,9 @@ export class FileDownload extends BasicCrawler {
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
#buildContextPipeline() {
|
|
47
|
-
return ContextPipeline.create().compose(this
|
|
47
|
+
return ContextPipeline.create().compose(this.#initiateDownload.bind(this));
|
|
48
48
|
}
|
|
49
|
-
async initiateDownload(context, onCleanup) {
|
|
49
|
+
async #initiateDownload(context, onCleanup) {
|
|
50
50
|
const response = await this.httpClient.sendRequest(context.request.intoFetchAPIRequest(), {
|
|
51
51
|
session: context.session,
|
|
52
52
|
});
|
|
@@ -381,38 +381,7 @@ export declare class HttpCrawler<Context extends InternalHttpCrawlingContext<any
|
|
|
381
381
|
/** @internal */
|
|
382
382
|
protected createDefaultConcurrencySystem(options: ConcurrencySystemOptions): ConcurrencySystem;
|
|
383
383
|
protected buildContextPipeline(): ContextPipeline<CrawlingContext, InternalHttpCrawlingContext>;
|
|
384
|
-
private prepareHttpRequest;
|
|
385
384
|
private makeHttpRequest;
|
|
386
|
-
private processHttpResponse;
|
|
387
|
-
private handleBlockedRequestByContent;
|
|
388
|
-
/**
|
|
389
|
-
* Function to make the HTTP request. It performs optimizations
|
|
390
|
-
* on the request such as only downloading the request body if the
|
|
391
|
-
* received content type matches text/html, application/xml, application/xhtml+xml.
|
|
392
|
-
*/
|
|
393
|
-
private requestFunction;
|
|
394
|
-
/**
|
|
395
|
-
* Encodes and parses response according to the provided content type
|
|
396
|
-
*/
|
|
397
|
-
private parseResponse;
|
|
398
|
-
/**
|
|
399
|
-
* Combines the provided `requestOptions` with mandatory (non-overridable) values.
|
|
400
|
-
*/
|
|
401
|
-
private getRequestOptions;
|
|
402
|
-
private encodeResponse;
|
|
403
|
-
/**
|
|
404
|
-
* Checks and extends supported mime types
|
|
405
|
-
*/
|
|
406
|
-
private extendSupportedMimeTypes;
|
|
407
|
-
/**
|
|
408
|
-
* Handles timeout request
|
|
409
|
-
*/
|
|
410
|
-
private handleRequestTimeout;
|
|
411
|
-
private abortDownloadOfBody;
|
|
412
|
-
/**
|
|
413
|
-
* @internal wraps public utility for mocking purposes
|
|
414
|
-
*/
|
|
415
|
-
private requestAsBrowser;
|
|
416
385
|
}
|
|
417
386
|
/**
|
|
418
387
|
* Creates new {@link Router} instance that works based on request labels.
|
|
@@ -147,7 +147,7 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
147
147
|
});
|
|
148
148
|
this.#supportedMimeTypes = new Set([...HTML_AND_XML_MIME_TYPES, APPLICATION_JSON_MIME_TYPE]);
|
|
149
149
|
if (additionalMimeTypes.length)
|
|
150
|
-
this
|
|
150
|
+
this.#extendSupportedMimeTypes(additionalMimeTypes);
|
|
151
151
|
if (suggestResponseEncoding && forceResponseEncoding) {
|
|
152
152
|
this.log.warning('Both forceResponseEncoding and suggestResponseEncoding options are set. Using forceResponseEncoding.');
|
|
153
153
|
}
|
|
@@ -160,7 +160,7 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
160
160
|
// members `extendContext` added at runtime.
|
|
161
161
|
this.#preNavigationHooks = preNavigationHooks;
|
|
162
162
|
this.#postNavigationHooks = [
|
|
163
|
-
({ request, response }) => this
|
|
163
|
+
({ request, response }) => this.#abortDownloadOfBody(request, response),
|
|
164
164
|
...postNavigationHooks,
|
|
165
165
|
];
|
|
166
166
|
this.#saveResponseCookies = saveResponseCookies;
|
|
@@ -191,7 +191,7 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
191
191
|
}
|
|
192
192
|
return addTimeoutToPromise(async () => step(ctx), remaining, navigationTimedOut);
|
|
193
193
|
});
|
|
194
|
-
let pipeline = ContextPipeline.create().compose(this
|
|
194
|
+
let pipeline = ContextPipeline.create().compose(this.#prepareHttpRequest.bind(this));
|
|
195
195
|
for (const hook of this.#preNavigationHooks) {
|
|
196
196
|
pipeline = pipeline.compose(windowGuard(hook));
|
|
197
197
|
}
|
|
@@ -200,20 +200,23 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
200
200
|
pipelineWithNavigation = pipelineWithNavigation.compose(windowGuard(hook));
|
|
201
201
|
}
|
|
202
202
|
return pipelineWithNavigation
|
|
203
|
-
.compose(this
|
|
204
|
-
.compose(this
|
|
203
|
+
.compose(this.#processHttpResponse.bind(this))
|
|
204
|
+
.compose(this.#handleBlockedRequestByContent.bind(this));
|
|
205
205
|
}
|
|
206
|
-
async prepareHttpRequest(crawlingContext) {
|
|
206
|
+
async #prepareHttpRequest(crawlingContext) {
|
|
207
207
|
const { request } = crawlingContext;
|
|
208
208
|
if (request.skipNavigation) {
|
|
209
209
|
return {
|
|
210
210
|
request: new Proxy(request, {
|
|
211
|
-
get(target, propertyName
|
|
211
|
+
get(target, propertyName) {
|
|
212
212
|
if (propertyName === 'loadedUrl') {
|
|
213
213
|
throw new NavigationSkippedError('The `request.loadedUrl` property is not available - `skipNavigation` was used');
|
|
214
214
|
}
|
|
215
|
-
|
|
215
|
+
// `target` as receiver and bound methods, or `#` members throw on the proxy
|
|
216
|
+
const value = Reflect.get(target, propertyName, target);
|
|
217
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
216
218
|
},
|
|
219
|
+
set: (target, propertyName, value) => Reflect.set(target, propertyName, value, target),
|
|
217
220
|
}),
|
|
218
221
|
get response() {
|
|
219
222
|
throw new NavigationSkippedError('The `response` property is not available - `skipNavigation` was used');
|
|
@@ -231,13 +234,13 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
231
234
|
// Bound the request by whatever is left of the shared navigation window (the pre-navigation hooks may
|
|
232
235
|
// have already spent part of it), so it produces a clean navigation-timeout error rather than the raw
|
|
233
236
|
// client abort.
|
|
234
|
-
const httpResponse = await addTimeoutToPromise(async () => this
|
|
237
|
+
const httpResponse = await addTimeoutToPromise(async () => this.#requestFunction({ request, session, proxyUrl }), Math.max(1, remainingNavigationWindowMillis(crawlingContext, this.#navigationTimeoutMillis)), `Navigation timed out after ${this.#navigationTimeoutMillis / 1000} seconds.`);
|
|
235
238
|
tryCancel();
|
|
236
239
|
request.loadedUrl = httpResponse?.url;
|
|
237
240
|
request.state = RequestState.AFTER_NAV;
|
|
238
241
|
return { request: request, response: httpResponse };
|
|
239
242
|
}
|
|
240
|
-
async processHttpResponse(crawlingContext) {
|
|
243
|
+
async #processHttpResponse(crawlingContext) {
|
|
241
244
|
if (crawlingContext.request.skipNavigation) {
|
|
242
245
|
return {
|
|
243
246
|
get contentType() {
|
|
@@ -276,7 +279,7 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
276
279
|
if (remaining <= 0) {
|
|
277
280
|
throw new TimeoutError(`Navigation timed out after ${this.#navigationTimeoutMillis / 1000} seconds.`);
|
|
278
281
|
}
|
|
279
|
-
const parsed = await addTimeoutToPromise(async () => this
|
|
282
|
+
const parsed = await addTimeoutToPromise(async () => this.#parseResponse(crawlingContext.request, crawlingContext.response), remaining, `Navigation timed out after ${this.#navigationTimeoutMillis / 1000} seconds.`);
|
|
280
283
|
tryCancel();
|
|
281
284
|
const response = parsed.response;
|
|
282
285
|
const contentType = parsed.contentType;
|
|
@@ -330,7 +333,7 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
330
333
|
body: parsed.body,
|
|
331
334
|
};
|
|
332
335
|
}
|
|
333
|
-
async handleBlockedRequestByContent(crawlingContext) {
|
|
336
|
+
async #handleBlockedRequestByContent(crawlingContext) {
|
|
334
337
|
if (this.retryOnBlocked) {
|
|
335
338
|
const error = await this.#isRequestBlocked(crawlingContext);
|
|
336
339
|
if (error)
|
|
@@ -356,14 +359,14 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
356
359
|
* on the request such as only downloading the request body if the
|
|
357
360
|
* received content type matches text/html, application/xml, application/xhtml+xml.
|
|
358
361
|
*/
|
|
359
|
-
async requestFunction({ request, session, proxyUrl }) {
|
|
360
|
-
const opts = this
|
|
362
|
+
async #requestFunction({ request, session, proxyUrl }) {
|
|
363
|
+
const opts = this.#getRequestOptions(request, proxyUrl);
|
|
361
364
|
try {
|
|
362
|
-
return await this
|
|
365
|
+
return await this.#requestAsBrowser(opts, session);
|
|
363
366
|
}
|
|
364
367
|
catch (e) {
|
|
365
368
|
if (e instanceof Error && e.constructor.name === 'TimeoutError') {
|
|
366
|
-
this
|
|
369
|
+
this.#handleRequestTimeout(session);
|
|
367
370
|
return new Response(); // this will never happen, as handleRequestTimeout always throws
|
|
368
371
|
}
|
|
369
372
|
if (this.isProxyError(e)) {
|
|
@@ -377,14 +380,14 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
377
380
|
/**
|
|
378
381
|
* Encodes and parses response according to the provided content type
|
|
379
382
|
*/
|
|
380
|
-
async parseResponse(request, response) {
|
|
383
|
+
async #parseResponse(request, response) {
|
|
381
384
|
const { status } = response;
|
|
382
385
|
const { type, charset } = parseContentTypeFromResponse(response);
|
|
383
386
|
if (status >= 400 && status <= 599) {
|
|
384
387
|
this.statistics.registerStatusCode(status);
|
|
385
388
|
}
|
|
386
389
|
if (this.isErrorStatusCode(status)) {
|
|
387
|
-
const { response: decoded } = this
|
|
390
|
+
const { response: decoded } = this.#encodeResponse(request, response, charset);
|
|
388
391
|
const body = await decoded.text(); // TODO - this always uses UTF-8 (see https://developer.mozilla.org/en-US/docs/Web/API/Request/text)
|
|
389
392
|
// Errors are often sent as JSON, so attempt to parse them,
|
|
390
393
|
// despite Accept header being set to text/html.
|
|
@@ -412,7 +415,7 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
412
415
|
: rawBytes.toString('utf8');
|
|
413
416
|
return { response, contentType: { type, encoding: 'utf-8' }, body };
|
|
414
417
|
}
|
|
415
|
-
const { response: reencodedResponse, encoding } = this
|
|
418
|
+
const { response: reencodedResponse, encoding } = this.#encodeResponse(request, response, charset);
|
|
416
419
|
const contentType = { type, encoding };
|
|
417
420
|
if (HTML_AND_XML_MIME_TYPES.includes(type)) {
|
|
418
421
|
return { response, contentType, body: await reencodedResponse.text() };
|
|
@@ -426,7 +429,7 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
426
429
|
/**
|
|
427
430
|
* Combines the provided `requestOptions` with mandatory (non-overridable) values.
|
|
428
431
|
*/
|
|
429
|
-
getRequestOptions(request, proxyUrl) {
|
|
432
|
+
#getRequestOptions(request, proxyUrl) {
|
|
430
433
|
const requestOptions = {
|
|
431
434
|
url: request.url,
|
|
432
435
|
method: request.method,
|
|
@@ -443,7 +446,7 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
443
446
|
requestOptions.body = request.payload ?? '';
|
|
444
447
|
return requestOptions;
|
|
445
448
|
}
|
|
446
|
-
encodeResponse(request, response, encoding) {
|
|
449
|
+
#encodeResponse(request, response, encoding) {
|
|
447
450
|
if (this.#forceResponseEncoding) {
|
|
448
451
|
encoding = this.#forceResponseEncoding;
|
|
449
452
|
}
|
|
@@ -479,7 +482,7 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
479
482
|
/**
|
|
480
483
|
* Checks and extends supported mime types
|
|
481
484
|
*/
|
|
482
|
-
extendSupportedMimeTypes(additionalMimeTypes) {
|
|
485
|
+
#extendSupportedMimeTypes(additionalMimeTypes) {
|
|
483
486
|
for (const mimeType of additionalMimeTypes) {
|
|
484
487
|
if (mimeType === '*/*') {
|
|
485
488
|
this.#supportedMimeTypes.add(mimeType);
|
|
@@ -497,11 +500,11 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
497
500
|
/**
|
|
498
501
|
* Handles timeout request
|
|
499
502
|
*/
|
|
500
|
-
handleRequestTimeout(session) {
|
|
503
|
+
#handleRequestTimeout(session) {
|
|
501
504
|
session.markBad();
|
|
502
505
|
throw new Error(`Request timed out after ${this.#navigationTimeoutMillis / 1000} seconds.`);
|
|
503
506
|
}
|
|
504
|
-
abortDownloadOfBody(request, response) {
|
|
507
|
+
#abortDownloadOfBody(request, response) {
|
|
505
508
|
const { status } = response;
|
|
506
509
|
const { type } = parseContentTypeFromResponse(response);
|
|
507
510
|
const isTransientContentType = status >= 500 || this.blockedStatusCodes.has(status);
|
|
@@ -514,7 +517,7 @@ export class HttpCrawler extends BasicCrawler {
|
|
|
514
517
|
/**
|
|
515
518
|
* @internal wraps public utility for mocking purposes
|
|
516
519
|
*/
|
|
517
|
-
requestAsBrowser = async (options, session) => {
|
|
520
|
+
#requestAsBrowser = async (options, session) => {
|
|
518
521
|
const opts = processHttpRequestOptions({
|
|
519
522
|
...options,
|
|
520
523
|
responseType: 'text',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/http",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.212",
|
|
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"
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@apify/timeout": "^1.0.1",
|
|
51
51
|
"@apify/utilities": "^3.0.1",
|
|
52
|
-
"@crawlee/basic": "4.0.0-beta.
|
|
53
|
-
"@crawlee/core": "4.0.0-beta.
|
|
54
|
-
"@crawlee/http-client": "4.0.0-beta.
|
|
55
|
-
"@crawlee/types": "4.0.0-beta.
|
|
56
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
52
|
+
"@crawlee/basic": "4.0.0-beta.212",
|
|
53
|
+
"@crawlee/core": "4.0.0-beta.212",
|
|
54
|
+
"@crawlee/http-client": "4.0.0-beta.212",
|
|
55
|
+
"@crawlee/types": "4.0.0-beta.212",
|
|
56
|
+
"@crawlee/utils": "4.0.0-beta.212",
|
|
57
57
|
"@types/content-type": "^1.1.8",
|
|
58
58
|
"cheerio": "^1.0.0",
|
|
59
59
|
"content-type": "^1.0.5",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "171a63a5d7f2a6d74cbce58479204ac20d30b87e"
|
|
74
74
|
}
|