blocket-api 0.3.9__tar.gz → 0.3.92__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.
- {blocket_api-0.3.9 → blocket_api-0.3.92}/PKG-INFO +31 -3
- {blocket_api-0.3.9 → blocket_api-0.3.92}/README.md +29 -2
- blocket_api-0.3.92/blocket_api/__init__.py +11 -0
- blocket_api-0.3.92/blocket_api/ad_parser.py +182 -0
- blocket_api-0.3.92/blocket_api/blocket.py +105 -0
- blocket_api-0.3.92/blocket_api/constants.py +350 -0
- {blocket_api-0.3.9 → blocket_api-0.3.92}/blocket_api.egg-info/PKG-INFO +31 -3
- {blocket_api-0.3.9 → blocket_api-0.3.92}/blocket_api.egg-info/SOURCES.txt +1 -0
- {blocket_api-0.3.9 → blocket_api-0.3.92}/blocket_api.egg-info/requires.txt +1 -0
- {blocket_api-0.3.9 → blocket_api-0.3.92}/pyproject.toml +2 -1
- blocket_api-0.3.9/blocket_api/__init__.py +0 -5
- blocket_api-0.3.9/blocket_api/blocket.py +0 -54
- blocket_api-0.3.9/blocket_api/constants.py +0 -107
- {blocket_api-0.3.9 → blocket_api-0.3.92}/LICENSE +0 -0
- {blocket_api-0.3.9 → blocket_api-0.3.92}/blocket_api.egg-info/dependency_links.txt +0 -0
- {blocket_api-0.3.9 → blocket_api-0.3.92}/blocket_api.egg-info/top_level.txt +0 -0
- {blocket_api-0.3.9 → blocket_api-0.3.92}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blocket_api
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.92
|
|
4
4
|
Summary: A python API wrapper for blocket.se
|
|
5
5
|
Author-email: dunderrrrrr <emil.bjorkroth@gmail.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -16,6 +16,7 @@ Requires-Dist: mkdocs>=1.6.1
|
|
|
16
16
|
Requires-Dist: mkdocs-material>=9.6.21
|
|
17
17
|
Requires-Dist: mkdocs-swagger-ui-tag>=0.7.2
|
|
18
18
|
Requires-Dist: websockets>=15.0.1
|
|
19
|
+
Requires-Dist: beautifulsoup4>=4.14.2
|
|
19
20
|
Requires-Dist: ruff>=0.14.5
|
|
20
21
|
Dynamic: license-file
|
|
21
22
|
|
|
@@ -47,16 +48,43 @@ or use [blocket-api.se](https://blocket-api.se) without installing anything!
|
|
|
47
48
|
## 💁♀️ Usage
|
|
48
49
|
|
|
49
50
|
```py
|
|
50
|
-
from blocket_api import
|
|
51
|
+
from blocket_api import (
|
|
52
|
+
BlocketAPI,
|
|
53
|
+
Category,
|
|
54
|
+
CarColor,
|
|
55
|
+
CarModel,
|
|
56
|
+
CarSortOrder,
|
|
57
|
+
CarTransmission,
|
|
58
|
+
Location,
|
|
59
|
+
)
|
|
51
60
|
|
|
52
61
|
api = BlocketAPI()
|
|
53
62
|
|
|
54
|
-
|
|
63
|
+
# search all of blocket
|
|
64
|
+
api.search(
|
|
55
65
|
"Vinterdäck Audi",
|
|
56
66
|
sort_order=SortOrder.PRICE_ASC,
|
|
57
67
|
locations=[Location.STOCKHOLM, Location.UPPSALA],
|
|
58
68
|
category=Category.FORDONSTILLBEHOR,
|
|
59
69
|
)
|
|
70
|
+
|
|
71
|
+
# search for cars only
|
|
72
|
+
api.search_car(
|
|
73
|
+
"Audi", # query is optional
|
|
74
|
+
sort_order=CarSortOrder.MILEAGE_ASC,
|
|
75
|
+
models=[CarModel.AUDI],
|
|
76
|
+
colors=[CarColor.GULD],
|
|
77
|
+
price_from=10000,
|
|
78
|
+
price_to=50000,
|
|
79
|
+
transmissions=[CarTransmission.MANUAL],
|
|
80
|
+
locations=[Location.STOCKHOLM],
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Get a recommerce or car ad
|
|
84
|
+
from blocket_api import CarAd, RecommerceAd
|
|
85
|
+
|
|
86
|
+
api.get_ad(RecommerceAd(12345678))
|
|
87
|
+
api.get_ad(CarAd(12345678))
|
|
60
88
|
```
|
|
61
89
|
|
|
62
90
|
## 📝 Notes
|
|
@@ -26,16 +26,43 @@ or use [blocket-api.se](https://blocket-api.se) without installing anything!
|
|
|
26
26
|
## 💁♀️ Usage
|
|
27
27
|
|
|
28
28
|
```py
|
|
29
|
-
from blocket_api import
|
|
29
|
+
from blocket_api import (
|
|
30
|
+
BlocketAPI,
|
|
31
|
+
Category,
|
|
32
|
+
CarColor,
|
|
33
|
+
CarModel,
|
|
34
|
+
CarSortOrder,
|
|
35
|
+
CarTransmission,
|
|
36
|
+
Location,
|
|
37
|
+
)
|
|
30
38
|
|
|
31
39
|
api = BlocketAPI()
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
# search all of blocket
|
|
42
|
+
api.search(
|
|
34
43
|
"Vinterdäck Audi",
|
|
35
44
|
sort_order=SortOrder.PRICE_ASC,
|
|
36
45
|
locations=[Location.STOCKHOLM, Location.UPPSALA],
|
|
37
46
|
category=Category.FORDONSTILLBEHOR,
|
|
38
47
|
)
|
|
48
|
+
|
|
49
|
+
# search for cars only
|
|
50
|
+
api.search_car(
|
|
51
|
+
"Audi", # query is optional
|
|
52
|
+
sort_order=CarSortOrder.MILEAGE_ASC,
|
|
53
|
+
models=[CarModel.AUDI],
|
|
54
|
+
colors=[CarColor.GULD],
|
|
55
|
+
price_from=10000,
|
|
56
|
+
price_to=50000,
|
|
57
|
+
transmissions=[CarTransmission.MANUAL],
|
|
58
|
+
locations=[Location.STOCKHOLM],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Get a recommerce or car ad
|
|
62
|
+
from blocket_api import CarAd, RecommerceAd
|
|
63
|
+
|
|
64
|
+
api.get_ad(RecommerceAd(12345678))
|
|
65
|
+
api.get_ad(CarAd(12345678))
|
|
39
66
|
```
|
|
40
67
|
|
|
41
68
|
## 📝 Notes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .ad_parser import CarAd as CarAd
|
|
2
|
+
from .ad_parser import RecommerceAd as RecommerceAd
|
|
3
|
+
from .blocket import BlocketAPI as BlocketAPI
|
|
4
|
+
from .blocket import Location as Location
|
|
5
|
+
from .constants import CarColor as CarColor
|
|
6
|
+
from .constants import CarModel as CarModel
|
|
7
|
+
from .constants import CarSortOrder as CarSortOrder
|
|
8
|
+
from .constants import CarTransmission as CarTransmission
|
|
9
|
+
from .constants import Category as Category
|
|
10
|
+
from .constants import SortOrder as SortOrder
|
|
11
|
+
from .constants import SubCategory as SubCategory
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from bs4 import BeautifulSoup, Tag
|
|
7
|
+
from httpx import Response
|
|
8
|
+
|
|
9
|
+
from .constants import SITE_URL
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class RecommerceAd:
|
|
14
|
+
id: int
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def url(self) -> str:
|
|
18
|
+
return f"{SITE_URL}/recommerce/forsale/item/{self.id}"
|
|
19
|
+
|
|
20
|
+
def parse(self, response: Response) -> Any:
|
|
21
|
+
soup = BeautifulSoup(response.content, "html.parser")
|
|
22
|
+
json_script_tag = soup.select_one(
|
|
23
|
+
'script:-soup-contains("window.__staticRouterHydrationData")'
|
|
24
|
+
)
|
|
25
|
+
if not json_script_tag:
|
|
26
|
+
return {}
|
|
27
|
+
|
|
28
|
+
if match := re.search(r'JSON\.parse\("(.+)"\)', json_script_tag.text):
|
|
29
|
+
raw_json = match.group(1)
|
|
30
|
+
escaped = raw_json.encode("utf-8").decode("unicode_escape")
|
|
31
|
+
return json.loads(escaped.encode("latin1").decode("utf-8"))
|
|
32
|
+
else:
|
|
33
|
+
return {}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class CarAd:
|
|
38
|
+
id: int
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def url(self) -> str:
|
|
42
|
+
return f"{SITE_URL}/mobility/item/{self.id}"
|
|
43
|
+
|
|
44
|
+
def parse(self, response: Response) -> dict:
|
|
45
|
+
soup = BeautifulSoup(response.content, "html.parser")
|
|
46
|
+
grid = soup.find("div", class_="grid grid-cols-1 md:grid-cols-3 md:gap-x-32")
|
|
47
|
+
|
|
48
|
+
if not grid:
|
|
49
|
+
return {}
|
|
50
|
+
|
|
51
|
+
data: dict[str, Any] = {"url": self.url}
|
|
52
|
+
|
|
53
|
+
self._extract_title_and_subtitle(grid, data)
|
|
54
|
+
self._extract_quick_specs(grid, data)
|
|
55
|
+
self._extract_price(grid, data)
|
|
56
|
+
self._extract_description(grid, data)
|
|
57
|
+
self._extract_specifications(grid, data)
|
|
58
|
+
self._extract_equipment(grid, data)
|
|
59
|
+
self._extract_seller_type(soup, data)
|
|
60
|
+
self._extract_ad_id(soup, data)
|
|
61
|
+
|
|
62
|
+
return data
|
|
63
|
+
|
|
64
|
+
def _extract_title_and_subtitle(self, grid: Tag, data: dict[str, Any]) -> None:
|
|
65
|
+
if title := grid.find("h1", class_=lambda x: x and "t1" in x):
|
|
66
|
+
data["title"] = title.get_text(strip=True)
|
|
67
|
+
|
|
68
|
+
if subtitle := grid.find(
|
|
69
|
+
"p", class_=lambda x: x and "s-text-subtle" in x and "mt-8" in x
|
|
70
|
+
):
|
|
71
|
+
data["subtitle"] = subtitle.get_text(strip=True)
|
|
72
|
+
|
|
73
|
+
def _extract_quick_specs(self, grid: Tag, data: dict[str, Any]) -> None:
|
|
74
|
+
if specs_grid := grid.find(
|
|
75
|
+
"div", class_=lambda x: x and "grid" in x and "gap-24" in x
|
|
76
|
+
):
|
|
77
|
+
spec_items = specs_grid.find_all("div", class_="flex gap-16 hyphens-auto")
|
|
78
|
+
for item in spec_items:
|
|
79
|
+
label = item.find("span", class_="s-text-subtle")
|
|
80
|
+
value = item.find("p", class_="m-0 font-bold")
|
|
81
|
+
if label and value:
|
|
82
|
+
label_text = label.get_text(strip=True)
|
|
83
|
+
value_text = value.get_text(strip=True)
|
|
84
|
+
|
|
85
|
+
key_mapping = {
|
|
86
|
+
"Modellår": "model_year",
|
|
87
|
+
"Miltal": "mileage",
|
|
88
|
+
"Växellåda": "transmission",
|
|
89
|
+
"Drivmedel": "fuel",
|
|
90
|
+
}
|
|
91
|
+
key = key_mapping.get(
|
|
92
|
+
label_text, label_text.lower().replace(" ", "_")
|
|
93
|
+
)
|
|
94
|
+
data[key] = value_text
|
|
95
|
+
|
|
96
|
+
def _extract_price(self, grid: Tag, data: dict[str, Any]) -> None:
|
|
97
|
+
if price_section := grid.find("div", class_="border-t pt-40 mt-40"):
|
|
98
|
+
price_labels = price_section.find_all("p", class_="s-text-subtle mb-0")
|
|
99
|
+
for price_label in price_labels:
|
|
100
|
+
label_text = price_label.get_text(strip=True).lower()
|
|
101
|
+
if "pris" in label_text:
|
|
102
|
+
price_elem = price_section.find("span", class_="t2")
|
|
103
|
+
if price_elem:
|
|
104
|
+
data["price"] = price_elem.get_text(strip=True)
|
|
105
|
+
break
|
|
106
|
+
elif "månadskostnad" in label_text:
|
|
107
|
+
monthly_elem = price_section.find("h2", class_="t2")
|
|
108
|
+
if monthly_elem:
|
|
109
|
+
data["monthly_cost"] = monthly_elem.get_text(strip=True)
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
def _extract_description(self, grid: Tag, data: dict[str, Any]) -> None:
|
|
113
|
+
desc_sections = grid.find_all("section", class_="border-t mt-40 pt-40")
|
|
114
|
+
for section in desc_sections:
|
|
115
|
+
h2 = section.find("h2", class_="t3 mb-0")
|
|
116
|
+
if h2 and "beskrivning" in h2.get_text(strip=True).lower():
|
|
117
|
+
desc_div = section.find("div", class_="whitespace-pre-wrap")
|
|
118
|
+
if desc_div:
|
|
119
|
+
data["description"] = desc_div.get_text(strip=True)
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
def _extract_specifications(self, grid: Tag, data: dict[str, Any]) -> None:
|
|
123
|
+
specs_section = grid.find("section", class_="key-info-section")
|
|
124
|
+
if not specs_section:
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
dl = specs_section.find("dl")
|
|
128
|
+
if not dl:
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
specifications: dict[str, str] = {}
|
|
132
|
+
divs = dl.find_all("div", style="break-inside:avoid-column")
|
|
133
|
+
for div in divs:
|
|
134
|
+
dt = div.find("dt")
|
|
135
|
+
dd = div.find("dd")
|
|
136
|
+
if dt and dd:
|
|
137
|
+
key_text = dt.get_text(strip=True)
|
|
138
|
+
value_text = dd.get_text(strip=True)
|
|
139
|
+
specifications[key_text] = value_text
|
|
140
|
+
|
|
141
|
+
if specifications:
|
|
142
|
+
data["specifications"] = specifications
|
|
143
|
+
|
|
144
|
+
def _extract_equipment(self, grid: Tag, data: dict[str, Any]) -> None:
|
|
145
|
+
equipment_section: Tag | None = None
|
|
146
|
+
for section in grid.find_all("section", class_="border-t pt-40 mt-40"):
|
|
147
|
+
h2 = section.find("h2", class_="t3 mb-0")
|
|
148
|
+
if h2 and "utrustning" in h2.get_text(strip=True).lower():
|
|
149
|
+
equipment_section = section
|
|
150
|
+
break
|
|
151
|
+
|
|
152
|
+
if not equipment_section:
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
equipment_list = equipment_section.find("ul")
|
|
156
|
+
if equipment_list:
|
|
157
|
+
equipment_items: list[str] = [
|
|
158
|
+
li.get_text(strip=True) for li in equipment_list.find_all("li")
|
|
159
|
+
]
|
|
160
|
+
if equipment_items:
|
|
161
|
+
data["equipment"] = equipment_items
|
|
162
|
+
|
|
163
|
+
def _extract_seller_type(self, soup: BeautifulSoup, data: dict[str, Any]) -> None:
|
|
164
|
+
seller_type = (
|
|
165
|
+
"dealer"
|
|
166
|
+
if soup.find("div", class_=lambda x: x and "dealer" in str(x).lower())
|
|
167
|
+
else "private"
|
|
168
|
+
)
|
|
169
|
+
data["seller_type"] = seller_type
|
|
170
|
+
|
|
171
|
+
def _extract_ad_id(self, soup: BeautifulSoup, data: dict[str, Any]) -> None:
|
|
172
|
+
ad_info_divs = soup.find_all(
|
|
173
|
+
"div", class_="text-m flex md:flex-row flex-col md:gap-x-56 gap-y-16"
|
|
174
|
+
)
|
|
175
|
+
for div in ad_info_divs:
|
|
176
|
+
ad_id_labels = div.find_all("p", class_="s-text-subtle mb-0")
|
|
177
|
+
for ad_id_label in ad_id_labels:
|
|
178
|
+
if "Annons-ID" in ad_id_label.get_text(strip=True):
|
|
179
|
+
ad_id_elem = ad_id_label.find_next_sibling("p")
|
|
180
|
+
if ad_id_elem:
|
|
181
|
+
data["ad_id"] = ad_id_elem.get_text(strip=True)
|
|
182
|
+
break
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
from httpx import Response
|
|
8
|
+
|
|
9
|
+
from .ad_parser import CarAd, RecommerceAd
|
|
10
|
+
from .constants import (
|
|
11
|
+
HEADERS,
|
|
12
|
+
SITE_URL,
|
|
13
|
+
CarColor,
|
|
14
|
+
CarModel,
|
|
15
|
+
CarSortOrder,
|
|
16
|
+
CarTransmission,
|
|
17
|
+
Category,
|
|
18
|
+
Location,
|
|
19
|
+
SortOrder,
|
|
20
|
+
SubCategory,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class QueryParam:
|
|
26
|
+
name: str
|
|
27
|
+
value: str | int
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _request(
|
|
31
|
+
*,
|
|
32
|
+
url: str,
|
|
33
|
+
params: list[QueryParam],
|
|
34
|
+
) -> Response:
|
|
35
|
+
response = httpx.get(
|
|
36
|
+
url,
|
|
37
|
+
headers=HEADERS,
|
|
38
|
+
params=[(param.name, param.value) for param in params],
|
|
39
|
+
)
|
|
40
|
+
response.raise_for_status()
|
|
41
|
+
return response
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class BlocketAPI:
|
|
46
|
+
def search(
|
|
47
|
+
self,
|
|
48
|
+
query: str,
|
|
49
|
+
*,
|
|
50
|
+
sort_order: SortOrder = SortOrder.RELEVANCE,
|
|
51
|
+
locations: list[Location] = [],
|
|
52
|
+
category: Category | None = None,
|
|
53
|
+
sub_category: SubCategory | None = None,
|
|
54
|
+
) -> Any:
|
|
55
|
+
if category and sub_category:
|
|
56
|
+
raise AssertionError("Cannot specify both category and sub_categories")
|
|
57
|
+
|
|
58
|
+
url = f"{SITE_URL}/recommerce-search-page/api/search/SEARCH_ID_BAP_COMMON"
|
|
59
|
+
params = [
|
|
60
|
+
QueryParam("q", query),
|
|
61
|
+
QueryParam("sort", sort_order.value),
|
|
62
|
+
*[QueryParam("location", location.value) for location in locations],
|
|
63
|
+
*([QueryParam("category", category.value)] if category else []),
|
|
64
|
+
*([QueryParam("sub_category", sub_category.value)] if sub_category else []),
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
return _request(url=url, params=params).json()
|
|
68
|
+
|
|
69
|
+
def search_car(
|
|
70
|
+
self,
|
|
71
|
+
query: str | None = None,
|
|
72
|
+
*,
|
|
73
|
+
sort_order: CarSortOrder = CarSortOrder.RELEVANCE,
|
|
74
|
+
locations: list[Location] = [],
|
|
75
|
+
models: list[CarModel] = [],
|
|
76
|
+
price_from: int | None = None,
|
|
77
|
+
price_to: int | None = None,
|
|
78
|
+
year_from: int | None = None,
|
|
79
|
+
year_to: int | None = None,
|
|
80
|
+
milage_from: int | None = None,
|
|
81
|
+
milage_to: int | None = None,
|
|
82
|
+
colors: list[CarColor] = [],
|
|
83
|
+
transmissions: list[CarTransmission] = [],
|
|
84
|
+
) -> Any:
|
|
85
|
+
url = f"{SITE_URL}/mobility/search/api/search/SEARCH_ID_CAR_USED"
|
|
86
|
+
params = [
|
|
87
|
+
*([QueryParam("q", query)] if query else []),
|
|
88
|
+
QueryParam("sort", sort_order.value),
|
|
89
|
+
*[QueryParam("location", location.value) for location in locations],
|
|
90
|
+
*[QueryParam("make", model.value) for model in models],
|
|
91
|
+
*([QueryParam("price_from", price_from)] if price_from else []),
|
|
92
|
+
*([QueryParam("price_to", price_to)] if price_to else []),
|
|
93
|
+
*([QueryParam("year_from", year_from)] if year_from else []),
|
|
94
|
+
*([QueryParam("year_to", year_to)] if year_to else []),
|
|
95
|
+
*([QueryParam("milage_from", milage_from)] if milage_from else []),
|
|
96
|
+
*([QueryParam("milage_to", milage_to)] if milage_to else []),
|
|
97
|
+
*[QueryParam("exterior_colour", color.value) for color in colors],
|
|
98
|
+
*[QueryParam("transmission", t.value) for t in transmissions],
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
return _request(url=url, params=params).json()
|
|
102
|
+
|
|
103
|
+
def get_ad(self, ad: RecommerceAd | CarAd) -> dict:
|
|
104
|
+
response = _request(url=ad.url, params=[])
|
|
105
|
+
return ad.parse(response)
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
from enum import IntEnum, StrEnum
|
|
2
|
+
|
|
3
|
+
SITE_URL = "https://www.blocket.se"
|
|
4
|
+
HEADERS = {
|
|
5
|
+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SortOrder(StrEnum):
|
|
10
|
+
RELEVANCE = "RELEVANCE"
|
|
11
|
+
PRICE_DESC = "PRICE_DESC"
|
|
12
|
+
PRICE_ASC = "PRICE_ASC"
|
|
13
|
+
PUBLISHED_DESC = "PUBLISHED_DESC"
|
|
14
|
+
PUBLISHED_ASC = "PUBLISHED_ASC"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CarSortOrder(StrEnum):
|
|
18
|
+
RELEVANCE = "RELEVANCE"
|
|
19
|
+
PRICE_DESC = "PRICE_DESC"
|
|
20
|
+
PRICE_ASC = "PRICE_ASC"
|
|
21
|
+
PUBLISHED_DESC = "PUBLISHED_DESC"
|
|
22
|
+
PUBLISHED_ASC = "PUBLISHED_ASC"
|
|
23
|
+
|
|
24
|
+
# Car specific
|
|
25
|
+
MILEAGE_DESC = "MILEAGE_DESC"
|
|
26
|
+
MILEAGE_ASC = "MILEAGE_ASC"
|
|
27
|
+
MODEL = "MODEL"
|
|
28
|
+
YEAR_DESC = "YEAR_DESC"
|
|
29
|
+
YEAR_ASC = "YEAR_ASC"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Location(StrEnum):
|
|
33
|
+
BLEKINGE = "0.300010"
|
|
34
|
+
DALARNA = "0.300020"
|
|
35
|
+
GOTLAND = "0.300009"
|
|
36
|
+
GAVLEBORG = "0.300021"
|
|
37
|
+
HALLAND = "0.300013"
|
|
38
|
+
JAMTLAND = "0.300023"
|
|
39
|
+
JONKOPING = "0.300006"
|
|
40
|
+
KALMAR = "0.300008"
|
|
41
|
+
KRONOBERG = "0.300007"
|
|
42
|
+
NORRBOTTEN = "0.300025"
|
|
43
|
+
SKANE = "0.300012"
|
|
44
|
+
STOCKHOLM = "0.300001"
|
|
45
|
+
SODERMANLAND = "0.300004"
|
|
46
|
+
UPPSALA = "0.300003"
|
|
47
|
+
VARMLAND = "0.300017"
|
|
48
|
+
VASTERBOTTEN = "0.300024"
|
|
49
|
+
VASTERNORRLAND = "0.300022"
|
|
50
|
+
VASTMANLAND = "0.300019"
|
|
51
|
+
VASTRA_GOTALAND = "0.300014"
|
|
52
|
+
OREBRO = "0.300018"
|
|
53
|
+
OSTERGOTLAND = "0.300005"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Category(StrEnum):
|
|
57
|
+
AFFARSVERKSAMHET = "0.91"
|
|
58
|
+
DJUR_OCH_TILLBEHOR = "0.77"
|
|
59
|
+
ELEKTRONIK_OCH_VITVAROR = "0.93"
|
|
60
|
+
FORDONSTILLBEHOR = "0.90"
|
|
61
|
+
FRITID_HOBBY_OCH_UNDERHALLNING = "0.86"
|
|
62
|
+
FORALDRAR_OCH_BARN = "0.68"
|
|
63
|
+
KLADER_KOSMETIKA_OCH_ACCESSOARER = "0.71"
|
|
64
|
+
KONST_OCH_ANTIKT = "0.76"
|
|
65
|
+
MOBLER_OCH_INREDNING = "0.78"
|
|
66
|
+
SPORT_OCH_FRITID = "0.69"
|
|
67
|
+
TRADGARD_OCH_RENOVERING = "0.67"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class SubCategory(StrEnum):
|
|
71
|
+
BUTIK_OCH_DETALJHANDEL = "1.91.3108"
|
|
72
|
+
CONTAINRAR_OCH_BARACKER = "1.91.3111"
|
|
73
|
+
DOMANER_OCH_SAJTER = "1.91.3106"
|
|
74
|
+
HALSA_OCH_FORSTA_HJALPEN = "1.91.8343"
|
|
75
|
+
JORDBRUK = "1.91.3114"
|
|
76
|
+
KONTORSUTRUSTNING_OCH_INREDNING = "1.91.3105"
|
|
77
|
+
LAST_OCH_TRANSPORT = "1.91.3113"
|
|
78
|
+
MASKINUTRUSTNING_OCH_RESERVDELAR = "1.91.3112"
|
|
79
|
+
SCEN = "1.91.3110"
|
|
80
|
+
STORKOK_OCH_RESTAURANG = "1.91.3103"
|
|
81
|
+
VERKSTAD_BYGG_OCH_KONSTRUKTION = "1.91.3102"
|
|
82
|
+
BILDELAR_OCH_TILLBEHOR = "1.90.82"
|
|
83
|
+
HUSVAGNS_OCH_HUSBILSDELAR = "1.90.70"
|
|
84
|
+
SLAP_OCH_TRAILER = "1.90.80"
|
|
85
|
+
BATDELAR_OCH_TILLBEHOR = "1.90.30"
|
|
86
|
+
AKVARIUM = "1.77.3976"
|
|
87
|
+
BURAR = "1.77.3977"
|
|
88
|
+
FISKAR = "1.77.5206"
|
|
89
|
+
FODER_DJURVARD_KENNLAR_OCH_STALL = "1.77.5186"
|
|
90
|
+
FAGLAR = "1.77.5205"
|
|
91
|
+
GNAGARE_OCH_KANINER = "1.77.5207"
|
|
92
|
+
HUNDAR = "1.77.5192"
|
|
93
|
+
HUNDTILLBEHOR = "1.77.5193"
|
|
94
|
+
HAST_OCH_RIDUTRUSTNING = "1.77.5195"
|
|
95
|
+
HASTAR = "1.77.5190"
|
|
96
|
+
KATTER = "1.77.5191"
|
|
97
|
+
KATTILLBEHOR = "1.77.5194"
|
|
98
|
+
LANTBRUKSDJUR = "1.77.9439"
|
|
99
|
+
REPTILER = "1.77.5210"
|
|
100
|
+
SPINDLAR_OCH_INSEKTER = "1.77.5208"
|
|
101
|
+
OVRIGA_DJUR = "1.77.5183"
|
|
102
|
+
OVRIGA_DJURTILLBEHOR = "1.77.5185"
|
|
103
|
+
DATORER = "1.93.3215"
|
|
104
|
+
FOTO_OCH_VIDEO = "1.93.3904"
|
|
105
|
+
HUSHALLSAPPARATER = "1.93.3216"
|
|
106
|
+
LJUD_OCH_BILD = "1.93.3906"
|
|
107
|
+
PERSONVARD = "1.93.9809"
|
|
108
|
+
TELEFONER_OCH_TILLBEHOR = "1.93.3217"
|
|
109
|
+
TV_SPEL_OCH_SPELKONSOLER = "1.93.3905"
|
|
110
|
+
VITVAROR = "1.93.3907"
|
|
111
|
+
ATV_RESERVDELAR = "1.90.3975"
|
|
112
|
+
MC_UTRUSTNING_OCH_RESERVDELAR = "1.90.20"
|
|
113
|
+
BILJETTER_OCH_RESOR = "1.86.7735"
|
|
114
|
+
BOCKER_OCH_TIDNINGAR = "1.86.5209"
|
|
115
|
+
HANTVERK = "1.86.7734"
|
|
116
|
+
MAT_OCH_DRYCK = "1.86.3972"
|
|
117
|
+
MODELLER_OCH_BYGGSATSER = "1.86.7732"
|
|
118
|
+
MUSIK_OCH_FILM = "1.86.3922"
|
|
119
|
+
MUSIKINSTRUMENT = "1.86.92"
|
|
120
|
+
RADIOSTYRDA_ENHETER = "1.86.7733"
|
|
121
|
+
SAMLAROBJEKT = "1.86.285"
|
|
122
|
+
SALLSKAPS_OCH_BRADSPEL = "1.86.5203"
|
|
123
|
+
BARNBOCKER = "1.68.8369"
|
|
124
|
+
BARNKLADER = "1.68.3913"
|
|
125
|
+
BARNMOBLER = "1.68.3916"
|
|
126
|
+
BARNSKOR = "1.68.3915"
|
|
127
|
+
BARNTILLBEHOR_OCH_SAKERHET = "1.68.3918"
|
|
128
|
+
BARNVAGNAR = "1.68.3914"
|
|
129
|
+
BILBARNSTOLAR_OCH_BABYSKYDD = "1.68.3911"
|
|
130
|
+
GRAVIDKLADER = "1.68.3948"
|
|
131
|
+
INREDNING_TILL_BARNRUM = "1.68.9445"
|
|
132
|
+
LEKSAKER = "1.68.3912"
|
|
133
|
+
ACCESSOARER = "1.71.9481"
|
|
134
|
+
DAMKLADER = "1.71.3941"
|
|
135
|
+
GLASOGON_OCH_SOLGLASOGON = "1.71.8344"
|
|
136
|
+
HERRKLADER = "1.71.3950"
|
|
137
|
+
HUD_HAR_OCH_KROPPSVARD = "1.71.8280"
|
|
138
|
+
KLOCKOR_OCH_ARMBANDSUR = "1.71.3945"
|
|
139
|
+
KOSMETIK = "1.71.8282"
|
|
140
|
+
MASKERADKLADER = "1.71.8349"
|
|
141
|
+
SKOR = "1.71.3949"
|
|
142
|
+
SMYCKEN_OCH_SMYCKESFORVARING = "1.71.7748"
|
|
143
|
+
VASKOR_OCH_PLANBOCKER = "1.71.3946"
|
|
144
|
+
ANTIKA_MOBLER = "1.76.5178"
|
|
145
|
+
KERAMIK_PORSLIN_OCH_GLAS = "1.76.5176"
|
|
146
|
+
KONST = "1.76.5177"
|
|
147
|
+
SILVERFOREMAL_OCH_SILVERBESTICK = "1.76.5179"
|
|
148
|
+
OVRIGA_ANTIKVITETER = "1.76.5175"
|
|
149
|
+
BORD_OCH_STOLAR = "1.78.5196"
|
|
150
|
+
DEKORATION_OCH_PRYDNADER = "1.78.5222"
|
|
151
|
+
GARDEROBER_OCH_FORVARING = "1.78.5198"
|
|
152
|
+
HYLLOR_OCH_BYRAER = "1.78.8345"
|
|
153
|
+
KOKSUTRUSTNING_OCH_PORSLIN = "1.78.5223"
|
|
154
|
+
LAMPOR = "1.78.5181"
|
|
155
|
+
MATTOR_OCH_TEXTILIER = "1.78.5180"
|
|
156
|
+
PYNT_TILL_HOGTIDER_OCH_FEST = "1.78.9760"
|
|
157
|
+
SOFFOR_OCH_FATOLJER = "1.78.7756"
|
|
158
|
+
SANGAR_OCH_MADRASSER = "1.78.5197"
|
|
159
|
+
OVRIGA_MOBLER_OCH_INREDNING = "1.78.3971"
|
|
160
|
+
BOLLSPORTER = "1.69.3961"
|
|
161
|
+
CYKEL = "1.69.3963"
|
|
162
|
+
EXTREMSPORT = "1.69.3938"
|
|
163
|
+
GOLF = "1.69.5164"
|
|
164
|
+
JAKT_FISKE_OCH_CAMPING = "1.69.3964"
|
|
165
|
+
KOSTTILLSKOTT = "1.69.8281"
|
|
166
|
+
RULLSKRIDSKOR_ISHOCKEY_OCH_KONSTAKNING = "1.69.8346"
|
|
167
|
+
SKYTTE = "1.69.3965"
|
|
168
|
+
SUPPORTERPRODUKTER = "1.69.3937"
|
|
169
|
+
TRANINGSKLOCKOR_OCH_AKTIVITETSARMBAND = "1.69.3967"
|
|
170
|
+
TRANINGSKLADER_OCH_SKOR = "1.69.3940"
|
|
171
|
+
TRANINGSUTRUSTNING = "1.69.5166"
|
|
172
|
+
VATTENSPORT = "1.69.7738"
|
|
173
|
+
VINTERSPORT = "1.69.3962"
|
|
174
|
+
OVRIGA_SPORTER = "1.69.3966"
|
|
175
|
+
BADRUM_OCH_BASTU = "1.67.7749"
|
|
176
|
+
BYGGMATERIAL_OCH_RENOVERING = "1.67.3899"
|
|
177
|
+
GARAGEDELAR_OCH_TILLBEHOR = "1.67.8348"
|
|
178
|
+
KOKSINREDNING_OCH_KOKSSTOMMAR = "1.67.3900"
|
|
179
|
+
LARM_OCH_SAKERHET = "1.67.8347"
|
|
180
|
+
TRADGARD_OCH_UTEMILJO = "1.67.3901"
|
|
181
|
+
UTRUSTNING_FOR_FRITIDSHUS = "1.67.3968"
|
|
182
|
+
VERKTYG = "1.67.5219"
|
|
183
|
+
VARME_OCH_VENTILATION = "1.67.5218"
|
|
184
|
+
OVRIGT = "1.67.3969"
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class CarModel(StrEnum):
|
|
188
|
+
ABARTH = "0.8093"
|
|
189
|
+
AC = "0.200673"
|
|
190
|
+
ACURA = "0.200674"
|
|
191
|
+
AIWAYS = "0.200681"
|
|
192
|
+
ALFA_ROMEO = "0.3233"
|
|
193
|
+
ALPINA = "0.8092"
|
|
194
|
+
AMC = "0.8103"
|
|
195
|
+
ARIEL = "0.8110"
|
|
196
|
+
ARMSTRONG_SIDDELEY = "0.1156"
|
|
197
|
+
ASTON_MARTIN = "0.6733"
|
|
198
|
+
AUDI = "0.744"
|
|
199
|
+
AUSTIN = "0.8076"
|
|
200
|
+
AUSTIN_HEALEY = "0.200688"
|
|
201
|
+
AUTO_UNION = "0.200689"
|
|
202
|
+
AUTOBIANCHI = "0.200690"
|
|
203
|
+
BEDFORD = "0.200693"
|
|
204
|
+
BENTLEY = "0.7166"
|
|
205
|
+
BMW = "0.749"
|
|
206
|
+
BUGATTI = "0.8111"
|
|
207
|
+
BUICK = "0.750"
|
|
208
|
+
BYD = "0.8101"
|
|
209
|
+
CADILLAC = "0.752"
|
|
210
|
+
CATERHAM = "0.200704"
|
|
211
|
+
CHEVROLET = "0.753"
|
|
212
|
+
CHRYSLER = "0.754"
|
|
213
|
+
CITROEN = "0.757"
|
|
214
|
+
CUPRA = "0.8106"
|
|
215
|
+
DACIA = "0.8079"
|
|
216
|
+
DAEWOO = "0.760"
|
|
217
|
+
DAF = "0.8090"
|
|
218
|
+
DAIHATSU = "0.762"
|
|
219
|
+
DAIMLER = "0.200711"
|
|
220
|
+
DATSUN = "0.8089"
|
|
221
|
+
DE_TOMASO = "0.8069"
|
|
222
|
+
DELOREAN = "0.8085"
|
|
223
|
+
DESOTO = "0.200715"
|
|
224
|
+
DFSK = "0.2174"
|
|
225
|
+
DKW = "0.200718"
|
|
226
|
+
DODGE = "0.764"
|
|
227
|
+
DS = "0.8091"
|
|
228
|
+
EDSEL = "0.200723"
|
|
229
|
+
ERSKINE = "0.200726"
|
|
230
|
+
EXCALIBUR = "0.200727"
|
|
231
|
+
FERRARI = "0.2999"
|
|
232
|
+
FIAT = "0.766"
|
|
233
|
+
FISKER = "0.8073"
|
|
234
|
+
FORD = "0.767"
|
|
235
|
+
FORDSON = "0.200730"
|
|
236
|
+
GAZ = "0.200734"
|
|
237
|
+
GINETTA = "0.200738"
|
|
238
|
+
GMC = "0.7547"
|
|
239
|
+
HEINKEL = "0.200745"
|
|
240
|
+
HILLMAN = "0.200746"
|
|
241
|
+
HOLDEN = "0.200747"
|
|
242
|
+
HONDA = "0.771"
|
|
243
|
+
HONGQI = "0.8107"
|
|
244
|
+
HUDSON = "0.200748"
|
|
245
|
+
HUMBER = "0.200749"
|
|
246
|
+
HUMMER = "0.7672"
|
|
247
|
+
HYUNDAI = "0.772"
|
|
248
|
+
INEOS = "0.2000665"
|
|
249
|
+
INFINITI = "0.8065"
|
|
250
|
+
INTERNATIONAL = "0.1160"
|
|
251
|
+
ISUZU = "0.7179"
|
|
252
|
+
IVECO = "0.7280"
|
|
253
|
+
JAC = "0.8114"
|
|
254
|
+
JAGUAR = "0.775"
|
|
255
|
+
JEEP = "0.776"
|
|
256
|
+
JENSEN = "0.774"
|
|
257
|
+
KAISER_JEEP = "0.1162"
|
|
258
|
+
KGM = "0.2000649"
|
|
259
|
+
KIA = "0.777"
|
|
260
|
+
KTM = "0.200761"
|
|
261
|
+
LADA = "0.779"
|
|
262
|
+
LAMBORGHINI = "0.6731"
|
|
263
|
+
LANCIA = "0.780"
|
|
264
|
+
LAND_ROVER = "0.781"
|
|
265
|
+
LEVC = "0.200764"
|
|
266
|
+
LEXUS = "0.782"
|
|
267
|
+
LEYLAND = "0.200765"
|
|
268
|
+
LINCOLN = "0.7153"
|
|
269
|
+
LOTUS = "0.7191"
|
|
270
|
+
LYNK_CO = "0.200769"
|
|
271
|
+
MAN = "0.8097"
|
|
272
|
+
MASERATI = "0.3001"
|
|
273
|
+
MAXUS = "0.8096"
|
|
274
|
+
MAZDA = "0.784"
|
|
275
|
+
MCLAREN = "0.8087"
|
|
276
|
+
MERCEDES_BENZ = "0.785"
|
|
277
|
+
MERCURY = "0.7554"
|
|
278
|
+
MESSERSCHMITT = "0.200774"
|
|
279
|
+
MG = "0.786"
|
|
280
|
+
MINI = "0.7147"
|
|
281
|
+
MINI_MARCOS = "0.200775"
|
|
282
|
+
MITSUBISHI = "0.787"
|
|
283
|
+
MORGAN = "0.788"
|
|
284
|
+
MORRIS = "0.789"
|
|
285
|
+
NIO = "0.8109"
|
|
286
|
+
NISSAN = "0.792"
|
|
287
|
+
OLDSMOBILE = "0.794"
|
|
288
|
+
OPEL = "0.795"
|
|
289
|
+
PACKARD = "0.8077"
|
|
290
|
+
PEUGEOT = "0.796"
|
|
291
|
+
PLYMOUTH = "0.797"
|
|
292
|
+
POLESTAR = "0.8102"
|
|
293
|
+
PONTIAC = "0.800"
|
|
294
|
+
PORSCHE = "0.801"
|
|
295
|
+
PRO_SPORT = "0.200792"
|
|
296
|
+
RADICAL = "0.8088"
|
|
297
|
+
RAM = "0.8100"
|
|
298
|
+
RENAULT = "0.804"
|
|
299
|
+
ROLLS_ROYCE = "0.7170"
|
|
300
|
+
ROVER = "0.805"
|
|
301
|
+
SAAB = "0.806"
|
|
302
|
+
SCION = "0.822"
|
|
303
|
+
SEAT = "0.807"
|
|
304
|
+
SERES = "0.8108"
|
|
305
|
+
SHELBY = "0.1142"
|
|
306
|
+
SIMCA = "0.200807"
|
|
307
|
+
SKODA = "0.808"
|
|
308
|
+
SMART = "0.7137"
|
|
309
|
+
SSANGYONG = "0.7190"
|
|
310
|
+
STANDARD = "0.200812"
|
|
311
|
+
STUDEBAKER = "0.200815"
|
|
312
|
+
SUBARU = "0.810"
|
|
313
|
+
SUZUKI = "0.811"
|
|
314
|
+
TESLA = "0.8078"
|
|
315
|
+
TOYOTA = "0.813"
|
|
316
|
+
TRABANT = "0.200824"
|
|
317
|
+
TRIUMPH = "0.814"
|
|
318
|
+
TVR = "0.820"
|
|
319
|
+
VAUXHALL = "0.200827"
|
|
320
|
+
VOLKSWAGEN = "0.817"
|
|
321
|
+
VOLVO = "0.818"
|
|
322
|
+
WILLYS = "0.200834"
|
|
323
|
+
XPENG = "0.8104"
|
|
324
|
+
ZEEKR = "0.200841"
|
|
325
|
+
ZIMMER = "0.200844"
|
|
326
|
+
OVRIGA = "0.2252"
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class CarColor(IntEnum):
|
|
330
|
+
BEIGE = 1
|
|
331
|
+
BLA = 2
|
|
332
|
+
BRONS = 3
|
|
333
|
+
BRUN = 4
|
|
334
|
+
GRA = 6
|
|
335
|
+
GRON = 5
|
|
336
|
+
GUL = 7
|
|
337
|
+
GULD = 8
|
|
338
|
+
VIT = 9
|
|
339
|
+
LILA = 10
|
|
340
|
+
ORANGE = 11
|
|
341
|
+
ROSA = 12
|
|
342
|
+
ROD = 13
|
|
343
|
+
SILVER = 15
|
|
344
|
+
SVART = 14
|
|
345
|
+
TURKOS = 16
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class CarTransmission(IntEnum):
|
|
349
|
+
AUTOMATIC = 2
|
|
350
|
+
MANUAL = 1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blocket_api
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.92
|
|
4
4
|
Summary: A python API wrapper for blocket.se
|
|
5
5
|
Author-email: dunderrrrrr <emil.bjorkroth@gmail.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -16,6 +16,7 @@ Requires-Dist: mkdocs>=1.6.1
|
|
|
16
16
|
Requires-Dist: mkdocs-material>=9.6.21
|
|
17
17
|
Requires-Dist: mkdocs-swagger-ui-tag>=0.7.2
|
|
18
18
|
Requires-Dist: websockets>=15.0.1
|
|
19
|
+
Requires-Dist: beautifulsoup4>=4.14.2
|
|
19
20
|
Requires-Dist: ruff>=0.14.5
|
|
20
21
|
Dynamic: license-file
|
|
21
22
|
|
|
@@ -47,16 +48,43 @@ or use [blocket-api.se](https://blocket-api.se) without installing anything!
|
|
|
47
48
|
## 💁♀️ Usage
|
|
48
49
|
|
|
49
50
|
```py
|
|
50
|
-
from blocket_api import
|
|
51
|
+
from blocket_api import (
|
|
52
|
+
BlocketAPI,
|
|
53
|
+
Category,
|
|
54
|
+
CarColor,
|
|
55
|
+
CarModel,
|
|
56
|
+
CarSortOrder,
|
|
57
|
+
CarTransmission,
|
|
58
|
+
Location,
|
|
59
|
+
)
|
|
51
60
|
|
|
52
61
|
api = BlocketAPI()
|
|
53
62
|
|
|
54
|
-
|
|
63
|
+
# search all of blocket
|
|
64
|
+
api.search(
|
|
55
65
|
"Vinterdäck Audi",
|
|
56
66
|
sort_order=SortOrder.PRICE_ASC,
|
|
57
67
|
locations=[Location.STOCKHOLM, Location.UPPSALA],
|
|
58
68
|
category=Category.FORDONSTILLBEHOR,
|
|
59
69
|
)
|
|
70
|
+
|
|
71
|
+
# search for cars only
|
|
72
|
+
api.search_car(
|
|
73
|
+
"Audi", # query is optional
|
|
74
|
+
sort_order=CarSortOrder.MILEAGE_ASC,
|
|
75
|
+
models=[CarModel.AUDI],
|
|
76
|
+
colors=[CarColor.GULD],
|
|
77
|
+
price_from=10000,
|
|
78
|
+
price_to=50000,
|
|
79
|
+
transmissions=[CarTransmission.MANUAL],
|
|
80
|
+
locations=[Location.STOCKHOLM],
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Get a recommerce or car ad
|
|
84
|
+
from blocket_api import CarAd, RecommerceAd
|
|
85
|
+
|
|
86
|
+
api.get_ad(RecommerceAd(12345678))
|
|
87
|
+
api.get_ad(CarAd(12345678))
|
|
60
88
|
```
|
|
61
89
|
|
|
62
90
|
## 📝 Notes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "blocket_api"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.92"
|
|
4
4
|
description = "A python API wrapper for blocket.se"
|
|
5
5
|
authors = [{name = "dunderrrrrr", email = "emil.bjorkroth@gmail.com"}]
|
|
6
6
|
readme = "README.md"
|
|
@@ -16,6 +16,7 @@ dependencies = [
|
|
|
16
16
|
"mkdocs-material>=9.6.21",
|
|
17
17
|
"mkdocs-swagger-ui-tag>=0.7.2",
|
|
18
18
|
"websockets>=15.0.1",
|
|
19
|
+
"beautifulsoup4>=4.14.2",
|
|
19
20
|
"ruff>=0.14.5",
|
|
20
21
|
]
|
|
21
22
|
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
import httpx
|
|
7
|
-
|
|
8
|
-
from .constants import HEADERS, SITE_URL, Category, Location, SortOrder, SubCategory
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@dataclass(frozen=True)
|
|
12
|
-
class QueryParam:
|
|
13
|
-
name: str
|
|
14
|
-
value: str
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def _request(
|
|
18
|
-
*,
|
|
19
|
-
url: str,
|
|
20
|
-
params: list[QueryParam],
|
|
21
|
-
) -> Any:
|
|
22
|
-
response = httpx.get(
|
|
23
|
-
url,
|
|
24
|
-
headers=HEADERS,
|
|
25
|
-
params=[(param.name, param.value) for param in params],
|
|
26
|
-
)
|
|
27
|
-
response.raise_for_status()
|
|
28
|
-
return response.json()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@dataclass(frozen=True)
|
|
32
|
-
class BlocketAPI:
|
|
33
|
-
def search(
|
|
34
|
-
self,
|
|
35
|
-
query: str,
|
|
36
|
-
*,
|
|
37
|
-
sort_order: SortOrder = SortOrder.RELEVANCE,
|
|
38
|
-
locations: list[Location] = [],
|
|
39
|
-
category: Category | None = None,
|
|
40
|
-
sub_category: SubCategory | None = None,
|
|
41
|
-
) -> Any:
|
|
42
|
-
if category and sub_category:
|
|
43
|
-
raise AssertionError("Cannot specify both category and sub_categories")
|
|
44
|
-
|
|
45
|
-
url = f"{SITE_URL}/recommerce-search-page/api/search/SEARCH_ID_BAP_COMMON"
|
|
46
|
-
params = [
|
|
47
|
-
QueryParam("q", query),
|
|
48
|
-
QueryParam("sort", sort_order.value),
|
|
49
|
-
*[QueryParam("location", location.value) for location in locations],
|
|
50
|
-
*([QueryParam("category", category.value)] if category else []),
|
|
51
|
-
*([QueryParam("sub_category", sub_category.value)] if sub_category else []),
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
return _request(url=url, params=params)
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
from enum import StrEnum
|
|
2
|
-
|
|
3
|
-
SITE_URL = "https://www.blocket.se"
|
|
4
|
-
HEADERS = {
|
|
5
|
-
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class SortOrder(StrEnum):
|
|
10
|
-
RELEVANCE = "RELEVANCE"
|
|
11
|
-
PRICE_DESC = "PRICE_DESC"
|
|
12
|
-
PRICE_ASC = "PRICE_ASC"
|
|
13
|
-
PUBLISHED_DESC = "PUBLISHED_DESC"
|
|
14
|
-
PUBLISHED_ASC = "PUBLISHED_ASC"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class Location(StrEnum):
|
|
18
|
-
BLEKINGE = "0.300010"
|
|
19
|
-
DALARNA = "0.300020"
|
|
20
|
-
GOTLAND = "0.300009"
|
|
21
|
-
GAVLEBORG = "0.300021"
|
|
22
|
-
HALLAND = "0.300013"
|
|
23
|
-
JAMTLAND = "0.300023"
|
|
24
|
-
JONKOPING = "0.300006"
|
|
25
|
-
KALMAR = "0.300008"
|
|
26
|
-
KRONOBERG = "0.300007"
|
|
27
|
-
NORRBOTTEN = "0.300025"
|
|
28
|
-
SKANE = "0.300012"
|
|
29
|
-
STOCKHOLM = "0.300001"
|
|
30
|
-
SODERMANLAND = "0.300004"
|
|
31
|
-
UPPSALA = "0.300003"
|
|
32
|
-
VARMLAND = "0.300017"
|
|
33
|
-
VASTERBOTTEN = "0.300024"
|
|
34
|
-
VASTERNORRLAND = "0.300022"
|
|
35
|
-
VASTMANLAND = "0.300019"
|
|
36
|
-
VASTRA_GÖTALAND = "0.300014"
|
|
37
|
-
OREBRO = "0.300018"
|
|
38
|
-
OSTERGOTLAND = "0.300005"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class Category(StrEnum):
|
|
42
|
-
AFFARSVERKSAMHET = "0.91"
|
|
43
|
-
# BIL_OCH_HUSVAGN = None
|
|
44
|
-
# BAT = None
|
|
45
|
-
DJUR_OCH_TILLBEHOR = "0.77"
|
|
46
|
-
ELEKTRONIK_OCH_VITVAROR = "0.93"
|
|
47
|
-
# ENTREPRENAD__OCH_LANTBRUKSMASKINER = None
|
|
48
|
-
FORDONSTILLBEHOR = "0.90"
|
|
49
|
-
FRITID_HOBBY_OCH_UNDERHALLNING = "0.86"
|
|
50
|
-
FORALDRAR_OCH_BARN = "0.68"
|
|
51
|
-
KLADER_KOSMETIKA_OCH_ACCESSOARER = "0.71"
|
|
52
|
-
KONST_OCH_ANTIKT = "0.76"
|
|
53
|
-
# MOTORCYKLAR = None
|
|
54
|
-
MOBLER_OCH_INREDNING = "0.78"
|
|
55
|
-
SPORT_OCH_FRITID = "0.69"
|
|
56
|
-
TRADGARD_OCH_RENOVERING = "0.67"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class SubCategory(StrEnum):
|
|
60
|
-
BYGGMATERIAL_OCH_RENOVERING = "1.67.3899"
|
|
61
|
-
BILDELAR_OCH_TILLBEHOR = "1.90.82"
|
|
62
|
-
CYKEL = "1.69.3963"
|
|
63
|
-
JAKT_FISKE_OCH_CAMPING = "1.69.3964"
|
|
64
|
-
TRADGARD_OCH_UTEMILJO = "1.67.3901"
|
|
65
|
-
MC_UTRUSTNING_OCH_RESERVDELAR = "1.90.20"
|
|
66
|
-
HUSVAGNS_OCH_HUSBILSDELAR = "1.90.70"
|
|
67
|
-
VERKTYG = "1.67.5219"
|
|
68
|
-
BUTIK_OCH_DETALJHANDEL = "1.91.3108"
|
|
69
|
-
CONTAINRAR_OCH_BARACKER = "1.91.3111"
|
|
70
|
-
DOMANER_OCH_SAJTER = "1.91.3106"
|
|
71
|
-
HALSA_OCH_FORSTA_HJALPEN = "1.91.8343"
|
|
72
|
-
JORDBRUK = "1.91.3114"
|
|
73
|
-
KONTORSUTRUSTNING_OCH_INREDNING = "1.91.3105"
|
|
74
|
-
LAST_OCH_TRANSPORT = "1.91.3113"
|
|
75
|
-
MASKINUTRUSTNING_OCH_RESERVDELAR = "1.91.3112"
|
|
76
|
-
SCEN = "1.91.3110"
|
|
77
|
-
STORKOK_OCH_RESTAURANG = "1.91.3103"
|
|
78
|
-
VERKSTAD_BYGG_OCH_KONSTRUKTION = "1.91.3102"
|
|
79
|
-
SLAP_OCH_TRAILER = "1.90.80"
|
|
80
|
-
BATDELAR_OCH_TILLBEHOR = "1.90.30"
|
|
81
|
-
AKVARIUM = "1.77.3976"
|
|
82
|
-
BURAR = "1.77.3977"
|
|
83
|
-
FISKAR = "1.77.5206"
|
|
84
|
-
FODER_DJURVARD_KENNLAR_OCH_STALL = "1.77.5186"
|
|
85
|
-
FAGLAR = "1.77.5205"
|
|
86
|
-
GNAGARE_OCH_KANINER = "1.77.5207"
|
|
87
|
-
HUNDAR = "1.77.5192"
|
|
88
|
-
HUNDTILLBEHOR = "1.77.5193"
|
|
89
|
-
HAST_OCH_RIDUTRUSTNING = "1.77.5195"
|
|
90
|
-
HASTAR = "1.77.5190"
|
|
91
|
-
KATTER = "1.77.5191"
|
|
92
|
-
KATTILLBEHOR = "1.77.5194"
|
|
93
|
-
LANTBRUKSDJUR = "1.77.9439"
|
|
94
|
-
REPTILER = "1.77.5210"
|
|
95
|
-
SPINDLAR_OCH_INSEKTER = "1.77.5208"
|
|
96
|
-
OVRIGA_DJUR = "1.77.5183"
|
|
97
|
-
OVRIGA_DJURTILLBEHOR = "1.77.5185"
|
|
98
|
-
DATORER = "1.93.3215"
|
|
99
|
-
FOTO_OCH_VIDEO = "1.93.3904"
|
|
100
|
-
HUSHALLSAPPARATER = "1.93.3216"
|
|
101
|
-
LJUD_OCH_BILD = "1.93.3906"
|
|
102
|
-
PERSONVARD = "1.93.9809"
|
|
103
|
-
TELEFONER_OCH_TILLBEHOR = "1.93.3217"
|
|
104
|
-
TV_SPEL_OCH_SPELKONSOLER = "1.93.3905"
|
|
105
|
-
VITVAROR = "1.93.3907"
|
|
106
|
-
ATV_RESERVDELAR = "1.90.3975"
|
|
107
|
-
OVRIGT = "1.90.3974"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|