서울시 부동산 실거래가 정보 API 크롤링 2 - 크롤링편 (XML)
Page content
개요
Open API
를 통해서 부동산 실거래가 정보를 pandas 데이터프레임으로 변환하는 코드를 구현한다.
요청인자 확인
- 샘플 URL은 크게 2가지를 제공한다.
- 서울시 부동산 실거래가 정보
- http://openapi.seoul.go.kr:8088/(인증키)/xml/tbLnOpendataRtmsV/1/5/
- 서울시 부동산 실거래가 정보(마곡일성트루엘플래닛)
- http://openapi.seoul.go.kr:8088/(인증키)/xml/tbLnOpendataRtmsV/1/5/2022/11500/강서구/10500/일반/0758/0002/마곡일성트루엘플래닛/오피스텔
- 서울시 부동산 실거래가 정보
- 출력 예제는 다음과 같다.
<?xml version="1.0" encoding="UTF-8"?>
<tbLnOpendataRtmsV>
<list_total_count>2639192</list_total_count>
<RESULT>
<CODE>INFO-000</CODE>
<MESSAGE>정상 처리되었습니다</MESSAGE>
</RESULT>
<row>
<ACC_YEAR>2023</ACC_YEAR>
<SGG_CD>11545</SGG_CD>
<SGG_NM>금천구</SGG_NM>
<BJDONG_CD>10100</BJDONG_CD>
<BJDONG_NM>가산동</BJDONG_NM>
<LAND_GBN>1</LAND_GBN>
<LAND_GBN_NM>대지</LAND_GBN_NM>
<BONBEON>0776</BONBEON>
<BUBEON>0000</BUBEON>
<BLDG_NM>가산대명벨리온</BLDG_NM>
<DEAL_YMD>20230127</DEAL_YMD>
<OBJ_AMT>12300</OBJ_AMT>
<BLDG_AREA>16.28</BLDG_AREA>
<TOT_AREA>25.630000</TOT_AREA>
<FLOOR>8</FLOOR>
<RIGHT_GBN/>
<CNTL_YMD/>
<BUILD_YEAR>2017</BUILD_YEAR>
<HOUSE_TYPE>오피스텔</HOUSE_TYPE>
<REQ_GBN>중개거래</REQ_GBN>
<RDEALER_LAWDNM>서울 금천구</RDEALER_LAWDNM>
</row>
<row>
<ACC_YEAR>2023</ACC_YEAR>
<SGG_CD>11500</SGG_CD>
<SGG_NM>강서구</SGG_NM>
<BJDONG_CD>10500</BJDONG_CD>
<BJDONG_NM>마곡동</BJDONG_NM>
<LAND_GBN>1</LAND_GBN>
<LAND_GBN_NM>대지</LAND_GBN_NM>
<BONBEON>0793</BONBEON>
<BUBEON>0000</BUBEON>
<BLDG_NM>유림트윈파크</BLDG_NM>
<DEAL_YMD>20230127</DEAL_YMD>
<OBJ_AMT>13900</OBJ_AMT>
<BLDG_AREA>19.99</BLDG_AREA>
<TOT_AREA>30.300000</TOT_AREA>
<FLOOR>6</FLOOR>
<RIGHT_GBN/>
<CNTL_YMD/>
<BUILD_YEAR>2015</BUILD_YEAR>
<HOUSE_TYPE>오피스텔</HOUSE_TYPE>
<REQ_GBN>중개거래</REQ_GBN>
<RDEALER_LAWDNM>서울 강서구</RDEALER_LAWDNM>
</row>
<row>
<ACC_YEAR>2023</ACC_YEAR>
<SGG_CD>11500</SGG_CD>
<SGG_NM>강서구</SGG_NM>
<BJDONG_CD>10300</BJDONG_CD>
<BJDONG_NM>화곡동</BJDONG_NM>
<LAND_GBN>1</LAND_GBN>
<LAND_GBN_NM>대지</LAND_GBN_NM>
<BONBEON>1115</BONBEON>
<BUBEON>0034</BUBEON>
<BLDG_NM>casagio</BLDG_NM>
<DEAL_YMD>20230127</DEAL_YMD>
<OBJ_AMT>22100</OBJ_AMT>
<BLDG_AREA>29.98</BLDG_AREA>
<TOT_AREA>21.430000</TOT_AREA>
<FLOOR>2</FLOOR>
<RIGHT_GBN/>
<CNTL_YMD/>
<BUILD_YEAR>2019</BUILD_YEAR>
<HOUSE_TYPE>연립다세대</HOUSE_TYPE>
<REQ_GBN>직거래</REQ_GBN>
<RDEALER_LAWDNM/>
</row>
<row>
<ACC_YEAR>2023</ACC_YEAR>
<SGG_CD>11320</SGG_CD>
<SGG_NM>도봉구</SGG_NM>
<BJDONG_CD>10600</BJDONG_CD>
<BJDONG_NM>방학동</BJDONG_NM>
<LAND_GBN>1</LAND_GBN>
<LAND_GBN_NM>대지</LAND_GBN_NM>
<BONBEON>0632</BONBEON>
<BUBEON>0023</BUBEON>
<BLDG_NM>(632-23)</BLDG_NM>
<DEAL_YMD>20230126</DEAL_YMD>
<OBJ_AMT>12500</OBJ_AMT>
<BLDG_AREA>41.58</BLDG_AREA>
<TOT_AREA>30.760000</TOT_AREA>
<FLOOR>1</FLOOR>
<RIGHT_GBN/>
<CNTL_YMD/>
<BUILD_YEAR>1986</BUILD_YEAR>
<HOUSE_TYPE>연립다세대</HOUSE_TYPE>
<REQ_GBN>직거래</REQ_GBN>
<RDEALER_LAWDNM/>
</row>
<row>
<ACC_YEAR>2023</ACC_YEAR>
<SGG_CD>11410</SGG_CD>
<SGG_NM>서대문구</SGG_NM>
<BJDONG_CD>11200</BJDONG_CD>
<BJDONG_NM>대현동</BJDONG_NM>
<LAND_GBN>1</LAND_GBN>
<LAND_GBN_NM>대지</LAND_GBN_NM>
<BONBEON>0101</BONBEON>
<BUBEON>0007</BUBEON>
<BLDG_NM>혜우</BLDG_NM>
<DEAL_YMD>20230126</DEAL_YMD>
<OBJ_AMT>80000</OBJ_AMT>
<BLDG_AREA>129.27</BLDG_AREA>
<TOT_AREA>0.000000</TOT_AREA>
<FLOOR>10</FLOOR>
<RIGHT_GBN/>
<CNTL_YMD/>
<BUILD_YEAR>1996</BUILD_YEAR>
<HOUSE_TYPE>아파트</HOUSE_TYPE>
<REQ_GBN>직거래</REQ_GBN>
<RDEALER_LAWDNM/>
</row>
</tbLnOpendataRtmsV>
- 각 요청인자에 대한 정보는 아래 싸이트에서 확인이 가능하다.
파이썬 코드
- 해당 싸이트에서는 OPEN API 샘플 코드를 제공하고 있다.
- 이제 본격적으로 주어진 코드를 활용하여 크롤링 데이터를 수집하도록 한다.
코드
- 우선 API가 정상적으로 호출이 되는지 확인하는 코드를 작성한다.
- 이 때,
~/1/5/2022
는 조회 날짜를 의미한다.- 즉, 여기 부분을 입력 받아서 처리할 수 있는 간단한 프로그램도 고민할 수 있다.
import lxml
import requests
from bs4 import BeautifulSoup
import pandas as pd
service_key = 'your_service_key'
url = f'http://openapi.seoul.go.kr:8088/{service_key}/xml/tbLnOpendataRtmsV/1/5/2022'
result = requests.get(url)
content = result.content
soup = BeautifulSoup(content, "lxml") # html.parser
# print(soup)
print(soup.prettify())
<?xml version="1.0" encoding="UTF-8"?>
<html>
<body>
<tblnopendatartmsv>
<list_total_count>
224085
</list_total_count>
<result>
<code>
INFO-000
</code>
<message>
정상 처리되었습니다
</message>
</result>
<row>
<acc_year>
2022
</acc_year>
<sgg_cd>
11680
</sgg_cd>
<sgg_nm>
강남구
</sgg_nm>
<bjdong_cd>
10300
</bjdong_cd>
<bjdong_nm>
개포동
</bjdong_nm>
<land_gbn>
1
</land_gbn>
<land_gbn_nm>
대지
</land_gbn_nm>
<bonbeon>
1165
</bonbeon>
<bubeon>
0016
</bubeon>
<bldg_nm>
칠성빌라나동
</bldg_nm>
<deal_ymd>
20230109
</deal_ymd>
<obj_amt>
69000
</obj_amt>
<bldg_area>
50.28
</bldg_area>
<tot_area>
41.770000
</tot_area>
<floor>
1
</floor>
<right_gbn>
</right_gbn>
<cntl_ymd>
</cntl_ymd>
<build_year>
1989
</build_year>
<house_type>
연립다세대
</house_type>
<req_gbn>
중개거래
</req_gbn>
<rdealer_lawdnm>
서울 강남구
</rdealer_lawdnm>
</row>
<row>
<acc_year>
2022
</acc_year>
<sgg_cd>
11200
</sgg_cd>
<sgg_nm>
성동구
</sgg_nm>
<bjdong_cd>
12200
</bjdong_cd>
<bjdong_nm>
용답동
</bjdong_nm>
<land_gbn>
1
</land_gbn>
<land_gbn_nm>
대지
</land_gbn_nm>
<bonbeon>
0238
</bonbeon>
<bubeon>
0012
</bubeon>
<bldg_nm>
서희스타힐스리버파크
</bldg_nm>
<deal_ymd>
20230106
</deal_ymd>
<obj_amt>
13500
</obj_amt>
<bldg_area>
20.21
</bldg_area>
<tot_area>
27.280000
</tot_area>
<floor>
4
</floor>
<right_gbn>
</right_gbn>
<cntl_ymd>
</cntl_ymd>
<build_year>
2016
</build_year>
<house_type>
오피스텔
</house_type>
<req_gbn>
중개거래
</req_gbn>
<rdealer_lawdnm>
서울 광진구
</rdealer_lawdnm>
</row>
<row>
<acc_year>
2022
</acc_year>
<sgg_cd>
11740
</sgg_cd>
<sgg_nm>
강동구
</sgg_nm>
<bjdong_cd>
10900
</bjdong_cd>
<bjdong_nm>
천호동
</bjdong_nm>
<land_gbn>
1
</land_gbn>
<land_gbn_nm>
대지
</land_gbn_nm>
<bonbeon>
0167
</bonbeon>
<bubeon>
0153
</bubeon>
<bldg_nm>
에코 타워빌
</bldg_nm>
<deal_ymd>
20230102
</deal_ymd>
<obj_amt>
22500
</obj_amt>
<bldg_area>
29.01
</bldg_area>
<tot_area>
38.620000
</tot_area>
<floor>
2
</floor>
<right_gbn>
</right_gbn>
<cntl_ymd>
</cntl_ymd>
<build_year>
2017
</build_year>
<house_type>
오피스텔
</house_type>
<req_gbn>
중개거래
</req_gbn>
<rdealer_lawdnm>
서울 강동구
</rdealer_lawdnm>
</row>
<row>
<acc_year>
2022
</acc_year>
<sgg_cd>
11320
</sgg_cd>
<sgg_nm>
도봉구
</sgg_nm>
<bjdong_cd>
10800
</bjdong_cd>
<bjdong_nm>
도봉동
</bjdong_nm>
<land_gbn>
1
</land_gbn>
<land_gbn_nm>
대지
</land_gbn_nm>
<bonbeon>
0062
</bonbeon>
<bubeon>
0043
</bubeon>
<bldg_nm>
도봉 투웨니퍼스트 2단지
</bldg_nm>
<deal_ymd>
20221231
</deal_ymd>
<obj_amt>
18400
</obj_amt>
<bldg_area>
29.66
</bldg_area>
<tot_area>
38.510000
</tot_area>
<floor>
2
</floor>
<right_gbn>
</right_gbn>
<cntl_ymd>
</cntl_ymd>
<build_year>
null
</build_year>
<house_type>
오피스텔
</house_type>
<req_gbn>
중개거래
</req_gbn>
<rdealer_lawdnm>
서울 도봉구
</rdealer_lawdnm>
</row>
<row>
<acc_year>
2022
</acc_year>
<sgg_cd>
11170
</sgg_cd>
<sgg_nm>
용산구
</sgg_nm>
<bjdong_cd>
12500
</bjdong_cd>
<bjdong_nm>
한강로2가
</bjdong_nm>
<land_gbn>
1
</land_gbn>
<land_gbn_nm>
대지
</land_gbn_nm>
<bonbeon>
0312
</bonbeon>
<bubeon>
0004
</bubeon>
<bldg_nm>
대우디오빌한강로오피스텔
</bldg_nm>
<deal_ymd>
20221231
</deal_ymd>
<obj_amt>
35000
</obj_amt>
<bldg_area>
33.95
</bldg_area>
<tot_area>
48.610000
</tot_area>
<floor>
7
</floor>
<right_gbn>
</right_gbn>
<cntl_ymd>
</cntl_ymd>
<build_year>
2004
</build_year>
<house_type>
오피스텔
</house_type>
<req_gbn>
중개거래
</req_gbn>
<rdealer_lawdnm>
서울 동작구, 서울 용산구
</rdealer_lawdnm>
</row>
</tblnopendatartmsv>
</body>
</html>
acc_year
부터rdealer_lawdnm
코드가 계속 반복되는 것을 확인할 수 있다.- 총 출력 값이 많기 때문에 일부만 가져오도록 한다.
- 특별한 의미는 없다.
years = soup.find_all('acc_year') # 접수년월
sgg_cds = soup.find_all('sgg_cd') # 자치구코드
bldg_nms = soup.find_all('bldg_nm') # 건물명
obj_amts = soup.find_all('obj_amt') # 물건금액(만원)
house_types = soup.find_all('house_type') # 건물용도
rdealer_lawdnms = soup.find_all('rdealer_lawdnm') # 신고한 개업공인중개사 시군구명
- 이제 반복문을 통해 코드를 작성하도록 한다. 코드에서 가장 큰 특징은
zip()
함수를 활용한 것이었다. - 그리고 반복문을 순환하면서 각 리스트에 담는다.
- 시간 테스트도 같이 진행했다.
%%time
year_list = []
sgg_cd_list = []
bldg_nm_list = []
obj_amt_list = []
house_type_list = []
rdealer_lawdnm_list = []
for year, sgg_cd, bldg_nm, obj_amt, house_type, rdealer_lawdnm in zip(years, sgg_cds, bldg_nms, obj_amts, house_types, rdealer_lawdnms):
year_list.append(year.get_text())
sgg_cd_list.append(sgg_cd.get_text())
bldg_nm_list.append(bldg_nm.get_text())
obj_amt_list.append(obj_amt.get_text())
house_type_list.append(house_type.get_text())
rdealer_lawdnm_list.append(rdealer_lawdnm.get_text())
df = pd.DataFrame({
"acc_year": year_list,
"sgg_cd": sgg_cd_list,
"bldg_nm" : bldg_nm_list,
"obj_amt": obj_amt_list,
"house_type" : house_type_list,
"rdealer_lawdnm": rdealer_lawdnm_list
})
df
- 이번에는 다른 방식으로 코드를 작성한 후, 시간을 측정하도록 한다.
%%time
data = []
for i in range(0,len(years)):
rows = [years[i].get_text(),
sgg_cds[i].get_text(),
bldg_nms[i].get_text(),
obj_amts[i].get_text(),
house_types[i].get_text(),
rdealer_lawdnms[i].get_text()]
data.append(rows)
cols = ['acc_year', 'sgg_cd', 'bldg_nm', 'obj_amt', 'house_type', 'rdealer_lawdnm']
df = pd.DataFrame(data, columns = cols)
df
- 두개의 코드 중, 코드를 작성하는 시간은 첫번째 방식이 더 오래 걸렸지만, 처리 속도는 2배 더 빨랐다.
- 이 둘중에서 어떤 것을 함수화 할 것인지는 독자가 판단해서 정하면 된다.