跳到正文
DEV Community· Norbert Sobala·· 4 小時前AI 評分42

如何用 Python 和 pandas 抓取 OLX.pl 公寓房源的每平方米價格

How to scrape OLX.pl apartment listings with price per m into Python and pandas

AI 導讀

這篇教學示範如何透過 Apify 的 OLX Real Estate Scraper 抓取 OLX.pl 公寓房源,並用 Python 和 pandas 整理及分析每平方米叫價。以 Kraków 的 50 個出售房源為例,執行需時 5.5 秒,中位每平方米價格為 16,147 PLN。Scraper 亦可監察新房源及價格變動,並透過 Apify Console 設定排程。

正文

Polish property portals don't offer a public export button, and OLX.pl is one of the biggest sources of apartment listings in the country. If you want asking prices per square metre for a district, or a daily feed of fresh listings for an agency CRM, you need the data in a table first.

This guide uses the OLX Real Estate Scraper on Apify. I ran it while writing this post with a small search: 2-room apartments for sale in Kraków, capped at 50 listings. The run finished in 5.5 seconds. Every number below comes from that dataset.

OLX real estate data from Kraków in a table

What you get per listing

One listing becomes one row. These are the fields I got back:

Field Example from the run Notes
title 2-room flat ad title As written by the advertiser
price, currency 607034, PLN Asking price
pricePerM2 16147.0 Calculated by the scraper from price and area
areaM2 40.0 Floor area
rooms "2 pokoje" Polish label from OLX
floor "5" Can be null
market Pierwotny / Wtórny Primary (developer) or secondary market
buildingType Blok, Apartamentowiec, Kamienica Block, apartment building, tenement
furnished Tak / Nie Yes / No
isBusiness true Agency or developer vs private owner
sellerName (empty in my run) Treat as personal data, see below
city, district, region Kraków, Podgórze, Małopolskie District was missing on 3 of 50 rows
lat, lon 50.09, 19.93 Map coordinates
createdTime ISO timestamp When the ad was posted
photos, description, url Description comes as HTML

In monitoring mode two extra fields appear: changeType (new listing or price change) and previousPrice.

What the 50 Kraków listings looked like

It's a small sample from one morning, so don't read too much into it.

  • Median asking price: 607,034 PLN. Cheapest 420,000, most expensive 1,210,023.
  • Median price per m²: 16,147 PLN. The range was wide, from 11,598 to 27,900.
  • Area: 30 to 69 m², median 40.
  • 22 listings were primary market and 28 secondary. Primary flats had a median of 16,650 PLN/m², secondary ones 15,040.
  • 43 of 50 ads came from businesses. Only 7 were private owners.
  • Podgórze had the most listings (9), then Prądnik Biały (7).

That 43 to 7 split is something you only notice once the data is in a dataframe. If you build lead lists for an agency, the ownerType filter matters a lot.

The main input options

The input form is in Polish, because the actor was built for Polish agencies. The values you will touch most:

  • subcategory: mieszkania (apartments), domy (houses), dzialki (plots), biura-lokale (offices and retail), garaze-parkingi (garages), stancje-pokoje (rooms).
  • transaction: sprzedaz (sale), wynajem (rent) or zamiana (swap).
  • city plus radiusKm (0, 5, 10, 15, 30, 50, 75 or 100). City Kraków with 30 km picks up the surrounding towns too, which helps for houses and plots.
  • district: optional, narrows to one district.
  • priceMin, priceMax, areaMin, areaMax and for plots plotAreaMin, plotAreaMax.
  • rooms: list with one, two, three, four (four means 4 or more).
  • buildingType, floor, market (primary / secondary), furniture.
  • ownerType: private or business.
  • postedWithin: 24h, 7d, 30d or all.
  • maxItems: a cost cap per run. Default is 500.
  • searchUrls: paste search links copied from OLX.pl. They override every filter above.

Option 1: no code

  1. Open the actor page and click Try for free. You need a free Apify account.
  2. Pick the category and transaction type. I used apartments for sale.
  3. Type the city and choose room count. Set Max items to something small for the first try, I used 50.
  4. Click Start. My run took a few seconds.
  5. Open the Output tab and export to CSV, Excel or JSON.

The input form with filters for category, city and price

If you already have a search open on OLX with all the filters you like, copy its URL into the advanced searchUrls field. Faster than rebuilding it field by field.

Option 2: Python with apify-client and pandas

Install the two packages:

pip install apify-client pandas

Then run the actor and load the results. Your API token is under Settings > API & Integrations in the Apify Console. Keep it in an environment variable.

import os
import pandas as pd
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run_input = {
    "subcategory": "mieszkania",
    "transaction": "sprzedaz",
    "city": "Kraków",
    "rooms": ["two"],
    "maxItems": 50,
}

run = client.actor("flarewow/olx-nieruchomosci-pl").call(run_input=run_input)
items = client.dataset(run["defaultDatasetId"]).list_items().items

df = pd.DataFrame(items)
df = df.drop(columns=["sellerName", "description", "photos"], errors="ignore")

print(len(df), "listings")
print("median price/m2:", df["pricePerM2"].median())
print(df.groupby("market")["pricePerM2"].median().round(0))
print(df["district"].value_counts().head(5))

df.to_csv("krakow_2_rooms.csv", index=False)

On my dataset this printed 50 listings, a median of 16,147 PLN/m² and the primary vs secondary split shown earlier. I drop sellerName right after loading because I don't need it for price analysis and I'd rather not store it.

A quick per-district view is one line more:

(df.dropna(subset=["district"])
   .groupby("district")
   .agg(listings=("id", "count"), median_m2=("pricePerM2", "median"))
   .sort_values("listings", ascending=False))

With 50 rows most districts have 2 to 4 listings, so raise maxItems before you draw conclusions per district.

Scheduling and monitoring new listings

The part agencies use most is monitoring. Set monitorNew to true and the actor remembers which listings it has already seen for that search. The first run builds the baseline. After that, each run returns only listings that are new since the last one. With detectPriceChanges on (the default) it also flags price drops and rises on listings it already knows, with the old price in previousPrice.

To make that useful, add a schedule in the Apify Console (Schedules tab): hourly for a busy city, daily for a small town. From there the results can go to Google Sheets, Slack, Make, Zapier, n8n or a webhook. The actor also works through the Apify MCP server, so you can call it from Claude, ChatGPT or Cursor.

Pricing

The actor is pay per event. Current prices per 1,000 scraped listings:

Apify plan Per 1,000 listings Per 1,000 new listings (monitoring)
Free $8 $15
Bronze $6 $12
Silver $5 $10
Gold and above $4 $8

Price changes in monitoring mode cost the same as a scraped listing. There is also a $0.05 charge per run start, and the first 100 listings are free for a new user. So a 50-listing test like mine fits inside the free allowance.

Responsible use

The scraper reads public listing data through the same JSON API the OLX app uses. Some ads come from private owners, and fields like sellerName or the free-text description can contain personal data. Under GDPR (RODO in Poland) you need a legal basis to store and process that. For price research, drop those columns right away, as in the code above. For lead generation, the ownerType: business filter keeps you to agencies and developers. Keep maxItems reasonable and don't hammer the site with schedules every few minutes.

This tutorial was written with AI assistance and checked by the author. All numbers come from a real run of the scraper.

來源:DEV Community · dev.to