scrapebadger-cli 0.3.0__tar.gz → 0.4.1__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 (31) hide show
  1. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/.gitignore +5 -0
  2. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/PKG-INFO +1 -1
  3. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/pyproject.toml +1 -1
  4. scrapebadger_cli-0.4.1/src/scrapebadger_cli/commands/depop.py +106 -0
  5. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/google.py +7 -0
  6. scrapebadger_cli-0.4.1/src/scrapebadger_cli/commands/linkedin.py +171 -0
  7. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/vinted.py +13 -0
  8. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/main.py +4 -0
  9. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/uv.lock +1 -1
  10. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/README.md +0 -0
  11. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/__init__.py +0 -0
  12. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/client.py +0 -0
  13. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/__init__.py +0 -0
  14. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/amazon.py +0 -0
  15. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/auth.py +0 -0
  16. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/ebay.py +0 -0
  17. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/idealista.py +0 -0
  18. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/immobiliare.py +0 -0
  19. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/leboncoin.py +0 -0
  20. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/loopnet.py +0 -0
  21. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/realtor.py +0 -0
  22. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/reddit.py +0 -0
  23. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/redfin.py +0 -0
  24. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/stream.py +0 -0
  25. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/tiktok.py +0 -0
  26. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/twitter.py +0 -0
  27. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/web.py +0 -0
  28. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/youtube.py +0 -0
  29. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/commands/zillow.py +0 -0
  30. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/config.py +0 -0
  31. {scrapebadger_cli-0.3.0 → scrapebadger_cli-0.4.1}/src/scrapebadger_cli/output.py +0 -0
@@ -186,3 +186,8 @@ playwright-report/
186
186
  # proprietary binaries, native .so libs, frida-server, handoff tarballs).
187
187
  # Local-only — not part of the deployable codebase.
188
188
  research/
189
+
190
+ # Customer support attachments (uploaded at runtime, may contain logs/PII).
191
+ # The blog upload dir predates this and is deliberately left as-is.
192
+ apps/portal/public/uploads/support/*
193
+ !apps/portal/public/uploads/support/.gitkeep
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scrapebadger-cli
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: ScrapeBadger CLI — Twitter, Vinted, Google, and Web Scraping from the command line
5
5
  Author: ScrapeBadger Team
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "scrapebadger-cli"
3
- version = "0.3.0"
3
+ version = "0.4.1"
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,106 @@
1
+ """Depop 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("us", "--market", "-m", help="us, gb (uk), au, ie, it, fr, de, es, nl, nz")
13
+
14
+
15
+ @app.command()
16
+ def search(
17
+ query: str = typer.Argument(..., help='Search text (e.g. "nike vintage")'),
18
+ market: str = MARKET,
19
+ per_page: int = typer.Option(24, "--per-page", "-p", help="1-24 (default 24)"),
20
+ page: int = typer.Option(1, "--page", help="Page number (default 1)"),
21
+ price_min: float | None = typer.Option(None, "--price-min"),
22
+ price_max: float | None = typer.Option(None, "--price-max"),
23
+ brands: str | None = typer.Option(None, "--brands", help="CSV of numeric brand IDs"),
24
+ sizes: str | None = typer.Option(
25
+ None, "--sizes", help='CSV of size labels (e.g. "US-M,UK-12")'
26
+ ),
27
+ colours: str | None = typer.Option(None, "--colours", help="CSV of colour slugs"),
28
+ conditions: str | None = typer.Option(
29
+ None, "--conditions", help="CSV: brand_new, used_like_new, used_good, used_fair"
30
+ ),
31
+ gender: str | None = typer.Option(None, "--gender", help="male or female"),
32
+ sort: str | None = typer.Option(
33
+ None,
34
+ "--sort",
35
+ help="relevance, newest, priceAscending, priceDescending, popularity",
36
+ ),
37
+ fmt: str = FMT,
38
+ fields: str | None = FIELDS,
39
+ ) -> None:
40
+ """Search Depop for second-hand fashion products."""
41
+ data = api_get(
42
+ "/v1/depop/search",
43
+ {
44
+ "query": query,
45
+ "market": market,
46
+ "per_page": per_page,
47
+ "page": page,
48
+ "price_min": price_min,
49
+ "price_max": price_max,
50
+ "brands": brands,
51
+ "sizes": sizes,
52
+ "colours": colours,
53
+ "conditions": conditions,
54
+ "gender": gender,
55
+ "sort": sort,
56
+ },
57
+ )
58
+ render(data, fmt, fields)
59
+
60
+
61
+ @app.command()
62
+ def product(
63
+ slug: str = typer.Argument(..., help="Depop product slug"),
64
+ market: str = MARKET,
65
+ fmt: str = FMT,
66
+ fields: str | None = FIELDS,
67
+ ) -> None:
68
+ """Get a Depop product's full detail by slug."""
69
+ data = api_get(f"/v1/depop/products/{slug}", {"market": market})
70
+ render(data, fmt, fields)
71
+
72
+
73
+ @app.command()
74
+ def user(
75
+ username: str = typer.Argument(..., help="Depop username"),
76
+ market: str = MARKET,
77
+ fmt: str = FMT,
78
+ fields: str | None = FIELDS,
79
+ ) -> None:
80
+ """Get a Depop shop/user profile by username."""
81
+ data = api_get(f"/v1/depop/users/{username}", {"market": market})
82
+ render(data, fmt, fields)
83
+
84
+
85
+ @app.command("user-products")
86
+ def user_products(
87
+ username: str = typer.Argument(..., help="Depop username"),
88
+ market: str = MARKET,
89
+ per_page: int = typer.Option(24, "--per-page", "-p", help="1-24 (default 24)"),
90
+ page: int = typer.Option(1, "--page", help="Page number (default 1)"),
91
+ fmt: str = FMT,
92
+ fields: str | None = FIELDS,
93
+ ) -> None:
94
+ """Get a Depop seller's listings by username."""
95
+ data = api_get(
96
+ f"/v1/depop/users/{username}/products",
97
+ {"market": market, "per_page": per_page, "page": page},
98
+ )
99
+ render(data, fmt, fields)
100
+
101
+
102
+ @app.command("markets")
103
+ def list_markets(fmt: str = FMT, fields: str | None = FIELDS) -> None:
104
+ """List Depop coverage markets (us, gb, au, ie, it, fr, de, es, nl, nz)."""
105
+ data = api_get("/v1/depop/markets", {})
106
+ render(data, fmt, fields)
@@ -1092,6 +1092,12 @@ def flights_search(
1092
1092
  hl: str | None = typer.Option(None, "--hl", help="Language code"),
1093
1093
  stops: str | None = typer.Option(None, "--stops", help="any | nonstop | one_stop | two_stops"),
1094
1094
  max_price: int | None = typer.Option(None, "--max-price", help="Upper price filter"),
1095
+ sort_by: str | None = typer.Option(
1096
+ None,
1097
+ "--sort-by",
1098
+ help="top (default, fast best picks) | price (FULL price-sorted inventory "
1099
+ "+ Google's price floor/typical range/history; slower)",
1100
+ ),
1095
1101
  fmt: str = FMT,
1096
1102
  fields: str | None = FIELDS,
1097
1103
  ) -> None:
@@ -1112,6 +1118,7 @@ def flights_search(
1112
1118
  "hl": hl,
1113
1119
  "stops": stops,
1114
1120
  "max_price": max_price,
1121
+ "sort_by": sort_by,
1115
1122
  },
1116
1123
  )
1117
1124
  render(data, fmt, fields)
@@ -0,0 +1,171 @@
1
+ """LinkedIn public-data 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
+ COUNTRY = typer.Option("us", "--country", "-c", help="Country code (default us)")
13
+
14
+
15
+ @app.command("jobs-search")
16
+ def jobs_search(
17
+ keywords: str | None = typer.Option(None, "--keywords", "-k", help="Search text"),
18
+ location: str | None = typer.Option(None, "--location", "-l", help="Location name"),
19
+ geo_id: str | None = typer.Option(None, "--geo-id", help="LinkedIn numeric geo id"),
20
+ company_id: str | None = typer.Option(None, "--company-id", help="LinkedIn numeric company id"),
21
+ date_posted: str | None = typer.Option(
22
+ None, "--date-posted", help="past_24h, past_week, past_month, any"
23
+ ),
24
+ experience: str | None = typer.Option(
25
+ None,
26
+ "--experience",
27
+ help="CSV: internship, entry, associate, mid_senior, director, executive",
28
+ ),
29
+ job_type: str | None = typer.Option(
30
+ None,
31
+ "--job-type",
32
+ help="CSV: full_time, part_time, contract, temporary, internship, volunteer, other",
33
+ ),
34
+ workplace: str | None = typer.Option(None, "--workplace", help="CSV: onsite, remote, hybrid"),
35
+ sort: str | None = typer.Option(None, "--sort", help="relevant or recent"),
36
+ start: int = typer.Option(0, "--start", help="Pagination offset (default 0)"),
37
+ country: str = COUNTRY,
38
+ fmt: str = FMT,
39
+ fields: str | None = FIELDS,
40
+ ) -> None:
41
+ """Search LinkedIn public job postings."""
42
+ data = api_get(
43
+ "/v1/linkedin/jobs/search",
44
+ {
45
+ "keywords": keywords,
46
+ "location": location,
47
+ "geo_id": geo_id,
48
+ "company_id": company_id,
49
+ "date_posted": date_posted,
50
+ "experience": experience,
51
+ "job_type": job_type,
52
+ "workplace": workplace,
53
+ "sort": sort,
54
+ "start": start,
55
+ "country": country,
56
+ },
57
+ )
58
+ render(data, fmt, fields)
59
+
60
+
61
+ @app.command()
62
+ def job(
63
+ job_id: str = typer.Argument(..., help="Numeric LinkedIn job id"),
64
+ country: str = COUNTRY,
65
+ fmt: str = FMT,
66
+ fields: str | None = FIELDS,
67
+ ) -> None:
68
+ """Get a LinkedIn job posting's full detail by numeric id."""
69
+ data = api_get(f"/v1/linkedin/jobs/{job_id}", {"country": country})
70
+ render(data, fmt, fields)
71
+
72
+
73
+ @app.command("company-jobs")
74
+ def company_jobs(
75
+ company_id: str = typer.Argument(..., help="Numeric LinkedIn company id"),
76
+ start: int = typer.Option(0, "--start", help="Pagination offset (default 0)"),
77
+ country: str = COUNTRY,
78
+ fmt: str = FMT,
79
+ fields: str | None = FIELDS,
80
+ ) -> None:
81
+ """Get a LinkedIn company's job postings by numeric company id."""
82
+ data = api_get(
83
+ f"/v1/linkedin/companies/{company_id}/jobs",
84
+ {"start": start, "country": country},
85
+ )
86
+ render(data, fmt, fields)
87
+
88
+
89
+ @app.command()
90
+ def company(
91
+ universal_name: str = typer.Argument(..., help="Company vanity slug (e.g. microsoft)"),
92
+ country: str = COUNTRY,
93
+ fmt: str = FMT,
94
+ fields: str | None = FIELDS,
95
+ ) -> None:
96
+ """Get a LinkedIn company profile by universal name."""
97
+ data = api_get(f"/v1/linkedin/companies/{universal_name}", {"country": country})
98
+ render(data, fmt, fields)
99
+
100
+
101
+ @app.command()
102
+ def school(
103
+ universal_name: str = typer.Argument(..., help="School vanity slug (e.g. stanford-university)"),
104
+ country: str = COUNTRY,
105
+ fmt: str = FMT,
106
+ fields: str | None = FIELDS,
107
+ ) -> None:
108
+ """Get a LinkedIn school profile by universal name."""
109
+ data = api_get(f"/v1/linkedin/schools/{universal_name}", {"country": country})
110
+ render(data, fmt, fields)
111
+
112
+
113
+ @app.command()
114
+ def profile(
115
+ public_id: str = typer.Argument(..., help="Profile public id (e.g. williamhgates)"),
116
+ country: str = COUNTRY,
117
+ fmt: str = FMT,
118
+ fields: str | None = FIELDS,
119
+ ) -> None:
120
+ """Get a LinkedIn member profile by public id."""
121
+ data = api_get(f"/v1/linkedin/profiles/{public_id}", {"country": country})
122
+ render(data, fmt, fields)
123
+
124
+
125
+ @app.command()
126
+ def post(
127
+ post_slug: str = typer.Argument(..., help="LinkedIn post slug"),
128
+ country: str = COUNTRY,
129
+ fmt: str = FMT,
130
+ fields: str | None = FIELDS,
131
+ ) -> None:
132
+ """Get a LinkedIn social post by its slug."""
133
+ data = api_get(f"/v1/linkedin/posts/{post_slug}", {"country": country})
134
+ render(data, fmt, fields)
135
+
136
+
137
+ @app.command()
138
+ def article(
139
+ article_slug: str = typer.Argument(..., help="LinkedIn article slug"),
140
+ country: str = COUNTRY,
141
+ fmt: str = FMT,
142
+ fields: str | None = FIELDS,
143
+ ) -> None:
144
+ """Get a LinkedIn article by its slug."""
145
+ data = api_get(f"/v1/linkedin/articles/{article_slug}", {"country": country})
146
+ render(data, fmt, fields)
147
+
148
+
149
+ @app.command()
150
+ def course(
151
+ course_slug: str = typer.Argument(..., help="LinkedIn Learning course slug"),
152
+ country: str = COUNTRY,
153
+ fmt: str = FMT,
154
+ fields: str | None = FIELDS,
155
+ ) -> None:
156
+ """Get a LinkedIn Learning course by its slug."""
157
+ data = api_get(f"/v1/linkedin/learning/{course_slug}", {"country": country})
158
+ render(data, fmt, fields)
159
+
160
+
161
+ @app.command("geo-suggest")
162
+ def geo_suggest(
163
+ query: str = typer.Argument(..., help="Location or company name to resolve"),
164
+ type: str = typer.Option("geo", "--type", "-t", help="geo or company"),
165
+ country: str = COUNTRY,
166
+ fmt: str = FMT,
167
+ fields: str | None = FIELDS,
168
+ ) -> None:
169
+ """Resolve a location or company name to LinkedIn ids for job-search filters."""
170
+ data = api_get("/v1/linkedin/geo/suggest", {"query": query, "type": type, "country": country})
171
+ render(data, fmt, fields)
@@ -21,6 +21,16 @@ def search(
21
21
  price_from: float | None = typer.Option(None, "--price-from"),
22
22
  price_to: float | None = typer.Option(None, "--price-to"),
23
23
  brand_ids: str | None = typer.Option(None, "--brands", help="Comma-separated brand IDs"),
24
+ catalog_ids: str | None = typer.Option(
25
+ None,
26
+ "--catalogs",
27
+ help="Comma-separated catalog (category) IDs, e.g. 1904 — the catalog[] value in a "
28
+ "Vinted category URL",
29
+ ),
30
+ color_ids: str | None = typer.Option(None, "--colors", help="Comma-separated color IDs"),
31
+ status_ids: str | None = typer.Option(
32
+ None, "--statuses", help="Comma-separated condition/status IDs"
33
+ ),
24
34
  order: str | None = typer.Option(None, "--order"),
25
35
  fmt: str = FMT,
26
36
  fields: str | None = FIELDS,
@@ -36,6 +46,9 @@ def search(
36
46
  "price_from": price_from,
37
47
  "price_to": price_to,
38
48
  "brand_ids": brand_ids,
49
+ "catalog_ids": catalog_ids,
50
+ "color_ids": color_ids,
51
+ "status_ids": status_ids,
39
52
  "order": order,
40
53
  },
41
54
  )
@@ -5,11 +5,13 @@ import typer
5
5
  from scrapebadger_cli.commands import (
6
6
  amazon,
7
7
  auth,
8
+ depop,
8
9
  ebay,
9
10
  google,
10
11
  idealista,
11
12
  immobiliare,
12
13
  leboncoin,
14
+ linkedin,
13
15
  loopnet,
14
16
  realtor,
15
17
  reddit,
@@ -45,7 +47,9 @@ app.add_typer(web.app, name="web", help="Web scraping commands")
45
47
  app.add_typer(google.app, name="google", help="Google scraper commands")
46
48
  app.add_typer(realtor.app, name="realtor", help="Realtor real-estate commands")
47
49
  app.add_typer(loopnet.app, name="loopnet", help="LoopNet commercial real estate commands")
50
+ app.add_typer(depop.app, name="depop", help="Depop marketplace commands")
48
51
  app.add_typer(redfin.app, name="redfin", help="Redfin real-estate commands")
52
+ app.add_typer(linkedin.app, name="linkedin", help="LinkedIn public data commands")
49
53
  app.command()(auth.auth)
50
54
  app.command(name="credits")(auth.credits)
51
55
  app.command(name="logout")(auth.logout)
@@ -228,7 +228,7 @@ wheels = [
228
228
 
229
229
  [[package]]
230
230
  name = "scrapebadger-cli"
231
- version = "0.2.1"
231
+ version = "0.4.1"
232
232
  source = { editable = "." }
233
233
  dependencies = [
234
234
  { name = "httpx" },