webcrawlerapi-js 1.0.7 → 1.0.9

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/README.md CHANGED
@@ -16,10 +16,10 @@ Install WebcrawlerAPI js package:
16
16
  ## Request example
17
17
 
18
18
  ```javascript
19
- import webcrawlerapi from "webcrawlerapi-js";
19
+ import { WebcrawlerClient } from "webcrawlerapi-js"
20
20
 
21
21
  async function main() {
22
- const client = new webcrawlerapi.WebcrawlerClient(
22
+ const client = new WebcrawlerClient(
23
23
  "YOUR API ACCESS KEY HERE",
24
24
  )
25
25
  // sync way - promise will be resolved with the all the data
@@ -29,6 +29,11 @@ async function main() {
29
29
  "scrape_type": "markdown"
30
30
  }
31
31
  )
32
+ for (const item of syncJob.job_items) {
33
+ item.getContent().then((content) => {
34
+ console.log(content.slice(0, 100));
35
+ })
36
+ }
32
37
  console.log(syncJob);
33
38
 
34
39
  // or async - get the job id and then poll the job status and get the data
@@ -74,6 +79,7 @@ main().catch(console.error);
74
79
  status: 'new',
75
80
  title: '',
76
81
  last_error: '',
82
+ error_code: '',
77
83
  created_at: '2024-12-28T21:36:04.468Z',
78
84
  updated_at: '2024-12-28T21:36:04.435Z',
79
85
  cost: 0,
package/api.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import {CrawlRequest, Job, JobId, ScrapeRequest, ScrapeResponse} from "./model";
2
+ import { JobStatus } from "./constants";
2
3
 
3
4
  const BASE_PATH = "https://api.webcrawlerapi.com"
4
5
  const initialPullDelayMs = 2000
@@ -67,7 +68,7 @@ export class WebcrawlerClient {
67
68
  if (scrapeRequest.debug) {
68
69
  console.log(`Scrape result: ${JSON.stringify(scrapeResult)}`);
69
70
  }
70
- if (scrapeResult.status !== 'in_progress' && scrapeResult.status !== 'new') {
71
+ if (scrapeResult.status !== JobStatus.IN_PROGRESS && scrapeResult.status !== JobStatus.NEW) {
71
72
  return scrapeResult;
72
73
  }
73
74
  if (scrapeResult.recommended_pull_delay_ms > 0) {
@@ -87,8 +88,12 @@ export class WebcrawlerClient {
87
88
  const requestOptions = {
88
89
  'method': 'GET',
89
90
  'headers': {
91
+ 'Content-Type': 'application/json',
90
92
  'Authorization': `Bearer ${this.apiKey}`,
91
- "User-Agent": "WebcrawlerAPI-NodeJS-Client"
93
+ "User-Agent": "WebcrawlerAPI-NodeJS-Client",
94
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
95
+ 'Pragma': 'no-cache',
96
+ 'Expires': '0'
92
97
  },
93
98
  };
94
99
  const response = await fetch(url, requestOptions);
@@ -116,7 +121,10 @@ export class WebcrawlerClient {
116
121
  'headers': {
117
122
  'Content-Type': 'application/json',
118
123
  'Authorization': `Bearer ${this.apiKey}`,
119
- "User-Agent": "WebcrawlerAPI-NodeJS-Client"
124
+ "User-Agent": "WebcrawlerAPI-NodeJS-Client",
125
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
126
+ 'Pragma': 'no-cache',
127
+ 'Expires': '0'
120
128
  },
121
129
  'body': JSON.stringify(crawlRequest),
122
130
  };
@@ -130,14 +138,55 @@ export class WebcrawlerClient {
130
138
  let delayIntervalMs = initialPullDelayMs;
131
139
  for (let i = 0; i < MaxPullRetries; i++) {
132
140
  await new Promise(resolve => setTimeout(resolve, delayIntervalMs));
133
- const job = await this.getJob(jobIdResponse.id);
134
- if (job.status !== 'in_progress' && job.status !== 'new') {
141
+ const timestamp = new Date().getTime();
142
+ const job = await this.getJob(`${jobIdResponse.id}?t=${timestamp}`);
143
+ if (job.status !== JobStatus.IN_PROGRESS && job.status !== JobStatus.NEW) {
144
+ // Transform each job item to include getContent method
145
+ job.job_items = job.job_items.map(item => ({
146
+ ...item,
147
+ getContent: async function(): Promise<string | null> {
148
+ if (job.status !== JobStatus.DONE || this.status !== JobStatus.DONE) {
149
+ return null;
150
+ }
151
+
152
+ let contentUrl: string | undefined;
153
+ switch (job.scrape_type) {
154
+ case 'html':
155
+ contentUrl = this.raw_content_url;
156
+ break;
157
+ case 'cleaned':
158
+ contentUrl = this.cleaned_content_url;
159
+ break;
160
+ case 'markdown':
161
+ contentUrl = this.markdown_content_url;
162
+ break;
163
+ }
164
+
165
+ if (!contentUrl) {
166
+ return null;
167
+ }
168
+
169
+ const response = await fetch(contentUrl, {
170
+ headers: {
171
+ 'Accept-Encoding': 'gzip, deflate, br',
172
+ 'Accept': '*/*'
173
+ }
174
+ });
175
+
176
+ if (!response.ok) {
177
+ throw new Error(`Failed to fetch content: ${response.statusText}`);
178
+ }
179
+
180
+ return await response.text();
181
+ }
182
+ }));
135
183
  return job;
136
184
  }
137
185
  if (job.recommended_pull_delay_ms > 0) {
138
186
  delayIntervalMs = job.recommended_pull_delay_ms;
139
187
  }
140
188
  }
189
+ throw new Error("Crawling took too long, please retry or increase the number of polling retries");
141
190
  }
142
191
 
143
192
  public async crawlAsync(crawlRequest: CrawlRequest): Promise<JobId> {
@@ -162,7 +211,10 @@ export class WebcrawlerClient {
162
211
  'headers': {
163
212
  'Content-Type': 'application/json',
164
213
  'Authorization': `Bearer ${this.apiKey}`,
165
- "User-Agent": "WebcrawlerAPI-NodeJS-Client"
214
+ "User-Agent": "WebcrawlerAPI-NodeJS-Client",
215
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
216
+ 'Pragma': 'no-cache',
217
+ 'Expires': '0'
166
218
  }
167
219
  }
168
220
  const response = await fetch(url, requestOptions);
package/constants.ts ADDED
@@ -0,0 +1,14 @@
1
+ export const JobStatus = {
2
+ IN_PROGRESS: 'in_progress',
3
+ NEW: 'new',
4
+ DONE: 'done'
5
+ } as const;
6
+
7
+ export const HttpStatus = {
8
+ OK: 200,
9
+ BAD_REQUEST: 400,
10
+ UNAUTHORIZED: 401,
11
+ FORBIDDEN: 403,
12
+ NOT_FOUND: 404,
13
+ INTERNAL_SERVER_ERROR: 500
14
+ } as const;
package/dist/api.js CHANGED
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.WebcrawlerClient = void 0;
13
+ const constants_1 = require("./constants");
13
14
  const BASE_PATH = "https://api.webcrawlerapi.com";
14
15
  const initialPullDelayMs = 2000;
15
16
  const MaxPullRetries = 100;
@@ -67,7 +68,7 @@ class WebcrawlerClient {
67
68
  if (scrapeRequest.debug) {
68
69
  console.log(`Scrape result: ${JSON.stringify(scrapeResult)}`);
69
70
  }
70
- if (scrapeResult.status !== 'in_progress' && scrapeResult.status !== 'new') {
71
+ if (scrapeResult.status !== constants_1.JobStatus.IN_PROGRESS && scrapeResult.status !== constants_1.JobStatus.NEW) {
71
72
  return scrapeResult;
72
73
  }
73
74
  if (scrapeResult.recommended_pull_delay_ms > 0) {
@@ -89,8 +90,12 @@ class WebcrawlerClient {
89
90
  const requestOptions = {
90
91
  'method': 'GET',
91
92
  'headers': {
93
+ 'Content-Type': 'application/json',
92
94
  'Authorization': `Bearer ${this.apiKey}`,
93
- "User-Agent": "WebcrawlerAPI-NodeJS-Client"
95
+ "User-Agent": "WebcrawlerAPI-NodeJS-Client",
96
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
97
+ 'Pragma': 'no-cache',
98
+ 'Expires': '0'
94
99
  },
95
100
  };
96
101
  const response = yield fetch(url, requestOptions);
@@ -114,7 +119,10 @@ class WebcrawlerClient {
114
119
  'headers': {
115
120
  'Content-Type': 'application/json',
116
121
  'Authorization': `Bearer ${this.apiKey}`,
117
- "User-Agent": "WebcrawlerAPI-NodeJS-Client"
122
+ "User-Agent": "WebcrawlerAPI-NodeJS-Client",
123
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
124
+ 'Pragma': 'no-cache',
125
+ 'Expires': '0'
118
126
  },
119
127
  'body': JSON.stringify(crawlRequest),
120
128
  };
@@ -125,14 +133,49 @@ class WebcrawlerClient {
125
133
  let delayIntervalMs = initialPullDelayMs;
126
134
  for (let i = 0; i < MaxPullRetries; i++) {
127
135
  yield new Promise(resolve => setTimeout(resolve, delayIntervalMs));
128
- const job = yield this.getJob(jobIdResponse.id);
129
- if (job.status !== 'in_progress' && job.status !== 'new') {
136
+ const timestamp = new Date().getTime();
137
+ const job = yield this.getJob(`${jobIdResponse.id}?t=${timestamp}`);
138
+ if (job.status !== constants_1.JobStatus.IN_PROGRESS && job.status !== constants_1.JobStatus.NEW) {
139
+ // Transform each job item to include getContent method
140
+ job.job_items = job.job_items.map(item => (Object.assign(Object.assign({}, item), { getContent: function () {
141
+ return __awaiter(this, void 0, void 0, function* () {
142
+ if (job.status !== constants_1.JobStatus.DONE || this.status !== constants_1.JobStatus.DONE) {
143
+ return null;
144
+ }
145
+ let contentUrl;
146
+ switch (job.scrape_type) {
147
+ case 'html':
148
+ contentUrl = this.raw_content_url;
149
+ break;
150
+ case 'cleaned':
151
+ contentUrl = this.cleaned_content_url;
152
+ break;
153
+ case 'markdown':
154
+ contentUrl = this.markdown_content_url;
155
+ break;
156
+ }
157
+ if (!contentUrl) {
158
+ return null;
159
+ }
160
+ const response = yield fetch(contentUrl, {
161
+ headers: {
162
+ 'Accept-Encoding': 'gzip, deflate, br',
163
+ 'Accept': '*/*'
164
+ }
165
+ });
166
+ if (!response.ok) {
167
+ throw new Error(`Failed to fetch content: ${response.statusText}`);
168
+ }
169
+ return yield response.text();
170
+ });
171
+ } })));
130
172
  return job;
131
173
  }
132
174
  if (job.recommended_pull_delay_ms > 0) {
133
175
  delayIntervalMs = job.recommended_pull_delay_ms;
134
176
  }
135
177
  }
178
+ throw new Error("Crawling took too long, please retry or increase the number of polling retries");
136
179
  });
137
180
  }
138
181
  crawlAsync(crawlRequest) {
@@ -157,7 +200,10 @@ class WebcrawlerClient {
157
200
  'headers': {
158
201
  'Content-Type': 'application/json',
159
202
  'Authorization': `Bearer ${this.apiKey}`,
160
- "User-Agent": "WebcrawlerAPI-NodeJS-Client"
203
+ "User-Agent": "WebcrawlerAPI-NodeJS-Client",
204
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
205
+ 'Pragma': 'no-cache',
206
+ 'Expires': '0'
161
207
  }
162
208
  };
163
209
  const response = yield fetch(url, requestOptions);
@@ -0,0 +1,13 @@
1
+ export declare const JobStatus: {
2
+ readonly IN_PROGRESS: "in_progress";
3
+ readonly NEW: "new";
4
+ readonly DONE: "done";
5
+ };
6
+ export declare const HttpStatus: {
7
+ readonly OK: 200;
8
+ readonly BAD_REQUEST: 400;
9
+ readonly UNAUTHORIZED: 401;
10
+ readonly FORBIDDEN: 403;
11
+ readonly NOT_FOUND: 404;
12
+ readonly INTERNAL_SERVER_ERROR: 500;
13
+ };
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpStatus = exports.JobStatus = void 0;
4
+ exports.JobStatus = {
5
+ IN_PROGRESS: 'in_progress',
6
+ NEW: 'new',
7
+ DONE: 'done'
8
+ };
9
+ exports.HttpStatus = {
10
+ OK: 200,
11
+ BAD_REQUEST: 400,
12
+ UNAUTHORIZED: 401,
13
+ FORBIDDEN: 403,
14
+ NOT_FOUND: 404,
15
+ INTERNAL_SERVER_ERROR: 500
16
+ };
package/dist/model.d.ts CHANGED
@@ -56,4 +56,7 @@ export interface JobItem {
56
56
  updated_at: string;
57
57
  cost: number;
58
58
  referred_url: string;
59
+ raw_content_url?: string;
60
+ cleaned_content_url?: string;
61
+ getContent(): Promise<string | null>;
59
62
  }
package/model.ts CHANGED
@@ -61,4 +61,7 @@ export interface JobItem {
61
61
  updated_at: string;
62
62
  cost: number;
63
63
  referred_url: string;
64
+ raw_content_url?: string;
65
+ cleaned_content_url?: string;
66
+ getContent(): Promise<string | null>;
64
67
  }
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "webcrawlerapi-js",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
+
4
5
  "description": "JS client for WecrawlerAPI",
5
6
  "main": "./dist/index.js",
6
7
  "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc --outDir dist/"
10
+ },
7
11
  "keywords": [
8
12
  "scraping",
9
13
  "scraper",
@@ -19,8 +23,5 @@
19
23
  },
20
24
  "author": "Andrew <sdk@webcrawlerapi.com>",
21
25
  "license": "MIT",
22
- "dependencies": {},
23
- "scripts": {
24
- "build": "tsc --outDir dist/"
25
- }
26
- }
26
+ "dependencies": {}
27
+ }