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

| 들어가기 전에


GIT 저장소


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


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



이전 포스팅


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

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




국토교통부 부동산 매매데이터 분석 및 추출 



국토교통부에서 제공하는 공공 데이터 포맷은 다음과 같이 xml 포맷으로 전송됩니다.  




이번 포스팅에서는 위 xml 데이터에서 거래금액, 건축년도, 년 등의 데이터를 추출하는 방법을 다룰 것입니다. 



| Selector와 xpath를 이용해서 매매데이터 쉽게 추출하기


프로젝트 구조

|   scrapy.cfg
|
\---invest_crawler
| consts.py
| settings.py
| __init__.py
|
+---items
| | apt_trade.py
| | __init__.py
+---spiders
| | apt_trade_spiders.py
| | __init__.py
  • 간단한 items 파이썬 패키지를 추가했습니다. item은 scrapy에서 데이터를 담아두는 바구니 역할을 합니다. 프로그래밍 세계에서 쓰는 고급진 말로 DTO(Data Transfer Object)라고 합니다.


소스 코드


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()
  • 매매 데이터를 담는 item인 인스턴스의 설계도인 AptTradeScrapy 클래스를 작성했습니다. scrapy.Item을 상속받았은 것에 주목하세요! 또한 scrapy.Field를 통해 파이썬의 각 속성이 scrapy의 필드라는 것을 명시해야 합니다.



apt_trade_spiders.py

import datetime as dt
from urllib.parse import urlencode

import scrapy
from scrapy import Selector

import invest_crawler.consts as CONST


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

def start_requests(self):
page_num = 1
date = dt.datetime(2006, 1, 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)
print(url)
yield scrapy.Request(url=url)

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

for item in items:
print(item)
  • parse 메서드에 scrapy에서 데이터를 추출하는 데 쓰이는 객체인 Selector가 코드에 들어있는 것을 볼 수 있습니다. 이 Selector는 xml, html 같은 HTTP 응답 포맷에 따라 데이터를 추출하는 데 유용하게 사용할 수 있습니다.
  • xpath란 xml 포맷에서 각 데이터가 위치한 경로를 효과적으로 나타내기 위한 방식입니다. 이 xpath를 이용하면 여러 개로 나뉘어진 xml 태그들을 쉽게 추출할 수 있는 것은 물론 다소 까다로운 조건으로 추출해야하는 xml 태그들도 손쉽게 뽑아낼 수 있습니다. 
    여기서 xpath는 위 코드에서 selector.xpath('//item')으로 해석되어 집니다. 이 의미는 xml 포맷 데이터에서 모든 item 태그들을 선택하라라는 의미와 같습니다. 
  • for .. in 구문을 이용하여 xpath를 통해 추출된 item들을 순회하는 코드입니다. 위 코드를 아래 실행 명령어를 통해 실행하게 되면 다음과 같은 결과값이 콘솔에 나올 것 입니다. 


실행 명령어

scrapy crawl trade


결과 화면

[... 생략 ...] <Selector xpath='//item' data='<item><거래금액> 8,950</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 9,100</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 14,000</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 13,000</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 10,000</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 9,050</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 9,200</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 9,500</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 9,350</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 10,000</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 9,300</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 13,100</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 7,800</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 8,500</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 21,600</거래금액><건축년도>20...'>
<Selector xpath='//item' data='<item><거래금액> 9,500</거래금액><건축년도>19...'>
<Selector xpath='//item' data='<item><거래금액> 20,250</거래금액><건축년도>20...'>
<Selector xpath='//item' data='<item><거래금액> 9,000</거래금액><건축년도>19...'> [... 생략 ...]


| Scrapy의 item 객체에 데이터 집어넣기 


위에서 한 대로 Selector 객체 형태로만 데이터를 처리하려고 하면 코딩하는 데 시간도 많이 걸릴 뿐더러 코드도 난잡해질 가능성이 커집니다! 이 때 위에서 설명한 Item 객체에 데이터를 넣어 처리하게 되면 scrapy를 통해 아주 손쉽게 데이터를 처리할 수 있습니다.


apt_trade_spiders.py

import datetime as dt
from urllib.parse import urlencode

import scrapy
from scrapy import Selector

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):
page_num = 1
date = dt.datetime(2006, 1, 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)
print(url)
yield scrapy.Request(url=url)

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

for item in items:
apt_trade = self.parse_item(item)
print(apt_trade)

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
  • item에 있는 데이터를 추출하는 parse_item 메서드를 추가하였습니다.
  • item에서 xpath를 사용하여 xml에 있는 데이터를 추출하는 작업을 하는 코드를 작성했습니다. text()는 xml 태그 안에 있는 텍스트로 된 데이터를 의미합니다. 
  • 크롤링에서 예기치 못한 공백 문자가 생겨서 같은 데이터라도 다르게 판단되거나 제대로 데이터가 처리되지 않는 경우가 종종 발생합니다. 따라서 strip 메서드를 써서 앞 뒤 공백 문자를 제거하는 작업을 하는 것이 좋습니다.


결과 화면

[... 생략...] {'address': '천안시 서북구 쌍용동 889',
'address_1': '천안시',
'address_2': '서북구',
'address_3': '쌍용동',
'address_4': '889',
'age': '1994',
'apt_name': '대우타워',
'available_space': '59.76',
'level': '17',
'trade_amount': '8450',
'trade_date': '2006/1/4'}
{'address': '천안시 서북구 쌍용동 1920',
'address_1': '천안시',
'address_2': '서북구',
'address_3': '쌍용동',
'address_4': '1920',
'age': '1999',
'apt_name': '쌍용마을 뜨란채,주공아파트',
'available_space': '84.61',
'level': '14',
'trade_amount': '14500',
'trade_date': '2006/1/5'} [... 생략...]


다음 포스팅에는 이 xml 데이터를 csv 파일 형태 혹은 excel에다가 저장하는 법을 다루도록 하겠습니다.

이 글을 공유하기

댓글(2)

Designed by JB FACTORY