Pythonを用いて新築マンションデータを収集する方法

プロジェクトの背景

新築マンションのデータは、不動産購入者にとっては購入判断の重要な参考資料となり、開発者にとっては競合プロジェクト分析のツールとしても役立ちます。また、不動産エージェントにとっては物件調査前の準備情報としても重要です。

今回は惠民之家を例に、Pythonを用いて惠州の新築マンションデータを一括収集する方法をご紹介します。収集したデータは、物件名、販売価格、主力間取り、発売日、容積率、緑化率を含む41の項目あります。

収集したデータは以下の形式で確認できます。

記事の最後に収集に使用したソースコードをダウンロード可能です。

プロジェクトの目的

惠民之家のホームページURL:

http://www.fz0752.com/

新築マンション一覧URL:

http://www.fz0752.com/project/list.shtml

一覧から任意の物件をクリックし詳細情報を確認できます。

プロジェクトの準備

  • ソフトウェア: PyCharm
  • 外部ライブラリ: requests, fake_useragent, lxml
  • サイトURL: http://www.fz0752.com/

Webページ分析

一覧ページの分析

新築マンション一覧ページを開くと、次へをクリックするとURLが以下のように変化します。

http://www.fz0752.com/project/list.shtml?state=&key=&qy=&area=&danjia=&func=&fea=&type=&kp=&mj=&sort=&pageNO=2

URLのパラメータpageNOがページ番号を表し、qyがエリアIDを表しています。エリアIDとページ番号を組み合わせることで、全ての物件のURLを収集可能です。

詳細ページの分析

物件の詳細ページURL例:

http://newhouse.fz0752.com/fontHtml/html/project/00020170060.html

URL内の00020170060が物件IDです。詳細ページのURLは以下のように生成されます。

http://newhouse.fz0752.com/project/detail.shtml?num=20170060

このIDは元のURLから抽出可能であることが確認できます。

反スクラッチ対策

同一IPアドレスからの繰り返しリクエストはサイトのIPブロックのリスクがあります。本記事ではfake_useragentを使用し、ランダムなUser-Agentヘッダーを生成することでリスクを軽減します。

コードの実装

スクラッチに必要なライブラリをインポートし、エリアIDとページ番号を組み合わせてURLを生成します。エリアIDを指定し、ページ番号を1から50までループさせます。

# -*- coding: utf-8 -*-
import csv
import time
import random
import requests
import traceback
from lxml import etree
from fake_useragent import UserAgent

def main():
    # 46:惠城区,47:仲恺区,171:惠阳区,172:大亚湾,173:博罗县,174:惠东县,175:龙门县
    region_ids = [46,47,171,172,173,174,175]
    for region in region_ids:
        for page in range(1,50):
            url = f'http://www.fz0752.com/project/list.shtml?state=&key=&qy={region}&area=&danjia=&func=&fea=&type=&kp=&mj=&sort=&pageNO={page}'
            response = requests.request("GET", url, headers=headers, timeout=5)
            print(response.status_code)
            if response.status_code == 200:
                content = response.content.decode('utf-8')
                print(f"正在解析エリアID {region} のページ {page}")
                print("-" * 80)
                parser = etree.HTML(content)
                extract_detailed_urls(parser, region)
                # 終了条件のチェック
                num = ''.join(parser.xpath('//*[@id="parent-content"]/div/div[6]/div/div[1]/div[2]/div[1]/div[2]/div[1]/div[1]/a/@href'))
                if len(num) == 0:
                    break

if __name__ == '__main__':
    ua = UserAgent(verify_ssl=False)
    headers = {"User-Agent": ua.random}
    time.sleep(random.uniform(1, 2))
    main()

詳細URLを取得し、各物件の詳細データを収集します。

def extract_detailed_urls(parser, region):
    items = parser.xpath('//*[@id="parent-content"]/div/div[6]/div/div[1]/div[2]/div')
    try:
        for item in items:
            initial_url = ''.join(item.xpath('./div[2]/div[1]/div[1]/a/@href')).strip()
            print(f"初期URL: {initial_url}")
            if len(initial_url) > 25:
                detail_url = f'http://newhouse.fz0752.com/project/detail.shtml?num={initial_url[52:].replace(".html","")}'
            else:
                detail_url = f'http://newhouse.fz0752.com/project/detail.shtml?num={initial_url[15:]}'
            print(f"詳細URL: {detail_url}")
            try:
                fetch_detailed_data(detail_url, region)
            except:
                pass
    except Exception:
        print(traceback.print_exc())

詳細ページのデータ収集を行います。

def fetch_detailed_data(detail_url, region):
    time.sleep(random.uniform(1, 2))
    response = requests.get(detail_url, headers=headers, timeout=5)
    if response.status_code == 200:
        content = response.text
        parser = etree.HTML(content)
        # 各項目のデータ収集(一部例示)
        try:
            status = parser.xpath('//*[@id="parent-content"]/div/div[3]/div[3]/div[1]/div[1]/text()')[0].strip()
        except:
            status = None
        try:
            name = parser.xpath('//*[@id="parent-content"]/div/div[3]/div[3]/div[1]/h1/text()')[0].strip()
        except:
            name = None
        try:
            summary = parser.xpath('//*[@id="parent-content"]/div/div[3]/div[5]/div[2]/div/p[1]/text()')[0].strip()
        except:
            summary = None
        # 他の項目の収集
        data = {
            'status': status,
            'name': name,
            'summary': summary,
            'region': region
        }
        print(data)

収集したデータをCSVファイルに保存します。

def save_to_csv(data):
    try:
        with open('hz_newhouse.csv', 'a', encoding='utf_8_sig', newline='') as file:
            fieldnames = ['status','name','summary','region']
            writer = csv.DictWriter(file, fieldnames=fieldnames)
            writer.writerow(data)
    except Exception:
        print(traceback.print_exc())

収集結果の加工

CSVファイルを読み込んで重複を削除し、Excel形式に変換します。

import pandas as pd

df = pd.read_csv("hz_newhouse.csv")
df = df.drop_duplicates()
df.to_excel("hz_newhouse.xlsx", index=False)

プロジェクトの総括

  • Pythonのスクラッチ技術を用いて、新築マンションデータの収集を効率的に行いました。
  • 収集量を適度に抑え、サイトの負荷を軽減します。

タグ: Python スクレイピング csv Excel

6月14日 20:30 投稿