Redfinは、新しくMLSに掲載された米国の物件の70%を5分以内に更新しています。新着物件の監視や市場分析に使うデータ基盤では、この更新頻度は重要な判断材料になります。一方、継続的に取得しようとすると、CloudflareやRedfin側のレート制限により、403などのブロックが発生することがあります。
私はThunderbitで長年データ抽出ツールの開発に携わってきました。本記事では、HTMLパース、Redfinの隠しAPI、Thunderbitを使ったノーコード方式の3つを取り上げ、必要な技術、取得規模、保守負荷、ブロック対策の違いを整理します。Pythonで細かく制御したい開発者だけでなく、短期案件で構造化データを用意したい非エンジニアも、用途に合う方法を比較できる構成です。あわせて、利用規約やrobots.txt、取得頻度など、実装前に検討すべき条件も説明します。
Redfinとは何か? なぜそのデータが重要なのか?
Redfinは、給与制のエージェントがMLSのフィードから直接物件情報を取り込む、テクノロジー主導の不動産仲介サービスです。米国とカナダの100以上の都市圏、42州をカバーし、月間約5,000万人の訪問者に利用されています。単なる集約型ポータルとは違って、Redfinのデータはエージェントによって検証されており、独自のRedfin Estimate AVMは9,200万件以上の物件をカバーし、市場掲載中物件の中央値誤差はわずか1.85%です。

この「MLS並みの鮮度」「仲介会社による検証品質」「精度の高いAVM」という組み合わせがあるからこそ、不動産投資家、エージェント、PropTechスタートアップ、データアナリストがみんなRedfinデータへのプログラム的アクセスを求めるわけです。この用途でPythonがよく選ばれるのも自然な流れです。requests、BeautifulSoup、Selenium、Playwrightといったスクレイピング周辺のエコシステムが成熟していて、コミュニティの支援も豊富だし、pandasやJupyterとつなげて分析しやすいからです。
なぜPythonでRedfinをスクレイピングするのか?
使い道は、データを必要とする人の数だけあります。Redfinのスクレイピングデータは、典型的には次のように活用されます。
| 対象 | 主な目的 | 利用例 |
|---|---|---|
| 不動産エージェント | リード獲得、市場動向の把握 | 担当地域の新着物件・販売期限切れ物件、競合分析のためのエージェントディレクトリ |
| 不動産投資家 | 案件発掘、キャップレート分析 | 賃貸利回りのスクリーニング、割安物件の検出、毎日の新着アラート |
| PropTechスタートアップ | プロダクト向けデータ基盤 | AVM学習データ、市場ダッシュボード、iBuyer向け取得エンジン |
| データアナリスト | 市場調査、BI | ZIPコード別の中央値価格推移、掲載日数の時系列、売値/掲載価格比率 |
| 卸売業者 / リノベ業者 | 困難物件の追跡 | 値下げ検知、差押え物件、オフマーケット比較物件 |
より大きな流れもこれを裏づけています。今では不動産企業の72%以上が、機会の発見とリスク管理のために予測分析を活用しています。PropTech市場は2025年に470億ドル規模に達し、年平均成長率は16.1%と予測されています。構造化された不動産データは、もはや「あると便利」ではなく、最低限必要な前提条件です。
Redfinから取得できるデータ項目の整理
コードを書く前に、実際に何が取れるのかを把握しておく必要があります。私はRedfinの検索結果ページ、物件詳細ページ、エージェントプロフィールを調べ、さらにreteps/redfinやRedfinPlusといったオープンソースのStingray APIラッパーとも突き合わせました。その結果、ページ種別をまたいで合計117個の異なるフィールドがあることが分かりました。
以下の表は、実装前に取得項目とデータ型を整理するための一覧です。対象ページや表示条件によって取得できる項目は変わるため、必要な列を決めたうえで実際のページと照合すると、セレクター設計を進めやすくなります。
検索結果ページのフィールド
これは掲載カード上で見られる軽量な項目です。完全なJavaScriptレンダリングなしでも取れることが多いです。
| 項目 | データ型 | 備考 |
|---|---|---|
| 物件ID | 数値 | Redfin内部の整数。href内の/home/{id}から抽出 |
| 掲載価格 | 数値 | |
| 住所全文 | テキスト | |
| ベッド数 / バス数 / 面積 | 数値 | 3つの値が順番に並ぶ |
| 物件タイプ | 単一選択 | SFH、Condo、Townhouse、Multi など |
| ステータス | テキスト | Active、Pending、Contingent など |
| 掲載日数 | 数値 | |
| 値下げ表示 | 数値 | 当初価格との差分 |
| メイン写真 | 画像URL | 1カードにつき1枚 |
| Hot Homeバッジ | 真偽値 | |
| オープンハウス日時 | テキスト | |
| 仲介会社情報 | テキスト |
物件詳細ページのフィールド
本当に情報量が多いのは詳細ページです。多くの項目はJavaScriptレンダリング、またはStingray APIが必要になります。
| 項目 | データ型 | 備考 |
|---|---|---|
| Redfin Estimate(市場掲載中) | 数値 | /stingray/api/home/details/avm 経由 |
| Redfin Estimate(非掲載) | 数値 | /stingray/api/home/details/owner-estimate 経由。中央値誤差7.25% |
| 築年数 / リノベーション年 | 数値 | |
| 敷地面積 | 数値 | |
| HOA費 | 数値 | 月額、該当する場合 |
| 固定資産税(年額) | 数値 | |
| 課税評価額 | 数値 | |
| 売買履歴テーブル | テーブル | 価格、日付、イベント種別 |
| 物件説明文 | テキスト | マーケティング用の説明文 |
| 写真URL(カルーセル) | 画像URL群 | 1件あたり20枚以上 |
| 担当エージェント名、電話、メール | テキスト / 電話 / メール | 電話はマスクされていることが多い |
| 学校評価(小・中・高) | 数値 | 学区名も含む |
| Walk / Transit / Bikeスコア | 数値 | |
| 気候リスクスコア | 数値 | 洪水、火災、暑熱、風 |
| 類似の販売中 / 成約済み / 周辺物件 | URL群 | カルーセルデータ |
| 駐車場、ガレージ、暖房、冷房 | テキスト | 設備グループ |
エージェントプロフィールのフィールド
| 項目 | データ型 | 備考 |
|---|---|---|
| エージェント名、写真、仲介会社、プロフィール | テキスト / 画像 | |
| 電話、問い合わせフォーム | 電話 / テキスト | クリックで表示 |
| 現在の掲載件数 | 数値 | |
| 過去12か月の販売件数 / 総取引額 | 数値 | |
| 平均掲載価格/成約価格比率 | 数値 | |
| 星評価 / レビュー数 | 数値 | |
| 経験年数 / ライセンス番号 | テキスト / 数値 |
ThunderbitのRedfinページでAI Suggest Fields機能を使うと、ページ構成に応じて、これらの列やデータ型の候補を自動で提案できます。提案結果は対象ページや表示状態によって異なるため、必要な項目がそろっているかをプレビューで照合し、不足する列だけ追加します。CSSセレクターを一から対応づける作業を減らしたい場合に向く方法です。詳しくは後ほど説明します。
Redfinのボット対策を解剖する(「プロキシを使う」だけではない)
ここはかなり大事なポイントです。多くの解説記事はブロック問題をあいまいに済ませて、「プロキシを買いましょう」で話を終わらせます。でも、それだけでは足りません。Redfinが何を見てスクレイパーを検出しているのかを理解しないままでは、プロキシ代だけが消えて、結局ブロックされたままです。ScrapeOpsはRedfinのボット対策難易度を10段階中7.5と評価し、Scraperlyは中程度(3/5)と分類しています。つまり、「ZillowのようなエンタープライズWAFほど攻撃的ではないが、独自のレート制限とJavaScriptチャレンジに依存している」ということです。
Redfinは多層構造の防御を採用しています。エッジでCloudflare(JSチャレンジ、Turnstile、TLS/JA3フィンガープリント)を使い、さらにRedfin独自のアプリケーション層レートリミッターが動いています。robots.txtにはCrawl-delayはありません。実際の制御はWAF層で行われているからです。
なぜ単純なrequests + BeautifulSoupではRedfinで失敗するのか
デフォルトのヘッダーでRedfinの物件ページにrequests.get()を投げると、典型的には次のどれかになります。
- HTTP 403 — CloudflareのJSチャレンジが解けず、掲載情報の代わりにチャレンジページが返る
- インタースティシャルのチャレンジページ — HTML本文に物件データではなくCloudflareのTurnstileウィジェットが含まれる
- HTTP 200だがHTMLが不完全 —
root.__reactServerState.InitialContext配下に大きなJSON blobはあるものの、事前描画済みの検索カードや価格履歴、学校評価はない
RedfinはReact SSRフレームワークを独自に使っており(Next.jsではありません)、ハイドレーションキーもRedfin固有です。root.__reactServerState.InitialContextの中にReactServerAgent.cache.dataCacheとして掲載データが入っています。これは__NEXT_DATA__でもwindow.__INITIAL_STATE__でもありません。
静かな403の原因として挙げられるものの一つが、Sec-Fetch-*ヘッダーの不足です。Redfin/CloudflareがSec-Fetch-Site、Sec-Fetch-Mode、Sec-Fetch-Dest、Sec-Fetch-Userを検証する構成では、これらがないリクエストがブロックの対象になる場合があります。
回避の実践ガイド:遅延、ヘッダー、プロキシ、セッション
以下に、防御ごとの対処法を整理しました。検知条件や有効な対処法は、アクセス元、対象ページ、リクエスト量、RedfinとCloudflareの設定によって変わります。
| Redfin側の防御 | 仕組み | 検知のサイン | 回避策 |
|---|---|---|---|
| Cloudflare JSチャレンジ | cf_clearanceクッキーを発行するインタースティシャル | 403 + CloudflareのHTML本文 | impersonate="chrome120"のcurl_cffi、ホームページ経由でのセッションウォームアップ、米国の住宅回線プロキシ |
| Cloudflare Turnstile | リスクの高いセッションに表示される対話型CAPTCHA | 403 + Turnstileウィジェット | 環境に応じて、ステルス対応のヘッドレスブラウザと住宅回線プロキシを検討 |
| Cloudflare Error 1020(ASN拒否) | フラグ付きIP/ASNをWAFで遮断 | 403本文に「Error 1020 Access Denied」 | 住宅回線またはモバイルプロキシへ切り替える。データセンターASNは対象環境での結果を検証する |
| TLS/JA3フィンガープリント | ブラウザ以外のTLSスタックを検出 | ヘッダーが適切でも静かに403 | curl_cffiの偽装、または実ブラウザ |
| HTTP/2フィンガープリント | HTTP/2のSETTINGSやHPACK順序を確認 | 静かなブロック | curl_cffiでChromeに近いHTTP/2設定を利用 |
| ヘッダー検証(UA、Sec-Fetch-*) | ブラウザらしいヘッダー一式をチェック | 初回リクエストで403 | Sec-Fetch-Site/Mode/Dest/Userを含むChrome相当のヘッダーと、遷移に合うReferer |
| Cookie/セッション継続 | cf_clearance、RF_BROWSER_IDを追跡 | 深いURLへ直接アクセスするとチャレンジ | 永続セッションを使い、まずホームページへアクセスする |
| アプリ層のレート制限 | IP単位のリクエスト制御 | 429 | 2〜5秒の遅延 + ジッター、指数バックオフ |
| データセンターIPの評判 | 既知のDC ASNを遮断 | 即座の1020/403 | 米国の住宅回線またはモバイルプロキシを候補にする |
| 同時実行検出 | 1つのIPからの並列リクエストを検知 | 急にTurnstileが強化される | IPあたり同時2本以下を目安に検証 |
コミュニティ検証で得られた実践的な目安:
- 安全な間隔:IPあたり2〜3秒に1リクエスト
- 単一のデータセンターIPで20〜30 req/minを超えて続けると、数分以内にチャレンジが発生しやすい
- ソフトなレート制限は、通信を止めると5〜15分で解除されることが多い
- データセンターIP(AWS、GCP、Azure、OVH)のBANは数時間〜数日続く場合がある
これらは保証値ではなく、検証環境ごとの目安です。標準のPython requests(urllib3 + OpenSSL)は、どのブラウザにも一致しないJA3フィンガープリントを生成する場合があります。対策の一つとして、**impersonate="chrome120"付きのcurl_cffi**を使い、Chromeに近いTLS + HTTP/2構成で接続する方法があります。
PythonでRedfinをスクレイピングする3つの方法(どれを選ぶべきか)

3つの方法は、必要な制御範囲、データ形式、保守に使える時間によって向き不向きが変わります。比較表では、導入条件と運用上の境界を整理します。
| 比較項目 | HTMLパース(BS4 + Selenium) | Stingray隠しAPI | Thunderbit(ノーコード) |
|---|---|---|---|
| 初期設定の難易度 | 中(Python環境 + ブラウザドライバー) | 高(エンドポイントのリバースエンジニアリング) | 低(Chrome拡張の導入と列設定) |
| ボット対策リスク | 高(DOMへのアクセスは目立ちやすい) | 中(API風リクエストでも環境によってブロックされる) | 比較的低い(既存のブラウザセッションを使う場合。対象サイトや実行モードで異なる) |
| データ構造の品質 | 中(HTMLが非構造化 → 手動解析) | 高い(事前構造化されたJSON) | 高い場合がある(AIがフィールドと型の候補を提案し、利用者が検証する) |
| メンテナンス負荷 | 高い — レイアウト変更でセレクターが壊れる | 中 — 予告なくエンドポイントが変わる可能性 | 比較的低い — AIがレイアウト変更への対応を支援するが、抽出結果の見直しは必要 |
| スケール | 小〜中規模(プロキシ使用で数百件) | 中〜大規模(数千件、よりクリーンなリクエスト) | 中規模(一部のクラウドスクレイピング構成では50ページ単位。上限はプランや設定で異なる) |
| 向いている人 | 完全な制御を求める開発者 | クリーンなJSONが必要な開発者 | 非エンジニア、短期案件、開発リソースなしで継続的に使いたい人 |
メンテナンスの観点は特に重要です。RedfinはカードDOMを2世代出してきました。旧版は(homecardV2Price)、現行は(span.bp-Homecard__Price--value)です。GitHubのコミュニティIssue履歴を見ると、CSSセレクターの破損はだいたい6〜12か月ごとに起きています。BeautifulSoupのスクレイパーでは、そのたびにセレクターの見直しが必要です。AIベースのフィールド検出はレイアウト変更への対応を支援できますが、項目名、型、欠損値が要件どおりかは運用側で検証します。
始める前に
- 難易度: 中級(方法1・2)、初級(方法3)
- 所要時間: 方法1・2で約30分、方法3で約5分
- 必要なもの:
- Python 3.8以上とpip(方法1・2)
- Chromeブラウザ(全方法)
- Thunderbit Chrome拡張(方法3)
- 大規模スクレイピング用の米国住宅回線プロキシ(方法1・2)
AIでRedfinデータをスクレイピング Get Started Free
方法1: HTMLパース(BeautifulSoup + Selenium)でPythonからRedfinをスクレイピングする
これは「自由度重視」の方法です。セレクターは自分で書き、ブラウザも管理し、エラー処理も自分でやります。
学びは多いですが、いちばん壊れやすい方法でもあります。
ステップ1: Python環境をセットアップする
仮想環境を作成し、必要なライブラリをインストールします。
python -m venv redfin-scraper
source redfin-scraper/bin/activate # Windowsでは: redfin-scraper\Scripts\activate
pip install requests beautifulsoup4 selenium webdriver-manager pandas curl_cffi
ここではcurl_cffiが重要です。Cloudflareにすぐブロックされる標準のPython requestsフィンガープリントではなく、本物のChromeに近いTLSフィンガープリントとしてHTTPリクエストを偽装できるからです。
ステップ2: ブラウザのヘッダーとセッションを設定する
ここで初心者がつまずきやすいです。Redfin/Cloudflareが明示的に検証するSec-Fetch-*を含む、Chrome相当の完全なヘッダーが必要です。
from curl_cffi import requests as curl_requests
HEADERS = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Sec-Fetch-Site": "none",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-User": "?1",
}
session = curl_requests.Session(impersonate="chrome120")
session.headers.update(HEADERS)
# セッションをウォームアップして、cf_clearance と RF_BROWSER_ID のクッキーを取得する
session.get("https://www.redfin.com/")
セッションのウォームアップはかなり重要です。深い物件URLにいきなりアクセスすると(事前のクッキーもRefererもないため)、Cloudflareの評価が下がります。
必ずホームページから始めてください。
ステップ3: Redfinの検索結果をスクレイピングする
セッションが温まったら、都市の検索結果ページを取得し、掲載カードを解析できます。現行のセレクター(2024〜2026年)は次のとおりです。
import time
import random
from bs4 import BeautifulSoup
base_url = "https://www.redfin.com/city/17151/CA/San-Francisco"
listings = []
for page_num in range(1, 6): # 1〜5ページ
url = f"{base_url}/page-{page_num}" if page_num > 1 else base_url
resp = session.get(url)
if resp.status_code != 200:
print(f"ページ {page_num} でブロックされました: HTTP {resp.status_code}")
break
soup = BeautifulSoup(resp.text, "html.parser")
cards = soup.select("[data-rf-test-id='property-card'], a.bp-Homecard")
for card in cards:
price_el = card.select_one("span.bp-Homecard__Price--value")
addr_el = card.select_one("a.bp-Homecard__Address")
stats = card.select("span.bp-Homecard__LockedStat--value")
listing = {
"price": price_el.text.strip() if price_el else None,
"address": addr_el.text.strip() if addr_el else None,
"beds": stats[0].text.strip() if len(stats) > 0 else None,
"baths": stats[1].text.strip() if len(stats) > 1 else None,
"sqft": stats[2].text.strip() if len(stats) > 2 else None,
"url": "https://www.redfin.com" + addr_el["href"] if addr_el else None,
}
listings.append(listing)
# 2〜5秒のランダム遅延
time.sleep(random.uniform(2, 5))
print(f"{len(listings)}件の掲載情報を取得しました")
San Franciscoの掲載物件について、価格、住所、ベッド数/バス数/面積、詳細URLを含む辞書が増えていくはずです。カードが0件ならHTTPステータスコードを確認してください。403ならCloudflareに検知されていて、住宅回線プロキシが必要な可能性があります。
ステップ4: 各物件の詳細ページをスクレイピングする
検索結果で取れるのは基本情報だけです。詳細ページではRedfin Estimate、築年、HOA、売買履歴、エージェント情報、写真が取れます。これらのページはJavaScriptレンダリングが必要なので、Seleniumに切り替えます。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
import time
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_argument("user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
for listing in listings[:10]: # 最初の10件を補完
driver.get(listing["url"])
time.sleep(random.uniform(3, 6)) # JSレンダリング待ち
try:
estimate_el = driver.find_element(By.CSS_SELECTOR, "[data-rf-test-name='avmLdpPrice']")
listing["redfin_estimate"] = estimate_el.text.strip()
except:
listing["redfin_estimate"] = None
try:
year_built = driver.find_element(By.XPATH, "//span[contains(text(),'Year Built')]/following-sibling::span")
listing["year_built"] = year_built.text.strip()
except:
listing["year_built"] = None
driver.quit()
このステップのあと、最初の10件の掲載情報にRedfin Estimateと築年データが追加されているはずです。こういう設備系の入れ子項目ではXPathのほうがCSSより頑丈ですが、それでも壊れやすいことに変わりはありません。DOMが少しでも組み替われば壊れます。
ステップ5: ブロックとエラーに対処する
指数バックオフ付きの再試行ロジックを入れましょう。
import time
def fetch_with_retry(session, url, max_retries=3):
for attempt in range(max_retries):
resp = session.get(url)
if resp.status_code == 200:
return resp
elif resp.status_code in (403, 429, 503):
wait = (2 ** attempt) + random.uniform(1, 3)
print(f"ブロックされました({resp.status_code})。{wait:.1f}秒後に再試行します...")
time.sleep(wait)
else:
print(f"予期しないステータスです: {resp.status_code}")
break
return None
ブロックされた兆候は、本文にCloudflareのHTMLが入ったHTTP 403、明示的なレート制限であるHTTP 429、空のレスポンス本文、またはページ内容内の「Error 1020 Access Denied」です。これらが続くなら、住宅回線プロキシを追加するか、API方式へ切り替えるタイミングです。
方法2: 隠しStingray APIを使ってPythonからRedfinをスクレイピングする
HTMLパースを避け、構造化されたJSONを扱いたい開発者に向く方法です。Redfinのフロントエンドは内部JSON APIの/stingray/api/home/details/*と通信していて、レスポンスは型付きのJSONとして返ってきます。ただし、非公式で文書化されていないエンドポイントのため、仕様変更や利用条件を前提に保守する必要があります。
Redfinの隠しAPIエンドポイントを見つける方法
Chrome DevToolsを開き、NetworkタブでFetch/XHRに絞り込み、任意のRedfin物件ページへ移動します。すると、次のようなエンドポイントへのリクエストが見つかります。
api/home/details/initialInfo— URLからpropertyId、listingIdを解決api/home/details/aboveTheFold— 価格、ベッド数、バス数、面積、写真、ステータス、エージェント、MLS番号api/home/details/belowTheFold— 設備、HOA、税金、駐車場、築年、敷地、履歴api/home/details/avm— 市場掲載中のRedfin Estimateapi/home/details/owner-estimate— 非掲載時のRedfin Estimateapi/home/details/descriptiveParagraph— 物件説明文
賃貸ページでは、rentalId(36文字のUUID)が<meta property="og:image">タグのURLから抽出されます。
Stingray APIで物件データを取得する
大事な注意点があります。StingrayのJSONレスポンスは、CSRF対策として文字列{}&&で始まります。パース前にこの接頭辞を外す必要があります。
import json
from curl_cffi import requests as curl_requests
session = curl_requests.Session(impersonate="chrome120")
session.headers.update(HEADERS)
# セッションをウォームアップ
session.get("https://www.redfin.com/")
# クッキーとproperty IDを得るために物件ページを取得
property_url = "https://www.redfin.com/CA/San-Francisco/123-Main-St-94102/home/12345678"
page_resp = session.get(property_url)
# Stingray APIを呼び出す
api_url = "https://www.redfin.com/stingray/api/home/details/aboveTheFold?propertyId=12345678"
api_resp = session.get(api_url, headers={"Referer": property_url})
# CSRF対策の接頭辞を削除
payload = json.loads(api_resp.text.replace("{}&&", "", 1))
# 構造化データを抽出
listing_data = payload.get("payload", {})
print(json.dumps(listing_data, indent=2))
このレスポンスには、価格は整数、ベッド数/バス数は数値、写真URLは配列、エージェント情報は入れ子オブジェクトという形で、型付きのフィールドが含まれています。BeautifulSoupもCSSセレクターも不要で、推測も要りません。
隠しAPI方式の長所と制約
長所:
- 事前構造化されたJSONで、HTMLパースより扱いやすい
- 1リクエストあたりが速い(ペイロードが小さく、レンダリング負荷がない)
- 適切なヘッダーとセッションを使う構成では、HTMLページを直接取得する方法よりブロックリスクが低い場合がある
制約:
- エンドポイントが予告なく変わる可能性がある。公式ドキュメントはない
robots.txtではワイルドカードユーザーエージェントに対して/stingray/が明示的に禁止されている- 新しいエンドポイントを見つけるにはリバースエンジニアリングが必要
- Cloudflareを避けるため、やはりセッションのウォームアップと適切なヘッダーは必要
ノーコードの代替案: ThunderbitでRedfinをスクレイピングする
Redfinのデータを短時間で表形式にしたい一方で、Pythonスクリプトの開発や保守に時間を割きにくい場合は、ノーコード方式が候補になります。私たちはその用途のためにThunderbitを作りました。公開Webページから構造化データを抽出しやすくするツールで、対応範囲や取得結果は、対象ページ、表示状態、実行モード、設定によって異なります。まず検索結果ページで必要な列が取得できるかを検証してから、サブページや定期運用へ広げます。
ステップ1: ThunderbitをインストールしてRedfinを開く
ChromeウェブストアからThunderbit Chrome拡張をインストールします。Redfinを開き、検索結果ページ、たとえばSan Franciscoの売り物件一覧に移動してください。
ステップ2: 「AI Suggest Fields」をクリックする
ブラウザのツールバーにあるThunderbitアイコンをクリックし、**「AI Suggest Fields」**を選択します。AIが表示中のRedfinページを読み取り、ページ構成に応じて「Address」「Price」「Beds」「Baths」「SqFt」「Property Type」「Listing Photo」などの列候補とデータ型を提案します。
不要な列は消せますし、**「+ Add Column」**をクリックして、欲しい項目を自然文で指定することもできます(例: 「listing agent name」「days on market」など)。
設定済みの列が表形式でプレビューされたら、項目名、型、空欄、サンプル値を見直します。必要な列がそろっていることを確かめてからデータ取得へ進みます。
ステップ3: 「Scrape」をクリックしてデータ取得を始める
**「Scrape」**をクリックすると、Thunderbitが表示中の掲載情報を処理して表に埋め込みます。ページネーションを検出できるページでは続きのページも処理できますが、対象ページの構造や設定によっては範囲の指定や結果の見直しが必要です。
私の検証では、50行のテーブルが約45秒で埋まりました。処理時間はページ構成や通信環境で変わるため、取得後に行数、重複、欠損値を検証してからエクスポートします。
ThunderbitがRedfinのボット対策をどう扱うか
Thunderbitのブラウザ内で実行するモードでは、既存のRedfinクッキー、セッション、ブラウザフィンガープリントを利用します。そのため、サーバー上のPythonスクリプトからrequestsを送る方法より、通常のブラウザ操作に近い条件で取得できる場合があります。ただし、ブロックされないことを保証するものではなく、RedfinやCloudflareの判定、アクセス頻度、ページ状態によって結果は変わります。
公開ページでは、対象サイトとプランの条件が合えば、Thunderbitのクラウドスクレイピングモードで一度に50ページ処理できます。クラウドモードはローカルのブラウザセッションとは実行環境が異なるため、対象ページごとに取得結果とアクセス条件を検証します。
既存のブラウザセッションを利用できる点は利点ですが、利用規約やアクセス頻度の判断まで自動的に解決するものではありません。
ThunderbitでRedfinのサブページをスクレイピングする
検索結果を取ったら、**「Scrape Subpages」**をクリックして、各物件詳細URLからRedfin Estimate、築年、HOA費、エージェント情報、物件写真、売買履歴などの追加項目を取得する設定を作れます。取得できる項目は詳細ページの構造や表示条件によって異なるため、数件の結果で列と値を照合してから対象を広げます。
これは方法1で40行ほど必要だったSeleniumループと同じ流れを、画面上の1クリックを起点に設定するイメージです。ただし、抽出項目の見直しやエラー行の処理まで不要になるわけではありません。
RedfinのDOMがhomecardV2Priceからspan.bp-Homecard__Price--valueに変わった場合も、AIによるフィールド候補の再提案を利用できます。Pythonのセレクターと同様に、変更後の値が正しく対応しているかは再検証します。
AIでRedfinのサブページをスクレイピング Get Started Free
CSVだけで終わらせない:RedfinデータをGoogle Sheets、Airtable、Notionへ出力する
単発の分析であれば、df.to_csv()でファイルを保存する方法でも対応できます。一方、不動産チームが継続的に使う場合は、担当者のPCに保存するだけでなく、更新、共同編集、担当者間の引き継ぎができる保存先を選ぶ必要があります。
Pythonでの出力(gspread + Airtable API)
gspreadでGoogle Sheetsへ:
import gspread
import pandas as pd
from gspread_dataframe import set_with_dataframe
df = pd.DataFrame(listings)
gc = gspread.service_account(filename="service_account.json")
sh = gc.open("Redfin Listings")
ws = sh.worksheet("Sheet1")
ws.clear()
set_with_dataframe(ws, df, include_index=False, resize=True)
# IMAGE()関数で物件写真をセル内表示する
image_col = df.columns.get_loc("image_url") + 1
for row_idx, url in enumerate(df["image_url"], start=2):
ws.update_cell(row_idx, image_col, f'=IMAGE("{url}")')
注意: Sheetsには1スプレッドシートあたり1,000万セルという上限があり、APIにもプロジェクトごとに1分あたり300回の読み取り + 300回の書き込み制限があります。数十行を超える場合は、1セルずつループするのではなくws.batch_update()を使ってください。
pyairtableでAirtableへ:
2024年の重要な変更点として、Airtableは2024年2月1日に従来のAPIキーを廃止しました。今はPersonal Access Token(PAT)を使う必要があります。api_key=...と書かれた古いチュートリアルは動きません。
from pyairtable import Api
api = Api("patXXXXXXXXXXXXXX.yyyyyyyyyyyyyyyyyyyy")
table = api.table("appBaseId123", "Redfin Listings")
records = [
{
"Address": row["address"],
"Price": row["price"],
"Beds": row["beds"],
"Photo": [{"url": row["image_url"]}], # Airtableが取得して再ホストする
}
for row in listings
]
created = table.batch_create(records, typecast=True)
Airtableのレート制限はベースごとに1秒あたり5リクエストで、違反すると30秒間ロックされます。添付フィールドには[{"url": ...}]形式を渡せます。対応する添付フィールドでは、Airtable側のサーバーがURLを取得して自社CDNで再ホストし、サムネイルを生成します。URLの公開状態やフィールド設定によって結果が変わるため、作成後の添付ファイルを照合します。
Thunderbitでの出力(Google Sheets / Airtable / Notionへ1クリック)
Thunderbitは、対応する出力先でGoogle Sheets、Airtable、Notionへの1クリック出力機能を利用できます。特に私が気に入っているのは、対象のフィールド形式と権限設定が合う場合に、物件写真をNotionやAirtableへインライン画像としてアップロード・表示できる点です。=IMAGE()の設定を個別に作らず、「Export to Airtable」からサムネイル付きの物件データベースを用意できます。出力後は、画像の欠損、URLの有効性、列の型を見直します。
物件写真を見ながら候補を絞り込む不動産チームでは、CSVだけで共有する場合よりも、一覧性を高めやすい方法です。
Redfinのスクレイピングは合法なのか? 利用規約、robots.txt、判例から見る
私は弁護士ではないので、これは法的助言ではありません。ただ、データ抽出の世界に長くいる立場から言えるのは、「合法なのか?」という問いこそが誰もが気にするのに、多くの解説が避けて通る論点だということです。
Redfinのrobots.txt
Redfinのrobots.txtはかなり細かいです。要点は次のとおりです。
- 完全にブロックされているボット:
peer39_crawler/1.0、AmazonAdBot、FireCrawlAgent— RedfinはLLM時代に広く使われるスクレイピングサービスを名指ししています - ワイルドカードの
User-agent: *に対するDisallowの主な対象:/stingray/(内部API名前空間全体)、/myredfin/、/api/v1/rentals/、/api/v1/properties/、/owner-estimate/ - どのユーザーエージェントにも**
Crawl-delay:指定なし** - 50以上のサイトマップを宣言 — サイトマップはURL列挙のなかで、WAFへの負荷がいちばん低い方法です
Redfinの利用規約
第2.3.5条には次のようにあります。「事前に明示的な書面許可を得ていない限り、いかなる目的・手段によっても、サービスを自動的にクロールまたは問い合わせてはなりません。」
これはbrowsewrap型の同意です。つまり、クリック同意ではなく、継続利用による同意とみなす形式です。米国の裁判所は歴史的に、実際の通知を受けていないユーザーに対するbrowsewrapの強制には慎重でした(Nguyen v. Barnes & Noble, 9th Cir. 2014 参照)。
関連判例(要点のみ)
- Van Buren v. United States(最高裁、2021年): CFAAの「exceeds authorized access」は「ゲートが開いているか閉じているか」のテストで判断される。開いているドアを好ましくない目的で使うことは、連邦法上のハッキングではない。
- hiQ Labs v. LinkedIn(第9巡回、2022年): 公開データのスクレイピングはCFAA違反ではない。ただしhiQは、LinkedInにアカウント登録して「I agree」を押していたため、契約違反を理由に最終的に50万ドルを支払って和解した。
- Meta Platforms v. Bright Data(N.D. Cal., 2024年1月): 裁判所はBright Dataに略式判決を認めた。ログアウト状態で公開データをスクレイピングする行為は、Bright DataをMetaの規約に拘束される「ユーザー」とはしなかった。
- X Corp. v. Bright Data(N.D. Cal., 2024年5月): Alsup判事はXの請求を棄却し、公開コンテンツの複製を制御しようとする州法上の請求は著作権法により排除されると判断した。
実務上の指針
- 取得対象は公開アクセス可能なデータに限定し、アカウント登録後のページを扱う場合は、クリックラップ契約やアクセス権限を含む追加のリスクを検討します
- 対象サイトのレート制限とサーバー負荷に配慮する — 過度な取得量は、動産侵害(trespass to chattels)などの主張に関係する可能性があります
- 生データや写真を大規模に再配布しない — CoStar v. Zillow訴訟(2025年7月提訴、損害額は10億ドル超の可能性)は、写真の著作権がかなり重要だという警告です
- Thunderbitのブラウザベースの方法は、自分の認証済みセッション内で動くため、ヘッドレスのデータセンターボットよりも通常のブラウザ操作に近い条件で実行できます。ただし、この技術的な違いだけで利用規約、著作権、契約上のリスクが解消されるわけではありません。ライセンス付きAPIが利用できる場合は、契約範囲と用途を比較します
コツとよくある落とし穴
抽出ツールを作り、何千人ものユーザーが不動産サイトをスクレイピングする様子を見てきた中で得た、実践的な教訓をいくつか紹介します。
- セッションのウォームアップを行う。 深いURLへ移動する前に
redfin.com/を開きます。事前のセッション情報がない状態で深いURLへ直接アクセスすると、Cloudflareチャレンジが発生する場合があります。 - User-Agentは現実的にローテーションする。 1種類だけを使わず、現在のChrome/Firefox UAを5〜10個ほど回してください。ただし、回しすぎも逆効果です(毎回UAが違うと不自然です)。
- 物件IDで重複排除する。 Redfinのページネーションは重複が出ることがあります。各掲載URLから
/home/{id}を取り出して、拡張前に重複排除してください。 - 可能ならピーク時間帯は避ける。 私の環境では、米国時間の深夜〜早朝にWAFの監視が比較的ゆるいケースがありましたが、一般化できる条件ではありません。時間帯ごとの403・429発生率を記録して判断します。
- 429が出たら指数的に引く。 すぐ再試行すると、ソフトな制限がハードなIP BANにエスカレートしやすくなります。
- 大規模案件(1,000ページ以上)なら、住宅回線プロキシの予算を確保する。 データセンターIP(AWS、GCP、Azure、OVH)はCloudflareのASN評判システムでブロック対象になりやすく、Error 1020が早い段階で発生する場合があります。
Redfinスクレイピング方法の選び方
選択基準は、必要な制御範囲、データ形式、取得規模、保守に使える時間です。
HTMLパース(BeautifulSoup + Selenium): 完全な制御を求め、CSSセレクターの保守に抵抗がなく、RedfinのDOM変更に合わせてコードを更新できる開発者向けです。6〜12か月ごとというコミュニティ上の目安だけに頼らず、取得件数や欠損率を監視して見直します。
隠しStingray API: クリーンで構造化されたJSONが必要で、未公開エンドポイントのリバースエンジニアリングと仕様変更への対応ができる開発者向けです。HTMLパースより保守しやすい場合がありますが、予告なく変わる可能性があります。しかも/stingray/はrobots.txtで明示的に禁止されています。
Thunderbit(ノーコード): 非エンジニア、短期案件、そして開発リソースなしで継続的にRedfinデータを使いたいチーム向けです。AIがレイアウト変更への対応を支援し、対応するページではサブページのデータを追加できます。Google Sheets、Airtable、Notionへの出力も、プラン、権限、対象フィールドの条件に応じて利用できます。単発のCSVではなく、チームで更新できる物件データベースを作りたい場合に、比較しやすい選択肢です。
どの方法を選ぶにせよ、始める前にRedfinのボット対策を理解し、必要な項目を把握し、チームのワークフローに合う出力形式を選び、法的ガイドラインの範囲内で進めてください。
ノーコードで試す場合は、Thunderbitの無料プランの現行条件を見たうえで、まず数ページを対象に、行数、欠損値、重複、出力形式を検証します。ページ構成や件数によっては数分で結果を確認できます。Pythonで進める場合は、上のコード例を出発点にし、現在のセレクター、アクセス頻度、プロキシ要否をテスト環境で見直します。
FAQ
Redfinに公開APIはありますか?
記事執筆時点では、Redfinは公式の公開APIを提供していません。隠しStingray API(/stingray/api/home/details/*)は構造化されたJSONを返し、Redfin自身のフロントエンドでも使われていますが、非公式で未文書、予告なく変更される可能性があり、Redfinのrobots.txtでも明示的に禁止されています。PyPI上のreteps/redfinのようなオープンソースラッパーでPythonからアクセスできますが、利用条件、保守負荷、ブロックの可能性を理解したうえで判断してください。
PythonなしでRedfinをスクレイピングできますか?
はい。ThunderbitはAI搭載のChrome拡張で、ブラウザ内で実行するモードでは既存のセッションを利用できます。この構成はサーバーから直接リクエストする方法より通常のブラウザ操作に近い場合がありますが、ブロック回避を保証するものではありません。Redfinを開いて「AI Suggest Fields」で列候補を作成し、数件の取得結果を検証してから、対応する形式でExcel、Google Sheets、Airtable、Notionへ出力します。代替手段として、他のノーコードスクレイピングツールや、あらかじめ用意されたデータセット提供サービスも市場にはあります。
Redfinのサイト構成はどのくらいの頻度で変わりますか?
GitHubのコミュニティIssue履歴を見ると、CSSセレクターの破損はだいたい6〜12か月ごとに起きています。RedfinはカードDOMを2世代出していて、旧版は(homecardV2Price、homeAddressV2)、現行は(bp-Homecard__Price--value、bp-Homecard__Address)です。成熟したスクレイパーは両方を順番に試します。
ThunderbitのようなAIベースのツールでは、CSSセレクターではなく内容を使ったフィールド検出により、リンク先で説明されている自動的に適応する仕組みを利用できます。ただし、変更後の列、型、欠損値が正しいかは再検証が必要です。
Redfinをスクレイピングするのに最適なプロキシは何ですか?
大規模スクレイピングでは、米国の住宅回線プロキシが候補になります。コミュニティのベンチマークでは成功率は約80%ですが、対象ページ、IPプール、頻度、測定方法によって変わる目安です。データセンタープロキシはCloudflareのError 1020に早い段階で引っかかる場合があり、AWS、GCP、Azure、OVHのIP帯も環境によってブロック対象になります。モバイルプロキシは成功率が高くなる場合がありますが、コストは5〜10倍かかります。
小規模な個人利用のスクレイピング(100ページ未満)なら、適切なヘッダー + curl_cffiの偽装 + 2〜5秒の遅延で、プロキシなしでも動くことがあります。
Redfinから成約済みや非掲載の物件データをスクレイピングできますか?
成約済み物件データや、非掲載のRedfin Estimate(中央値誤差7.25%)は、対象ページに表示されている場合や対応するエンドポイントが利用できる場合に、同じスクレイピング手法で取得できます。対象によって項目は少し異なり、非掲載ページでは売却価格、売却日、物件履歴、owner-estimateエンドポイントが使えますが、現在の掲載価格、掲載日数、オープンハウス情報はありません。非掲載時のStingray APIエンドポイントはapi/home/details/owner-estimateで、api/home/details/avmではありません。
RedfinスクレイピングにThunderbitを試す Get Started Free
さらに学ぶ


