@finch_ren/x-scraper 0.1.0 → 0.1.2
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/LICENSE +21 -0
- package/README.md +175 -97
- package/README.zh-CN.md +337 -0
- package/dist/openapi/placeholder.json +2285 -0
- package/dist/src/openapi/src/models/ItemResult.js +8 -1
- package/dist/src/openapi/src/models/TimelineAddEntry.js +8 -1
- package/dist/src/openapi/src/models/TimelineTimelineItem.js +8 -1
- package/dist/src/openapi/src/models/TimelineTimelineModule.js +8 -1
- package/dist/src/openapi/src/models/TimelineTweet.js +16 -2
- package/dist/src/openapi/src/models/Tweet.js +16 -2
- package/package.json +4 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Finch R
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,17 +1,129 @@
|
|
|
1
1
|
# x-scraper
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
重点是让常见场景开箱即用,同时保持类型清晰、便于扩展。
|
|
3
|
+
English | [简体中文](./README.zh-CN.md)
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
`x-scraper` is a TypeScript SDK for X/Twitter internal GraphQL endpoints.
|
|
6
|
+
It focuses on practical, ready-to-run usage while keeping strong OpenAPI-generated types.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
- 支持 Cookie 登录态客户端
|
|
10
|
-
- 面向常用场景提供开箱即用调用入口(用户、推文、互动等)
|
|
8
|
+
## Features
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
- Automatic guest token initialization
|
|
11
|
+
- Cookie-authenticated client support
|
|
12
|
+
- Practical high-level APIs for common user/tweet/interaction flows
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
<details open>
|
|
17
|
+
<summary><strong>npm</strong></summary>
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @finch_ren/x-scraper
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
</details>
|
|
24
|
+
|
|
25
|
+
<details>
|
|
26
|
+
<summary><strong>pnpm</strong></summary>
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm add @finch_ren/x-scraper
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
</details>
|
|
33
|
+
|
|
34
|
+
<details>
|
|
35
|
+
<summary><strong>yarn</strong></summary>
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
yarn add @finch_ren/x-scraper
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
</details>
|
|
42
|
+
|
|
43
|
+
## Simple Call Example
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { XScraper } from '@finch_ren/x-scraper';
|
|
47
|
+
|
|
48
|
+
async function main() {
|
|
49
|
+
const scraper = new XScraper();
|
|
50
|
+
const client = await scraper.getClientFromCookies({
|
|
51
|
+
__cf_bm: '<__cf_bm>',
|
|
52
|
+
__cuid: '<__cuid>',
|
|
53
|
+
_ga: '<_ga>',
|
|
54
|
+
_ga_BLY4P7T5KW: '<_ga_BLY4P7T5KW>',
|
|
55
|
+
_twitter_sess: '<_twitter_sess>',
|
|
56
|
+
auth_token: '<auth_token>',
|
|
57
|
+
ct0: '<ct0>',
|
|
58
|
+
guest_id: '<guest_id>',
|
|
59
|
+
guest_id_ads: '<guest_id_ads>',
|
|
60
|
+
guest_id_marketing: '<guest_id_marketing>',
|
|
61
|
+
kdt: '<kdt>',
|
|
62
|
+
lang: '<lang>',
|
|
63
|
+
personalization_id: '<personalization_id>',
|
|
64
|
+
twid: '<twid>',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const res = await client.getTweetDetail({
|
|
68
|
+
focalTweetId: '2018440335140024383',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
main().catch(console.error);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
<details open>
|
|
76
|
+
<summary><strong>Example Response (JSON) (Display only, actual response may vary)</strong></summary>
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"raw": {
|
|
81
|
+
"instruction": [
|
|
82
|
+
{ "type": "TimelineClearCache" },
|
|
83
|
+
{ "type": "TimelineAddEntries", "entries": [/* ... */] },
|
|
84
|
+
{ "type": "TimelineTerminateTimeline" }
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
"cursor": {
|
|
88
|
+
"bottom": {
|
|
89
|
+
"typename": "TimelineTimelineCursor",
|
|
90
|
+
"cursorType": "Bottom",
|
|
91
|
+
"entryType": "TimelineTimelineCursor",
|
|
92
|
+
"value": "DAAKCgABHB6qs_W__pULAAIAAAGoRW1QQzZ3QUFBZlEvZ0dKTjB2R3AvQUFBQUNVY0F2UXhkOXN4dEJ3Qzl4Y3gyNkJ0SEFNbGdrOFhvU3djQXZHZ0hCZWhBeHdDODE5MVd1SGRIQUwzU3c4Ym9UMGNBdlkrcGhyeDBCd0M4VXJZMXFFZEhBTTJhdnlhTVVBY0F2Z24zSnN3TUJ3Qys2aFJHbEdvSEFOYjhqZmFrVjRjQXZtYzBadkF3UndDOE96NDJsRmtIQUwvWUxVV3NkY2NBeEM2dFJ0d1N4d0MrdFY4R3BFYUhBTDdySXNhMGNzY0F2REthRmR3UHh3REEwNnAydEFxSEFOSDA4eWFZV3NjQXhFN3I5c1JTaHdEQVpFSkc4RXFIQUwwNXpmYTBZd2NBdmJ6SHBxeFJod0M5V1BmVzREWkhBTUJ6b3ZhRVl3Y0F2R3Z0TmN4MGh3QzgzM0Myb0V0SEFMOGNBYmJzUDRjQTdxQm1ScUEyQndDK0Y1SzI4QnJIQUx5OFl5YkVaQWNBdjY5THRyeGJSd0RCQ24wVzBGdUhBTDR6TGJiRVBvY0F2UFZqRnRCVWc9PQgAAwAAAAILAAQAAAAGQm90dG9tAAA"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"data": [
|
|
96
|
+
{
|
|
97
|
+
"raw": { "typename": "Tweet", "restId": "2018440335140024383" },
|
|
98
|
+
"tweet": {
|
|
99
|
+
"id": "2018440335140024383",
|
|
100
|
+
"text": "SpaceX has acquired xAI, forming one of the most ambitious...",
|
|
101
|
+
"createdAt": "Mon Feb 02 21:44:11 +0000 2026",
|
|
102
|
+
"favoriteCount": 44183,
|
|
103
|
+
"retweetCount": 7746,
|
|
104
|
+
"replyCount": 3354
|
|
105
|
+
},
|
|
106
|
+
"user": {
|
|
107
|
+
"id": "34743251",
|
|
108
|
+
"name": "SpaceX",
|
|
109
|
+
"screenName": "SpaceX",
|
|
110
|
+
"followersCount": 41074731
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"raw": { /* ... */ },
|
|
115
|
+
"tweet": { /* ... */ },
|
|
116
|
+
"user": { /* ... */ }
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
</details>
|
|
123
|
+
|
|
124
|
+
## Quick Start
|
|
125
|
+
|
|
126
|
+
### Guest mode
|
|
15
127
|
|
|
16
128
|
```ts
|
|
17
129
|
import { XScraper } from '@finch_ren/x-scraper';
|
|
@@ -30,9 +142,9 @@ async function main() {
|
|
|
30
142
|
main().catch(console.error);
|
|
31
143
|
```
|
|
32
144
|
|
|
33
|
-
### Cookie
|
|
145
|
+
### Cookie mode
|
|
34
146
|
|
|
35
|
-
|
|
147
|
+
A) Browser-exported cookie array
|
|
36
148
|
|
|
37
149
|
```ts
|
|
38
150
|
import { XScraper } from '@finch_ren/x-scraper';
|
|
@@ -41,17 +153,8 @@ async function main() {
|
|
|
41
153
|
const scraper = new XScraper();
|
|
42
154
|
|
|
43
155
|
const client = await scraper.getClientFromCookies([
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
name: 'ct0',
|
|
47
|
-
value: '<csrf_token>',
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
domain: '.x.com',
|
|
51
|
-
name: 'auth_token',
|
|
52
|
-
value: '<auth_token>',
|
|
53
|
-
},
|
|
54
|
-
...
|
|
156
|
+
{ domain: '.x.com', name: 'ct0', value: '<csrf_token>' },
|
|
157
|
+
{ domain: '.x.com', name: 'auth_token', value: '<auth_token>' },
|
|
55
158
|
]);
|
|
56
159
|
|
|
57
160
|
const profile = await client.getUserByScreenName({ screenName: 'jack' });
|
|
@@ -61,7 +164,7 @@ async function main() {
|
|
|
61
164
|
main().catch(console.error);
|
|
62
165
|
```
|
|
63
166
|
|
|
64
|
-
|
|
167
|
+
B) Cookie map (`name -> value`)
|
|
65
168
|
|
|
66
169
|
```ts
|
|
67
170
|
import { XScraper } from '@finch_ren/x-scraper';
|
|
@@ -70,9 +173,20 @@ async function main() {
|
|
|
70
173
|
const scraper = new XScraper();
|
|
71
174
|
|
|
72
175
|
const client = await scraper.getClientFromCookies({
|
|
73
|
-
|
|
176
|
+
__cf_bm: '<__cf_bm>',
|
|
177
|
+
__cuid: '<__cuid>',
|
|
178
|
+
_ga: '<_ga>',
|
|
179
|
+
_ga_BLY4P7T5KW: '<_ga_BLY4P7T5KW>',
|
|
180
|
+
_twitter_sess: '<_twitter_sess>',
|
|
74
181
|
auth_token: '<auth_token>',
|
|
75
|
-
|
|
182
|
+
ct0: '<ct0>',
|
|
183
|
+
guest_id: '<guest_id>',
|
|
184
|
+
guest_id_ads: '<guest_id_ads>',
|
|
185
|
+
guest_id_marketing: '<guest_id_marketing>',
|
|
186
|
+
kdt: '<kdt>',
|
|
187
|
+
lang: '<lang>',
|
|
188
|
+
personalization_id: '<personalization_id>',
|
|
189
|
+
twid: '<twid>',
|
|
76
190
|
});
|
|
77
191
|
|
|
78
192
|
const profile = await client.getUserByScreenName({ screenName: 'jack' });
|
|
@@ -82,67 +196,35 @@ async function main() {
|
|
|
82
196
|
main().catch(console.error);
|
|
83
197
|
```
|
|
84
198
|
|
|
85
|
-
##
|
|
86
|
-
|
|
87
|
-
同一能力通常有两种调用方式:
|
|
199
|
+
## API Entry Styles
|
|
88
200
|
|
|
89
|
-
|
|
90
|
-
- 分组方法:`client.user.getUserByScreenName(...)`
|
|
201
|
+
Most APIs support both styles:
|
|
91
202
|
|
|
92
|
-
|
|
203
|
+
- Flat shortcut: `client.getUserByScreenName(...)`
|
|
204
|
+
- Grouped API: `client.user.getUserByScreenName(...)`
|
|
93
205
|
|
|
94
206
|
```ts
|
|
95
|
-
// User
|
|
96
207
|
const userA = await client.getUserByScreenName({ screenName: 'elonmusk' });
|
|
97
208
|
const userB = await client.user.getUserByScreenName({ screenName: 'elonmusk' });
|
|
98
209
|
|
|
99
|
-
// Tweet timeline
|
|
100
210
|
const tweetsA = await client.getUserTweets({ userId: '44196397' });
|
|
101
211
|
const tweetsB = await client.tweet.getUserTweets({ userId: '44196397' });
|
|
102
212
|
|
|
103
|
-
// Post actions
|
|
104
213
|
await client.createTweet({ tweetText: 'hello' });
|
|
105
214
|
await client.post.postCreateTweet({ tweetText: 'hello' });
|
|
106
215
|
```
|
|
107
216
|
|
|
108
|
-
|
|
217
|
+
## Auth and Runtime Notes
|
|
109
218
|
|
|
110
|
-
|
|
111
|
-
- `client.getUserTweets(...)`
|
|
112
|
-
- `client.getTweetDetail(...)`
|
|
113
|
-
- `client.createTweet(...)`
|
|
114
|
-
- `client.deleteTweet(...)`
|
|
115
|
-
- `client.likeTweet(...)`
|
|
116
|
-
- `client.unlikeTweet(...)`
|
|
117
|
-
- `client.retweet(...)`
|
|
118
|
-
....
|
|
219
|
+
### Cookie notes
|
|
119
220
|
|
|
120
|
-
|
|
221
|
+
- You can use the `Cookie-Editor` browser extension to export cookies as JSON and pass it to `getClientFromCookies([...])`
|
|
222
|
+
- Do not commit cookies to git
|
|
223
|
+
- Use local files or environment variables for secret injection
|
|
121
224
|
|
|
122
|
-
|
|
123
|
-
- `client.user`
|
|
124
|
-
- `client.users`
|
|
125
|
-
- `client.userList`
|
|
126
|
-
- `client.post`
|
|
127
|
-
- `client.space`
|
|
128
|
-
- `client.v11Get`
|
|
129
|
-
- `client.v11Post`
|
|
130
|
-
- `client.v20Get`
|
|
131
|
-
- `client.default`
|
|
132
|
-
- `client.initialState`
|
|
133
|
-
....
|
|
225
|
+
### Header/platform mismatch
|
|
134
226
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
### 1) 关于 Cookie
|
|
138
|
-
|
|
139
|
-
- 推荐从已登录浏览器导出 `ct0` / `auth_token`(可选 `gt`)。
|
|
140
|
-
- `getClientFromCookies` 支持两种格式:对象映射或浏览器 cookie 数组。
|
|
141
|
-
- 不要把 Cookie 写入仓库,建议走本地文件或环境变量注入。
|
|
142
|
-
|
|
143
|
-
### 2) 平台 Header 一致性
|
|
144
|
-
|
|
145
|
-
某些账号 Cookie 与 `sec-ch-ua-platform` 不一致时可能失败,可手动覆盖:
|
|
227
|
+
If cookie origin and `sec-ch-ua-platform` mismatch, override header manually:
|
|
146
228
|
|
|
147
229
|
```ts
|
|
148
230
|
import { XScraper } from '@finch_ren/x-scraper';
|
|
@@ -153,13 +235,13 @@ scraper.setAdditionalApiHeaders({
|
|
|
153
235
|
});
|
|
154
236
|
```
|
|
155
237
|
|
|
156
|
-
###
|
|
238
|
+
### Risk notice
|
|
157
239
|
|
|
158
|
-
-
|
|
159
|
-
-
|
|
160
|
-
-
|
|
240
|
+
- This SDK depends on private endpoints that may change without notice
|
|
241
|
+
- Authenticated calls may trigger account risk controls
|
|
242
|
+
- Add retry + backoff in production
|
|
161
243
|
|
|
162
|
-
##
|
|
244
|
+
## Local Development
|
|
163
245
|
|
|
164
246
|
```bash
|
|
165
247
|
pnpm install
|
|
@@ -167,48 +249,44 @@ pnpm build
|
|
|
167
249
|
pnpm test
|
|
168
250
|
```
|
|
169
251
|
|
|
170
|
-
##
|
|
252
|
+
## Regenerate OpenAPI Code
|
|
171
253
|
|
|
172
254
|
```bash
|
|
173
255
|
pnpm generate
|
|
174
256
|
```
|
|
175
257
|
|
|
176
|
-
## openapi/placeholder.json
|
|
258
|
+
## `openapi/placeholder.json`
|
|
177
259
|
|
|
178
|
-
`openapi/placeholder.json`
|
|
179
|
-
SDK
|
|
260
|
+
`openapi/placeholder.json` is a runtime GraphQL operation template registry.
|
|
261
|
+
It is used by the SDK to build request variables/features/fieldToggles and endpoint metadata.
|
|
180
262
|
|
|
181
|
-
|
|
263
|
+
Typical fields:
|
|
182
264
|
|
|
183
|
-
- `@path
|
|
184
|
-
- `@method
|
|
185
|
-
- `queryId
|
|
186
|
-
- `variables
|
|
187
|
-
- `features` / `fieldToggles
|
|
265
|
+
- `@path`: endpoint path (used in transaction id generation)
|
|
266
|
+
- `@method`: HTTP method (used in transaction id generation)
|
|
267
|
+
- `queryId`: GraphQL query id
|
|
268
|
+
- `variables`: default variable template
|
|
269
|
+
- `features` / `fieldToggles`: default toggles
|
|
188
270
|
|
|
189
|
-
|
|
271
|
+
Code linkage:
|
|
190
272
|
|
|
191
|
-
- `src/api.ts`
|
|
192
|
-
- `src/utils/api.ts#getKwargs`
|
|
193
|
-
-
|
|
273
|
+
- `src/api.ts` loads it as `flagData`
|
|
274
|
+
- `src/utils/api.ts#getKwargs` builds request params from templates
|
|
275
|
+
- API groups (`client.tweet.*`, `client.user.*`, `client.space.*`) read templates via `this.flag[...]`
|
|
194
276
|
|
|
195
|
-
|
|
277
|
+
Notes:
|
|
196
278
|
|
|
197
|
-
- `client.space.getAudioSpaceById`
|
|
198
|
-
- `client.getLiveVideoStreamStatus
|
|
279
|
+
- `client.space.getAudioSpaceById` and `client.space.getBroadcastQuery` depend on this file
|
|
280
|
+
- `client.getLiveVideoStreamStatus` (v1.1 endpoint) does not depend on this file
|
|
199
281
|
|
|
200
|
-
##
|
|
282
|
+
## Acknowledgements
|
|
201
283
|
|
|
202
|
-
|
|
284
|
+
Core source is based on:
|
|
203
285
|
|
|
204
286
|
- [fa0311/twitter-openapi-typescript](https://github.com/fa0311/twitter-openapi-typescript)
|
|
205
287
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
- 整合 `twitter-openapi` 相关生成内容到当前 SDK 结构
|
|
209
|
-
- 提供更直接的调用入口与项目化组织方式
|
|
210
|
-
|
|
211
|
-
另外补充了 Space 相关接口能力(`client.space`):
|
|
288
|
+
This project adds integration and higher-level SDK ergonomics on top of upstream,
|
|
289
|
+
including additional Space APIs:
|
|
212
290
|
|
|
213
291
|
- `getAudioSpaceById`
|
|
214
292
|
- `getLiveVideoStreamStatus`
|