scrapebadger-cli 0.2.1__py3-none-any.whl

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.
@@ -0,0 +1,283 @@
1
+ """TikTok scraper commands."""
2
+
3
+ import typer
4
+
5
+ from scrapebadger_cli.client import api_get
6
+ from scrapebadger_cli.output import render
7
+
8
+ app = typer.Typer(no_args_is_help=True)
9
+
10
+ FMT = typer.Option("json", "--output", "-o", help="Output format: json, csv, table, markdown")
11
+ FIELDS = typer.Option(None, "--fields", "-f", help="Comma-separated fields to include")
12
+ REGION = typer.Option("US", "--region", "-r", help="Content region (US, GB, DE, BR, JP, ...)")
13
+ COUNT = typer.Option(30, "--count", "-c", help="Items to return (1-50)")
14
+
15
+
16
+ @app.command()
17
+ def user(
18
+ username: str = typer.Argument(..., help="TikTok handle (without @)"),
19
+ region: str = REGION,
20
+ fmt: str = FMT,
21
+ fields: str | None = FIELDS,
22
+ ) -> None:
23
+ """Get a user's full profile."""
24
+ render(api_get(f"/v1/tiktok/users/{username}", {"region": region}), fmt, fields)
25
+
26
+
27
+ @app.command(name="user-videos")
28
+ def user_videos(
29
+ username: str = typer.Argument(..., help="TikTok handle"),
30
+ region: str = REGION,
31
+ count: int = COUNT,
32
+ fmt: str = FMT,
33
+ fields: str | None = FIELDS,
34
+ ) -> None:
35
+ """Get a user's posted videos."""
36
+ render(
37
+ api_get(f"/v1/tiktok/users/{username}/videos", {"region": region, "count": count}),
38
+ fmt,
39
+ fields,
40
+ )
41
+
42
+
43
+ @app.command()
44
+ def followers(
45
+ username: str = typer.Argument(..., help="TikTok handle"),
46
+ region: str = REGION,
47
+ count: int = COUNT,
48
+ fmt: str = FMT,
49
+ fields: str | None = FIELDS,
50
+ ) -> None:
51
+ """Get a user's followers (best-effort)."""
52
+ render(
53
+ api_get(f"/v1/tiktok/users/{username}/followers", {"region": region, "count": count}),
54
+ fmt,
55
+ fields,
56
+ )
57
+
58
+
59
+ @app.command()
60
+ def following(
61
+ username: str = typer.Argument(..., help="TikTok handle"),
62
+ region: str = REGION,
63
+ count: int = COUNT,
64
+ fmt: str = FMT,
65
+ fields: str | None = FIELDS,
66
+ ) -> None:
67
+ """Get accounts a user follows (best-effort)."""
68
+ render(
69
+ api_get(f"/v1/tiktok/users/{username}/following", {"region": region, "count": count}),
70
+ fmt,
71
+ fields,
72
+ )
73
+
74
+
75
+ @app.command()
76
+ def video(
77
+ video_id: str = typer.Argument(..., help="Numeric video id"),
78
+ region: str = REGION,
79
+ username: str | None = typer.Option(
80
+ None, "--username", "-u", help="Author handle (skips oEmbed)"
81
+ ),
82
+ fmt: str = FMT,
83
+ fields: str | None = FIELDS,
84
+ ) -> None:
85
+ """Get full metadata for a single video."""
86
+ render(
87
+ api_get(f"/v1/tiktok/videos/{video_id}", {"region": region, "username": username}),
88
+ fmt,
89
+ fields,
90
+ )
91
+
92
+
93
+ @app.command()
94
+ def comments(
95
+ video_id: str = typer.Argument(..., help="Numeric video id"),
96
+ region: str = REGION,
97
+ count: int = typer.Option(20, "--count", "-c"),
98
+ username: str | None = typer.Option(None, "--username", "-u"),
99
+ fmt: str = FMT,
100
+ fields: str | None = FIELDS,
101
+ ) -> None:
102
+ """Get top-level comments on a video."""
103
+ render(
104
+ api_get(
105
+ f"/v1/tiktok/videos/{video_id}/comments",
106
+ {"region": region, "count": count, "username": username},
107
+ ),
108
+ fmt,
109
+ fields,
110
+ )
111
+
112
+
113
+ @app.command()
114
+ def related(
115
+ video_id: str = typer.Argument(..., help="Numeric video id"),
116
+ region: str = REGION,
117
+ count: int = typer.Option(16, "--count", "-c"),
118
+ username: str | None = typer.Option(None, "--username", "-u"),
119
+ fmt: str = FMT,
120
+ fields: str | None = FIELDS,
121
+ ) -> None:
122
+ """Get related videos for a video."""
123
+ render(
124
+ api_get(
125
+ f"/v1/tiktok/videos/{video_id}/related",
126
+ {"region": region, "count": count, "username": username},
127
+ ),
128
+ fmt,
129
+ fields,
130
+ )
131
+
132
+
133
+ @app.command()
134
+ def transcript(
135
+ video_id: str = typer.Argument(..., help="Numeric video id"),
136
+ region: str = REGION,
137
+ username: str | None = typer.Option(None, "--username", "-u"),
138
+ fmt: str = FMT,
139
+ fields: str | None = FIELDS,
140
+ ) -> None:
141
+ """Get subtitle/caption tracks for a video."""
142
+ render(
143
+ api_get(
144
+ f"/v1/tiktok/videos/{video_id}/transcript", {"region": region, "username": username}
145
+ ),
146
+ fmt,
147
+ fields,
148
+ )
149
+
150
+
151
+ @app.command()
152
+ def oembed(
153
+ url: str = typer.Argument(..., help="Full TikTok video or profile URL"),
154
+ region: str = REGION,
155
+ fmt: str = FMT,
156
+ fields: str | None = FIELDS,
157
+ ) -> None:
158
+ """Get cheap unauthenticated oEmbed metadata for a URL."""
159
+ render(api_get("/v1/tiktok/oembed", {"url": url, "region": region}), fmt, fields)
160
+
161
+
162
+ @app.command()
163
+ def hashtag(
164
+ name: str = typer.Argument(..., help="Hashtag (without #)"),
165
+ region: str = REGION,
166
+ fmt: str = FMT,
167
+ fields: str | None = FIELDS,
168
+ ) -> None:
169
+ """Get hashtag/challenge detail."""
170
+ render(api_get(f"/v1/tiktok/hashtags/{name}", {"region": region}), fmt, fields)
171
+
172
+
173
+ @app.command(name="hashtag-videos")
174
+ def hashtag_videos(
175
+ name: str = typer.Argument(..., help="Hashtag (without #)"),
176
+ region: str = REGION,
177
+ count: int = COUNT,
178
+ fmt: str = FMT,
179
+ fields: str | None = FIELDS,
180
+ ) -> None:
181
+ """Get videos tagged with a hashtag."""
182
+ render(
183
+ api_get(f"/v1/tiktok/hashtags/{name}/videos", {"region": region, "count": count}),
184
+ fmt,
185
+ fields,
186
+ )
187
+
188
+
189
+ @app.command()
190
+ def music(
191
+ music_id: str = typer.Argument(..., help="Numeric music id"),
192
+ region: str = REGION,
193
+ fmt: str = FMT,
194
+ fields: str | None = FIELDS,
195
+ ) -> None:
196
+ """Get sound/music detail."""
197
+ render(api_get(f"/v1/tiktok/music/{music_id}", {"region": region}), fmt, fields)
198
+
199
+
200
+ @app.command(name="music-videos")
201
+ def music_videos(
202
+ music_id: str = typer.Argument(..., help="Numeric music id"),
203
+ region: str = REGION,
204
+ count: int = COUNT,
205
+ fmt: str = FMT,
206
+ fields: str | None = FIELDS,
207
+ ) -> None:
208
+ """Get videos using a sound."""
209
+ render(
210
+ api_get(f"/v1/tiktok/music/{music_id}/videos", {"region": region, "count": count}),
211
+ fmt,
212
+ fields,
213
+ )
214
+
215
+
216
+ @app.command()
217
+ def search(
218
+ query: str = typer.Argument(..., help="Search keyword"),
219
+ kind: str = typer.Option("general", "--kind", "-k", help="general | users | videos | hashtags"),
220
+ region: str = REGION,
221
+ count: int = typer.Option(20, "--count", "-c"),
222
+ fmt: str = FMT,
223
+ fields: str | None = FIELDS,
224
+ ) -> None:
225
+ """Search TikTok (general/users/videos/hashtags)."""
226
+ path = "/v1/tiktok/search" if kind == "general" else f"/v1/tiktok/search/{kind}"
227
+ render(api_get(path, {"query": query, "region": region, "count": count}), fmt, fields)
228
+
229
+
230
+ @app.command()
231
+ def trending(
232
+ kind: str = typer.Option("videos", "--kind", "-k", help="videos | hashtags | songs"),
233
+ region: str = REGION,
234
+ period: int = typer.Option(7, "--period", help="Trend window in days (7/30/120)"),
235
+ count: int = typer.Option(20, "--count", "-c"),
236
+ fmt: str = FMT,
237
+ fields: str | None = FIELDS,
238
+ ) -> None:
239
+ """Get trending data (videos/hashtags/songs)."""
240
+ params = {"region": region, "count": count}
241
+ if kind in ("hashtags", "songs"):
242
+ params["period"] = period
243
+ render(api_get(f"/v1/tiktok/trending/{kind}", params), fmt, fields)
244
+
245
+
246
+ @app.command()
247
+ def ads(
248
+ query: str = typer.Option(
249
+ "", "--query", "-q", help="Keyword (ignored when --advertiser-id is set)"
250
+ ),
251
+ advertiser_id: str = typer.Option("", "--advertiser-id", help="Advertiser business id(s)"),
252
+ region: str = typer.Option(
253
+ "DE", "--region", "-r", help="EU region code (Ad Library is EU-only)"
254
+ ),
255
+ days: int = typer.Option(30, "--days", help="Look-back window in days"),
256
+ count: int = typer.Option(20, "--count", "-c"),
257
+ fmt: str = FMT,
258
+ fields: str | None = FIELDS,
259
+ ) -> None:
260
+ """Search the TikTok Ad Library (Commercial Content Library) by keyword or advertiser."""
261
+ render(
262
+ api_get(
263
+ "/v1/tiktok/ads/search",
264
+ {
265
+ "query": query,
266
+ "advertiser_id": advertiser_id,
267
+ "region": region,
268
+ "days": days,
269
+ "count": count,
270
+ },
271
+ ),
272
+ fmt,
273
+ fields,
274
+ )
275
+
276
+
277
+ @app.command()
278
+ def regions(
279
+ fmt: str = FMT,
280
+ fields: str | None = FIELDS,
281
+ ) -> None:
282
+ """List supported TikTok content regions."""
283
+ render(api_get("/v1/tiktok/regions"), fmt, fields)
@@ -0,0 +1,235 @@
1
+ """Twitter/X commands."""
2
+
3
+ import typer
4
+
5
+ from scrapebadger_cli.client import api_get
6
+ from scrapebadger_cli.output import render
7
+
8
+ app = typer.Typer(no_args_is_help=True)
9
+
10
+ FMT = typer.Option("json", "--output", "-o", help="Output format: json, csv, table, markdown")
11
+ FIELDS = typer.Option(None, "--fields", "-f", help="Comma-separated fields to include")
12
+ CURSOR = typer.Option(None, "--cursor", "-c", help="Pagination cursor")
13
+
14
+
15
+ @app.command()
16
+ def user(
17
+ username: str = typer.Argument(..., help="Twitter username (without @)"),
18
+ fmt: str = FMT,
19
+ fields: str | None = FIELDS,
20
+ ) -> None:
21
+ """Get a Twitter user's profile by username."""
22
+ data = api_get(f"/v1/twitter/users/{username}/by_username")
23
+ render(data, fmt, fields)
24
+
25
+
26
+ @app.command(name="user-id")
27
+ def user_by_id(
28
+ user_id: str = typer.Argument(..., help="Numeric user ID"),
29
+ fmt: str = FMT,
30
+ fields: str | None = FIELDS,
31
+ ) -> None:
32
+ """Get a Twitter user's profile by numeric ID."""
33
+ data = api_get(f"/v1/twitter/users/{user_id}/by_id")
34
+ render(data, fmt, fields)
35
+
36
+
37
+ @app.command()
38
+ def tweets(
39
+ username: str = typer.Argument(..., help="Twitter username"),
40
+ cursor: str | None = CURSOR,
41
+ fmt: str = FMT,
42
+ fields: str | None = FIELDS,
43
+ ) -> None:
44
+ """Get latest tweets from a user."""
45
+ data = api_get(f"/v1/twitter/users/{username}/latest_tweets", {"cursor": cursor})
46
+ render(data, fmt, fields)
47
+
48
+
49
+ @app.command()
50
+ def tweet(
51
+ tweet_id: str = typer.Argument(..., help="Tweet ID"),
52
+ fmt: str = FMT,
53
+ fields: str | None = FIELDS,
54
+ ) -> None:
55
+ """Get a single tweet by ID."""
56
+ data = api_get(f"/v1/twitter/tweets/tweet/{tweet_id}")
57
+ render(data, fmt, fields)
58
+
59
+
60
+ @app.command()
61
+ def search(
62
+ query: str = typer.Argument(..., help="Search query"),
63
+ query_type: str | None = typer.Option(None, "--type", "-t", help="Search type: Latest, Top"),
64
+ count: int | None = typer.Option(None, "--count", "-n", help="Max results"),
65
+ cursor: str | None = CURSOR,
66
+ fmt: str = FMT,
67
+ fields: str | None = FIELDS,
68
+ ) -> None:
69
+ """Advanced tweet search."""
70
+ data = api_get(
71
+ "/v1/twitter/tweets/advanced_search",
72
+ {"query": query, "query_type": query_type, "count": count, "cursor": cursor},
73
+ )
74
+ render(data, fmt, fields)
75
+
76
+
77
+ @app.command()
78
+ def followers(
79
+ username: str = typer.Argument(..., help="Twitter username"),
80
+ cursor: str | None = CURSOR,
81
+ fmt: str = FMT,
82
+ fields: str | None = FIELDS,
83
+ ) -> None:
84
+ """Get followers of a user."""
85
+ data = api_get(f"/v1/twitter/users/{username}/followers", {"cursor": cursor})
86
+ render(data, fmt, fields)
87
+
88
+
89
+ @app.command()
90
+ def following(
91
+ username: str = typer.Argument(..., help="Twitter username"),
92
+ cursor: str | None = CURSOR,
93
+ fmt: str = FMT,
94
+ fields: str | None = FIELDS,
95
+ ) -> None:
96
+ """Get users that a user is following."""
97
+ data = api_get(f"/v1/twitter/users/{username}/followings", {"cursor": cursor})
98
+ render(data, fmt, fields)
99
+
100
+
101
+ @app.command()
102
+ def mentions(
103
+ username: str = typer.Argument(..., help="Twitter username"),
104
+ count: int | None = typer.Option(None, "--count", "-n", help="Max results"),
105
+ cursor: str | None = CURSOR,
106
+ fmt: str = FMT,
107
+ fields: str | None = FIELDS,
108
+ ) -> None:
109
+ """Get tweets mentioning a user."""
110
+ data = api_get(
111
+ f"/v1/twitter/users/{username}/mentions",
112
+ {"count": count, "cursor": cursor},
113
+ )
114
+ render(data, fmt, fields)
115
+
116
+
117
+ @app.command()
118
+ def trends(
119
+ place: str | None = typer.Option(None, "--place", "-p", help="WOEID for location trends"),
120
+ category: str | None = typer.Option(None, "--category", help="Trend category"),
121
+ count: int | None = typer.Option(None, "--count", "-n", help="Max results"),
122
+ fmt: str = FMT,
123
+ fields: str | None = FIELDS,
124
+ ) -> None:
125
+ """Get trending topics."""
126
+ if place:
127
+ data = api_get(f"/v1/twitter/trends/place/{place}")
128
+ else:
129
+ data = api_get("/v1/twitter/trends/", {"category": category, "count": count})
130
+ render(data, fmt, fields)
131
+
132
+
133
+ @app.command()
134
+ def replies(
135
+ tweet_id: str = typer.Argument(..., help="Tweet ID"),
136
+ cursor: str | None = CURSOR,
137
+ fmt: str = FMT,
138
+ fields: str | None = FIELDS,
139
+ ) -> None:
140
+ """Get replies to a tweet."""
141
+ data = api_get(f"/v1/twitter/tweets/tweet/{tweet_id}/replies", {"cursor": cursor})
142
+ render(data, fmt, fields)
143
+
144
+
145
+ @app.command()
146
+ def quotes(
147
+ tweet_id: str = typer.Argument(..., help="Tweet ID"),
148
+ cursor: str | None = CURSOR,
149
+ fmt: str = FMT,
150
+ fields: str | None = FIELDS,
151
+ ) -> None:
152
+ """Get quote tweets."""
153
+ data = api_get(f"/v1/twitter/tweets/tweet/{tweet_id}/quotes", {"cursor": cursor})
154
+ render(data, fmt, fields)
155
+
156
+
157
+ @app.command()
158
+ def retweeters(
159
+ tweet_id: str = typer.Argument(..., help="Tweet ID"),
160
+ cursor: str | None = CURSOR,
161
+ fmt: str = FMT,
162
+ fields: str | None = FIELDS,
163
+ ) -> None:
164
+ """Get users who retweeted a tweet."""
165
+ data = api_get(f"/v1/twitter/tweets/tweet/{tweet_id}/retweeters", {"cursor": cursor})
166
+ render(data, fmt, fields)
167
+
168
+
169
+ @app.command()
170
+ def community(
171
+ community_id: str = typer.Argument(..., help="Community ID"),
172
+ fmt: str = FMT,
173
+ fields: str | None = FIELDS,
174
+ ) -> None:
175
+ """Get community details."""
176
+ data = api_get(f"/v1/twitter/communities/{community_id}")
177
+ render(data, fmt, fields)
178
+
179
+
180
+ @app.command(name="community-tweets")
181
+ def community_tweets(
182
+ community_id: str = typer.Argument(..., help="Community ID"),
183
+ cursor: str | None = CURSOR,
184
+ fmt: str = FMT,
185
+ fields: str | None = FIELDS,
186
+ ) -> None:
187
+ """Get tweets from a community."""
188
+ data = api_get(f"/v1/twitter/communities/{community_id}/tweets", {"cursor": cursor})
189
+ render(data, fmt, fields)
190
+
191
+
192
+ @app.command(name="search-communities")
193
+ def search_communities(
194
+ query: str = typer.Argument(..., help="Search query"),
195
+ cursor: str | None = CURSOR,
196
+ fmt: str = FMT,
197
+ fields: str | None = FIELDS,
198
+ ) -> None:
199
+ """Search for communities."""
200
+ data = api_get("/v1/twitter/communities/search", {"query": query, "cursor": cursor})
201
+ render(data, fmt, fields)
202
+
203
+
204
+ @app.command(name="list")
205
+ def list_detail(
206
+ list_id: str = typer.Argument(..., help="List ID"),
207
+ fmt: str = FMT,
208
+ fields: str | None = FIELDS,
209
+ ) -> None:
210
+ """Get list details."""
211
+ data = api_get(f"/v1/twitter/lists/{list_id}/detail")
212
+ render(data, fmt, fields)
213
+
214
+
215
+ @app.command(name="list-tweets")
216
+ def list_tweets(
217
+ list_id: str = typer.Argument(..., help="List ID"),
218
+ cursor: str | None = CURSOR,
219
+ fmt: str = FMT,
220
+ fields: str | None = FIELDS,
221
+ ) -> None:
222
+ """Get tweets from a list."""
223
+ data = api_get(f"/v1/twitter/lists/{list_id}/tweets", {"cursor": cursor})
224
+ render(data, fmt, fields)
225
+
226
+
227
+ @app.command()
228
+ def space(
229
+ space_id: str = typer.Argument(..., help="Space ID"),
230
+ fmt: str = FMT,
231
+ fields: str | None = FIELDS,
232
+ ) -> None:
233
+ """Get Twitter Space details."""
234
+ data = api_get(f"/v1/twitter/spaces/{space_id}")
235
+ render(data, fmt, fields)
@@ -0,0 +1,105 @@
1
+ """Vinted marketplace commands."""
2
+
3
+ import typer
4
+
5
+ from scrapebadger_cli.client import api_get
6
+ from scrapebadger_cli.output import render
7
+
8
+ app = typer.Typer(no_args_is_help=True)
9
+
10
+ FMT = typer.Option("json", "--output", "-o", help="Output format: json, csv, table, markdown")
11
+ FIELDS = typer.Option(None, "--fields", "-f", help="Comma-separated fields to include")
12
+ MARKET = typer.Option("fr", "--market", "-m", help="Vinted market code (fr, de, uk, us, etc.)")
13
+
14
+
15
+ @app.command()
16
+ def search(
17
+ query: str = typer.Argument(..., help="Search text"),
18
+ market: str = MARKET,
19
+ page: int = typer.Option(1, "--page", "-p"),
20
+ per_page: int = typer.Option(20, "--per-page"),
21
+ price_from: float | None = typer.Option(None, "--price-from"),
22
+ price_to: float | None = typer.Option(None, "--price-to"),
23
+ brand_ids: str | None = typer.Option(None, "--brands", help="Comma-separated brand IDs"),
24
+ order: str | None = typer.Option(None, "--order"),
25
+ fmt: str = FMT,
26
+ fields: str | None = FIELDS,
27
+ ) -> None:
28
+ """Search Vinted marketplace for items."""
29
+ data = api_get(
30
+ "/v1/vinted/search",
31
+ {
32
+ "query": query,
33
+ "market": market,
34
+ "page": page,
35
+ "per_page": per_page,
36
+ "price_from": price_from,
37
+ "price_to": price_to,
38
+ "brand_ids": brand_ids,
39
+ "order": order,
40
+ },
41
+ )
42
+ render(data, fmt, fields)
43
+
44
+
45
+ @app.command()
46
+ def item(
47
+ item_id: int = typer.Argument(..., help="Vinted item ID"),
48
+ market: str = MARKET,
49
+ fmt: str = FMT,
50
+ fields: str | None = FIELDS,
51
+ ) -> None:
52
+ """Get item details."""
53
+ data = api_get(f"/v1/vinted/items/{item_id}", {"market": market})
54
+ render(data, fmt, fields)
55
+
56
+
57
+ @app.command()
58
+ def user(
59
+ user_id: int = typer.Argument(..., help="Vinted user ID"),
60
+ market: str = MARKET,
61
+ fmt: str = FMT,
62
+ fields: str | None = FIELDS,
63
+ ) -> None:
64
+ """Get seller/buyer profile."""
65
+ data = api_get(f"/v1/vinted/users/{user_id}", {"market": market})
66
+ render(data, fmt, fields)
67
+
68
+
69
+ @app.command(name="user-items")
70
+ def user_items(
71
+ user_id: int = typer.Argument(..., help="Vinted user ID"),
72
+ market: str = MARKET,
73
+ page: int = typer.Option(1, "--page", "-p"),
74
+ per_page: int = typer.Option(20, "--per-page"),
75
+ fmt: str = FMT,
76
+ fields: str | None = FIELDS,
77
+ ) -> None:
78
+ """Get items listed by a user."""
79
+ data = api_get(
80
+ f"/v1/vinted/users/{user_id}/items",
81
+ {"market": market, "page": page, "per_page": per_page},
82
+ )
83
+ render(data, fmt, fields)
84
+
85
+
86
+ @app.command()
87
+ def brands(
88
+ keyword: str = typer.Argument(..., help="Brand search keyword"),
89
+ market: str = MARKET,
90
+ fmt: str = FMT,
91
+ fields: str | None = FIELDS,
92
+ ) -> None:
93
+ """Search Vinted brands."""
94
+ data = api_get("/v1/vinted/brands", {"keyword": keyword, "market": market})
95
+ render(data, fmt, fields)
96
+
97
+
98
+ @app.command()
99
+ def markets(
100
+ fmt: str = FMT,
101
+ fields: str | None = FIELDS,
102
+ ) -> None:
103
+ """List all supported Vinted markets."""
104
+ data = api_get("/v1/vinted/markets")
105
+ render(data, fmt, fields)