Real estate data scraping - collect property listings with Python and proxies - DataImpulse

不動産データのスクレイピングとは、不動産プラットフォーム、投資家、プロップテックチームが、オンライン上に散在する物件情報を、価格、住所、寝室数、浴室数、平方フィート、市場全体のトレンドを含む構造化データセットに変換する手法です。本ガイドでは、Pythonを使って物件リスティングを堅牢にスクレイピングする方法、特にサイトに埋め込まれた構造化データの解析方法を紹介します。さらに、地域をまたぐ収集を継続するためのアンチボット対策とプロキシ設定も解説します。

私はAndrii Byzovです。AIネイティブなフラクショナルCMOとして、Webデータパイプラインを構築しています。以下では、収集できる不動産データ、法的な境界線、検索結果ページと個別リスティング向けの実用的なコード、地域精度を高めてブロックを受けにくくする住宅用プロキシについて説明します。


重要なポイント

  • リスティング情報の多くは公開情報であり、比較的リスクが低い:価格、寝室数/浴室数、sqft、物件タイプなど。ただし、住所、写真、説明文、エージェント名は個人情報または著作権保護の対象となる可能性があるため、再利用前に確認してください。
  • 壊れやすいCSSセレクタではなく、利用できる場合は埋め込みJSONを解析してください。 多くの不動産サイトはJSON-LD(schema.org)としてリスティングデータを公開しています。利用可能な場合はそれを解析し、ない場合のみHTMLカードにフォールバックしてください。
  • 一部のサイトは地域に応じてパーソナライズされます。検索結果、空き状況、言語、通貨などが変わるため、ジオターゲティング対応プロキシは特定の市場に合わせるのに役立ちます。
  • 大手ポータルは防御が強固です。 Zillow、Realtor.com、Rightmoveなどはレート制限を設け、データセンターIPをすばやく検知します。ローテーション型の住宅用プロキシは役立ちますが、アクセスやコンプライアンスを保証するものではありません。
  • エージェントや所有者の連絡先情報は個人データです。 パイプラインに含めないでください。収集するのは人物情報ではなく、物件情報です。

どのような不動産データをスクレイピングできますか?

物件リスティングには、豊富な構造化データが含まれています。一般に収集する価値がある項目と、慎重に扱うべき項目は次のとおりです。

  • リスクの低い物件情報:価格、寝室数、浴室数、延床面積、敷地面積、物件タイプ、築年、掲載ステータス、掲載日数、価格履歴。
  • 慎重に扱う、または避けるべきもの:正確な住所、写真や全文説明(著作権で保護されていることが多い)、エージェント/所有者の連絡先情報(個人データ)。公開されているからといって、このデータを自動的に再利用できるわけではありません。

不動産スクレイピングは合法ですか?

公開されている物件情報の収集は、一般に正当化しやすく、価格インテリジェンスやプロップテックの基盤にもなっています。ただし、公開されているからといって、データが自動的に非個人情報になったり、再利用可能になったりするわけではありません。プライバシー、著作権、データベース権、契約、現地法を確認してください。実務上は、ログインが必要なページをスクレイピングしないこと、個人データ(エージェント/所有者の詳細)を含めないこと、著作権で保護された写真や説明文を再公開しないこと、各サイトの利用規約とrobots.txtを尊重すること、アクセス制御やCAPTCHAを回避しないことです。大手ポータルは自動アクセスを制限しているため、そのような場合は公式API、ライセンス付きデータフィード、または書面による許可を優先してください。全体の枠組みについては、Webスクレイピングが合法かどうかに関する当社ガイドをご覧ください。これは一般的な情報であり、法的助言ではありません。


ステップ 1:環境をセットアップする

不動産スクレイパーに必要なライブラリをインストールします。静的なページやJSON-LDを提供するサイトはrequestsで動作しますが、ZillowのようにJavaScriptが多用されているポータルにはヘッドレスブラウザが必要です。



# Core libraries for real estate data scraping
# requests -> fetch pages | beautifulsoup4 + lxml -> parse HTML/JSON
pip install requests beautifulsoup4 lxml

# Many listing sites are JavaScript-heavy and well defended (e.g. Zillow).
# For those, add a headless browser:
pip install playwright && playwright install chromium







ステップ 2:物件一覧ページを取得する

適切なヘッダーを設定し、住宅用プロキシ経由で検索結果ページ(都市の売出中住宅のURL)を取得します。これにより、物件一覧を対象市場に合わせられます。



import requests

HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                         "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
           "Accept-Language": "en-US,en;q=0.9"}

# Route through residential proxies for geo-accurate listings (see Step 5)
proxies = {"http":  "http://LOGIN:[email protected]:823",
           "https": "http://LOGIN:[email protected]:823"}

def get_html(url):
    r = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30)
    r.raise_for_status()
    return r.text

html = get_html("https://example-realestate.com/homes/for_sale/CityName/")
















ステップ 3:埋め込みJSONから物件情報を抽出する

利用できる場合、物件情報をスクレイピングする最も信頼性の高い方法は、サイトにすでに埋め込まれている構造化データを読み取ることです。多くの不動産プラットフォームでは、物件の価格、住所、URL を含む application/ld+json(schema.org)が用意されており、CSSクラス名とは異なり、デザインが変更されても安定しています。すべてのサイトがこれを公開しているわけではなく(一部のサイトはJSアプリの状態にデータを保持しています)、各サイトで確認し、利用できない場合はステップ 4 でカードを解析する方法にフォールバックしてください。



import json
from bs4 import BeautifulSoup

LISTING_TYPES = ("Residence", "Product", "RealEstateListing", "Offer",
                 "SingleFamilyResidence", "Apartment", "House")

def listings_from_jsonld(html):
    """Where available, sites embed listing data as JSON-LD (schema.org).
    Parsing it is more stable than CSS classes — but not every site has it,
    so fall back to the cards in Step 4 when this returns nothing."""
    soup = BeautifulSoup(html, "lxml")
    nodes, out = [], []
    for tag in soup.find_all("script", type="application/ld+json"):
        try:
            data = json.loads(tag.string or "{}")
        except json.JSONDecodeError:
            continue
        stack = data if isinstance(data, list) else [data]
        while stack:                      # flatten @graph / ItemList / nested
            n = stack.pop()
            if not isinstance(n, dict):
                continue
            if "@graph" in n:
                stack.extend(n["@graph"])
            if n.get("@type") == "ItemList":
                stack.extend(x.get("item", x) for x in n.get("itemListElement", []))
            nodes.append(n)
    for n in nodes:
        types = n.get("@type", "")
        types = types if isinstance(types, list) else [types]
        if any(t in LISTING_TYPES for t in types):
            offers = n.get("offers") or {}
            if isinstance(offers, list):
                offers = offers[0] if offers else {}
            out.append({"name": n.get("name"),
                        "price": offers.get("price") or n.get("price"),
                        "address": n.get("address"),
                        "url": n.get("url")})
    return out

print(listings_from_jsonld(html)[:3])









































ステップ 4:リスティングカードを解析する(フォールバック)

サイトが適切なJSON-LDを公開していない場合は、検索結果カードから価格、住所、寝室数、浴室数、面積(sqft)、リスティングリンクを直接解析します。セレクタはサイトによって異なり、頻繁に変わるため、DevToolsで確認してください。



def parse_listing_cards(html):
    """Fallback: parse the search-results cards directly. Selectors differ by
    site and change often — confirm the current ones in DevTools."""
    soup = BeautifulSoup(html, "lxml")
    cards = []
    for card in soup.select("[data-test='property-card']"):
        def t(sel):
            el = card.select_one(sel)
            return el.get_text(strip=True) if el else None
        cards.append({
            "price":   t("[data-test='property-price']"),
            "address": t("[data-test='property-address']"),
            "beds":    t("[data-test='property-beds']"),
            "baths":   t("[data-test='property-baths']"),
            "sqft":    t("[data-test='property-sqft']"),
            "url":     (card.select_one("a[href]") or {}).get("href"),
        })
    return cards


















ステップ 5:ページネーションを処理する

1ページ分の結果だけでは、十分なデータセットとはいえません。結果がなくなるか上限に達するまでページをループし、レート制限に引っかからないようリクエストの間隔を調整します。



import time, random

def scrape_all_pages(base_url, max_pages=10):
    """Page through search results until empty or max_pages."""
    all_listings = []
    for page in range(1, max_pages + 1):
        html = get_html(f"{base_url}?page={page}")
        cards = parse_listing_cards(html)
        if not cards:        # no more results -> stop
            break
        all_listings.extend(cards)
        time.sleep(random.uniform(1, 3))   # pace requests
    return all_listings













ステップ 6:プロキシを使用して、地域精度を高め、ブロックを受けにくくする

プロキシなしでは、不動産 Web スクレイピングを難しくする要因が 2 つあります。まず、location です。一部のサイトでは、地域に応じて検索結果、空室状況、言語、通貨がパーソナライズされるため、特定の市場に合わせるには、その地域の IP からクエリを送信する必要があります。次に、blocking です。大手ポータルはデータセンター IP を素早く検知し、厳格にレート制限をかけます。DataImpulse residential proxies($1/GB、ローテーション対応、都市/州ターゲティングに対応した 195 か国)は、その両方に役立ちます。適切な市場の実ユーザー IP を利用することで、IP ベースのブロックを軽減できます。ただし、アクセスが保証されるわけではなく、各サイトのルールに従う必要があります。リクエストをこれらのプロキシ経由で送信し、収集したデータをエクスポートします。



import time, random, json, csv

# Listings differ by region: route through a proxy in the target market so
# prices, availability, and currency match what a local user sees.
proxies = {
    "http":  "http://LOGIN__cr.us;sid.re1:[email protected]:823",
    "https": "http://LOGIN__cr.us;sid.re1:[email protected]:823",
}

listings = scrape_all_pages("https://example-realestate.com/homes/for_sale/CityName/")

with open("listings.json", "w", encoding="utf-8") as f:
    json.dump(listings, f, indent=2, ensure_ascii=False)
if listings:
    with open("listings.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=listings[0].keys())
        w.writeheader(); w.writerows(listings)

















よくある質問

不動産データのスクレイピングは合法ですか?

公開されている非個人の物件データ(価格、住所、ベッド数、sqft)の収集は、プロップテックでは広く受け入れられている標準的な手法です。個人データ(エージェント/所有者の連絡先)は避け、ログインの背後にある情報をスクレイピングせず、著作権で保護された写真や説明文を再公開せず、サイトの利用規約とrobots.txtを尊重してください。大手ポータルは自動アクセスを制限しているため、商用利用では許可を得るか、専門家に相談してください。これは一般的な情報であり、法的助言ではありません。当社のWebスクレイピングの合法性ガイドをご覧ください。

物件リスティングをスクレイピングする最適な方法は何ですか?

サイトにすでに埋め込まれている構造化データを解析してください。ほとんどの不動産プラットフォームは、ページHTML内にJSON-LD(schema.org)としてリスティングを配信しており、リデザインのたびに変わるCSSクラス名を追いかけるよりもはるかに安定しています。クリーンなJSONが利用できない場合にのみ、リスティングカードの解析にフォールバックしてください。

不動産サイトをスクレイピングするにはプロキシが必要ですか?

大量に収集する場合は、通常必要です。大手ポータル(Zillow、Realtor.com、Rightmove)はレート制限を設け、データセンターIPをすばやく検知します。また、一部のリスティングは地域に依存します。ターゲット市場のローテーション型住宅プロキシは、地域に即したデータを取得し、IPベースのブロックを減らすのに役立ちます。ただし、アクセスを保証したり、スクレイピングをコンプライアンスに準拠させたりするものではありません。

ZillowやRealtor.comを直接スクレイピングできますか?

これらは公開されていますが、強固に保護されており、JavaScriptでレンダリングされ、利用規約では自動アクセスが制限されています。これらのポータルについては、防御策を回避するのではなく、公式API、ライセンス済みデータフィード、または書面による許可を優先してください。公開データを収集する場合は、公開されている物件情報だけを取得し、ログアウトした状態を保ち、アクセス制御をバイパスせず、リクエスト間隔を調整してください。当社のZillowに最適なプロキシガイドをご覧ください。

どのような不動産データを収集できますか?

主に公開されている物件情報です。価格、寝室数、浴室数、床面積、敷地面積、物件タイプ、ステータス、掲載日数、価格履歴などが該当します。ただし、公開されているからといって、そのデータが自動的に非個人データまたは再利用可能なデータになるわけではありません。正確な住所、写真、説明文、エージェント/所有者の詳細にはプライバシーや著作権に関する問題が生じる可能性があるため、保存または再公開する前に評価してください。


まとめ

不動産データのスクレイピングは、散在する物件情報を活用可能なデータセットに変換します。信頼性の高い手順は、可能な場合は埋め込まれたJSON-LDを読み取り、それができない場合はカードの解析にフォールバックし、検索結果をページ送りで取得し、すべてを地域ターゲティングされた住宅用プロキシ経由で送信することです。これにより、地域に即したデータを取得しやすくなり、ブロックも受けにくくなります。公開されている非個人の物件情報に限定すれば、正当化しやすい範囲にとどまれます。インフラについては、不動産データに最適なプロキシおよび、より広範なWebスクレイピングに最適なプロキシガイドをご覧ください。

最終更新日: June 25, 2026.




Share article: