@crawlee/cheerio 4.0.0-beta.99 → 4.0.0-rc.0

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,7 +1,7 @@
1
- import type { BasicCrawlingContext, CrawlingContext, EnqueueLinksOptions, ErrorHandler, GetUserDataFromRequest, HttpCrawlerOptions, InternalHttpCrawlingContext, InternalHttpHook, IRequestManager, RequestHandler, RouterHandler, RouterRoutes, RouteSchemas, RoutesFromSchemas, SkippedRequestCallback } from '@crawlee/http';
1
+ import type { AddRequestsBatchedResult, CrawlingContext, EnqueueLinksOptions, ErrorHandler, ExtractLinksOptions, GetUserDataFromRequest, HttpCrawlerOptions, InternalHttpCrawlingContext, InternalHttpHook, RequestHandler, RouterHandler, RouterRoutes, RouteSchemas, RoutesFromSchemas } from '@crawlee/http';
2
2
  import { HttpCrawler } from '@crawlee/http';
3
- import type { BatchAddRequestsResult, Dictionary } from '@crawlee/types';
4
- import { type CheerioRoot, type RobotsTxtFile } from '@crawlee/utils';
3
+ import type { Dictionary } from '@crawlee/types';
4
+ import { type CheerioRoot } from '@crawlee/utils/internal';
5
5
  import type { CheerioAPI } from 'cheerio';
6
6
  import * as cheerio from 'cheerio';
7
7
  export type CheerioErrorHandler<UserData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
@@ -9,7 +9,7 @@ JSONData extends Dictionary = any, // with default to Dictionary we cant use a t
9
9
  ContextExtension = Dictionary<never>> = ErrorHandler<CrawlingContext, CheerioCrawlingContext<UserData, JSONData> & ContextExtension>;
10
10
  export interface CheerioCrawlerOptions<ContextExtension = Dictionary<never>, ExtendedContext extends CheerioCrawlingContext = CheerioCrawlingContext & ContextExtension, UserData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
11
11
  JSONData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
12
- Routes extends Record<keyof Routes, Dictionary> = Record<string, UserData>> extends HttpCrawlerOptions<CheerioCrawlingContext<UserData, JSONData>, ContextExtension, ExtendedContext, Routes> {
12
+ Routes extends Record<keyof Routes, Dictionary> = Record<string, UserData>, StatisticStateExtension extends object = {}> extends HttpCrawlerOptions<CheerioCrawlingContext<UserData, JSONData>, ContextExtension, ExtendedContext, Routes, StatisticStateExtension> {
13
13
  }
14
14
  export type CheerioHook<UserData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
15
15
  JSONData extends Dictionary = any> = InternalHttpHook<CheerioCrawlingContext<UserData, JSONData>>;
@@ -52,10 +52,14 @@ JSONData extends Dictionary = any> extends InternalHttpCrawlingContext<UserData,
52
52
  * ```
53
53
  */
54
54
  parseWithCheerio(selector?: string, timeoutMs?: number): Promise<CheerioRoot>;
55
+ /**
56
+ * Extracts URLs from the parsed HTML, without adding them to the request queue.
57
+ */
58
+ extractLinks(options?: ExtractLinksOptions): Promise<string[]>;
55
59
  /**
56
60
  * Helper function for extracting URLs from the parsed HTML and adding them to the request queue.
57
61
  */
58
- enqueueLinks(options?: EnqueueLinksOptions): Promise<BatchAddRequestsResult>;
62
+ enqueueLinks(options?: EnqueueLinksOptions): Promise<AddRequestsBatchedResult>;
59
63
  }
60
64
  export type CheerioRequestHandler<UserData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
61
65
  JSONData extends Dictionary = any> = RequestHandler<CheerioCrawlingContext<UserData, JSONData>>;
@@ -138,41 +142,24 @@ JSONData extends Dictionary = any> = RequestHandler<CheerioCrawlingContext<UserD
138
142
  * ```
139
143
  * @category Crawlers
140
144
  */
141
- export declare class CheerioCrawler<ContextExtension = Dictionary<never>, ExtendedContext extends CheerioCrawlingContext = CheerioCrawlingContext & ContextExtension, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<CheerioCrawlingContext['request']>>> extends HttpCrawler<CheerioCrawlingContext, ContextExtension, ExtendedContext, Routes> {
145
+ export declare class CheerioCrawler<ContextExtension = Dictionary<never>, ExtendedContext extends CheerioCrawlingContext = CheerioCrawlingContext & ContextExtension, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<CheerioCrawlingContext['request']>>, StatisticStateExtension extends object = {}> extends HttpCrawler<CheerioCrawlingContext, ContextExtension, ExtendedContext, Routes, StatisticStateExtension> {
142
146
  /**
143
147
  * All `CheerioCrawler` parameters are passed via an options object.
144
148
  */
145
- constructor(options?: CheerioCrawlerOptions<ContextExtension, ExtendedContext, any, any, Routes>);
149
+ constructor(options?: CheerioCrawlerOptions<ContextExtension, ExtendedContext, any, any, Routes, StatisticStateExtension>);
146
150
  // @ts-ignore optional peer dependency or compatibility with es2022
147
151
  protected buildContextPipeline(): import("@crawlee/http").ContextPipeline<CrawlingContext<Dictionary>, InternalHttpCrawlingContext<any, any> & {
148
152
  readonly body: string;
149
153
  readonly $: CheerioAPI;
150
154
  } & {
151
- enqueueLinks: (enqueueOptions?: EnqueueLinksOptions) => Promise<BatchAddRequestsResult>;
155
+ extractLinks: (options?: ExtractLinksOptions) => Promise<string[]>;
156
+ enqueueLinks: (options?: EnqueueLinksOptions) => Promise<AddRequestsBatchedResult>;
152
157
  waitForSelector: (selector: string, _timeoutMs?: number) => Promise<void>;
153
158
  parseWithCheerio: (selector?: string, timeoutMs?: number) => Promise<CheerioAPI>;
154
159
  }>;
155
160
  private parseContent;
156
161
  private addHelpers;
157
162
  }
158
- interface EnqueueLinksInternalOptions {
159
- options?: EnqueueLinksOptions;
160
- $: cheerio.CheerioAPI | null;
161
- requestManager: IRequestManager;
162
- robotsTxtFile?: RobotsTxtFile;
163
- onSkippedRequest?: SkippedRequestCallback;
164
- originalRequestUrl: string;
165
- finalRequestUrl?: string;
166
- }
167
- interface BoundEnqueueLinksInternalOptions {
168
- enqueueLinks: BasicCrawlingContext['enqueueLinks'];
169
- options?: EnqueueLinksOptions;
170
- $: cheerio.CheerioAPI | null;
171
- originalRequestUrl: string;
172
- finalRequestUrl?: string;
173
- }
174
- /** @internal */
175
- export declare function cheerioCrawlerEnqueueLinks(options: EnqueueLinksInternalOptions | BoundEnqueueLinksInternalOptions): Promise<unknown>;
176
163
  /**
177
164
  * Creates new {@link Router} instance that works based on request labels.
178
165
  * This instance can then serve as a `requestHandler` of your {@link CheerioCrawler}.
@@ -200,4 +187,3 @@ export declare function cheerioCrawlerEnqueueLinks(options: EnqueueLinksInternal
200
187
  export declare function createCheerioRouter<Context extends CheerioCrawlingContext = CheerioCrawlingContext, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>>(routes?: RouterRoutes<Context, Routes>): RouterHandler<Context, Routes>;
201
188
  export declare function createCheerioRouter<Context extends CheerioCrawlingContext = CheerioCrawlingContext, UserData extends Dictionary = GetUserDataFromRequest<Context['request']>>(routes?: RouterRoutes<Context, Record<string, UserData>>): RouterHandler<Context, Record<string, UserData>>;
202
189
  export declare function createCheerioRouter<Context extends CheerioCrawlingContext = CheerioCrawlingContext, const Schemas extends RouteSchemas = RouteSchemas>(schemas: Schemas): RouterHandler<Context, RoutesFromSchemas<Schemas>>;
203
- export {};
@@ -1,5 +1,5 @@
1
- import { enqueueLinks, HttpCrawler, NavigationSkippedError, resolveBaseUrlForEnqueueLinksFiltering, Router, } from '@crawlee/http';
2
- import { extractUrlsFromCheerio } from '@crawlee/utils';
1
+ import { EnqueueStrategy, HttpCrawler, NavigationSkippedError, resolveBaseUrlForEnqueueLinksFiltering, Router, } from '@crawlee/http';
2
+ import { extractUrlsFromCheerio } from '@crawlee/utils/internal';
3
3
  import * as cheerio from 'cheerio';
4
4
  import { parseDocument } from 'htmlparser2';
5
5
  /**
@@ -130,22 +130,28 @@ export class CheerioCrawler extends HttpCrawler {
130
130
  }
131
131
  }
132
132
  async addHelpers(crawlingContext) {
133
- const originalEnqueueLinks = crawlingContext.enqueueLinks;
133
+ const addRequests = crawlingContext.addRequests;
134
+ const extractLinks = async (options) => {
135
+ if (!crawlingContext.$) {
136
+ throw new Error('Cannot extract links because the DOM is not available.');
137
+ }
138
+ return extractUrlsFromCheerio(crawlingContext.$, options?.selector ?? 'a', options?.baseUrl ?? crawlingContext.request.loadedUrl ?? crawlingContext.request.url);
139
+ };
134
140
  return {
135
- enqueueLinks: async (enqueueOptions) => {
136
- return (await cheerioCrawlerEnqueueLinks({
137
- options: {
138
- ...enqueueOptions,
139
- limit: await this.calculateEnqueuedRequestLimit(enqueueOptions?.limit),
140
- },
141
- $: crawlingContext.$,
142
- requestManager: await this.getRequestManager(),
143
- robotsTxtFile: await this.getRobotsTxtFileForUrl(crawlingContext.request.url),
144
- onSkippedRequest: this.handleSkippedRequest,
145
- originalRequestUrl: crawlingContext.request.url,
141
+ extractLinks,
142
+ enqueueLinks: async (options = {}) => {
143
+ const baseUrl = resolveBaseUrlForEnqueueLinksFiltering({
144
+ enqueueStrategy: options.strategy,
146
145
  finalRequestUrl: crawlingContext.request.loadedUrl,
147
- enqueueLinks: originalEnqueueLinks,
148
- })); // TODO make this type safe
146
+ originalRequestUrl: crawlingContext.request.url,
147
+ userProvidedBaseUrl: options.baseUrl,
148
+ });
149
+ const urls = await extractLinks(options);
150
+ return addRequests(urls, {
151
+ ...options,
152
+ baseUrl,
153
+ strategy: options.strategy ?? EnqueueStrategy.SameHostname,
154
+ });
149
155
  },
150
156
  waitForSelector: async (selector, _timeoutMs) => {
151
157
  if (crawlingContext.$(selector).get().length === 0) {
@@ -161,39 +167,6 @@ export class CheerioCrawler extends HttpCrawler {
161
167
  };
162
168
  }
163
169
  }
164
- /** @internal */
165
- function containsEnqueueLinks(options) {
166
- return !!options.enqueueLinks;
167
- }
168
- /** @internal */
169
- export async function cheerioCrawlerEnqueueLinks(options) {
170
- const { options: enqueueLinksOptions, $, originalRequestUrl, finalRequestUrl } = options;
171
- if (!$) {
172
- throw new Error('Cannot enqueue links because the DOM is not available.');
173
- }
174
- const baseUrl = resolveBaseUrlForEnqueueLinksFiltering({
175
- enqueueStrategy: enqueueLinksOptions?.strategy,
176
- finalRequestUrl,
177
- originalRequestUrl,
178
- userProvidedBaseUrl: enqueueLinksOptions?.baseUrl,
179
- });
180
- const urls = extractUrlsFromCheerio($, enqueueLinksOptions?.selector ?? 'a', enqueueLinksOptions?.baseUrl ?? finalRequestUrl ?? originalRequestUrl);
181
- if (containsEnqueueLinks(options)) {
182
- return options.enqueueLinks({
183
- urls,
184
- baseUrl,
185
- ...enqueueLinksOptions,
186
- });
187
- }
188
- return enqueueLinks({
189
- requestManager: options.requestManager,
190
- robotsTxtFile: options.robotsTxtFile,
191
- onSkippedRequest: options.onSkippedRequest,
192
- urls,
193
- baseUrl,
194
- ...enqueueLinksOptions,
195
- });
196
- }
197
170
  export function createCheerioRouter(routesOrSchemas) {
198
171
  return Router.create(routesOrSchemas);
199
172
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlee/cheerio",
3
- "version": "4.0.0-beta.99",
3
+ "version": "4.0.0-rc.0",
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"
@@ -47,9 +47,9 @@
47
47
  "access": "public"
48
48
  },
49
49
  "dependencies": {
50
- "@crawlee/http": "4.0.0-beta.99",
51
- "@crawlee/types": "4.0.0-beta.99",
52
- "@crawlee/utils": "4.0.0-beta.99",
50
+ "@crawlee/http": "4.0.0-rc.0",
51
+ "@crawlee/types": "4.0.0-rc.0",
52
+ "@crawlee/utils": "4.0.0-rc.0",
53
53
  "cheerio": "^1.0.0",
54
54
  "htmlparser2": "^10.0.0",
55
55
  "tslib": "^2.8.1"
@@ -61,5 +61,5 @@
61
61
  }
62
62
  }
63
63
  },
64
- "gitHead": "ad2748380941842bb10cff100f4b4caad92049e3"
64
+ "gitHead": "79ab33dacdacb83e0197e6516d145f3aceef80c7"
65
65
  }