pyfunda 2.1.0__tar.gz → 2.2.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.
- {pyfunda-2.1.0 → pyfunda-2.2.0}/PKG-INFO +52 -1
- {pyfunda-2.1.0 → pyfunda-2.2.0}/README.md +51 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/examples/price_history.py +2 -2
- {pyfunda-2.1.0 → pyfunda-2.2.0}/funda/__init__.py +1 -1
- {pyfunda-2.1.0 → pyfunda-2.2.0}/funda/funda.py +1 -3
- {pyfunda-2.1.0 → pyfunda-2.2.0}/pyproject.toml +1 -1
- {pyfunda-2.1.0 → pyfunda-2.2.0}/.github/FUNDING.yml +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/.github/workflows/publish.yml +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/.gitignore +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/LICENSE +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/examples/analysis.ipynb +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/examples/export_to_csv.py +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/examples/new_listings_alert.py +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/examples/poll_new_listings.py +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/examples/price_tracker.py +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/funda/listing.py +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/funda/py.typed +0 -0
- {pyfunda-2.1.0 → pyfunda-2.2.0}/test_all_flows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyfunda
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Python API for Funda.nl real estate listings
|
|
5
5
|
Project-URL: Homepage, https://github.com/0xMH/pyfunda
|
|
6
6
|
Project-URL: Repository, https://github.com/0xMH/pyfunda
|
|
@@ -265,6 +265,34 @@ for listing in f.poll_new_listings(
|
|
|
265
265
|
|
|
266
266
|
This bypasses ES search and queries the detail API directly, catching listings that haven't been indexed yet.
|
|
267
267
|
|
|
268
|
+
#### get_price_history(listing)
|
|
269
|
+
|
|
270
|
+
Get historical price data for a listing, including previous asking prices, WOZ tax assessments, and sale history.
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
listing = f.get_listing(43032486)
|
|
274
|
+
history = f.get_price_history(listing)
|
|
275
|
+
|
|
276
|
+
for change in history:
|
|
277
|
+
print(change['date'], change['human_price'], change['status'])
|
|
278
|
+
# 22 okt, 2025 €435.000 asking_price
|
|
279
|
+
# 1 jan, 2025 €472.000 woz
|
|
280
|
+
# 19 aug, 2019 €300.000 asking_price
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Returns:** List of price changes, each containing:
|
|
284
|
+
|
|
285
|
+
| Field | Description |
|
|
286
|
+
|-------|-------------|
|
|
287
|
+
| `price` | Numeric price |
|
|
288
|
+
| `human_price` | Formatted price (e.g., "€435.000") |
|
|
289
|
+
| `date` | Human readable date |
|
|
290
|
+
| `timestamp` | ISO timestamp |
|
|
291
|
+
| `source` | "Funda" or "WOZ" |
|
|
292
|
+
| `status` | `asking_price`, `sold`, or `woz` |
|
|
293
|
+
|
|
294
|
+
> **Note:** This fetches data from the Walter Living API. Only called when explicitly requested (lazy-loaded).
|
|
295
|
+
|
|
268
296
|
### Listing
|
|
269
297
|
|
|
270
298
|
Listing objects support dict-like access with convenient aliases.
|
|
@@ -516,6 +544,29 @@ for listing in f.poll_new_listings(since_id=latest_id, offering_type="buy"):
|
|
|
516
544
|
|
|
517
545
|
The generator stops after 20 consecutive 404s (configurable via `max_consecutive_404s`).
|
|
518
546
|
|
|
547
|
+
### Get price history for a listing
|
|
548
|
+
|
|
549
|
+
```python
|
|
550
|
+
from funda import Funda
|
|
551
|
+
|
|
552
|
+
f = Funda()
|
|
553
|
+
listing = f.get_listing(43032486)
|
|
554
|
+
|
|
555
|
+
# Fetch historical prices (WOZ assessments, previous asking prices, sales)
|
|
556
|
+
history = f.get_price_history(listing)
|
|
557
|
+
|
|
558
|
+
print(f"Price history for {listing['title']}:")
|
|
559
|
+
for change in history:
|
|
560
|
+
print(f" {change['date']}: {change['human_price']} ({change['status']})")
|
|
561
|
+
|
|
562
|
+
# Calculate price change over time
|
|
563
|
+
funda_prices = [c for c in history if c['source'] == 'Funda']
|
|
564
|
+
if len(funda_prices) >= 2:
|
|
565
|
+
newest, oldest = funda_prices[0]['price'], funda_prices[-1]['price']
|
|
566
|
+
change_pct = ((newest - oldest) / oldest) * 100
|
|
567
|
+
print(f"\nPrice change: {change_pct:+.1f}%")
|
|
568
|
+
```
|
|
569
|
+
|
|
519
570
|
## License
|
|
520
571
|
|
|
521
572
|
AGPL-3.0
|
|
@@ -243,6 +243,34 @@ for listing in f.poll_new_listings(
|
|
|
243
243
|
|
|
244
244
|
This bypasses ES search and queries the detail API directly, catching listings that haven't been indexed yet.
|
|
245
245
|
|
|
246
|
+
#### get_price_history(listing)
|
|
247
|
+
|
|
248
|
+
Get historical price data for a listing, including previous asking prices, WOZ tax assessments, and sale history.
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
listing = f.get_listing(43032486)
|
|
252
|
+
history = f.get_price_history(listing)
|
|
253
|
+
|
|
254
|
+
for change in history:
|
|
255
|
+
print(change['date'], change['human_price'], change['status'])
|
|
256
|
+
# 22 okt, 2025 €435.000 asking_price
|
|
257
|
+
# 1 jan, 2025 €472.000 woz
|
|
258
|
+
# 19 aug, 2019 €300.000 asking_price
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Returns:** List of price changes, each containing:
|
|
262
|
+
|
|
263
|
+
| Field | Description |
|
|
264
|
+
|-------|-------------|
|
|
265
|
+
| `price` | Numeric price |
|
|
266
|
+
| `human_price` | Formatted price (e.g., "€435.000") |
|
|
267
|
+
| `date` | Human readable date |
|
|
268
|
+
| `timestamp` | ISO timestamp |
|
|
269
|
+
| `source` | "Funda" or "WOZ" |
|
|
270
|
+
| `status` | `asking_price`, `sold`, or `woz` |
|
|
271
|
+
|
|
272
|
+
> **Note:** This fetches data from the Walter Living API. Only called when explicitly requested (lazy-loaded).
|
|
273
|
+
|
|
246
274
|
### Listing
|
|
247
275
|
|
|
248
276
|
Listing objects support dict-like access with convenient aliases.
|
|
@@ -494,6 +522,29 @@ for listing in f.poll_new_listings(since_id=latest_id, offering_type="buy"):
|
|
|
494
522
|
|
|
495
523
|
The generator stops after 20 consecutive 404s (configurable via `max_consecutive_404s`).
|
|
496
524
|
|
|
525
|
+
### Get price history for a listing
|
|
526
|
+
|
|
527
|
+
```python
|
|
528
|
+
from funda import Funda
|
|
529
|
+
|
|
530
|
+
f = Funda()
|
|
531
|
+
listing = f.get_listing(43032486)
|
|
532
|
+
|
|
533
|
+
# Fetch historical prices (WOZ assessments, previous asking prices, sales)
|
|
534
|
+
history = f.get_price_history(listing)
|
|
535
|
+
|
|
536
|
+
print(f"Price history for {listing['title']}:")
|
|
537
|
+
for change in history:
|
|
538
|
+
print(f" {change['date']}: {change['human_price']} ({change['status']})")
|
|
539
|
+
|
|
540
|
+
# Calculate price change over time
|
|
541
|
+
funda_prices = [c for c in history if c['source'] == 'Funda']
|
|
542
|
+
if len(funda_prices) >= 2:
|
|
543
|
+
newest, oldest = funda_prices[0]['price'], funda_prices[-1]['price']
|
|
544
|
+
change_pct = ((newest - oldest) / oldest) * 100
|
|
545
|
+
print(f"\nPrice change: {change_pct:+.1f}%")
|
|
546
|
+
```
|
|
547
|
+
|
|
497
548
|
## License
|
|
498
549
|
|
|
499
550
|
AGPL-3.0
|
|
@@ -5,8 +5,8 @@ Shows previous asking prices, WOZ tax assessments, and sale history
|
|
|
5
5
|
using the Walter Living API.
|
|
6
6
|
|
|
7
7
|
Usage:
|
|
8
|
-
uv run examples/price_history.py
|
|
9
|
-
uv run examples/price_history.py "https://www.funda.nl/detail/koop
|
|
8
|
+
uv run examples/price_history.py 43032486
|
|
9
|
+
uv run examples/price_history.py "https://www.funda.nl/en/detail/koop/amsterdam/appartement-pieter-calandlaan-400/43032486/"
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import argparse
|
|
@@ -18,13 +18,11 @@ API_WALTER = "https://api.walterliving.com/hunter/lookup"
|
|
|
18
18
|
|
|
19
19
|
# Headers for mobile API
|
|
20
20
|
HEADERS = {
|
|
21
|
-
"user-agent": "Dart/3.9 (dart:io)",
|
|
22
21
|
"x-funda-app-platform": "android",
|
|
23
22
|
"content-type": "application/json",
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
SEARCH_HEADERS = {
|
|
27
|
-
"user-agent": "Dart/3.9 (dart:io)",
|
|
28
26
|
"content-type": "application/json",
|
|
29
27
|
"accept": "application/json",
|
|
30
28
|
"referer": "https://www.funda.nl/",
|
|
@@ -71,7 +69,7 @@ class Funda:
|
|
|
71
69
|
def session(self) -> requests.Session:
|
|
72
70
|
"""Lazily create HTTP session."""
|
|
73
71
|
if self._session is None:
|
|
74
|
-
self._session = requests.Session(impersonate="
|
|
72
|
+
self._session = requests.Session(impersonate="chrome")
|
|
75
73
|
self._session.headers.update(HEADERS)
|
|
76
74
|
return self._session
|
|
77
75
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|