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