[파이썬 부동산] 아파트 매매 실거래가 API 활용

이현지·2024년 8월 17일
0

데이터 분석

목록 보기
9/13

1. 라이브러리 불러오기

import json            
import requests    
import urllib.request as req
from bs4 import BeautifulSoup
import xmltodict       
import pandas as pd

import json: JSON 데이터 형식을 다루기 위한 라이브러리입니다.
import requests: HTTP 요청을 보내고 응답을 받을 수 있는 라이브러리입니다.
import urllib.request as req: URL을 통해 데이터를 가져오기 위한 라이브러리입니다.
import BeautifulSoup: HTML/XML 데이터를 파싱하기 위한 라이브러리입니다.
import xmltodict: XML 데이터를 Python 딕셔너리 형태로 변환하기 위한 라이브러리입니다.
import pandas as pd: 데이터 분석과 조작을 위한 라이브러리로, DataFrame이라는 자료구조를 제공합니다.

2. API 인증키와 URL 설정

# 1) 본인의 인증키(Decoding 버전)
serviceKey = '키주소'
# 2) 서비스 URL
url = "http://apis.data.go.kr/1613000/RTMSDataSvcAptTradeDev/getRTMSDataSvcAptTradeDev?"

serviceKey: 공공데이터 포털에서 제공된 API 사용을 위한 인증키입니다.
url: 아파트 실거래 데이터를 제공하는 공공데이터 API의 기본 URL입니다.

3. API 요청 변수 설정

# 3) API 요청변수(Request Parameter)
pageNo = '1'
numOfRows = '5'
DEAL_YMD = "202001" # 필요한 데이터의 연도 + 월
LAWD_CD = '11110' # 법정동 코드 앞자리 5글자 = 법정 구코드 (서울특별시 종로구)

pageNo: API 요청 시 데이터를 가져올 페이지 번호입니다. 첫 페이지를 가져오기 위해 1로 설정합니다.
numOfRows: 한 페이지에서 가져올 데이터의 수를 의미하며, 여기서는 5개로 설정되어 있습니다.
DEAL_YMD: 데이터의 연도와 월을 의미하며, 202001은 2020년 1월을 나타냅니다.
LAWD_CD: 법정동 코드입니다. 11110은 서울특별시 종로구를 나타냅니다.

4. 요청 파라미터를 조합하여 URL 구성 및 데이터 요청

payload = "serviceKey=" + serviceKey + "&" + \
          "pageNo=" + pageNo + "&" + \
          "numOfRows=" + numOfRows + "&" \
          "LAWD_CD=" + LAWD_CD + "&" + \
          "DEAL_YMD=" + DEAL_YMD + "&"

res = requests.get(url + payload)
data = json.loads(json.dumps(xmltodict.parse(res.text)))
print(data)

payload: API 요청에 필요한 파라미터들을 조합하여 하나의 문자열로 만듭니다.

requests.get(url + payload): 위에서 만든 url + payload로 HTTP GET 요청을 보내 데이터를 가져옵니다.

xmltodict.parse(res.text): XML 형식의 응답을 Python 딕셔너리로 변환합니다.

json.dumps(xmltodict.parse(res.text)): 딕셔너리 형식을 JSON 문자열로 변환합니다.

json.loads(json.dumps(...)): JSON 문자열을 다시
Python 객체로 변환하여 data 변수에 저장합니다.

print(data): 가져온 데이터를 출력합니다.

5. API 응답 데이터를 DataFrame으로 변환하는 함수

def getRTMSDataSvcAptTradeDev(pageNo,numOfRows,LAWD_CD,DEAL_YMD,serviceKey):
url="http://apis.data.go.kr/1613000/RTMSDataSvcAptTradeDev/getRTMSDataSvcAptTradeDev?"

payload = "serviceKey=" + serviceKey + "&" + \
          "pageNo=" + pageNo + "&" + \
          "numOfRows=" + numOfRows + "&" \
          "LAWD_CD=" + LAWD_CD + "&" + \
          "DEAL_YMD=" + DEAL_YMD + "&"   
          
          

getRTMSDataSvcAptTradeDev 함수: API 요청을 보내고 받은 데이터를 처리하여 DataFrame으로 반환하는 함수입니다.
url, payload: API 요청을 위한 URL과 파라미터를 설정합니다.

6. 데이터 가져오기

    xml = req.urlopen(url + payload)
    result = xml.read()
    soup = BeautifulSoup(result, 'lxml-xml')
    items = soup.findAll("item")
    aptTrade = pd.DataFrame()
    temp = []

req.urlopen(url + payload): URL에 요청을 보내 XML 데이터를 가져옵니다.

result = xml.read(): XML 데이터를 읽습니다.

BeautifulSoup(result, 'lxml-xml'): 가져온 XML 데이터를 파싱하기 위해 BeautifulSoup 객체로 변환합니다.

items = soup.findAll("item"): XML 데이터에서 태그를 가진 모든 요소를 찾아 리스트로 반환합니다.

aptTrade = pd.DataFrame(): 데이터를 저장할 빈 DataFrame을 생성합니다.

temp = []: 데이터를 임시로 저장할 리스트입니다.

for문

for item in items:
      try:
          년 = item.find("dealYear").text if item.find("dealYear") else ""                           # 계약년도
          월 = item.find("dealMonth").text if item.find("dealMonth") else ""                         # 계약월
          일 = item.find("dealDay").text if item.find("dealDay") else ""                             # 계약일
          도로명 = item.find("roadNm").text if item.find("roadNm") else ""                           # 도로명
          법정동 = item.find("umdNm").text if item.find("umdNm") else ""                             # 법정동코드
          아파트 = item.find("aptNm").text if item.find("aptNm") else ""                             # 아파트단지명
          건축년도 = item.find("buildYear").text if item.find("buildYear") else ""                   # 건축년도                 
          층 = item.find("floor").text if item.find("floor") else ""                                 # 층
          거래금액 = item.find("dealAmount").text if item.find("dealAmount") else ""                 # 거래금액(만원)
          도로명건물본번호코드 = item.find("roadNmBonbun").text if item.find("roadNmBonbun") else ""  # 도로명건물본번호코드
          도로명건물부번호코드 = item.find("roadNmBubun").text if item.find("roadNmBubun") else ""    # 도로명건물부번호코드
          도로명시군구코드 = item.find("roadNmSggCd").text if item.find("roadNmSggCd") else ""        # 도로명시군구코드
          도로명일련번호코드 = item.find("roadNmSeq").text if item.find("roadNmSeq") else ""          # 도로명일련번호코드
          도로명지상지하코드 = item.find("roadNmbCd").text if item.find("roadNmbCd") else ""          # 도로명지상지하코드
          도로명코드 = item.find("roadNmCd").text if item.find("roadNmCd") else ""                    # 도로명코드
          법정동본번코드 = item.find("bonbun").text if item.find("bonbun") else ""                    # 법정동본번코드 
          법정동부번코드 = item.find("bubun").text if item.find("bubun") else ""                      # 법정동부번코드
          법정동시군구코드 = item.find("sggCd").text if item.find("sggCd") else ""                    # 법정동시군구코드
          법정동읍면동코드 = item.find("umdCd").text if item.find("umdCd") else ""                    # 법정동읍면동코드
          법정동지번코드 = item.find("landCd").text if item.find("landCd") else ""                    # 법정동지번코드
          일련번호 = item.find("aptSeq").text if item.find("aptSeq") else ""                          # 단지 일련번호 
          전용면적 = item.find("excluUseAr").text if item.find("excluUseAr") else ""                  # 전용면적     
          지번 = item.find("jibun").text if item.find("jibun") else ""                                # 지번
          지역코드 = item.find("LAWD_CD").text if item.find("LAWD_CD") else ""                       # 지역코드

          # 데이터를 리스트로 저장
          items_data = [
              년, 월, 일, 도로명, 법정동, 아파트, 건축년도, 층, 거래금액,
              도로명건물본번호코드, 도로명건물부번호코드, 도로명시군구코드,
              도로명일련번호코드, 도로명지상지하코드, 도로명코드,
              법정동본번코드, 법정동부번코드, 법정동시군구코드, 법정동읍면동코드,
              법정동지번코드, 일련번호, 전용면적, 지번, 지역코드
          ]
          temp.append(items_data)  # 리스트에 추가
          
      except Exception as e:
          # 에러 발생시 continue로 넘어감
          print(f"Error processing item: {e}")
          continue
          


          

  temp = pd.DataFrame(temp,columns=["년","월","일","도로명","법정동","아파트","건축년도","층","거래금액","도로명건물본번호코드","도로명건물부번호코드","도로명시군구코드","도로명일련번호코드","도로명지상지하코드","도로명코드"
                                 ,"법정동본번코드","법정동부번코드","법정동시군구코드","법정동읍면동코드","법정동지번코드","일련번호","전용면적","지번","지역코드"]) 

  aptTrade = pd.concat([aptTrade,temp])
  aptTrade = aptTrade.reset_index(drop=True)

  return aptTrade
serviceKey = "코드주소"
pageNo = '1'
numOfRows = '10000'
DEAL_YMD = '202312' # 필요한 데이터의 연도 + 월
LAWD_CD = '11470' # 법정동 코드의 앞자리 5글자 = 법정 구코드 (서울특별시 양천구)

asset = getRTMSDataSvcAptTradeDev(pageNo,numOfRows,LAWD_CD,DEAL_YMD,serviceKey)
asset

거래금액

# 거래금액 컬럼 type 변경
asset.dtypes
asset['거래금액']  = asset['거래금액'].str.replace(",", "")
asset = asset.astype({'거래금액':int})
asset = asset.astype({'전용면적':float})

# 전용면적 평으로 변경
asset['전용면적_평'] = asset['전용면적']/3.3

# 평당 거래금액 min, mean, max 
asset_max = asset[['법정동','거래금액']].groupby('법정동').max()
asset_min = asset[['법정동','거래금액']].groupby('법정동').min()
asset_avg = asset[['법정동','거래금액','아파트','전용면적','전용면적_평']].groupby(['법정동','아파트','전용면적','전용면적_평']).mean()

asset_avg_r = asset_avg.reset_index()
asset_avg_r['평당_실거래가'] = asset_avg_r['거래금액']/asset_avg_r['전용면적_평']

신시가지 키워드로 검색

keyword = '신시가지'
asset_avg_r_key = asset_avg_r[asset_avg_r.아파트.str.contains(keyword)]
asset_avg_r_key.sort_values(by=['평당_실거래가'], axis=0, ascending=False)
import pandas as pd

file = 'C:/주소자세히/jscode20240801(말소코드포함)/KIKmix.csv'

# 인코딩을 cp949로 시도
try:
  code = pd.read_csv(file, sep=',', encoding='cp949')
  print("File read successfully with cp949 encoding")
except UnicodeDecodeError:
  # cp949 인코딩 실패 시 euc-kr로 시도
  try:
      code = pd.read_csv(file, sep=',', encoding='euc-kr')
      print("File read successfully with euc-kr encoding")
  except UnicodeDecodeError:
      print("Failed to read the file with cp949 and euc-kr encodings")

말소일자 결측치

code = code[code['말소일자'].isna()]
#말소일자가 결측치인 것을 가져옴

주소병합

# 주소 병합 함수
def merge_address(row):
  # 각 부분을 결합하면서, 중복되지 않는 경우에만 추가
  parts = [row['시도명']]
  
  if pd.notna(row['시군구명']) and row['시군구명'] != row['동리명']:
      parts.append(row['시군구명'])
      
  if pd.notna(row['읍면동명']):
      parts.append(row['읍면동명'])
      
  if pd.notna(row['동리명']) and row['시도명'] != row['동리명']:
      parts.append(row['동리명'])
  
  return ' '.join(parts)
# '주소' 컬럼 생성
code['주소'] = code.apply(merge_address, axis=1)
code.head(2)
code.info()
## 구 이름을 입력하면 법정동 코드를 찾아보자

gu = 'OO'
def find_gu(gu) : 
  gu_code = code[code['주소'].str.contains(gu)]
  gu_code = gu_code['법정동코드'].reset_index(drop = True)[0]
  gu_code = gu_code[0:5]
  #print(gu_code)
  return ```
gu_code
  
  
  

find_gu('ㅇㅇ')

  

year = [str("%04d" %(y)) for y in range(2011, 2023)]
month = [str("%02d" %(m)) for m in range(1, 13)]
yyyymm_list = ["%s%s" % (y, m) for y in year for m in month]
yyyymm_list

  

def yr_list(fy, ty, fm, tm):
year = [str("%04d" %(y)) for y in range(fy, ty+1)]
month = [str("%02d" %(m)) for m in range(fm, tm+1)]
yyyymm_list = ["%s%s" % (y, m) for y in year for m in month]
#print(yyyymm_list)
return yyyymm_list

yyyymm_list = yr_list(2021,2023,1,12)

  
  

apt_Trade = pd.DataFrame()

LAWD_CD = find_gu('계양')
pageNo = '1'
numOfRows = '10000'
yyyymm_list = yr_list(2021,2023,1,12)

for yyyymm in yyyymm_list :
DEAL_YMD = yyyymm
temp = getRTMSDataSvcAptTradeDev(pageNo,numOfRows,LAWD_CD,DEAL_YMD,serviceKey)
print('작업(YMD) : %s, 데이터건수 : %s ' %(DEAL_YMD, temp.shape))
apt_Trade = pd.concat([apt_Trade, temp]).reset_index(drop = True)

 
  

apt_Trade.shape

  

apt_Trade_asset = apt_Trade

  

거래금액 컬럼 type 변경

apt_Trade_asset.dtypes
apt_Trade_asset['거래금액'] = apt_Trade_asset['거래금액'].str.replace(",", "")
apt_Trade_asset = apt_Trade_asset.astype({'거래금액':int})
apt_Trade_asset = apt_Trade_asset.astype({'전용면적':float})

전용면적 평으로 변경

aptTrade_asset['전용면적평'] = apt_Trade_asset['전용면적']/3.3

평당 거래금액 min, mean, max

aptTrade_asset_max = apt_Trade_asset[['법정동','거래금액']].groupby('법정동').max()
apt_Trade_asset_min = apt_Trade_asset[['법정동','거래금액']].groupby('법정동').min()
apt_Trade_asset_avg = apt_Trade_asset[['법정동','거래금액','아파트','전용면적','전용면적
평']].groupby(['법정동','아파트','전용면적','전용면적_평']).mean()

aptTrade_asset_avg_r = apt_Trade_asset_avg.reset_index()
apt_Trade_asset_avg_r['평당
실거래가'] = aptTrade_asset_avg_r['거래금액']/apt_Trade_asset_avg_r['전용면적평']

  

aptTrade_asset_avg_r.sort_values(by=['평당실거래가'], axis=0, ascending=False)

  

aptTrade_asset_avg_r.sort_values(by=['평당실거래가'], axis=0, ascending=False)

  

apt_Trade.to_excel('C:/주소/pyhome/aptTrade.xlsx', sheet_name = LAWD_CD, index = False)

  
  

#파일 두개 다운
with pd.ExcelWriter('C:/Users/506-405/바탕 화면/pyhome/output.xlsx') as writer:
aptTrade.to_excel(writer, sheet_name=LAWD_CD, index=False)
apt_Trade_asset_avg_r.to_excel(writer, sheet_name='평당
실거래가', index=False)
apt_Trade_asset_avg_r.head()

  

apt_Trade_asset_avg_r.head()

profile
관심분야: 추천시스템, 자연어처리, 머신러닝, 딥러닝

0개의 댓글