# Naver & Zigbang Real Estate Scraper + Gap Finder (`substitute99/naver-real-estate-scraper`) Actor

Scrape Korean apartment listings from Naver 부동산 and Zigbang (직방) and auto-surface low-gap 갭투자 opportunities (sale vs jeonse) with 전세가율, market position, and recent actual transaction prices. Zigbang mode is token-free; Naver mode needs a KR residential proxy.

- **URL**: https://apify.com/substitute99/naver-real-estate-scraper.md
- **Developed by:** [Dongwook Kim](https://apify.com/substitute99) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 record scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Naver & Zigbang Real Estate Scraper + Gap-Investment Finder

Scrape Korean apartment listings from **Naver 부동산** and **Zigbang (직방)**, and
automatically surface low-gap **갭투자 (gap-investment)** opportunities — where a
tenant's 전세 deposit covers most of the purchase price, so a buyer fronts only
the gap.

- 🟢 **Zigbang mode (default)** — token-free, works on any IP, **no proxy needed**.
- 🔵 **Naver mode** — richer coverage + 시세/실거래가 (actual transaction prices),
  needs a **KR residential proxy** (Naver blocks datacenter IPs).
- 💡 **Gap finder** — pairs 매매 vs 전세 per complex/평형 and ranks the lowest-gap
  opportunities with 전세가율, market position, and recent actual transactions.

### Quick start (free, no proxy)

Just press **Run** — the default input scrapes Zigbang in 강남 (geohash `wydm6`):

```json
{ "site": "zigbang", "targets": ["wydm6"], "maxListings": 300, "gapAnalysis": true }
````

#### Target other regions (geohash)

Zigbang targets are **geohashes**. Verified examples (2026-06):

| Region | geohash | Region | geohash |
|---|---|---|---|
| 서울 강남/서초 | `wydm6` | 성남 분당 | `wydks` |
| 서울 송파 잠실 | `wydmk` | 수원 영통 | `wyd7g` |
| 서울 마포 | `wydm8` | 부산 해운대 | `wy7b5` |
| 서울 성동 | `wydmd` | 대구 수성 | `wy77z` |
| 서울 (광역) | `wydm` (4-char = wider) | 인천 송도 | `wydh9` |

Shorter geohash = wider area. Get any area's geohash from its lat/lng (e.g.
geohash.org) or pass a `https://www.zigbang.com/...?geohash=...` URL.

### Naver mode (premium coverage, needs proxy)

```json
{
  "site": "naver",
  "targets": ["https://new.land.naver.com/complexes?ms=37.4949,127.0521,15&a=APT&e=RETAIL"],
  "useResidentialProxy": true,
  "enrichMarket": true,
  "maxListings": 500
}
```

Naver mode adds, per gap opportunity: **전세가율**, **시세위치** (where the sale
sits in the complex's asking range), and the **latest actual transaction (실거래가)**.
It requires `useResidentialProxy: true` with a KR residential proxy.

### Output

Dataset records:

- **listings** — `site, complexName, areaLabel, tradeType (SALE/JEONSE/MONTHLY), priceManwon, floor, ...`
- **gap-opportunity** — `complexName, areaLabel, saleManwon, jeonseManwon, gapManwon, gapRatioPct, bestCaseGapManwon, bestCaseGapRatioPct, sampleConfidence, saleCount, jeonseCount, saleListingId, jeonseListingId` (+ Naver: `complexSalePositionPct, recentRealSaleManwon, askingVsRealPct`)
- **run-insights** — one per run, with counts and top gap opportunities for quick inspection

Prices are in **만원** (10,000 KRW). Gap opportunities are sorted lowest-gap first.

### Pricing (pay-per-event)

| Event | When |
|---|---|
| `listing-scraped` | each unique listing |
| `gap-opportunity` | each low-gap opportunity found (premium) |

***

### For developers — architecture

```
src/
  core/types.ts         SiteAdapter boundary (swap data source without touching
                        billing/analysis)
  analysis/gap.ts       Pure 갭투자 logic (median headline + best-case + counts)
  adapters/naver*.ts    Naver: fingerprinted browser, JWT capture, in-page fetch
                        pagination, 시세/실거래가 enrichment
  adapters/zigbang*.ts  Zigbang: token-free HTTP, concurrent per-danji fetch
  main.ts               site router → adapter → PPE charge → dataset → RUN_SUMMARY
```

Both adapters implement the same `SiteAdapter`; `main.ts` is site-agnostic.
The actor emits a `RUN_SUMMARY` with a `DATA_QUALITY_FAIL` flag and per-run stats
for health monitoring. Run `npm run health:local` before deployment; it fails on
non-OK run quality, low listing count, high parse failure rate, and actor
timeout/start failures. Run `npm run verify` for the local release gate
(tests, TypeScript build, and Apify schema validation), and `npm run verify:ops`
to add the latest cloud-health report status, scheduled-task status snapshot,
scheduled-task registration preview self-test, and scheduled-task wrapper
self-test without starting a new cloud run. After the scheduled task is
registered, use `npm run verify:ops:strict` so a missing task fails the gate.

After deployment, run `npm run health:cloud` to smoke the latest Apify build and
verify the Actor output links, `RUN_SUMMARY`, and dataset row mix. For schedulers
or alerting wrappers, set `HEALTH_REPORT_PATH` to write a small machine-readable
success/failure report, e.g. PowerShell:
`$env:HEALTH_REPORT_PATH='storage-healthcheck/cloud-health-latest.json'; npm run health:cloud`.
Use `npm run health:cloud:status` as a cheap follow-up probe; it reads the latest
report and fails if it is stale, failed, missing `run-insights`, or below the
minimum listing count. The shared health thresholds are `MIN_LISTINGS`,
`MAX_PARTIAL_PCT`, `MAX_COMPLEX_FAILURE_PCT` (default `25`), and
`MAX_MALFORMED_RESPONSES` (default `0`).

On Windows, preview the scheduled-task registration with:

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/register-cloud-health-task.ps1
```

Register or update the daily task only when the preview looks right:

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/register-cloud-health-task.ps1 -Register -Force -At 08:00
```

For failure alerts, set `ALERT_WEBHOOK_URL` in the task user's environment. Treat
that URL as a credential; do not put it in the command line, README, logs, or
Task Scheduler arguments. The task wrapper posts only on failure and logs only
whether the alert send succeeded.

The task log is bounded by default. `scripts/run-cloud-health-task.ps1` rotates
`storage-healthcheck/cloud-health-task.log` to `.log.1` when it reaches 1 MB;
override with `-MaxLogBytes` if needed.

Check both Task Scheduler registration state and the latest health report with
`npm run health:cloud:task:status`. Add `-RequireRegisteredTask` when calling
`scripts/check-cloud-health-task.ps1` directly if a missing scheduled task should
fail the check, or use `npm run health:cloud:task:status:strict` after the task
has been registered.

Exercise the wrapper failure path without starting an Apify cloud run with
`npm run health:cloud:task:self-test`. The self-test suppresses webhook delivery,
expects the wrapped task to fail, and passes only if the failure marker and alert
suppression are written to the self-test log.

Exercise the scheduled-task registration preview without creating or updating an
OS task with `npm run health:cloud:task:register:self-test`. It verifies that
the preview remains non-mutating and that the generated task arguments include
the report path, log path, report staleness threshold, and log rotation limit.

#### Notes

- Naver's PC API requires an `Authorization: Bearer` JWT and rejects datacenter
  direct fetch (401/429); the adapter captures the JWT from the warmed browser
  session and refreshes on expiry.
- Zigbang apartment inventory is sparser than Naver's (it began as 원룸/오피스텔);
  many large 재건축 complexes have 0 Zigbang listings.

### Legal / ToS

Scrapes publicly visible listings. Operate on public data, respect rate limits,
and review each portal's ToS before commercial use.

# Actor input Schema

## `site` (type: `string`):

Which Korean portal to scrape. Zigbang works token-free on any IP (no proxy). Naver has richer coverage + 실거래가 but needs a KR residential proxy.

## `targets` (type: `array`):

For Zigbang: geohash(es) of the area, e.g. "wydm6" (강남). For Naver: map/region URLs (new.land.naver.com/complexes?ms=lat,lng,zoom\&a=APT).

## `maxListings` (type: `integer`):

Cost guard. Stops after this many unique listings.

## `useResidentialProxy` (type: `boolean`):

Naver blocks datacenter IPs — turn this ON (and have a KR residential proxy) for Naver. Leave OFF for Zigbang.

## `gapAnalysis` (type: `boolean`):

Pair sale vs jeonse per complex/area and surface low-gap opportunities.

## `enrichMarket` (type: `boolean`):

Naver-only: one extra call per complex adds asking range, latest actual transaction (실거래가), and 전세가율 to gap opportunities.

## `maxGapRatioPct` (type: `integer`):

Only report opportunities at or below this gap ratio.

## `gapSortBy` (type: `string`):

gapRatioPct = lowest leverage first. gapManwon = lowest absolute capital first (fixed-budget investors).

## Actor input object example

```json
{
  "site": "zigbang",
  "targets": [
    "wydm6"
  ],
  "maxListings": 300,
  "useResidentialProxy": false,
  "gapAnalysis": true,
  "enrichMarket": false,
  "maxGapRatioPct": 60,
  "gapSortBy": "gapRatioPct"
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped listings, gap opportunities, and run-insights records from the default dataset.

## `runSummary` (type: `string`):

Structured run-quality summary written by the Actor under RUN\_SUMMARY.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "targets": [
        "wydm6"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("substitute99/naver-real-estate-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "targets": ["wydm6"] }

# Run the Actor and wait for it to finish
run = client.actor("substitute99/naver-real-estate-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "targets": [
    "wydm6"
  ]
}' |
apify call substitute99/naver-real-estate-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=substitute99/naver-real-estate-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Naver & Zigbang Real Estate Scraper + Gap Finder",
        "description": "Scrape Korean apartment listings from Naver 부동산 and Zigbang (직방) and auto-surface low-gap 갭투자 opportunities (sale vs jeonse) with 전세가율, market position, and recent actual transaction prices. Zigbang mode is token-free; Naver mode needs a KR residential proxy.",
        "version": "0.1",
        "x-build-id": "eNkpOFqoYYcvXlRy1"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/substitute99~naver-real-estate-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-substitute99-naver-real-estate-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/substitute99~naver-real-estate-scraper/runs": {
            "post": {
                "operationId": "runs-sync-substitute99-naver-real-estate-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/substitute99~naver-real-estate-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-substitute99-naver-real-estate-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "site": {
                        "title": "Source site",
                        "enum": [
                            "zigbang",
                            "naver"
                        ],
                        "type": "string",
                        "description": "Which Korean portal to scrape. Zigbang works token-free on any IP (no proxy). Naver has richer coverage + 실거래가 but needs a KR residential proxy.",
                        "default": "zigbang"
                    },
                    "targets": {
                        "title": "Targets",
                        "type": "array",
                        "description": "For Zigbang: geohash(es) of the area, e.g. \"wydm6\" (강남). For Naver: map/region URLs (new.land.naver.com/complexes?ms=lat,lng,zoom&a=APT).",
                        "default": [
                            "wydm6"
                        ],
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxListings": {
                        "title": "Max listings per run",
                        "minimum": 1,
                        "maximum": 50000,
                        "type": "integer",
                        "description": "Cost guard. Stops after this many unique listings.",
                        "default": 300
                    },
                    "useResidentialProxy": {
                        "title": "Use KR residential proxy (Naver only)",
                        "type": "boolean",
                        "description": "Naver blocks datacenter IPs — turn this ON (and have a KR residential proxy) for Naver. Leave OFF for Zigbang.",
                        "default": false
                    },
                    "gapAnalysis": {
                        "title": "Run gap-investment (갭투자) analysis",
                        "type": "boolean",
                        "description": "Pair sale vs jeonse per complex/area and surface low-gap opportunities.",
                        "default": true
                    },
                    "enrichMarket": {
                        "title": "Enrich with 시세/실거래가 (Naver only)",
                        "type": "boolean",
                        "description": "Naver-only: one extra call per complex adds asking range, latest actual transaction (실거래가), and 전세가율 to gap opportunities.",
                        "default": false
                    },
                    "maxGapRatioPct": {
                        "title": "Max gap ratio (%)",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Only report opportunities at or below this gap ratio.",
                        "default": 60
                    },
                    "gapSortBy": {
                        "title": "Sort gap opportunities by",
                        "enum": [
                            "gapRatioPct",
                            "gapManwon"
                        ],
                        "type": "string",
                        "description": "gapRatioPct = lowest leverage first. gapManwon = lowest absolute capital first (fixed-budget investors).",
                        "default": "gapRatioPct"
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
