sg-property-scraper
使用灵活的过滤器搜索新加坡房产租赁和销售列表。当被要求搜索新加坡房产、查找出租或出售清单、检查地铁站附近的房产价格或比较通勤时间时使用。支持按列表类型(出租/出售)、房产类型(HDB/Condo/Landed)、卧室、浴室、价格范围、面积、TOP年份、地铁站代码、距地铁的距离、房间类型、可用性和到目的地的通勤时间进行过滤。将 JSON 输出到标准输出。
安装 / 下载方式
TotalClaw CLI推荐
totalclaw skills install shop:totalclaw:totalclaw~5kbpers-sg-property-scrapercURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Atotalclaw~5kbpers-sg-property-scraper/file -o 5kbpers-sg-property-scraper.md## 概述(中文)
使用灵活的过滤器搜索新加坡房产租赁和销售列表。当被要求搜索新加坡房产、查找出租或出售清单、检查地铁站附近的房产价格或比较通勤时间时使用。支持按列表类型(出租/出售)、房产类型(HDB/Condo/Landed)、卧室、浴室、价格范围、面积、TOP年份、地铁站代码、距地铁的距离、房间类型、可用性和到目的地的通勤时间进行过滤。将 JSON 输出到标准输出。
## 原文
# Singapore Property Scraper
Scrapes Singapore property listings via HTTP requests. Returns structured JSON.
## Script Location
```
scripts/scrape.py
```
Relative to this SKILL directory. Run with:
```bash
python3 <SKILL_DIR>/scripts/scrape.py [OPTIONS]
```
## Dependencies
- Python 3.8+
- `pip install curl_cffi beautifulsoup4 lxml`
- Optional: `GOOGLE_MAPS_API_KEY` env var for commute time calculation (Google Routes API)
## Quick Start
```bash
# Search 2BR condos for rent under SGD 4000 near Circle Line
python3 scripts/scrape.py \
--listing-type rent --bedrooms 2 --max-price 4000 \
--property-type-group N --mrt-range CC:20-24 \
--output json
# JSON input mode (easier for AI tools)
python3 scripts/scrape.py --json '{
"listingType": "rent",
"bedrooms": 2,
"maxPrice": 4000,
"propertyTypeGroup": ["N"],
"mrtStations": ["CC20","CC21","CC22","CC23","CC24"]
}'
# Dry run: print URL only without scraping
python3 scripts/scrape.py --dry-run --listing-type rent --bedrooms 3
```
## Filter Parameters
| Flag | URL Param | Type | Description |
|------|-----------|------|-------------|
| `--listing-type` | `listingType` | string | `rent` or `sale` |
| `--property-type-group` | `propertyTypeGroup` | string (repeatable) | `N`=Condo, `L`=Landed, `H`=HDB |
| `--entire-unit-or-room` | `entireUnitOrRoom` | string | `ent` for entire unit only; omit for all |
| `--room-type` | `roomType` | string (repeatable) | `master`, `common`, `shared` |
| `--bedrooms` | `bedrooms` | int | `-1`=room, `0`=studio, `1`-`5` |
| `--bathrooms` | `bathrooms` | int | Number of bathrooms |
| `--min-price` | `minPrice` | int | Minimum price (SGD) |
| `--max-price` | `maxPrice` | int | Maximum price (SGD) |
| `--min-size` | `minSize` | int | Minimum size (sqft) |
| `--max-size` | `maxSize` | int | Maximum size (sqft) |
| `--min-top-year` | `minTopYear` | int | Minimum TOP year |
| `--max-top-year` | `maxTopYear` | int | Maximum TOP year |
| `--distance-to-mrt` | `distanceToMRT` | float | Max distance to MRT in km (e.g. `0.5`, `0.75`) |
| `--availability` | `availability` | int | Availability filter |
| `--mrt-station` | `mrtStations` | string (repeatable) | MRT station code, e.g. `CC20` |
| `--mrt-range` | `mrtStations` | string (repeatable) | MRT range, e.g. `CC:20-24` |
| `--sort` | `sort` | string | `date`, `price`, `psf`, `size` |
| `--order` | `order` | string | `asc`, `desc` |
| `--commute-to` | `commuteTo` | string | Destination address for commute time (requires `GOOGLE_MAPS_API_KEY`) |
## Bedroom/Room Logic
- `--entire-unit-or-room ent --bedrooms 4` = 4-bedroom entire unit
- `--entire-unit-or-room ent --bedrooms 0` = studio
- `--bedrooms -1 --room-type master --room-type common` = room rental (master or common room)
- Omit `--entire-unit-or-room` to show both entire units and rooms
## MRT Station Syntax
- Individual station: `--mrt-station CC20`
- Range (same line): `--mrt-range CC:20-24` (expands to CC20, CC21, CC22, CC23, CC24)
- Multiple lines: use multiple flags
- In JSON: `"mrtStations": ["CC20", "EW15"]` or `[["CC", [20, 24]]]` (tuple format)
See `references/params.md` for the complete list of ~213 valid MRT station codes.
## Execution Parameters
| Flag | Description |
|------|-------------|
| `--pages N` | Number of pages to scrape (default: 1) |
| `--dry-run` | Build and print URL(s), skip scraping |
| `--no-validate` | Skip parameter validation |
| `--timeout N` | HTTP request timeout in seconds (default: 30) |
| `--raw-param K=V` | Extra URL query param (repeatable) |
| `--output json\|text\|none` | Output format (default: json when piped) |
| `--verbose` | Verbose logging to stderr |
## JSON Input Mode
Pass filters as a JSON string with `--json`. Keys use camelCase matching the URL parameter names:
```bash
python3 scripts/scrape.py --json '{
"listingType": "rent",
"propertyTypeGroup": ["N"],
"bedrooms": 2,
"bathrooms": 2,
"maxPrice": 4000,
"mrtStations": ["EW16", "EW17", "EW18"],
"distanceToMRT": 0.75,
"minTopYear": 1990
}'
```
Or load from a file: `--config filters.json`
## Output Format
JSON array on stdout (empty `[]` if no results):
```json
[
{
"id": "23744236",
"name": "Kingsford Waterbay",
"price": "S$ 3,900 /mo",
"psf": "S$ 4.53 psf",
"address": "68 Upper Serangoon View",
"bedrooms": "2",
"bathrooms": "2",
"area": "861 sqft",
"type": "Condominium",
"built": "Built: 2018",
"availability": "Ready to Move",
"mrt_distance": "14 min (1.15 km) from SE4 Kangkar LRT Station",
"list_date": "Listed on Feb 15, 2026 (2d ago)",
"agent": "May Chong",
"agency": "PROPNEX REALTY PTE. LTD.",
"headline": "Perfect work from home unit, river facing, unblocked high floor cozy",
"link": "https://www.propertyguru.com.sg/listing/for-rent-kingsford-waterbay-23744236",
"commute_driving": "25 mins",
"commute_transit": "45 mins"
}
]
```
## Exit Codes
- `0`: Success, results found
- `1`: Error (bad parameters, scraping failure)
- `2`: Success but zero listings found
## Agent Usage Notes
When calling this script from an AI agent:
1. Use `--output json` for structured output (default when piped)
2. Use `--json` flag for easier parameter passing than individual CLI flags
3. Use `--dry-run` to preview the search URL before scraping
4. Use `--pages N` if the user wants more results (each page has ~20 listings)
5. Use `--commute-to` with a destination address to calculate commute times (driving + transit) for each listing. Requires `GOOGLE_MAPS_API_KEY` env var. If the key is not set, commute fields are omitted silently.
6. `commute_driving` and `commute_transit` fields are empty strings `""` when API key is missing or calculation fails