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.
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/PKG-INFO +16 -2
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/README.md +15 -1
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/pyproject.toml +1 -1
- scrapebadger_cli-0.22.0/src/scrapebadger_cli/__init__.py +1 -0
- scrapebadger_cli-0.22.0/src/scrapebadger_cli/commands/aliexpress.py +181 -0
- scrapebadger_cli-0.22.0/src/scrapebadger_cli/commands/asos.py +188 -0
- scrapebadger_cli-0.22.0/src/scrapebadger_cli/commands/bestbuy.py +142 -0
- scrapebadger_cli-0.22.0/src/scrapebadger_cli/commands/etsy.py +191 -0
- scrapebadger_cli-0.22.0/src/scrapebadger_cli/commands/target.py +182 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/main.py +10 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/uv.lock +1 -1
- scrapebadger_cli-0.21.2/src/scrapebadger_cli/__init__.py +0 -1
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/.gitignore +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/client.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/__init__.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/airbnb.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/amazon.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/apartments.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/app_store.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/auth.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/baidu.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/bing.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/booking.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/chatgpt.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/depop.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/duckduckgo.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/ebay.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/facebook.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/gemini.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/google.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/google_play.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/gumtree.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/idealista.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/immobiliare.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/instagram.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/leboncoin.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/linkedin.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/loopnet.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/naver.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/perplexity.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/realtor.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/reddit.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/redfin.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/stream.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/tiktok.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/twitter.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/vinted.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/wallapop.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/walmart.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/web.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/yahoo.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/yandex.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/youtube.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/zillow.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/config.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/output.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/tests/test_naver.py +0 -0
- {scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/tests/test_tiktok_pagination.py +0 -0
- {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.
|
|
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
|
|
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
|
|
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.
|
|
@@ -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")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""ScrapeBadger CLI — Twitter, Vinted, and Web Scraping from the command line."""
|
|
File without changes
|
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/apartments.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/app_store.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/booking.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/chatgpt.py
RENAMED
|
File without changes
|
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/duckduckgo.py
RENAMED
|
File without changes
|
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/facebook.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/google_play.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/gumtree.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/idealista.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/immobiliare.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/instagram.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/leboncoin.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/linkedin.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/loopnet.py
RENAMED
|
File without changes
|
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/perplexity.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/realtor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/twitter.py
RENAMED
|
File without changes
|
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/wallapop.py
RENAMED
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/walmart.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scrapebadger_cli-0.21.2 → scrapebadger_cli-0.22.0}/src/scrapebadger_cli/commands/youtube.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|