반응형

<< 문제 정의 >>

부동산 매매데이터를 KB부동산 사이트 를 통해서 활용하는 경우가 많습니다.

그러나, 일일이 아파트를 검색하고, 매매거래가를 다운로드 받아서 활용해야 하는 불편함이 있습니다. '크롤링을 통해서 프로그램화 하면 되지 않아?' 라고 생각할 수 있지만, KB부동산의 경우 크롤링에 필요한 html 코드를 볼 수 없게 막아두었답니다.ㅠㅠ

 

다행히도 공공데이터(www.data.go.kr)에서는 OpenAPI를 제공해주고 있습니다. 이를 이용하여 부동산 실거래 데이터를 수집해보시죠!

<< 사전 준비 사항 >>

  • 공공데이터 가입 (www.data.go.kr)
  • 사용할 API: 공공데이터 포털(국토교통부 - 아파트매매 실거래데이터)
  • 언어 & 환경(IDE) : Python3.6 & Jupter notebook

<< 순서 >>

Step 1 아파트매매 실거래 데이터 OpenAPI 신청하기
※  OpenAPI 사용시 제공기관 연계서버와 일정시간을 주기로 동기화되며 동기화에 시간이 소요될 수 있다. 
Step 2 OpenAPI 테스트하기
Step 3 OpenAPI 파라메터 이해하기
Step 4 법정동 코드 다운로드
Step 5 구현
1) 아파트 실거래 OpenAPI 호출
2) 하드코딩한 법정동 코드값 변경하기
3) 하드코딩한 날짜값 변경하기
4) 특정 '구'를 요청 날짜만큼 실거래데이터 저장하기

 

Step1) 아파트매매 실거래자료 OpenAPI 신청하기

신청 순서 : www.data.go.kr > [부동산거래정보] 클릭 > [공공데이터보기] 클릭 > [오픈API] 클릭 > [국토교통부 실거래가 정보] 클릭 > [아파트매매 실거래자료 - 활용신청] 클릭 > [신청] 클릭 

[부동산거래정보] 클릭 > [공공데이터보기] 클릭 

 

[오픈API] 클릭 > [국토교통부 실거래가 정보] 클릭

 

[아파트매매 실거래자료 - 활용신청] 클릭 > [신청] 클릭 

신청완료!

Step2) OpenAPI 테스트하기

2-1) 일반 인증키 란이 비어 있다면? [일반 인증키 받기]

 

2-2) 테스트

※  OpenAPI 사용시 제공기관 연계서버와 일정시간을 주기로 동기화되며 동기화에 시간이 소요될 수 있습니다.

(좌) 미리보기 / (우) 결과

참고) "아파트매매 실거래 상세 자료" API의 경우는 빨간색 박스가 추가됨. 

Step3) OpenAPI 파라메터 이해하기

- "아파트매매 신고정보 조회 기술문서.hwp" 파일 다운로드 > 파라메터 확인

 

법정동 코드가 필요함을 알 수 있다.

Step4) 법정동 코드 다운로드

www.code.go.kr > [법정동] 선택 > [법정동 코드 전체자료] 클릭 > 법정동코드 다운로드 > 파일 포맷을 utf-8 로 변경

www.code.go.kr 

한글이 깨지는 경우가 있어 메모장으로 파일을 열어 "UTF-8" 포맷으로 변경하여 재저장 합니다.

utf-8로 인코딩 변경하여 재저장

Step5) 구현

Step 5-1) 아파트 실거래 OpenAPI 호출

호출시 전달해야 할 파라메터는 다음과 같습니다.

정상동작 확인을 위해서 "지역코드"와 "계약월"은 하드코딩해서 진행하겠습니다.

step5-2부터는 지역코드/계약월을 필요에 따라 설정하도록 변경하겠습니다.

# 1-1. 데이터 가져오기
import requests
import datetime

url ="http://openapi.molit.go.kr:8081/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc/getRTMSDataSvcAptTrade?"
service_key = "<인증키를 입력하세요> "
base_date = "202001" 
gu_code = '11215' ## 법정동 코드 5자리라면, 구 단위로 데이터를 확보하는 것. 11215 = 광진구

payload = "LAWD_CD=" + gu_code + "&" + \
          "DEAL_YMD=" + base_date + "&" + \
          "serviceKey=" + service_key + "&" 
          
res = requests.get(url + payload)
print(res)

200이 나오면 성공입니다.

전달된 데이터가 xml형태이므로, 라이브러리를 사용해서 parsing합니다.

결과 데이터를 dataframe 형태로 관리하도록 하겠습니다.

import xml.etree.ElementTree as ET

def get_items(response):
    root = ET.fromstring(response.content)
    item_list = []
    for child in root.find('body').find('items'):
        elements = child.findall('*')
        data = {}
        for element in elements:
            tag = element.tag.strip()
            text = element.text.strip()
            # print tag, text
            data[tag] = text
        item_list.append(data)  
    return item_list
    
items_list = get_items(res)
items = pd.DataFrame(items_list) 
items.head()

[결과 확인] 

 

이제부터는 하드코딩한 내용을 제거하고, 파일로 저장하는 것 까지를 구현해 보시죠!!

Step5-2) 하드코딩한 법정동 코드값 변경하기

1. 다운로드 받아둔 "법정동코드 전체자료.txt" 파일 읽기

법정동 코드의 경우 tab으로 분리가 되어 있음을 알 수 있습니다.

code_file = "법정동코드 전체자료/법정동코드 전체자료.txt"
code = pd.read_csv(code_file, sep='\t')

 

2. 데이터 클린징 

 

2-1. 컬럼명이 한글로 되어 있으면 코딩하기 번거로우므로 한글로 변경

code.columns = ['code', 'name', 'is_exist']

 

2-2. 폐지된 법정동 코드는 사용할 필요 없으므로 삭제

code = code [code['is_exist'] == '존재']

 

2-3. 법정동 코드가 int로 되어 있는데, string으로 변경

print(code['code'][0])
print(type(code['code'][0])) ## int64타입

## string으로 변경
code['code'] = code['code'].apply(str) 

 

Step5-3) 하드코딩한 날짜값 변경하기

2015년 01월 ~ 2019년 12월 까지 데이터를 받아보도록 하겠습니다.

year = [str("%02d" %(y)) for y in range(2015, 2020)]
month = [str("%02d" %(m)) for m in range(1, 13)]
base_date_list = ["%s%s" %(y, m) for y in year for m in month ]

 

Step5-4) 특정 '구'를 요청 날짜만큼 실거래데이터 저장하기

1. "광진구"의 법정동 코드 5자리 추출

gu = "광진구"
gu_code = code[ (code['name'].str.contains(gu) )]
gu_code = gu_code['code'].reset_index(drop=True)
gu_code = str(gu_code[0])[0:5]
print(gu_code)

 

2. step5-1)에서 수행했던 소스코드를 함수형태로 만들어 사용

import requests
import datetime

def get_data(gu_code, base_date):
    url ="http://openapi.molit.go.kr:8081/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc/getRTMSDataSvcAptTrade?"
    service_key = "<인증키를 입력하세요>"    
    payload = "LAWD_CD=" + gu_code + "&" + \
              "DEAL_YMD=" + base_date + "&" + \
              "serviceKey=" + service_key + "&"

    res = requests.get(url + payload)
    
    return res

 

3. step5-3) 에서 준비해둔 날짜만큼 반복하며 실거래 데이터 얻기

items_list = []
for base_date in base_date_list:
    res = get_data(gu_code, base_date)
    items_list += get_items(res)
    
len(items_list)

총, 10,073건의 데이터를 얻었네요^^

 

4. dataframe 형태로 변경하여 csv파일로 저장

파일명은 [구이름]_[시작년도]_[끝년도].csv로 저장하겠습니다.

items = pd.DataFrame(items_list) 
items.head()
items.to_csv(os.path.join("%s_%s~%s.csv" %(gu, year[0], year[-1])), index=False,encoding="euc-kr") 

[결과 확인]

저장된 csv 파일명

 

이것으로 실거래가 데이터를 가지고 오는것은 완성! 하였습니다.

다른 지역의 데이터를 가져와 저장하고 싶다면, gu = "광진구" 만 변경하면 됩니다~!!

< 참고 >

- 국토부에서 제공하는 (OpenAPI) 데이터는 전용면적을 기준으로 데이터를 제공

- 네이버 부동산, KB, 호갱노노 모두 공급면적 기준으로 데이터를 제공

 

출처: https://ai-creator.tistory.com/24

반응형
반응형

부동산 정보의 끝판왕 네이버 부동산

네이버 부동산엔 잘 찾을수 없는 정보들이 많다. 예를 들어 매달 관리비가 얼마나 나오는지, 그 부동산에서 어디 초등학교에 배정받을 수 있는지 등 부동산 데이터의 총집합이라고 볼 수 있다.

네이버 부동산의 무슨 데이터를 저장할 것인지?

제일먼저 https://land.naver.com/ 를 들어가 홈화면을 보자

이창에서 서울시-> 강남구->개포동을 선택하고 “확인매물보기”를 누르면 아래와 같이 사진이 뜰것이다.

우리가 원하는것은 위와같이 GUI로 표시된 정보를 컴퓨터가 읽기 편하게 Table형식으로 만드는 것 목표이다.

그렇다면 무슨 정보를 저장할 것인지는 아래 사진과 같이 단지 버튼을 누르로 LG개포자이를 선택해보고 결정하자.

LG개포자이를 선택하면 단지정보, 시세/실거래가, 동호수/공시가격, 학군정보등의 정보들이 있다.

아래로 내려보면 단지 내 면적별 정보가 있는데 이또한 모두 저장한다.

이와 같이 다른 정보들도 모두 저장하여 나중에 취사 선택하는것이 좋을 것 같으니 코딩으로 들어가보자

네이버 부동산 긁기

아까 개포동을 선택하여 LG개포자이를 선택하기전 F12버튼을 눌러주자(크롬기준) 그러면 알수 없는 정보들이 떠있는 요상한 화면이 뜰것이다.

이때 맨위에 network 탭을 누르고 아까와 같이 LG개포자이를 마우스로 클릭해보도록 하자

LG개포자이를 클릭했다면 위의 사진과 같이 이상한 정보들이 주르륵 뜰것이다 여기서 preview탭을 누르고 위에서 부터 하나씩 누르면 정보가 떠있는것 같은 창이 보일것이다. 그걸 클릭하자.

그다음 header탭을 클릭하여 어떤식으로 데이터를 요청하는지 보자

GET방식을 이용하여 데이터를 받는 것을 볼수 있다. 똑같이 LG개포자이 데이터를 받기위한 코드를 파이썬으로 작성해보면 아래와 같다.

import requests
import json
down_url = 'https://new.land.naver.com/api/complexes/8928'
r = requests.get(down_url,data={"sameAddressGroup":"false"},headers={
    "Accept-Encoding": "gzip",
    "Host": "new.land.naver.com",
    "Referer": "https://new.land.naver.com/complexes/8928?ms=37.482968,127.0634,16&a=APT&b=A1&e=RETAIL",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-origin",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
})
r.encoding = "utf-8-sig"
temp=json.loads(r.text)

temp가 preview탭에 들어있는 정보가 동일하게 있는 것을 볼수 있다.

저기서 데이터를 요청할때 Request header를 최대한 동일하게 맞춰주는게 중요하다.

Header중 Referer에 a=APT:ABYG:JGC 이부분을 APT하나로 바꿔주는게 좋다 네이버 부동산 페이지를 자세히 보면 아파트, 아파트 분양권, 재건축이 모두 선택되어 있어 아파트 정보만 얻기 위해선 APT만 선택해야하기 때문이다.

temp 에 Json형식으로 데이터를 받았다면 그다음은 필요한 데이터를 선택하는 것이 중요하다.

위의 사진을 보면 complexDetail 안에 단지정보가 포함돼있는것을 볼수 있다.

이 데이터를 LG개포자이라는 index에 면적별로 컬럼으로 저장하려면 아래와 같이 코드를 짤수 있다.

import pandas as pd
apt_data=temp["complexDetail"]
pyoeng_list=apt_data["pyoengNames"].split(", ")
apt_data_pd=pd.DataFrame(data=apt_data,index=range(len(pyoeng_list)))
for i in range(len(pyoeng_list)):
    apt_data_pd.loc[i,"pyoengNames"]=pyoeng_list[i]
apt_data_pd

위와 같이 코딩을 하면 “pyoengNames”안에 들어있던 평수리스트가 하나씩 분리가 된다.

pyoeng_list로 따로 변수명을 선언한 이유는 저 순서대로 데이터를 긁을 필요가 많기때문에 따로설정하였다.

결과는 아래와 같이 dataframe으로 깔끔하게 저장이 된다.

다른 정보들도 위의 플로우를 따라서 비슷하게 긁어서 apt_data_pd 변수에 저장된 dataframe에 join함수를 이용하여 합치면 된다.

다른 아파트 정보를 자동으로 가져오려면?

위의 크롤링 예시는 LG개포자만을 긁는 코드이다. 하지만 데이터 분석을 위해선 아파트 하나하나를 직접쳐 가져올수가 없다. 네이버 부동산의 아파트,오피스텔 탭을 다시 클릭하여 지도로 돌아가서 f12를 다시 누르자

그리고 서울시를 클릭하면 list?contarNo=000000이라는 파일이 Name탭에 표시될것이다 그걸 클릭하여 preview를보자

여기에 contarNo라는 변수가 서울시를 표시하는 코드인것이다. 또 여기서 서울시를 클릭하면 아래와 같이 시/군/구의 코드를 볼수 있다.

이때 Request URL에 contarNo가 위의위 사진에 있는 서울시의 contarNo인것을 볼수 있다.

즉 서울시를 선택하면 서울시의 구들이 list형식으로 표시되고 이때 또 구를 선택하면 동이 list형식으로 반환되고 동을 선택하면 아파트들이 list형식으로 반환되는 형식인것이다.

예를들어 LG개포자이를 선택하려면 서울시, 강남구, 개포동, LG개포자이의 고유 넘버를 다 알아야 정보를 얻을수 있는 것이다. 이를 코드로 표현하면 아래와 같다.

def get_sido_info():
    down_url = 'https://new.land.naver.com/api/regions/list?cortarNo=0000000000'
    r = requests.get(down_url,data={"sameAddressGroup":"false"},headers={
        "Accept-Encoding": "gzip",
        "Host": "new.land.naver.com",
        "Referer": "https://new.land.naver.com/complexes/102378?ms=37.5018495,127.0438028,16&a=APT&b=A1&e=RETAIL",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
    })
    r.encoding = "utf-8-sig"
    temp=json.loads(r.text)
    temp=list(pd.DataFrame(temp["regionList"])["cortarNo"])
    return temp
def get_gungu_info(sido_code):
    down_url = 'https://new.land.naver.com/api/regions/list?cortarNo='+sido_code
    r = requests.get(down_url,data={"sameAddressGroup":"false"},headers={
        "Accept-Encoding": "gzip",
        "Host": "new.land.naver.com",
        "Referer": "https://new.land.naver.com/complexes/102378?ms=37.5018495,127.0438028,16&a=APT&b=A1&e=RETAIL",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
    })
    r.encoding = "utf-8-sig"
    temp=json.loads(r.text)
    temp=list(pd.DataFrame(temp['regionList'])["cortarNo"])
    return temp
def get_dong_info(gungu_code):
    down_url = 'https://new.land.naver.com/api/regions/list?cortarNo='+gungu_code
    r = requests.get(down_url,data={"sameAddressGroup":"false"},headers={
        "Accept-Encoding": "gzip",
        "Host": "new.land.naver.com",
        "Referer": "https://new.land.naver.com/complexes/102378?ms=37.5018495,127.0438028,16&a=APT&b=A1&e=RETAIL",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
    })
    r.encoding = "utf-8-sig"
    temp=json.loads(r.text)
    temp=list(pd.DataFrame(temp['regionList'])["cortarNo"])
    return temp
def get_apt_list(dong_code):
    down_url = 'https://new.land.naver.com/api/regions/complexes?cortarNo='+dong_code+'&realEstateType=APT&order='
    r = requests.get(down_url,data={"sameAddressGroup":"false"},headers={
        "Accept-Encoding": "gzip",
        "Host": "new.land.naver.com",
        "Referer": "https://new.land.naver.com/complexes/102378?ms=37.5018495,127.0438028,16&a=APT&b=A1&e=RETAIL",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
    })
    r.encoding = "utf-8-sig"
    temp=json.loads(r.text)
    try:
        temp=list(pd.DataFrame(temp['complexList'])["complexNo"])
    except:
        temp=[]
    return temp

get_sido_info()의 함수는 서울시, 경기도, 부산시…등의 특별시와 도의 고유코드를 list 형식으로 return 해주는 함수이다.

get_gungu_info(sido_code)함수는 get_sido_info()에서 return받은 리스트의 값중 하나를 함수 인자로 넣으면 시/군/구의 고유코드를 list 형식으로 return 해주는 함수이다.

get_dong_info(gungu_code)함수는 get_gungu_info(sido_code)에서 return받은 리스트의 값중 하나를 함수 인자로 넣으면 읍/면/동의 고유코드를 list 형식으로 return 해주는 함수이다.

get_apt_list(dong_code)함수는 get_dong_info(gungu_code)에서 return받은 리스트의 값중 하나를 함수 인자로 넣으면 아파트의 고유코드를 list 형식으로 return 해주는 함수이다.

즉 LG개포자이의 고유 코드는 아래와 같이 구할수 있는 것이다.

sido_list=get_sido_info() 
gungu_list=get_gungu_info(sido_list[0])
dong_list=get_dong_info(gungu_list[0])
get_apt_list(dong_list[0])[0]

위의 사진처럼 LG개포자이의 고유 코드는 8928이고 이는 LG개포자이의 정보를 GET함수를 이용하여 구할때 코드안에 포함된것을 볼수 있다.(아래 코드)

import requests
import json
down_url = 'https://new.land.naver.com/api/complexes/8928'
r = requests.get(down_url,data={"sameAddressGroup":"false"},headers={
    "Accept-Encoding": "gzip",
    "Host": "new.land.naver.com",
    "Referer": "https://new.land.naver.com/complexes/8928?ms=37.482968,127.0634,16&a=APT&b=A1&e=RETAIL",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-origin",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
})
r.encoding = "utf-8-sig"
temp=json.loads(r.text)

이를 8298을 변수로 바꾸고 함수화 시키면 아래와 같이 표현할 수 있다.

def get_apt_info(apt_code):
    down_url = 'https://new.land.naver.com/api/complexes/'+apt_code+'?sameAddressGroup=false'
    r = requests.get(down_url,data={"sameAddressGroup":"false"},headers={
        "Accept-Encoding": "gzip",
        "Host": "new.land.naver.com",
        "Referer": "https://new.land.naver.com/complexes/"+apt_code+"?ms=37.482968,127.0634,16&a=APT&b=A1&e=RETAIL",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
    })
    r.encoding = "utf-8-sig"
    temp=json.loads(r.text)
    return temp

위의 코드들을 이용하여 네이버 부동산의 코드를 긁을수 있다. 아래의 코드는 학군정보와 아파트 가격정보를 가져오는 함수이다.

def get_school_info(apt_code):
    down_url = 'https://new.land.naver.com/api/complexes/'+apt_code+'/schools'
    r = requests.get(down_url,headers={
        "Accept-Encoding": "gzip",
        "Host": "new.land.naver.com",
        "Referer": "https://new.land.naver.com/complexes/"+apt_code+"?ms=37.482968,127.0634,16&a=APT&b=A1&e=RETAIL",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
    })
    r.encoding = "utf-8-sig"
    temp_school=json.loads(r.text)
    return temp_school
##################가격정보
def apt_price(apt_code,index):
    p_num=temp["complexPyeongDetailList"][index]["pyeongNo"]
    down_url = 'https://new.land.naver.com/api/complexes/'+apt_code+'/prices?complexNo='+apt_code+'&tradeType=A1&year=5&priceChartChange=true&areaNo='+p_num+'&areaChange=true&type=table'

    r = requests.get(down_url,headers={
        "Accept-Encoding": "gzip",
        "Host": "new.land.naver.com",
        "Referer": "https://new.land.naver.com/complexes/"+apt_code+"?ms=37.4830877,127.0579863,15&a=APT&b=A1&e=RETAIL",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
    })
    r.encoding = "utf-8-sig"
    temp_price=json.loads(r.text)
    return temp_price

get_school_info(apt_code) 함수는 apt_code에 아파트 고유 코드를 str형태로 넣으면 학군정보를 return해준다.

apt_price(apt_code,index) 함수는 apt_code에 아파트 고유 코드를 str형태로 넣고, index에 위에 선언한 pyoeng_list의 인덱스를 넣으면 가격정보를 return해준다.

아래는 내가 전국 아파트의 정보를 긁은 코드이다. 참고하실분은 참고하면된다.

sido_list=get_sido_info()
for m in range(len(sido_list)):
    gungu_list=get_gungu_info(sido_list[m])
    gungu_apt_list=[0]*len(gungu_list)
    for j in range(len(gungu_list)):#구 마다 하나씩 저장
        dong_list=get_dong_info(gungu_list[j])
        dong_apt_list=[0]*len(dong_list)
        for k in range(len(dong_list)):#동마다 하나씩 저장
            apt_list=get_apt_list(dong_list[k])
            apt_list_data=[0]*len(apt_list)
            for n in range(len(apt_list)):#아파트 마다 하나씩 저장
                temp=get_apt_info(apt_list[n])
                try:
                    area_list=temp["complexDetail"]["pyoengNames"].split(", ")
                    ex_flag=1
                except KeyError:   
                    ex_flag=0
                    temp_data=pd.DataFrame(columns=temp_data.columns)
                if ex_flag==1:
                    temp_school=get_school_info(apt_list[n])
                    temp_data=pd.DataFrame(index=range(len(area_list)))
                    for i in range(len(area_list)):
                        print(temp["complexDetail"]["address"],temp["complexDetail"]["complexName"])
                        temp_data.loc[i,"아파트명"]=temp["complexDetail"]["complexName"]
                        temp_data.loc[i,"면적"]=area_list[i]
                        temp_data.loc[i,"법정동주소"]=temp["complexDetail"]["address"]+" "+temp["complexDetail"]["detailAddress"]
                        try:
                            temp_data.loc[i,"도로명주소"]=temp["complexDetail"]["roadAddressPrefix"]+" "+temp["complexDetail"]["roadAddress"]
                        except KeyError:
                            temp_data.loc[i,"도로명주소"]=temp["complexDetail"]["roadAddressPrefix"]
                        temp_data.loc[i,"latitude"]=temp["complexDetail"]["latitude"]
                        temp_data.loc[i,"longitude"]=temp["complexDetail"]["longitude"]
                        temp_data.loc[i,"세대수"]=temp["complexDetail"]["totalHouseholdCount"]
                        temp_data.loc[i,"임대세대수"]=temp["complexDetail"]["totalLeaseHouseholdCount"]
                        temp_data.loc[i,"최고층"]=temp["complexDetail"]["highFloor"]
                        temp_data.loc[i,"최저층"]=temp["complexDetail"]["lowFloor"]
                        try:
                            temp_data.loc[i,"용적률"]=temp["complexDetail"]["batlRatio"]
                        except KeyError:
                            temp_data.loc[i,"용적률"]=""
                        try:
                            temp_data.loc[i,"건폐율"]=temp["complexDetail"]["btlRatio"]
                        except KeyError:
                            temp_data.loc[i,"건폐율"]=""
                        try:
                            temp_data.loc[i,"주차대수"]=temp["complexDetail"]["parkingPossibleCount"]
                        except KeyError:
                            temp_data.loc[i,"주차대수"]=""
                        try:
                            temp_data.loc[i,"건설사"]=temp["complexDetail"]["constructionCompanyName"]
                        except KeyError:   
                            temp_data.loc[i,"건설사"]=""
                        try:
                            temp_data.loc[i,"난방"]=temp["complexDetail"]["heatMethodTypeCode"]
                        except KeyError:   
                            temp_data.loc[i,"난방"]=""
                        try:
                            temp_data.loc[i,"공급면적"]=temp["complexPyeongDetailList"][i]["supplyArea"]
                        except KeyError:   
                            temp_data.loc[i,"공급면적"]=""
                        try:
                            temp_data.loc[i,"전용면적"]=temp["complexPyeongDetailList"][i]["exclusiveArea"]
                        except KeyError:   
                            temp_data.loc[i,"전용면적"]=""
                        try:
                            temp_data.loc[i,"전용율"]=temp["complexPyeongDetailList"][i]["exclusiveRate"]
                        except KeyError:   
                            temp_data.loc[i,"전용율"]=""
                        try:
                            temp_data.loc[i,"방수"]=temp["complexPyeongDetailList"][i]["roomCnt"]
                        except KeyError:   
                            temp_data.loc[i,"방수"]=""
                        try:
                            temp_data.loc[i,"욕실수"]=temp["complexPyeongDetailList"][i]["bathroomCnt"]
                        except KeyError:   
                            temp_data.loc[i,"욕실수"]=""
                        try:
                            temp_data.loc[i,"해당면적_세대수"]=temp["complexPyeongDetailList"][i]["householdCountByPyeong"]
                        except KeyError:   
                            temp_data.loc[i,"해당면적_세대수"]=""
                        try:
                            temp_data.loc[i,"현관구조"]=temp["complexPyeongDetailList"][i]["entranceType"]
                        except KeyError:   
                            temp_data.loc[i,"현관구조"]=""
                        try:
                            temp_data.loc[i,"재산세"]=temp["complexPyeongDetailList"][i]["landPriceMaxByPtp"]["landPriceTax"]["propertyTax"]
                        except KeyError:   
                            temp_data.loc[i,"재산세"]=""
                        try:
                            temp_data.loc[i,"재산세합계"]=temp["complexPyeongDetailList"][i]["landPriceMaxByPtp"]["landPriceTax"]["propertyTotalTax"]
                        except KeyError:   
                            temp_data.loc[i,"재산세합계"]=""
                        try:
                            temp_data.loc[i,"지방교육세"]=temp["complexPyeongDetailList"][i]["landPriceMaxByPtp"]["landPriceTax"]["localEduTax"]
                        except KeyError:   
                            temp_data.loc[i,"지방교육세"]=""
                        try:
                            temp_data.loc[i,"재산세_도시지역분"]=temp["complexPyeongDetailList"][i]["landPriceMaxByPtp"]["landPriceTax"]["cityAreaTax"]
                        except KeyError:   
                            temp_data.loc[i,"재산세_도시지역분"]=""
                        try:
                            temp_data.loc[i,"종합부동산세"]=temp["complexPyeongDetailList"][i]["landPriceMaxByPtp"]["landPriceTax"]["realEstateTotalTax"]
                        except KeyError:   
                            temp_data.loc[i,"종합부동산세"]=""
                        try:
                            temp_data.loc[i,"결정세액"]=temp["complexPyeongDetailList"][i]["landPriceMaxByPtp"]["landPriceTax"]["decisionTax"]
                        except KeyError:   
                            temp_data.loc[i,"결정세액"]=""
                        try:
                            temp_data.loc[i,"농어촌특별세"]=temp["complexPyeongDetailList"][i]["landPriceMaxByPtp"]["landPriceTax"]["ruralSpecialTax"]
                        except KeyError:   
                            temp_data.loc[i,"농어촌특별세"]=""    

                        temp_price=apt_price(apt_list[0],i)
                        try:
                            temp_data.loc[i,"가격"]=temp_price["marketPrices"][0]["dealAveragePrice"]
                        except KeyError:   
                            temp_data.loc[i,"가격"]=""
                        try:
                            temp_data.loc[i,"겨울관리비"]=temp["complexPyeongDetailList"][i]["averageMaintenanceCost"]["winterTotalPrice"]
                        except KeyError:   
                            temp_data.loc[i,"겨울관리비"]=""
                        try:
                            temp_data.loc[i,"여름관리비"]=temp["complexPyeongDetailList"][i]["averageMaintenanceCost"]["summerTotalPrice"]
                        except KeyError:   
                            temp_data.loc[i,"여름관리비"]=""
                        try:
                            temp_data.loc[i,"매매호가"]=temp["complexPyeongDetailList"][i]["articleStatistics"]["dealPriceString"]
                        except KeyError:   
                            temp_data.loc[i,"매매호가"]=""
                        try:
                            temp_data.loc[i,"전세호가"]=temp["complexPyeongDetailList"][i]["articleStatistics"]["leasePriceString"]
                        except KeyError:   
                            temp_data.loc[i,"전세호가"]=""
                        try:
                            temp_data.loc[i,"월세호가"]=temp["complexPyeongDetailList"][i]["articleStatistics"]["rentPriceString"]
                        except KeyError:   
                            temp_data.loc[i,"월세호가"]=""
                        try:
                            temp_data.loc[i,"실거래가"]=temp["complexPyeongDetailList"][i]["articleStatistics"]["rentPriceString"]
                        except KeyError:   
                            temp_data.loc[i,"실거래가"]=""
                        try:
                            temp_data.loc[i,"초등학교_학군정보"]=temp_school['schools'][0]["schoolName"]
                        except KeyError:   
                            temp_data.loc[i,"초등학교_학군정보"]=""
                        except IndexError :   
                            temp_data.loc[i,"초등학교_학군정보"]=""
                        try:
                            temp_data.loc[i,"초등학교_설립정보"]=temp_school['schools'][0]["organizationType"]
                        except KeyError:   
                            temp_data.loc[i,"초등학교_설립정보"]=""
                        except IndexError :   
                            temp_data.loc[i,"초등학교_설립정보"]=""
                        try:
                            temp_data.loc[i,"초등학교_남학생수"]=temp_school['schools'][0]["maleStudentCount"]
                        except KeyError:   
                            temp_data.loc[i,"초등학교_남학생수"]=""
                        except IndexError :   
                            temp_data.loc[i,"초등학교_남학생수"]=""
                        try:
                            temp_data.loc[i,"초등학교_여학생수"]=temp_school['schools'][0]["femaleStudentCount"]
                        except KeyError:   
                            temp_data.loc[i,"초등학교_여학생수"]=""
                        except IndexError :   
                            temp_data.loc[i,"초등학교_여학생수"]=""

                    #time.sleep(1)
                apt_list_data[n]=temp_data
            if apt_list_data==[]:
                dong_apt_list[k]=pd.DataFrame(columns=temp_data.columns)
            else:
                dong_apt_list[k]=pd.concat(apt_list_data)
        gungu_apt_list[j]=pd.concat(dong_apt_list)
        gungu_apt_list[j].to_csv(temp["complexDetail"]["roadAddressPrefix"]+".csv",encoding="CP949")
    final_data=pd.concat(gungu_apt_list)
    final_data.to_csv(temp["complexDetail"]["roadAddressPrefix"].split()[0]+".csv",encoding="CP949")

시도, 시군구, 동면읍 아파트 별로 빈 리스트를 만들어 그 리스트 안에 DataFrame으로 저장하고 나중에 한꺼번에 concat하는 원리이다.

네이버 부동산에서 빈데이터가 return되면 멈추는 현상이 있어 데이터 저장 시점마다 try문을 넣어 빈칸을 저장하도록 하였다.

 

출처: https://leesunkyu94.github.io/data%20%EB%A7%8C%EB%93%A4%EA%B8%B0/naver-real-estate/#

반응형
반응형

Python 실행 파일(exe) 만들기

매번 .py로 실행하기 번거로워서 .exe 실행파일로 만들었습니다. exe 파일을 만드는 방법은 여러가지 많지만, 여러 방법 중 하나인 pyinstaller 모듈을 이용한 exe 파일 만드는 방법을 포스팅 했습니다.

 

실행환경

운영체제 : window 10

파이썬 버전 : python 3.7

 

pyinstaller 설치

pip를 이용하면 간단하게 설치 할 수 있습니다.

 

커맨드 명령어

>> pip install pyinstaller
C:\Users\75385\Desktop>pip install pyinstaller
Collecting pyinstaller
  Downloading https://files.pythonhosted.org/packages/03/32/0e0de593f129bf1d1e77eed562496d154ef4460fd5cecfd78612ef39a0cc/PyInstaller-3.4.tar.gz (3.5MB)
    100% |████████████████████████████████| 3.5MB 3.3MB/s
  Installing build dependencies ... done
Requirement already satisfied: setuptools in c:\users\75385\appdata\local\programs\python\python37-32\lib\site-packages (from pyinstaller) (40.6.2)
Collecting pefile>=2017.8.1 (from pyinstaller)
  Downloading https://files.pythonhosted.org/packages/ed/cc/157f20038a80b6a9988abc06c11a4959be8305a0d33b6d21a134127092d4/pefile-2018.8.8.tar.gz (62kB)
    100% |████████████████████████████████| 71kB 5.2MB/s
Collecting macholib>=1.8 (from pyinstaller)
  Downloading https://files.pythonhosted.org/packages/41/f1/6d23e1c79d68e41eb592338d90a33af813f98f2b04458aaf0b86908da2d8/macholib-1.11-py2.py3-none-any.whl
Collecting altgraph (from pyinstaller)
  Downloading https://files.pythonhosted.org/packages/0a/cc/646187eac4b797069e2e6b736f14cdef85dbe405c9bfc7803ef36e4f62ef/altgraph-0.16.1-py2.py3-none-any.whl
Collecting pywin32-ctypes (from pyinstaller)
  Downloading https://files.pythonhosted.org/packages/9e/4b/3ab2720f1fa4b4bc924ef1932b842edf10007e4547ea8157b0b9fc78599a/pywin32_ctypes-0.2.0-py2.py3-none-any.whl
Collecting future (from pefile>=2017.8.1->pyinstaller)
  Downloading https://files.pythonhosted.org/packages/90/52/e20466b85000a181e1e144fd8305caf2cf475e2f9674e797b222f8105f5f/future-0.17.1.tar.gz (829kB)
    100% |████████████████████████████████| 829kB 5.0MB/s
Installing collected packages: future, pefile, altgraph, macholib, pywin32-ctypes, pyinstaller
  Running setup.py install for future ... done
  Running setup.py install for pefile ... done
  Running setup.py install for pyinstaller ... done
Successfully installed altgraph-0.16.1 future-0.17.1 macholib-1.11 pefile-2018.8.8 pyinstaller-3.4 pywin32-ctypes-0.2.0

설치가 완료되었습니다.

 

exe 파일 만들기

테스트용 test.py 만들기

import datetime

if __name__ == "__main__" :
    print("Start.")

    cur_time = datetime.datetime.now()
    print("Current time : %s" % cur_time)
    
    print("End.")

현재 시간을 출력해주는 간단한 프로그램입니다.

 

test.py가 있는 디렉토리로 이동하여, pyinstaller 명령어를 입력해줍니다.

>> pyinstaller --onefile test.py

참고로 --onefile 이라는 옵션을 넣어주면, 하나의 실행파일로 생성이 됩니다. (참고로, --noconsole 옵션을 넣어주면 콘솔창이 뜨지 않고 실행이 됩니다.)

 

실행 결과

명령어를 실행하면, 이와같이 dist, build 등 여러개의 파일이 생성됩니다.

dist 폴더로 들어가보면, 아래와 같이 실행파일(test.exe)이 생성된것을 확인 할 수 있습니다.

 

더블 클릭하여 실행할 수 도 있고, cmd로 확인할 수도 있습니다.

 

실행 결과가 너무 빨라서 사진을 찍기 힘들어, cmd를 이용하여 실행 해봤습니다.

 

참고 - 실행파일에 이미지 넣기

실행파일의 아이콘을 바꾸고 싶다면, 실행파일로 빌드하는 명령어에 --icon 옵션을 추가해줍니다.

>> pyinstaller --icon=test.ico --onefile test.py

그리고 옵션값에 원하는 이미지의 경로를 넣어줍니다. 이미지의 포맷은 ico이어야 합니다.

(https://icoconvert.com/ 해당 사이트에서 ico 이미지로 convert 가능합니다.)

 

 

실행파일이 이쁘게 잘 만들어졌습니다.

 

출처 : https://hongku.tistory.com/338

반응형
반응형

Python pip 설치시 이런 애러도 발생합니다.

 

...
... 


LINK : fatal error LNK1158: cannot run 'rc.exe'
error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\x86_amd64\\link.exe' failed with exit status 1158

 저와 같은 경우에는 Python package jpype1 설치 시에 발생 하였는데, 해당 패키지에 포함된 C++ 관련 build 중에 지정된 경로와 실제 compiler의 위치에 차이가 있었지 않았나 추정 합니다.

 

 이를 해결하는 방법으로 윈도우에 기본 내장되어 있는 rc.exe 및 연관 파일을 설치 시스템이 접근하고 있는 폴더로 복사하하는 방법이 있습니다.

Copy File

 - rc.exe

 - rcdll.dll

 

[From]
C:\Program Files (x86)\Windows Kits\8.1\bin\x86

 

[To]

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\x86_amd64

 본 에러가 왜 발생하는지는 정확히 파악하지 못하였습니다. 추정컨데, 64비트용 Visual C++ 2015 Build Tools 설치시에 해당 32비트(x86) 파일이 포함되지 않은 상태로 시스템 Path 의 참조 위치가 변경된 것이 아닌가 생각합니다.

출처: https://ohohs.tistory.com/entry/Error-Solution-fatal-error-LNK1158-cannot-run-rcexe [Jay-go]

반응형

+ Recent posts