scrapebadger-cli 0.21.2__tar.gz → 0.22.0__tar.gz

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.
Files changed (59) hide show
  1. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/PKG-INFO +16 -2
  2. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/README.md +15 -1
  3. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/pyproject.toml +1 -1
  4. scrapebadger_cli-0.22.0/src/scrapebadger_cli/__init__.py +1 -0
  5. scrapebadger_cli-0.22.0/src/scrapebadger_cli/commands/aliexpress.py +181 -0
  6. scrapebadger_cli-0.22.0/src/scrapebadger_cli/commands/asos.py +188 -0
  7. scrapebadger_cli-0.22.0/src/scrapebadger_cli/commands/bestbuy.py +142 -0
  8. scrapebadger_cli-0.22.0/src/scrapebadger_cli/commands/etsy.py +191 -0
  9. scrapebadger_cli-0.22.0/src/scrapebadger_cli/commands/target.py +182 -0
  10. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/main.py +10 -0
  11. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/uv.lock +1 -1
  12. scrapebadger_cli-0.21.2/src/scrapebadger_cli/__init__.py +0 -1
  13. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/.gitignore +0 -0
  14. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/client.py +0 -0
  15. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/__init__.py +0 -0
  16. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/airbnb.py +0 -0
  17. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/amazon.py +0 -0
  18. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/apartments.py +0 -0
  19. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/app_store.py +0 -0
  20. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/auth.py +0 -0
  21. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/baidu.py +0 -0
  22. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/bing.py +0 -0
  23. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/booking.py +0 -0
  24. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/chatgpt.py +0 -0
  25. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/depop.py +0 -0
  26. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/duckduckgo.py +0 -0
  27. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/ebay.py +0 -0
  28. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/facebook.py +0 -0
  29. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/gemini.py +0 -0
  30. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/google.py +0 -0
  31. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/google_play.py +0 -0
  32. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/gumtree.py +0 -0
  33. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/idealista.py +0 -0
  34. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/immobiliare.py +0 -0
  35. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/instagram.py +0 -0
  36. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/leboncoin.py +0 -0
  37. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/linkedin.py +0 -0
  38. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/loopnet.py +0 -0
  39. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/naver.py +0 -0
  40. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/perplexity.py +0 -0
  41. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/realtor.py +0 -0
  42. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/reddit.py +0 -0
  43. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/redfin.py +0 -0
  44. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/stream.py +0 -0
  45. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/tiktok.py +0 -0
  46. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/twitter.py +0 -0
  47. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/vinted.py +0 -0
  48. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/wallapop.py +0 -0
  49. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/walmart.py +0 -0
  50. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/web.py +0 -0
  51. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/yahoo.py +0 -0
  52. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/yandex.py +0 -0
  53. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/youtube.py +0 -0
  54. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/zillow.py +0 -0
  55. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/config.py +0 -0
  56. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/output.py +0 -0
  57. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/tests/test_naver.py +0 -0
  58. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/tests/test_tiktok_pagination.py +0 -0
  59. {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/tests/test_vinted_image_search.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: scrapebadger-cli
3
- Version: 0.21.2
3
+ Version: 0.22.0
4
4
  Summary: ScrapeBadger CLI — Twitter, Vinted, Google, and Web Scraping from the command line
5
5
  Author: ScrapeBadger Team
6
6
  License: MIT
@@ -22,7 +22,7 @@ Description-Content-Type: text/markdown
22
22
 
23
23
  # ScrapeBadger CLI
24
24
 
25
- Command-line interface for ScrapeBadger — Twitter, Vinted, and Web Scraping.
25
+ Command-line access to ScrapeBadger APIs, including Etsy, AliExpress, Target, ASOS, Best Buy, search engines, social media and general web scraping.
26
26
 
27
27
  ## Install
28
28
 
@@ -71,3 +71,17 @@ WebP up to 5 MiB are supported. Returns active listings in visual ranking order;
71
71
  10 credits per successful request. Filter with `--catalog-ids`, `--brand-ids`,
72
72
  `--status-ids`, `--color-ids`, `--size-ids`, `--material-ids` and price bounds.
73
73
  Resend the same image and `--time` from pagination when requesting `--page 2`.
74
+
75
+ ## Dedicated ecommerce APIs
76
+
77
+ ```bash
78
+ scrapebadger etsy search "ceramic mug" --currency USD --country US
79
+ scrapebadger aliexpress search "keyboard"
80
+ scrapebadger target search "laptop" --zip-code 55403 --store-id 1375
81
+ scrapebadger asos search "shoes" --market uk
82
+ scrapebadger bestbuy search "laptop"
83
+ ```
84
+
85
+ Run `scrapebadger <api> --help` to list product, review, store, availability and
86
+ reference commands for that API. Each command supports `--help` for its exact
87
+ parameters. Requests use your API key's existing permissions and credit balance.
@@ -1,6 +1,6 @@
1
1
  # ScrapeBadger CLI
2
2
 
3
- Command-line interface for ScrapeBadger — Twitter, Vinted, and Web Scraping.
3
+ Command-line access to ScrapeBadger APIs, including Etsy, AliExpress, Target, ASOS, Best Buy, search engines, social media and general web scraping.
4
4
 
5
5
  ## Install
6
6
 
@@ -49,3 +49,17 @@ WebP up to 5 MiB are supported. Returns active listings in visual ranking order;
49
49
  10 credits per successful request. Filter with `--catalog-ids`, `--brand-ids`,
50
50
  `--status-ids`, `--color-ids`, `--size-ids`, `--material-ids` and price bounds.
51
51
  Resend the same image and `--time` from pagination when requesting `--page 2`.
52
+
53
+ ## Dedicated ecommerce APIs
54
+
55
+ ```bash
56
+ scrapebadger etsy search "ceramic mug" --currency USD --country US
57
+ scrapebadger aliexpress search "keyboard"
58
+ scrapebadger target search "laptop" --zip-code 55403 --store-id 1375
59
+ scrapebadger asos search "shoes" --market uk
60
+ scrapebadger bestbuy search "laptop"
61
+ ```
62
+
63
+ Run `scrapebadger <api> --help` to list product, review, store, availability and
64
+ reference commands for that API. Each command supports `--help` for its exact
65
+ parameters. Requests use your API key's existing permissions and credit balance.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "scrapebadger-cli"
3
- version = "0.21.2"
3
+ version = "0.22.0"
4
4
  description = "ScrapeBadger CLI — Twitter, Vinted, Google, and Web Scraping from the command line"
5
5
  requires-python = ">=3.10"
6
6
  license = {text = "MIT"}
@@ -0,0 +1 @@
1
+ """ScrapeBadger CLI for ecommerce, search, social and general web scraping APIs."""
@@ -0,0 +1,181 @@
1
+ """AliExpress dedicated API commands."""
2
+
3
+ from urllib.parse import quote
4
+
5
+ import typer
6
+
7
+ from scrapebadger_cli.client import api_get
8
+ from scrapebadger_cli.output import render
9
+
10
+ app = typer.Typer(no_args_is_help=True)
11
+
12
+
13
+ @app.command("search")
14
+ def search(
15
+ query: str = typer.Argument(...),
16
+ page: int = typer.Option(1, "--page"),
17
+ country: str = typer.Option("US", "--country"),
18
+ currency: str = typer.Option("USD", "--currency"),
19
+ language: str = typer.Option("en", "--language"),
20
+ sort: str = typer.Option("default", "--sort"),
21
+ min_price: float | None = typer.Option(None, "--min-price"),
22
+ max_price: float | None = typer.Option(None, "--max-price"),
23
+ fmt: str = typer.Option("json", "--output", "-o"),
24
+ fields: str | None = typer.Option(None, "--fields", "-f"),
25
+ ) -> None:
26
+ "Search products. 5 credits."
27
+ render(
28
+ api_get(
29
+ "/v1/aliexpress/search",
30
+ {
31
+ "query": query,
32
+ "page": page,
33
+ "country": country,
34
+ "currency": currency,
35
+ "language": language,
36
+ "sort": sort,
37
+ "min_price": min_price,
38
+ "max_price": max_price,
39
+ },
40
+ ),
41
+ fmt,
42
+ fields,
43
+ )
44
+
45
+
46
+ @app.command("product")
47
+ def product(
48
+ product_id: str = typer.Argument(...),
49
+ country: str = typer.Option("US", "--country"),
50
+ currency: str = typer.Option("USD", "--currency"),
51
+ language: str = typer.Option("en_US", "--language"),
52
+ fmt: str = typer.Option("json", "--output", "-o"),
53
+ fields: str | None = typer.Option(None, "--fields", "-f"),
54
+ ) -> None:
55
+ "Get product detail. 5 credits."
56
+ render(
57
+ api_get(
58
+ f"/v1/aliexpress/products/{quote(str(product_id), safe='')}",
59
+ {"country": country, "currency": currency, "language": language},
60
+ ),
61
+ fmt,
62
+ fields,
63
+ )
64
+
65
+
66
+ @app.command("reviews")
67
+ def reviews(
68
+ product_id: str = typer.Argument(...),
69
+ page: int = typer.Option(1, "--page"),
70
+ page_size: int = typer.Option(10, "--page-size"),
71
+ sort: str = typer.Option("complex_default", "--sort"),
72
+ fmt: str = typer.Option("json", "--output", "-o"),
73
+ fields: str | None = typer.Option(None, "--fields", "-f"),
74
+ ) -> None:
75
+ "Get product reviews. 5 credits."
76
+ render(
77
+ api_get(
78
+ f"/v1/aliexpress/products/{quote(str(product_id), safe='')}/reviews",
79
+ {"page": page, "page_size": page_size, "sort": sort},
80
+ ),
81
+ fmt,
82
+ fields,
83
+ )
84
+
85
+
86
+ @app.command("store")
87
+ def store(
88
+ store_id: str = typer.Argument(...),
89
+ country: str = typer.Option("US", "--country"),
90
+ currency: str = typer.Option("USD", "--currency"),
91
+ language: str = typer.Option("en", "--language"),
92
+ fmt: str = typer.Option("json", "--output", "-o"),
93
+ fields: str | None = typer.Option(None, "--fields", "-f"),
94
+ ) -> None:
95
+ "Get store profile. 10 credits."
96
+ render(
97
+ api_get(
98
+ f"/v1/aliexpress/stores/{quote(str(store_id), safe='')}",
99
+ {"country": country, "currency": currency, "language": language},
100
+ ),
101
+ fmt,
102
+ fields,
103
+ )
104
+
105
+
106
+ @app.command("store-products")
107
+ def store_products(
108
+ store_id: str = typer.Argument(...),
109
+ page: int = typer.Option(1, "--page"),
110
+ page_size: int = typer.Option(24, "--page-size"),
111
+ country: str = typer.Option("US", "--country"),
112
+ currency: str = typer.Option("USD", "--currency"),
113
+ language: str = typer.Option("en", "--language"),
114
+ query: str = typer.Option("", "--query"),
115
+ sort: str = typer.Option("bestmatch_sort", "--sort"),
116
+ fmt: str = typer.Option("json", "--output", "-o"),
117
+ fields: str | None = typer.Option(None, "--fields", "-f"),
118
+ ) -> None:
119
+ "Get store products. 10 credits."
120
+ render(
121
+ api_get(
122
+ f"/v1/aliexpress/stores/{quote(str(store_id), safe='')}/products",
123
+ {
124
+ "page": page,
125
+ "page_size": page_size,
126
+ "country": country,
127
+ "currency": currency,
128
+ "language": language,
129
+ "query": query,
130
+ "sort": sort,
131
+ },
132
+ ),
133
+ fmt,
134
+ fields,
135
+ )
136
+
137
+
138
+ @app.command("autocomplete")
139
+ def autocomplete(
140
+ query: str = typer.Argument(...),
141
+ country: str = typer.Option("US", "--country"),
142
+ language: str = typer.Option("en", "--language"),
143
+ fmt: str = typer.Option("json", "--output", "-o"),
144
+ fields: str | None = typer.Option(None, "--fields", "-f"),
145
+ ) -> None:
146
+ "Get search suggestions. 5 credits."
147
+ render(
148
+ api_get(
149
+ "/v1/aliexpress/autocomplete",
150
+ {"query": query, "country": country, "language": language},
151
+ ),
152
+ fmt,
153
+ fields,
154
+ )
155
+
156
+
157
+ @app.command("categories")
158
+ def categories(
159
+ fmt: str = typer.Option("json", "--output", "-o"),
160
+ fields: str | None = typer.Option(None, "--fields", "-f"),
161
+ ) -> None:
162
+ "Get category navigation. 5 credits."
163
+ render(api_get("/v1/aliexpress/categories", {}), fmt, fields)
164
+
165
+
166
+ @app.command("store-categories")
167
+ def store_categories(
168
+ store_id: str = typer.Argument(...),
169
+ country: str = typer.Option("US", "--country"),
170
+ fmt: str = typer.Option("json", "--output", "-o"),
171
+ fields: str | None = typer.Option(None, "--fields", "-f"),
172
+ ) -> None:
173
+ "Get store product categories. 5 credits."
174
+ render(
175
+ api_get(
176
+ f"/v1/aliexpress/stores/{quote(str(store_id), safe='')}/categories",
177
+ {"country": country},
178
+ ),
179
+ fmt,
180
+ fields,
181
+ )
@@ -0,0 +1,188 @@
1
+ """ASOS fashion product 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
+ FMT = typer.Option("json", "--output", "-o", help="json, csv, table, markdown")
10
+ FIELDS = typer.Option(None, "--fields", "-f", help="Comma-separated fields")
11
+ MARKET = typer.Option("uk", "--market", "-m", help="uk, us, au, de, fr, it, es, nl, se, dk, pl")
12
+
13
+
14
+ @app.command()
15
+ def search(
16
+ query: str | None = typer.Argument(None),
17
+ market: str = MARKET,
18
+ category_id: int | None = typer.Option(None, "--category-id"),
19
+ offset: int = typer.Option(0),
20
+ limit: int = typer.Option(48),
21
+ sort: str = typer.Option("recommended", help="recommended, freshness, priceasc, pricedesc"),
22
+ brand: str | None = typer.Option(None, help="Comma-separated brand facet ids"),
23
+ colour: str | None = typer.Option(None, help="Comma-separated colour facet ids"),
24
+ size: str | None = typer.Option(None, help="Comma-separated size facet ids"),
25
+ min_price: float | None = typer.Option(None),
26
+ max_price: float | None = typer.Option(None),
27
+ store: str | None = typer.Option(None),
28
+ country: str | None = typer.Option(None),
29
+ currency: str | None = typer.Option(None),
30
+ language: str | None = typer.Option(None),
31
+ fmt: str = FMT,
32
+ fields: str | None = FIELDS,
33
+ ) -> None:
34
+ """Search by keyword, category, price and fashion facets."""
35
+ params = {k: v for k, v in locals().items() if k not in ("fmt", "fields")}
36
+ render(api_get("/v1/asos/search", params), fmt, fields)
37
+
38
+
39
+ @app.command()
40
+ def product(
41
+ product_id: int,
42
+ market: str = MARKET,
43
+ store: str | None = typer.Option(None),
44
+ country: str | None = typer.Option(None),
45
+ currency: str | None = typer.Option(None),
46
+ language: str | None = typer.Option(None),
47
+ fmt: str = FMT,
48
+ fields: str | None = FIELDS,
49
+ ) -> None:
50
+ """Product details with live prices; --currency overrides prices while copy follows --market."""
51
+ render(
52
+ api_get(
53
+ f"/v1/asos/products/{product_id}",
54
+ {
55
+ "market": market,
56
+ "store": store,
57
+ "country": country,
58
+ "currency": currency,
59
+ "language": language,
60
+ },
61
+ ),
62
+ fmt,
63
+ fields,
64
+ )
65
+
66
+
67
+ @app.command("stock-price")
68
+ def stock_price(
69
+ product_ids: str,
70
+ market: str = MARKET,
71
+ store: str | None = typer.Option(None, "--store"),
72
+ country: str | None = typer.Option(None, "--country"),
73
+ currency: str | None = typer.Option(None, "--currency"),
74
+ language: str | None = typer.Option(None, "--language"),
75
+ fmt: str = FMT,
76
+ fields: str | None = FIELDS,
77
+ ) -> None:
78
+ """Refresh live stock and prices for comma-separated product IDs (up to 50)."""
79
+ render(
80
+ api_get(
81
+ "/v1/asos/stock-price",
82
+ {
83
+ "product_ids": product_ids,
84
+ "market": market,
85
+ "store": store,
86
+ "country": country,
87
+ "currency": currency,
88
+ "language": language,
89
+ },
90
+ ),
91
+ fmt,
92
+ fields,
93
+ )
94
+
95
+
96
+ @app.command()
97
+ def categories(
98
+ market: str = MARKET,
99
+ gender: str = typer.Option("men"),
100
+ store: str | None = typer.Option(None, "--store"),
101
+ country: str | None = typer.Option(None, "--country"),
102
+ currency: str | None = typer.Option(None, "--currency"),
103
+ language: str | None = typer.Option(None, "--language"),
104
+ fmt: str = FMT,
105
+ fields: str | None = FIELDS,
106
+ ) -> None:
107
+ """Nested navigation for men or women."""
108
+ render(
109
+ api_get(
110
+ "/v1/asos/categories",
111
+ {
112
+ "market": market,
113
+ "gender": gender,
114
+ "store": store,
115
+ "country": country,
116
+ "currency": currency,
117
+ "language": language,
118
+ },
119
+ ),
120
+ fmt,
121
+ fields,
122
+ )
123
+
124
+
125
+ @app.command()
126
+ def autocomplete(
127
+ query: str,
128
+ market: str = MARKET,
129
+ store: str | None = typer.Option(None, "--store"),
130
+ country: str | None = typer.Option(None, "--country"),
131
+ currency: str | None = typer.Option(None, "--currency"),
132
+ language: str | None = typer.Option(None, "--language"),
133
+ fmt: str = FMT,
134
+ fields: str | None = FIELDS,
135
+ ) -> None:
136
+ """Search suggestions with result counts."""
137
+ render(
138
+ api_get(
139
+ "/v1/asos/autocomplete",
140
+ {
141
+ "query": query,
142
+ "market": market,
143
+ "store": store,
144
+ "country": country,
145
+ "currency": currency,
146
+ "language": language,
147
+ },
148
+ ),
149
+ fmt,
150
+ fields,
151
+ )
152
+
153
+
154
+ @app.command()
155
+ def filters(
156
+ query: str | None = typer.Argument(None),
157
+ market: str = MARKET,
158
+ category_id: int | None = typer.Option(None, "--category-id"),
159
+ store: str | None = typer.Option(None, "--store"),
160
+ country: str | None = typer.Option(None, "--country"),
161
+ currency: str | None = typer.Option(None, "--currency"),
162
+ language: str | None = typer.Option(None, "--language"),
163
+ fmt: str = FMT,
164
+ fields: str | None = FIELDS,
165
+ ) -> None:
166
+ """Facet option IDs and counts for keyword/category searches."""
167
+ render(
168
+ api_get(
169
+ "/v1/asos/filters",
170
+ {
171
+ "query": query,
172
+ "market": market,
173
+ "category_id": category_id,
174
+ "store": store,
175
+ "country": country,
176
+ "currency": currency,
177
+ "language": language,
178
+ },
179
+ ),
180
+ fmt,
181
+ fields,
182
+ )
183
+
184
+
185
+ @app.command()
186
+ def markets(fmt: str = FMT, fields: str | None = FIELDS) -> None:
187
+ """Supported storefront defaults. Free."""
188
+ render(api_get("/v1/asos/markets"), fmt, fields)
@@ -0,0 +1,142 @@
1
+ """Best Buy catalogue 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
+
11
+ @app.command("markets")
12
+ def markets(
13
+ output: str = typer.Option("json", "--output", "-o"),
14
+ fields: str | None = typer.Option(None, "--fields"),
15
+ ) -> None:
16
+ """List supported markets."""
17
+ render(api_get("/v1/bestbuy/markets", {}), output, fields)
18
+
19
+
20
+ @app.command("search")
21
+ def search(
22
+ query: str = typer.Option(..., "--query"),
23
+ page: int = typer.Option(1, "--page"),
24
+ sort: str = typer.Option("best_match", "--sort"),
25
+ output: str = typer.Option("json", "--output", "-o"),
26
+ fields: str | None = typer.Option(None, "--fields"),
27
+ ) -> None:
28
+ """Search products."""
29
+ render(
30
+ api_get("/v1/bestbuy/search", {"query": query, "page": page, "sort": sort}), output, fields
31
+ )
32
+
33
+
34
+ @app.command("category")
35
+ def category(
36
+ category_id: str = typer.Option(..., "--category-id"),
37
+ page: int = typer.Option(1, "--page"),
38
+ output: str = typer.Option("json", "--output", "-o"),
39
+ fields: str | None = typer.Option(None, "--fields"),
40
+ ) -> None:
41
+ """Browse a category."""
42
+ render(
43
+ api_get("/v1/bestbuy/category", {"category_id": category_id, "page": page}), output, fields
44
+ )
45
+
46
+
47
+ @app.command("categories")
48
+ def categories(
49
+ query: str = typer.Option("laptop", "--query"),
50
+ output: str = typer.Option("json", "--output", "-o"),
51
+ fields: str | None = typer.Option(None, "--fields"),
52
+ ) -> None:
53
+ """Get categories."""
54
+ render(api_get("/v1/bestbuy/categories", {"query": query}), output, fields)
55
+
56
+
57
+ @app.command("deals")
58
+ def deals(
59
+ page: int = typer.Option(1, "--page"),
60
+ output: str = typer.Option("json", "--output", "-o"),
61
+ fields: str | None = typer.Option(None, "--fields"),
62
+ ) -> None:
63
+ """Get laptop and computer deals."""
64
+ render(api_get("/v1/bestbuy/deals", {"page": page}), output, fields)
65
+
66
+
67
+ @app.command("autocomplete")
68
+ def autocomplete(
69
+ query: str = typer.Option(..., "--query"),
70
+ output: str = typer.Option("json", "--output", "-o"),
71
+ fields: str | None = typer.Option(None, "--fields"),
72
+ ) -> None:
73
+ """Get search suggestions."""
74
+ render(api_get("/v1/bestbuy/autocomplete", {"query": query}), output, fields)
75
+
76
+
77
+ @app.command("products-sku-reviews")
78
+ def products_sku_reviews(
79
+ sku: str = typer.Argument(...),
80
+ page: int = typer.Option(1, "--page"),
81
+ page_size: int = typer.Option(10, "--page-size"),
82
+ sort: str = typer.Option("MOST_RECENT", "--sort"),
83
+ output: str = typer.Option("json", "--output", "-o"),
84
+ fields: str | None = typer.Option(None, "--fields"),
85
+ ) -> None:
86
+ """Get product reviews."""
87
+ render(
88
+ api_get(
89
+ f"/v1/bestbuy/products/{sku}/reviews",
90
+ {"page": page, "page_size": page_size, "sort": sort},
91
+ ),
92
+ output,
93
+ fields,
94
+ )
95
+
96
+
97
+ @app.command("products-sku-questions")
98
+ def products_sku_questions(
99
+ sku: str = typer.Argument(...),
100
+ page: int = typer.Option(1, "--page"),
101
+ page_size: int = typer.Option(10, "--page-size"),
102
+ query: str = typer.Option("", "--query"),
103
+ output: str = typer.Option("json", "--output", "-o"),
104
+ fields: str | None = typer.Option(None, "--fields"),
105
+ ) -> None:
106
+ """Get product questions."""
107
+ render(
108
+ api_get(
109
+ f"/v1/bestbuy/products/{sku}/questions",
110
+ {"page": page, "page_size": page_size, "query": query},
111
+ ),
112
+ output,
113
+ fields,
114
+ )
115
+
116
+
117
+ @app.command("products-sku-availability")
118
+ def products_sku_availability(
119
+ sku: str = typer.Argument(...),
120
+ zip_code: str = typer.Option(..., "--zip-code"),
121
+ store_id: str = typer.Option("7", "--store-id"),
122
+ output: str = typer.Option("json", "--output", "-o"),
123
+ fields: str | None = typer.Option(None, "--fields"),
124
+ ) -> None:
125
+ """Get product availability."""
126
+ render(
127
+ api_get(
128
+ f"/v1/bestbuy/products/{sku}/availability", {"zip_code": zip_code, "store_id": store_id}
129
+ ),
130
+ output,
131
+ fields,
132
+ )
133
+
134
+
135
+ @app.command("get-product")
136
+ def get_product(
137
+ sku: str = typer.Argument(...),
138
+ output: str = typer.Option("json", "--output", "-o"),
139
+ fields: str | None = typer.Option(None, "--fields"),
140
+ ) -> None:
141
+ """Get product detail."""
142
+ render(api_get(f"/v1/bestbuy/products/{sku}", {}), output, fields)
@@ -0,0 +1,191 @@
1
+ """Etsy dedicated API commands."""
2
+
3
+ from urllib.parse import quote
4
+
5
+ import typer
6
+
7
+ from scrapebadger_cli.client import api_get
8
+ from scrapebadger_cli.output import render
9
+
10
+ app = typer.Typer(no_args_is_help=True)
11
+
12
+
13
+ @app.command("search")
14
+ def search(
15
+ query: str = typer.Argument(...),
16
+ page: int = typer.Option(1, "--page"),
17
+ country: str = typer.Option("US", "--country"),
18
+ currency: str = typer.Option("USD", "--currency"),
19
+ language: str = typer.Option("en-US", "--language"),
20
+ sort: str = typer.Option("most_relevant", "--sort"),
21
+ min_price: float | None = typer.Option(None, "--min-price"),
22
+ max_price: float | None = typer.Option(None, "--max-price"),
23
+ fmt: str = typer.Option("json", "--output", "-o"),
24
+ fields: str | None = typer.Option(None, "--fields", "-f"),
25
+ ) -> None:
26
+ "Search listings. 10 credits."
27
+ render(
28
+ api_get(
29
+ "/v1/etsy/search",
30
+ {
31
+ "query": query,
32
+ "page": page,
33
+ "country": country,
34
+ "currency": currency,
35
+ "language": language,
36
+ "sort": sort,
37
+ "min_price": min_price,
38
+ "max_price": max_price,
39
+ },
40
+ ),
41
+ fmt,
42
+ fields,
43
+ )
44
+
45
+
46
+ @app.command("listing")
47
+ def listing(
48
+ listing_id: str = typer.Argument(...),
49
+ country: str = typer.Option("US", "--country"),
50
+ currency: str = typer.Option("USD", "--currency"),
51
+ language: str = typer.Option("en-US", "--language"),
52
+ fmt: str = typer.Option("json", "--output", "-o"),
53
+ fields: str | None = typer.Option(None, "--fields", "-f"),
54
+ ) -> None:
55
+ "Get listing detail. 10 credits."
56
+ render(
57
+ api_get(
58
+ f"/v1/etsy/listings/{quote(str(listing_id), safe='')}",
59
+ {"country": country, "currency": currency, "language": language},
60
+ ),
61
+ fmt,
62
+ fields,
63
+ )
64
+
65
+
66
+ @app.command("shop")
67
+ def shop(
68
+ shop_name: str = typer.Argument(...),
69
+ country: str = typer.Option("US", "--country"),
70
+ currency: str = typer.Option("USD", "--currency"),
71
+ language: str = typer.Option("en-US", "--language"),
72
+ fmt: str = typer.Option("json", "--output", "-o"),
73
+ fields: str | None = typer.Option(None, "--fields", "-f"),
74
+ ) -> None:
75
+ "Get shop profile. 10 credits."
76
+ render(
77
+ api_get(
78
+ f"/v1/etsy/shops/{quote(str(shop_name), safe='')}",
79
+ {"country": country, "currency": currency, "language": language},
80
+ ),
81
+ fmt,
82
+ fields,
83
+ )
84
+
85
+
86
+ @app.command("shop-listings")
87
+ def shop_listings(
88
+ shop_name: str = typer.Argument(...),
89
+ page: int = typer.Option(1, "--page"),
90
+ country: str = typer.Option("US", "--country"),
91
+ currency: str = typer.Option("USD", "--currency"),
92
+ language: str = typer.Option("en-US", "--language"),
93
+ fmt: str = typer.Option("json", "--output", "-o"),
94
+ fields: str | None = typer.Option(None, "--fields", "-f"),
95
+ ) -> None:
96
+ "Get shop listings. 10 credits."
97
+ render(
98
+ api_get(
99
+ f"/v1/etsy/shops/{quote(str(shop_name), safe='')}/listings",
100
+ {"page": page, "country": country, "currency": currency, "language": language},
101
+ ),
102
+ fmt,
103
+ fields,
104
+ )
105
+
106
+
107
+ @app.command("shop-reviews")
108
+ def shop_reviews(
109
+ shop_name: str = typer.Argument(...),
110
+ page: int = typer.Option(1, "--page"),
111
+ country: str = typer.Option("US", "--country"),
112
+ currency: str = typer.Option("USD", "--currency"),
113
+ language: str = typer.Option("en-US", "--language"),
114
+ fmt: str = typer.Option("json", "--output", "-o"),
115
+ fields: str | None = typer.Option(None, "--fields", "-f"),
116
+ ) -> None:
117
+ "Get shop reviews. 20 credits."
118
+ render(
119
+ api_get(
120
+ f"/v1/etsy/shops/{quote(str(shop_name), safe='')}/reviews",
121
+ {"page": page, "country": country, "currency": currency, "language": language},
122
+ ),
123
+ fmt,
124
+ fields,
125
+ )
126
+
127
+
128
+ @app.command("categories")
129
+ def categories(
130
+ country: str = typer.Option("US", "--country"),
131
+ currency: str = typer.Option("USD", "--currency"),
132
+ language: str = typer.Option("en-US", "--language"),
133
+ fmt: str = typer.Option("json", "--output", "-o"),
134
+ fields: str | None = typer.Option(None, "--fields", "-f"),
135
+ ) -> None:
136
+ "Get categories. 10 credits."
137
+ render(
138
+ api_get(
139
+ "/v1/etsy/categories", {"country": country, "currency": currency, "language": language}
140
+ ),
141
+ fmt,
142
+ fields,
143
+ )
144
+
145
+
146
+ @app.command("listing-reviews")
147
+ def listing_reviews(
148
+ listing_id: str = typer.Argument(...),
149
+ page: int = typer.Option(1, "--page"),
150
+ sort: str = typer.Option("Relevancy", "--sort"),
151
+ country: str = typer.Option("US", "--country"),
152
+ currency: str = typer.Option("USD", "--currency"),
153
+ language: str = typer.Option("en-US", "--language"),
154
+ fmt: str = typer.Option("json", "--output", "-o"),
155
+ fields: str | None = typer.Option(None, "--fields", "-f"),
156
+ ) -> None:
157
+ "Get listing reviews. 20 credits."
158
+ render(
159
+ api_get(
160
+ f"/v1/etsy/listings/{quote(str(listing_id), safe='')}/reviews",
161
+ {
162
+ "page": page,
163
+ "sort": sort,
164
+ "country": country,
165
+ "currency": currency,
166
+ "language": language,
167
+ },
168
+ ),
169
+ fmt,
170
+ fields,
171
+ )
172
+
173
+
174
+ @app.command("autocomplete")
175
+ def autocomplete(
176
+ query: str = typer.Argument(...),
177
+ country: str = typer.Option("US", "--country"),
178
+ currency: str = typer.Option("USD", "--currency"),
179
+ language: str = typer.Option("en-US", "--language"),
180
+ fmt: str = typer.Option("json", "--output", "-o"),
181
+ fields: str | None = typer.Option(None, "--fields", "-f"),
182
+ ) -> None:
183
+ "Get search suggestions. 20 credits."
184
+ render(
185
+ api_get(
186
+ "/v1/etsy/autocomplete",
187
+ {"query": query, "country": country, "currency": currency, "language": language},
188
+ ),
189
+ fmt,
190
+ fields,
191
+ )
@@ -0,0 +1,182 @@
1
+ """Target catalogue 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
+
11
+ @app.command("markets")
12
+ def markets(
13
+ output: str = typer.Option("json", "--output", "-o"),
14
+ fields: str | None = typer.Option(None, "--fields"),
15
+ ) -> None:
16
+ """List supported markets."""
17
+ render(api_get("/v1/target/markets", {}), output, fields)
18
+
19
+
20
+ @app.command("search")
21
+ def search(
22
+ query: str = typer.Option(..., "--query"),
23
+ page: int = typer.Option(1, "--page"),
24
+ page_size: int = typer.Option(24, "--page-size"),
25
+ store_id: str = typer.Option("1375", "--store-id"),
26
+ zip_code: str = typer.Option("55403", "--zip-code"),
27
+ sort: str = typer.Option("relevance", "--sort"),
28
+ output: str = typer.Option("json", "--output", "-o"),
29
+ fields: str | None = typer.Option(None, "--fields"),
30
+ ) -> None:
31
+ """Search products."""
32
+ render(
33
+ api_get(
34
+ "/v1/target/search",
35
+ {
36
+ "query": query,
37
+ "page": page,
38
+ "page_size": page_size,
39
+ "store_id": store_id,
40
+ "zip_code": zip_code,
41
+ "sort": sort,
42
+ },
43
+ ),
44
+ output,
45
+ fields,
46
+ )
47
+
48
+
49
+ @app.command("category")
50
+ def category(
51
+ category_id: str = typer.Option(..., "--category-id"),
52
+ page: int = typer.Option(1, "--page"),
53
+ page_size: int = typer.Option(24, "--page-size"),
54
+ store_id: str = typer.Option("1375", "--store-id"),
55
+ zip_code: str = typer.Option("55403", "--zip-code"),
56
+ output: str = typer.Option("json", "--output", "-o"),
57
+ fields: str | None = typer.Option(None, "--fields"),
58
+ ) -> None:
59
+ """Browse a category."""
60
+ render(
61
+ api_get(
62
+ "/v1/target/category",
63
+ {
64
+ "category_id": category_id,
65
+ "page": page,
66
+ "page_size": page_size,
67
+ "store_id": store_id,
68
+ "zip_code": zip_code,
69
+ },
70
+ ),
71
+ output,
72
+ fields,
73
+ )
74
+
75
+
76
+ @app.command("autocomplete")
77
+ def autocomplete(
78
+ query: str = typer.Option(..., "--query"),
79
+ output: str = typer.Option("json", "--output", "-o"),
80
+ fields: str | None = typer.Option(None, "--fields"),
81
+ ) -> None:
82
+ """Get search suggestions."""
83
+ render(api_get("/v1/target/autocomplete", {"query": query}), output, fields)
84
+
85
+
86
+ @app.command("stores")
87
+ def stores(
88
+ zip_code: str = typer.Option(..., "--zip-code"),
89
+ limit: int = typer.Option(10, "--limit"),
90
+ output: str = typer.Option("json", "--output", "-o"),
91
+ fields: str | None = typer.Option(None, "--fields"),
92
+ ) -> None:
93
+ """Find nearby stores."""
94
+ render(api_get("/v1/target/stores", {"zip_code": zip_code, "limit": limit}), output, fields)
95
+
96
+
97
+ @app.command("get-store")
98
+ def get_store(
99
+ store_id: str = typer.Argument(...),
100
+ output: str = typer.Option("json", "--output", "-o"),
101
+ fields: str | None = typer.Option(None, "--fields"),
102
+ ) -> None:
103
+ """Get store detail."""
104
+ render(api_get(f"/v1/target/stores/{store_id}", {}), output, fields)
105
+
106
+
107
+ @app.command("categories")
108
+ def categories(
109
+ category_id: str = typer.Option("5xtly", "--category-id"),
110
+ output: str = typer.Option("json", "--output", "-o"),
111
+ fields: str | None = typer.Option(None, "--fields"),
112
+ ) -> None:
113
+ """Get categories."""
114
+ render(api_get("/v1/target/categories", {"category_id": category_id}), output, fields)
115
+
116
+
117
+ @app.command("products-tcin-reviews")
118
+ def products_tcin_reviews(
119
+ tcin: str = typer.Argument(...),
120
+ page: int = typer.Option(1, "--page"),
121
+ page_size: int = typer.Option(10, "--page-size"),
122
+ sort: str = typer.Option("most_recent", "--sort"),
123
+ output: str = typer.Option("json", "--output", "-o"),
124
+ fields: str | None = typer.Option(None, "--fields"),
125
+ ) -> None:
126
+ """Get product reviews."""
127
+ render(
128
+ api_get(
129
+ f"/v1/target/products/{tcin}/reviews",
130
+ {"page": page, "page_size": page_size, "sort": sort},
131
+ ),
132
+ output,
133
+ fields,
134
+ )
135
+
136
+
137
+ @app.command("products-tcin-questions")
138
+ def products_tcin_questions(
139
+ tcin: str = typer.Argument(...),
140
+ page: int = typer.Option(1, "--page"),
141
+ page_size: int = typer.Option(10, "--page-size"),
142
+ output: str = typer.Option("json", "--output", "-o"),
143
+ fields: str | None = typer.Option(None, "--fields"),
144
+ ) -> None:
145
+ """Get product questions."""
146
+ render(
147
+ api_get(f"/v1/target/products/{tcin}/questions", {"page": page, "page_size": page_size}),
148
+ output,
149
+ fields,
150
+ )
151
+
152
+
153
+ @app.command("products-tcin-availability")
154
+ def products_tcin_availability(
155
+ tcin: str = typer.Argument(...),
156
+ zip_code: str = typer.Option(..., "--zip-code"),
157
+ limit: int = typer.Option(10, "--limit"),
158
+ output: str = typer.Option("json", "--output", "-o"),
159
+ fields: str | None = typer.Option(None, "--fields"),
160
+ ) -> None:
161
+ """Get product availability."""
162
+ render(
163
+ api_get(f"/v1/target/products/{tcin}/availability", {"zip_code": zip_code, "limit": limit}),
164
+ output,
165
+ fields,
166
+ )
167
+
168
+
169
+ @app.command("get-product")
170
+ def get_product(
171
+ tcin: str = typer.Argument(...),
172
+ store_id: str = typer.Option("1375", "--store-id"),
173
+ zip_code: str = typer.Option("55403", "--zip-code"),
174
+ output: str = typer.Option("json", "--output", "-o"),
175
+ fields: str | None = typer.Option(None, "--fields"),
176
+ ) -> None:
177
+ """Get product detail."""
178
+ render(
179
+ api_get(f"/v1/target/products/{tcin}", {"store_id": store_id, "zip_code": zip_code}),
180
+ output,
181
+ fields,
182
+ )
@@ -4,17 +4,21 @@ import typer
4
4
 
5
5
  from scrapebadger_cli.commands import (
6
6
  airbnb,
7
+ aliexpress,
7
8
  amazon,
8
9
  apartments,
9
10
  app_store,
11
+ asos,
10
12
  auth,
11
13
  baidu,
14
+ bestbuy,
12
15
  bing,
13
16
  booking,
14
17
  chatgpt,
15
18
  depop,
16
19
  duckduckgo,
17
20
  ebay,
21
+ etsy,
18
22
  facebook,
19
23
  gemini,
20
24
  google,
@@ -32,6 +36,7 @@ from scrapebadger_cli.commands import (
32
36
  reddit,
33
37
  redfin,
34
38
  stream,
39
+ target,
35
40
  tiktok,
36
41
  twitter,
37
42
  vinted,
@@ -58,6 +63,11 @@ app.add_typer(vinted.app, name="vinted", help="Vinted marketplace commands")
58
63
  app.add_typer(amazon.app, name="amazon", help="Amazon marketplace commands")
59
64
  app.add_typer(ebay.app, name="ebay", help="eBay marketplace commands")
60
65
  app.add_typer(walmart.app, name="walmart", help="Walmart marketplace commands")
66
+ app.add_typer(aliexpress.app, name="aliexpress", help="Aliexpress scraper commands")
67
+ app.add_typer(etsy.app, name="etsy", help="Etsy scraper commands")
68
+ app.add_typer(bestbuy.app, name="bestbuy", help="Bestbuy scraper commands")
69
+ app.add_typer(target.app, name="target", help="Target scraper commands")
70
+ app.add_typer(asos.app, name="asos", help="Asos scraper commands")
61
71
  app.add_typer(gumtree.app, name="gumtree", help="Gumtree classifieds commands (UK, AU, ZA)")
62
72
  app.add_typer(google_play.app, name="google-play", help="Google Play Store commands")
63
73
  app.add_typer(app_store.app, name="app-store", help="Apple App Store commands")
@@ -228,7 +228,7 @@ wheels = [
228
228
 
229
229
  [[package]]
230
230
  name = "scrapebadger-cli"
231
- version = "0.21.1"
231
+ version = "0.22.0"
232
232
  source = { editable = "." }
233
233
  dependencies = [
234
234
  { name = "httpx" },
@@ -1 +0,0 @@
1
- """ScrapeBadger CLI — Twitter, Vinted, and Web Scraping from the command line."""