Skip to content

Commit 0e8a088

Browse files
committed
add missing endpoints
1 parent 425e8b2 commit 0e8a088

18 files changed

Lines changed: 1895 additions & 112 deletions

CLAUDE.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
PHP client library for 4 Apify actors that scrape Yandex data: places/businesses from Yandex Maps, reviews from Yandex Maps, products from Yandex Market, and real estate listings from Yandex Realty. Returns typed DTOs for each data type.
8+
9+
## Build Commands
10+
11+
```bash
12+
composer install # Install dependencies
13+
composer test # Run Pest tests
14+
composer test -- --filter TestName # Run single test
15+
composer cs # Check code style (Laravel Pint)
16+
composer cs:fix # Fix code style
17+
composer analyse # Run PHPStan static analysis (level 8)
18+
```
19+
20+
## Architecture
21+
22+
```
23+
src/
24+
Client.php # Main API client with 4 scraping methods
25+
Config.php # Configuration (API token, actor IDs, base URL, timeout)
26+
Language.php # Enum: auto, ru, en, tr, uk, kk
27+
ReviewSort.php # Enum: relevance, newest, highest, lowest
28+
MarketSort.php # Enum: default, dpop, aprice, dprice, rating
29+
MarketRegion.php # Enum: Moscow=213, SPb=2, etc. (16 cities)
30+
DealType.php # Enum: SELL, RENT
31+
PropertyCategory.php # Enum: APARTMENT, ROOMS, HOUSE, LOT, COMMERCIAL, GARAGE
32+
RealtySort.php # Enum: RELEVANCE, DATE_DESC, PRICE, PRICE_DESC, AREA, AREA_DESC, COMMISSIONING_DATE
33+
DTO/
34+
Place.php # Place/business with contacts, ratings, schedule, reviews, photos, videos
35+
Review.php # Review with author info, business reply, AI analysis, translations
36+
Product.php # Yandex Market product with pricing, seller, specs, delivery, media
37+
Listing.php # Yandex Realty listing with price, location, property details, predictions
38+
Exception/
39+
ApiException.php # Base API exception
40+
RateLimitException.php
41+
tests/
42+
```
43+
44+
## Actors
45+
46+
| Actor | ID | Description |
47+
|-------|----|-------------|
48+
| Places | `zen-studio/yandex-places-scraper` | Search Yandex Maps by keyword + location |
49+
| Reviews | `zen-studio/yandex-reviews-scraper` | Scrape reviews from Yandex Maps businesses |
50+
| Market | `zen-studio/yandex-market-scraper-parser` | Scrape products from Yandex Market |
51+
| Realty | `zen-studio/yandex-realty-scraper` | Scrape real estate listings from Yandex Realty |
52+
53+
## Key Design Decisions
54+
55+
- DTOs are immutable (readonly class with readonly properties)
56+
- All DTOs use named constructors (`fromArray()`) for Apify JSON mapping
57+
- Nullable types for optional fields from the API
58+
- Backed string enums for all filter/sort/language/region values
59+
- Empty string enum values (Auto/Default) are excluded from API input
60+
- Shared `runActor()` private method handles POST to run actor + fetch dataset
61+
- Client constructor: `new Client('apify_api_token')` or `new Client('token', new Config(...))`

README.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[English](https://github.com/Scraper-APIs/yandex-scraper-php) | **Русский**
44

5-
PHP-библиотека для парсинга данных Яндекса: организации и места (Яндекс Карты), отзывы (Яндекс Карты), товары (Яндекс Маркет).
5+
PHP-библиотека для парсинга данных Яндекса: организации и места (Яндекс Карты), отзывы (Яндекс Карты), товары (Яндекс Маркет), недвижимость (Яндекс Недвижимость).
66

77
Работает через [Apify API](https://apify.com/) — запускает акторы и возвращает типизированные DTO.
88

@@ -107,6 +107,40 @@ foreach ($products as $product) {
107107
}
108108
```
109109

110+
### Недвижимость (Яндекс Недвижимость)
111+
112+
```php
113+
use YandexParser\DealType;
114+
use YandexParser\PropertyCategory;
115+
use YandexParser\RealtySort;
116+
117+
$listings = $client->scrapeListings(
118+
location: 'Москва',
119+
dealType: DealType::Sell,
120+
category: PropertyCategory::Apartment,
121+
maxItems: 50,
122+
sort: RealtySort::PriceAsc,
123+
roomsTotal: ['1', '2'],
124+
options: [
125+
'priceMin' => 5000000,
126+
'priceMax' => 15000000,
127+
],
128+
);
129+
130+
foreach ($listings as $listing) {
131+
echo "{$listing->getAddress()} — {$listing->getPriceValue()} ₽" . PHP_EOL;
132+
echo "Площадь: {$listing->getAreaValue()} м², этаж: {$listing->floorsOffered[0] ?? '?'}/{$listing->floorsTotal}" . PHP_EOL;
133+
134+
if ($listing->hasPhones()) {
135+
echo "Тел: {$listing->getFirstPhone()}" . PHP_EOL;
136+
}
137+
138+
if (!$listing->isFromOwner()) {
139+
echo "Агентство: {$listing->getSellerName()}" . PHP_EOL;
140+
}
141+
}
142+
```
143+
110144
## Перечисления
111145

112146
| Enum | Значения |
@@ -115,6 +149,9 @@ foreach ($products as $product) {
115149
| `ReviewSort` | `Relevance`, `Newest`, `Highest`, `Lowest` |
116150
| `MarketSort` | `Default`, `Popular`, `PriceAsc`, `PriceDesc`, `Rating` |
117151
| `MarketRegion` | `Moscow`, `SaintPetersburg`, `Yekaterinburg`, `Kazan`, `Novosibirsk`, `NizhnyNovgorod`, `Samara`, `RostovOnDon`, `Krasnodar`, `Chelyabinsk`, `Ufa`, `Perm`, `Voronezh`, `Volgograd`, `Krasnoyarsk`, `Omsk` |
152+
| `DealType` | `Sell`, `Rent` |
153+
| `PropertyCategory` | `Apartment`, `Rooms`, `House`, `Lot`, `Commercial`, `Garage` |
154+
| `RealtySort` | `Relevance`, `Newest`, `PriceAsc`, `PriceDesc`, `AreaAsc`, `AreaDesc`, `CommissioningDate` |
118155

119156
## Конфигурация
120157

src/Client.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use GuzzleHttp\Client as HttpClient;
88
use GuzzleHttp\Exception\GuzzleException;
9+
use YandexParser\DTO\Listing;
910
use YandexParser\DTO\Place;
1011
use YandexParser\DTO\Product;
1112
use YandexParser\DTO\Review;
@@ -173,6 +174,54 @@ public function scrapeProducts(
173174
}
174175
}
175176

177+
/**
178+
* Scrape real estate listings from Yandex Realty.
179+
*
180+
* @param string[] $roomsTotal Room count filter: ['STUDIO', '1', '2', '3', 'PLUS_4']
181+
* @param array<string, mixed> $options Optional (priceMin, priceMax, areaMin, areaMax, floorMin, floorMax, agents, regionId, includePhones, includePriceHistory)
182+
* @return Listing[]
183+
*
184+
* @throws ApiException
185+
* @throws RateLimitException
186+
*/
187+
public function scrapeListings(
188+
string $location = 'Москва',
189+
DealType $dealType = DealType::Sell,
190+
PropertyCategory $category = PropertyCategory::Apartment,
191+
int $maxItems = 100,
192+
RealtySort $sort = RealtySort::Relevance,
193+
array $roomsTotal = [],
194+
array $options = [],
195+
): array {
196+
$input = [
197+
'location' => $location,
198+
'dealType' => $dealType->value,
199+
'category' => $category->value,
200+
'maxItems' => $maxItems,
201+
];
202+
203+
if ($sort !== RealtySort::Relevance) {
204+
$input['sort'] = $sort->value;
205+
}
206+
207+
if (count($roomsTotal) > 0) {
208+
$input['roomsTotal'] = $roomsTotal;
209+
}
210+
211+
$input = array_merge($input, $options);
212+
213+
try {
214+
$items = $this->runActor(Config::REALTY_ACTOR_ID, $input);
215+
216+
return array_map(
217+
static fn (array $item): Listing => Listing::fromArray($item),
218+
$items
219+
);
220+
} catch (GuzzleException $e) {
221+
$this->handleGuzzleException($e);
222+
}
223+
}
224+
176225
/**
177226
* Run an Apify actor and return the dataset items.
178227
*

src/Config.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
public const MARKET_ACTOR_ID = 'zen-studio/yandex-market-scraper-parser';
1414

15+
public const REALTY_ACTOR_ID = 'zen-studio/yandex-realty-scraper';
16+
1517
public function __construct(
1618
public string $apiToken,
1719
public string $baseUrl = 'https://api.apify.com/v2',

0 commit comments

Comments
 (0)