-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathanycrawler.js
More file actions
149 lines (127 loc) · 3.58 KB
/
Copy pathanycrawler.js
File metadata and controls
149 lines (127 loc) · 3.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
class AnyCrawlerError extends Error {
constructor({ status, errorCode, errorMessage, data, meta }) {
super(errorMessage);
this.name = "AnyCrawlerError";
this.status = status;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.data = data;
this.meta = meta;
}
}
function normalizeBaseUrl(baseUrl) {
return baseUrl.replace(/\/+$/, "");
}
function parseOptionalNumber(value) {
if (value === null || value === undefined || value === "") {
return null;
}
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
function buildMeta(response) {
return {
status: response.status,
requestId: response.headers.get("x-request-id"),
creditsReserved: parseOptionalNumber(response.headers.get("x-credits-reserved")),
creditsUsed: parseOptionalNumber(response.headers.get("x-credits-used")),
browserMsUsed: parseOptionalNumber(response.headers.get("x-browser-ms-used")),
};
}
function parseJson(text) {
if (!text) {
return null;
}
return JSON.parse(text);
}
class AnyCrawlerClient {
constructor(options) {
if (!options || !options.apiKey) {
throw new Error("AnyCrawlerClient requires an apiKey.");
}
this.apiKey = options.apiKey;
this.baseUrl = normalizeBaseUrl(options.baseUrl || "https://api.anycrawler.com");
this.defaultHeaders = options.headers || {};
this.fetchImpl = options.fetch || fetch;
}
async getAccountUsage() {
return this.request("/v1/account/usage", {
method: "GET",
});
}
async crawlPage(payload) {
return this.request("/v1/crawl/page", {
method: "POST",
body: JSON.stringify(payload),
});
}
async crawlScreenshot(payload) {
return this.request("/v1/crawl/screenshot", {
method: "POST",
body: JSON.stringify(payload),
});
}
async search(payload) {
return this.request("/v1/search", {
method: "POST",
body: JSON.stringify(payload),
});
}
async searchPage(payload) {
return this.request("/v1/search/page", {
method: "POST",
body: JSON.stringify(payload),
});
}
async searchNews(payload) {
return this.request("/v1/search/news", {
method: "POST",
body: JSON.stringify(payload),
});
}
async request(pathname, init) {
const headers = new Headers(this.defaultHeaders);
headers.set("accept", "application/json");
headers.set("authorization", `Bearer ${this.apiKey}`);
if (init.body !== undefined) {
headers.set("content-type", "application/json");
}
const response = await this.fetchImpl(`${this.baseUrl}${pathname}`, {
...init,
headers,
});
const meta = buildMeta(response);
const text = await response.text();
const data = parseJson(text);
if (!response.ok) {
const body = data && typeof data === "object" && !Array.isArray(data) ? data : null;
const errorCode =
body && typeof body.error_code === "string"
? body.error_code
: body && typeof body.errorCode === "string"
? body.errorCode
: null;
const errorMessage =
body && typeof body.error_message === "string"
? body.error_message
: body && typeof body.message === "string"
? body.message
: `AnyCrawler request failed with status ${response.status}.`;
throw new AnyCrawlerError({
status: response.status,
errorCode,
errorMessage,
data,
meta,
});
}
return {
data,
meta,
};
}
}
module.exports = {
AnyCrawlerClient,
AnyCrawlerError,
};