[파이썬 크롤링/부동산 데이터] scrapy를 이용하여 부동산 공공 데이터 DB에 저장하기

| 들어가기 전에


GIT 저장소


지금 포스팅은 국토교통부에서 제공하는 부동산 공공데이터 API를 사용합니다. 아래 포스팅을 보시고 먼저 부동산 공공데이터 API를 신청해주시길 바래요!


[유용한 정보들] - 국토교통부 공공데이터 부동산 실거래가 API 신청 방법



이전 포스팅


[파이썬/파이썬 웹 크롤링 - 부동산 공공데이터] - [파이썬 크롤링/부동산 데이터] 스크래피(scrapy) startproject로 초기 프로젝트 구성하기

[파이썬/파이썬 웹 크롤링 - 부동산 공공데이터] - [파이썬 크롤링/부동산 데이터] scrapy를 이용한 부동산 공공 데이터 간단하게 받아오기

[파이썬/파이썬 웹 크롤링 - 부동산 공공데이터] - [파이썬 크롤링/부동산 데이터] scrapy를 이용한 부동산 공공 데이터 파싱 및 추출하기

[파이썬/파이썬 웹 크롤링 - 부동산 공공데이터] - [파이썬 크롤링/부동산 데이터] scrapy를 이용한 부동산 공공 데이터 저장하기(csv/excel)

[파이썬/파이썬 웹 크롤링 - 부동산 공공데이터] - [파이썬 크롤링/부동산 데이터] scrapy를 이용하여 재귀적으로 부동산 공공 데이터 연속적으로 가져오기 및 excel 저장



포스팅에 있는 내용을 따라하기 위해서는 sqlalchemy 패키지를 설치해야합니다. sqlalchemy는 쉽게 설명하자면 python으로 DB에 접근해서 데이터를 저장, 삭제, 갱신 등 데이터를 조작할 수 있게 도와주는 이브러리입니다.  자세히 설명하자면 포스팅의 내용이 길어질 수 있기 때문에 이렇게 짧게 설명하도록 하겠습니다.

pip install sqlalchemy


또한 이번에는 DataFrame에 저장된 데이터를 SQLite라는 DB에 저장할 것입니다. 이 DB의 데이터를 보려면 해당 DB에 접속하는 툴이 필요합니다. 여기서는 DB Browser for SQLite를 써서 DB에 접속해 볼 것입니다. 


해당 툴 다운로드는 여기를 방문해서 다운로드 받으시면 됩니다.



 재귀적으로 이용해서 공공데이터를 연속적으로 가져오기



프로젝트 구조

| scrapy.cfg
\---invest_crawler
| consts.py
| settings.py
| __init__.py
|
+---items
| | apt_trade.py
| | __init__.py
|
+---spiders
| | apt_trade_spiders.py
| | __init__.py


소스 코드


consts.py

# 샘플 더미 데이터 입니다. 어떻게 세팅하는 지 보여드리기 위해 넣은 데이터이기 때문에 그대로 사용하시면 에러가 납니다.
APT_DETAIL_ENDPOINT = "http://openapi.molit.go.kr:8081/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc/getRTMSDataSvcAptTrade?serviceKey=asdsdfsdfWiZGAJkCsr3wM0YkDO%2BssYpNXZ%2FEWZfuIW5k%2FcHFtD5k1zcCVasdfEtBQID5rIcjXsg%3D%3D&"


apt_trade.py

import scrapy


class AptTradeScrapy(scrapy.Item):

apt_name = scrapy.Field()
address_1 = scrapy.Field()
address_2 = scrapy.Field()
address_3 = scrapy.Field()
address_4 = scrapy.Field()
address = scrapy.Field()
age = scrapy.Field()
level = scrapy.Field()
available_space = scrapy.Field()
trade_date = scrapy.Field()
trade_amount = scrapy.Field()

def to_dict(self):
return {
'아파트': self['apt_name'],
'시/도': self['address_1'],
'군/구': self['address_2'],
'동/읍/면': self['address_3'],
'번지': self['address_4'],
'전체주소': self['address'],
'연식': self['age'],
'층': self['level'],
'면적': self['available_space'],
'거래일자': self['trade_date'],
'매매가격': self['trade_amount']
}


apt_trade_spiders.py

import datetime as dt
from urllib.parse import urlencode

import pandas as pd
import scrapy
from dateutil.relativedelta import relativedelta
from openpyxl import Workbook
from scrapy import Selector
from sqlalchemy import create_engine

import invest_crawler.consts as CONST
from invest_crawler.items.apt_trade import AptTradeScrapy


class TradeSpider(scrapy.spiders.XMLFeedSpider):
name = 'trade'

def start_requests(self):
self.engine = create_engine('sqlite:///CRAWLER.DB', echo=False)
date = dt.datetime(2006, 1, 1)
Workbook().save('APT_TRADE.xlsx')
yield from self.get_realestate_trade_data(date)

def get_realestate_trade_data(self, date):
page_num = 1
urls = [
CONST.APT_DETAIL_ENDPOINT
]
params = {
"pageNo": str(page_num),
"numOfRows": "999",
"LAWD_CD": "44133",
"DEAL_YMD": date.strftime("%Y%m"),
}
for url in urls:
url += urlencode(params)
yield scrapy.Request(url=url, callback=self.parse, cb_kwargs=dict(page_num=page_num, date=date))

def parse(self, response, page_num, date):
selector = Selector(response, type='xml')
items = selector.xpath('//%s' % self.itertag) # self.intertag는 기본적으로 item으로 되어 있음
if not items:
return

"""
To save to Excel Sheet
"""
# To remove 'Sheet' worksheet created automatically
# if date.strftime("%Y%m") == "200604":
# wb = load_workbook('APT_TRADE.xlsx')
# wb.remove(wb['Sheet'])
# wb.save('APT_TRADE.xlsx')
# return

# apt_trades = [self.parse_item(item) for item in items]
# apt_dataframe = pd.DataFrame.from_records([apt_trade.to_dict() for apt_trade in apt_trades])
#
# writer = pd.ExcelWriter('APT_TRADE.xlsx', engine='openpyxl', mode='a')
# apt_dataframe.to_excel(writer, sheet_name='천안-' + date.strftime("%Y%m"), index=False)
# writer.save()

apt_trades = [self.parse_item(item) for item in items]
apt_dataframe = pd.DataFrame.from_records([apt_trade.to_dict() for apt_trade in apt_trades])
apt_dataframe.to_sql('APT_TRADE', con=self.engine, if_exists='append')

date += relativedelta(months=1)
yield from self.get_realestate_trade_data(date)

def parse_item(self, item):
state = "천안시"
district = "서북구"

try:
apt_trade_data = AptTradeScrapy(
apt_name=item.xpath("./아파트/text()").get(),
address_1=state,
address_2=district,
address_3=item.xpath("./법정동/text()").get().strip(),
address_4=item.xpath("./지번/text()").get(),
address=state + " " + district + " " + item.xpath("./법정동/text()").get().strip() + " " +
item.xpath("./지번/text()").get(),
age=item.xpath("./건축년도/text()").get(),
level=item.xpath("./층/text()").get(),
available_space=item.xpath("./전용면적/text()").get(),
trade_date=item.xpath("./년/text()").get() + "/" +
item.xpath("./월/text()").get() + "/" +
item.xpath("./일/text()").get(),
trade_amount=item.xpath("./거래금액/text()").get().strip().replace(',', ''),
)

except Exception as e:
print(e)
self.logger.error(item)
self.logger.error(item.xpath("./아파트/text()").get())

return apt_trade_data
  • start_requests 메서드에 self.engine = create_engine('sqlite:///CRAWLER.DB', echo=False)라는 코드 한 줄이 추가되었습니다. create_engine은 SQLAlchemy에서 mysql, oracle, sqlite 같은 DB의 접속과 메타데이터 생성 등을 수행하는 역할을 하는 객체를 반환합니다. 
    참고로 echo=False의 의미는 SQLAlchemy가 관련 engine을 생성하면서 나오는 로그를 콘솔창에 출력하지 않게 만드는 설정 옵션입니다.
     self.engine = create_engine('sqlite:///CRAWLER.DB', echo=False)
  • parse 메서드에서 아래와 같이 딱 한 줄의 파이썬 코드가 추가되었습니다.  이 한 줄로 천안의 2006~2020 년도의 데이터를 sqlite DB에 저장할 수 있습니다. 'APT_TRADE' 은 데이터가 저장될 DB의 테이블의 이름을 나타냅니다. con은 pandas의 DataFrame이 저장될 DB의 엔진을 의미합니다. if_exists는 만약 DB에 같은 테이블이 있을 시 그 테이블에 데이터를 이어 붙이겠다는 뜻입니다. 
    apt_dataframe.to_sql('APT_TRADE', con=self.engine, if_exists='append')

   


 DB Browser로 데이터 보기



먼저 DB Browser를 실행합니다. 실행하게되면 다음과 같은 화면을 볼 수 있을 겁니다. 여기서 데이터베이스 열기를 클릭합니다.



그리고 위 코드를 실행하고 나온 sqlite db file을 클릭합니다.



아래와 같이 데이터보기를 클릭하게 되면 2006~2020 천안의 공공데이터가 들어있는 것을 볼 수 있습니다.



이렇게 DB를 이용하여 데이터를 저장하게 되면 Excel를 통해서 저장할 내용을 체계적으로 저장하는 것이 가능합니다. 또한 데이터를 저장할 때의 속도도 Excel보다 훨씬 빠르죠 ( DB : 2~3분 vs Excel : 50~60분)


지금까지 scrapy를 이용하여 부동산 공공데이터를 크롤링하고 저장하는 내용을 다뤘습니다. 다음 포스팅에서는 Jupyter Notebook으로 이 데이터들을 어떻게 분석해볼 수 있을 지 다뤄보도록 하겠습니다.

이 글을 공유하기

댓글(1)

Designed by JB FACTORY