@the-convocation/twitter-scraper 0.18.3 → 0.19.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.
- package/README.md +35 -0
- package/dist/cycletls/cjs/index.cjs +99 -0
- package/dist/cycletls/cjs/index.cjs.map +1 -0
- package/dist/cycletls/esm/index.mjs +96 -0
- package/dist/cycletls/esm/index.mjs.map +1 -0
- package/dist/cycletls/index.d.ts +11 -0
- package/dist/default/cjs/index.js +75 -18
- package/dist/default/cjs/index.js.map +1 -1
- package/dist/default/esm/index.mjs +75 -18
- package/dist/default/esm/index.mjs.map +1 -1
- package/dist/node/cjs/index.cjs +75 -18
- package/dist/node/cjs/index.cjs.map +1 -1
- package/dist/node/esm/index.mjs +75 -18
- package/dist/node/esm/index.mjs.map +1 -1
- package/examples/cycletls/README.md +54 -0
- package/examples/cycletls/package.json +12 -0
- package/package.json +25 -9
- package/rollup.config.mjs +34 -0
package/dist/node/esm/index.mjs
CHANGED
|
@@ -51,13 +51,13 @@ class AuthenticationError extends Error {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const log$
|
|
54
|
+
const log$4 = debug("twitter-scraper:rate-limit");
|
|
55
55
|
class WaitingRateLimitStrategy {
|
|
56
56
|
async onRateLimit({ response: res }) {
|
|
57
57
|
const xRateLimitLimit = res.headers.get("x-rate-limit-limit");
|
|
58
58
|
const xRateLimitRemaining = res.headers.get("x-rate-limit-remaining");
|
|
59
59
|
const xRateLimitReset = res.headers.get("x-rate-limit-reset");
|
|
60
|
-
log$
|
|
60
|
+
log$4(
|
|
61
61
|
`Rate limit event: limit=${xRateLimitLimit}, remaining=${xRateLimitRemaining}, reset=${xRateLimitReset}`
|
|
62
62
|
);
|
|
63
63
|
if (xRateLimitRemaining == "0" && xRateLimitReset) {
|
|
@@ -86,16 +86,47 @@ class Platform {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
const log$3 = debug("twitter-scraper:requests");
|
|
89
90
|
async function updateCookieJar(cookieJar, headers) {
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
let setCookieHeaders = [];
|
|
92
|
+
if (typeof headers.getSetCookie === "function") {
|
|
93
|
+
setCookieHeaders = headers.getSetCookie();
|
|
94
|
+
} else {
|
|
95
|
+
const setCookieHeader = headers.get("set-cookie");
|
|
96
|
+
if (setCookieHeader) {
|
|
97
|
+
setCookieHeaders = setCookie.splitCookiesString(setCookieHeader);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (setCookieHeaders.length > 0) {
|
|
101
|
+
for (const cookieStr of setCookieHeaders) {
|
|
102
|
+
const cookie = Cookie.parse(cookieStr);
|
|
103
|
+
if (!cookie) {
|
|
104
|
+
log$3(`Failed to parse cookie: ${cookieStr.substring(0, 100)}`);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (cookie.maxAge === 0 || cookie.expires && cookie.expires < /* @__PURE__ */ new Date()) {
|
|
108
|
+
if (cookie.key === "ct0") {
|
|
109
|
+
log$3(`Skipping deletion of ct0 cookie (Max-Age=0)`);
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const url = `${cookie.secure ? "https" : "http"}://${cookie.domain}${cookie.path}`;
|
|
115
|
+
await cookieJar.setCookie(cookie, url);
|
|
116
|
+
if (cookie.key === "ct0") {
|
|
117
|
+
log$3(
|
|
118
|
+
`Successfully set ct0 cookie with value: ${cookie.value.substring(
|
|
119
|
+
0,
|
|
120
|
+
20
|
|
121
|
+
)}...`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
} catch (err) {
|
|
125
|
+
log$3(`Failed to set cookie ${cookie.key}: ${err}`);
|
|
126
|
+
if (cookie.key === "ct0") {
|
|
127
|
+
log$3(`FAILED to set ct0 cookie! Error: ${err}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
99
130
|
}
|
|
100
131
|
} else if (typeof document !== "undefined") {
|
|
101
132
|
for (const cookie of document.cookie.split(";")) {
|
|
@@ -113,9 +144,8 @@ async function jitter(maxMs) {
|
|
|
113
144
|
const jitter2 = Math.random() * maxMs;
|
|
114
145
|
await new Promise((resolve) => setTimeout(resolve, jitter2));
|
|
115
146
|
}
|
|
116
|
-
async function requestApi(url, auth, method = "GET", platform = new Platform()) {
|
|
147
|
+
async function requestApi(url, auth, method = "GET", platform = new Platform(), headers = new Headers()) {
|
|
117
148
|
log$2(`Making ${method} request to ${url}`);
|
|
118
|
-
const headers = new Headers();
|
|
119
149
|
await auth.installTo(headers, url);
|
|
120
150
|
await platform.randomizeCiphers();
|
|
121
151
|
let res;
|
|
@@ -301,6 +331,16 @@ class TwitterGuestAuth {
|
|
|
301
331
|
}
|
|
302
332
|
headers.set("cookie", await this.getCookieString());
|
|
303
333
|
}
|
|
334
|
+
async setCookie(key, value) {
|
|
335
|
+
const cookie = Cookie.parse(`${key}=${value}`);
|
|
336
|
+
if (!cookie) {
|
|
337
|
+
throw new Error("Failed to parse cookie.");
|
|
338
|
+
}
|
|
339
|
+
await this.jar.setCookie(cookie, this.getCookieJarUrl());
|
|
340
|
+
if (typeof document !== "undefined") {
|
|
341
|
+
document.cookie = cookie.toString();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
304
344
|
async getCookies() {
|
|
305
345
|
return this.jar.getCookies(this.getCookieJarUrl());
|
|
306
346
|
}
|
|
@@ -351,6 +391,8 @@ class TwitterGuestAuth {
|
|
|
351
391
|
}
|
|
352
392
|
this.guestToken = newGuestToken;
|
|
353
393
|
this.guestCreatedAt = /* @__PURE__ */ new Date();
|
|
394
|
+
await this.setCookie("gt", newGuestToken);
|
|
395
|
+
log$1(`Updated guest token: ${newGuestToken}`);
|
|
354
396
|
}
|
|
355
397
|
/**
|
|
356
398
|
* Returns if the authentication token needs to be updated or not.
|
|
@@ -477,7 +519,11 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
477
519
|
}
|
|
478
520
|
async installTo(headers) {
|
|
479
521
|
headers.set("authorization", `Bearer ${this.bearerToken}`);
|
|
480
|
-
|
|
522
|
+
const cookie = await this.getCookieString();
|
|
523
|
+
headers.set("cookie", cookie);
|
|
524
|
+
if (this.guestToken) {
|
|
525
|
+
headers.set("x-guest-token", this.guestToken);
|
|
526
|
+
}
|
|
481
527
|
await this.installCsrfToken(headers);
|
|
482
528
|
}
|
|
483
529
|
async initLogin() {
|
|
@@ -690,16 +736,27 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
690
736
|
);
|
|
691
737
|
}
|
|
692
738
|
const headers = new Headers({
|
|
693
|
-
|
|
694
|
-
|
|
739
|
+
accept: "*/*",
|
|
740
|
+
"accept-language": "en-US,en;q=0.9",
|
|
695
741
|
"content-type": "application/json",
|
|
696
|
-
"
|
|
742
|
+
"cache-control": "no-cache",
|
|
743
|
+
origin: "https://x.com",
|
|
744
|
+
pragma: "no-cache",
|
|
745
|
+
priority: "u=1, i",
|
|
746
|
+
referer: "https://x.com/",
|
|
747
|
+
"sec-ch-ua": '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
|
|
748
|
+
"sec-ch-ua-mobile": "?0",
|
|
749
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
750
|
+
"sec-fetch-dest": "empty",
|
|
751
|
+
"sec-fetch-mode": "cors",
|
|
752
|
+
"sec-fetch-site": "same-origin",
|
|
753
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
|
|
697
754
|
"x-guest-token": token,
|
|
698
755
|
"x-twitter-auth-type": "OAuth2Client",
|
|
699
756
|
"x-twitter-active-user": "yes",
|
|
700
757
|
"x-twitter-client-language": "en"
|
|
701
758
|
});
|
|
702
|
-
await this.
|
|
759
|
+
await this.installTo(headers);
|
|
703
760
|
let res;
|
|
704
761
|
do {
|
|
705
762
|
const fetchParameters = [
|