반응형

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

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

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

제일먼저 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/#

반응형
반응형

Prettier란?

코드 포멧터(Code Formatter)란 개발자가 작성한 코드를 정해진 코딩 스타일을 따르도록 변환해주는 도구를 말합니다. Prettier는 이러한 코드 포멧터 중에서도 최근에 가장 인기를 많이 얻어 거의 표준이 되고 가고 있는 자바스크립트 라이브러리 이다.

코드를 저장 시 정해놓은 규칙에 맞게 자동으로 정렬해서 가독성을 높이고 코드 스타일을 통일할 수 있다.

Prettier 설정방법

  1. IntelliJ IDEA Plugins 설치
    Settings(Ctrl + Alt + s) > plugins > Prettier 검색 > Install > IDE restart
  2. 필요한 Dependencies 설치
   $ npm install --save-dev --save-exact prettier 
   // 정확히 일치하는 버전의 패키지를 추가한다.
  1. Prettier 설정
    Settings(Ctrl + Alt + s) > Languages & Framworks > JavaScript > Prettier
    • Node Interpreter : 프로젝트에 사용 중인 버전의 node를 선택
    • Prettier package : 프로젝트 루트 디렉토리/node_modules/prettier 모듈 디렉토리를 선택

  2. .prettierrc.js 파일 생성
   module.exports =  {
          semi:  true,
          trailingComma:  'all',
          singleQuote:  true,
          printWidth:  100,
          tabWidth:  2,
  };

Prettier 적용방법

Ctrl + Shift + Alt + P

반응형
반응형

css 가상요소(Pseudo-Element) :before :after사용법

 
 
*** 들어가기 전에***
가상선택자, 가상클래스, 가상요소??
 
가상클래스

CSS pseudo-class 는 선택될 요소(element)의 특별한 상태를 지정하는 선택자(selector)에 추가된 키워드입니다. 예를 들어 :hover는 사용자가 선택자에 의해 지정된 요소 위를 맴돌(hover) 때 스타일을 적용합니다.

가상 클래스(pseudo-class)는, 가상 요소(pseudo-elements)와 함께, 문서 트리의 콘텐츠 뿐만 아니라, 탐색기 히스토리 (가령, :visited), 콘텐츠 상태(일부 폼 요소의 :checked 같은) 혹은 마우스 위치 (마우스가 요소 위인지 알 수 있는 :hover 같이)처럼 외부 요인(factor) 관련 요소에 스타일을 적용할 수 있습니다.

 

 
 
가상요소
가상 클래스(pseudo-classes)처럼, 가상 요소(pseudo-element)는 선택자(selector)에 추가되지만 특별한 상태를 기술하는 대신, 문서의 특정 부분을 스타일할 수 있습니다. 예를 들어, ::first-line 가상 요소는 선택자에 의해 지정된 요소의 첫 줄만을 대상으로 합니다.
 
 
 
 
 
:before
:after

가상선택자는 꾸밈을 위해서 의미없는 태그를 더 추가해야 될 때, 태그 대신에 가상으로 처리해 주는 쓸모 많은 css 기능입니다. 만일 html 구조에서 필요한 내용이라면 가상선택자로 만들면 안되겠죠~

  1. 어떤 사항일 때 css 가상선택자를 사용하나요?
의미없는 태그를 쓰지 않도록 도와줌, 태그 최소화.
  • 목록형 꾸밈
  • 버튼 꾸밈
  • 간단한 아이콘 만들기(닫기버튼, 화살표 등)  ** 도형을 만들 때 주의할 점: 보여지는 픽셀두께가 세밀하지 않기 때문에 비주얼적으로 중요한 아이콘이면 이미지를 쓰는 것을 추천
  • 이미지 아이콘을 넣고 싶을 때
  • 꾸밈 한글을 넣고 싶을 때 (tag의 #, 또는 콤마 등)
.class-name:before {
	content:'';
	width:;
	height:;
	
}

:before 해당 태그의 앞에 놓여진다.
:after 해당 태그의 다음 위치에 놓여진다.

content:'' : 가상선택자에 필수로 들어가는 요소. 작음따음표'' 안에는 텍스트 내용을 넣고, 없으면 작은따음표만 넣기
가상선택자는 부피가 없으므로, 아이콘을 표현할 땐 꼭 너비와 높이를 정해주어야 한다. transform을 쓸 때는 블럭요소(display:block 또는 display:inline-block)가 되어야 적용가능하다.

:before 와 ::before의 차이

차이는 없다. 오히여 더블콜론(::)::before을 쓴다면 ie8 이하 버전은 적용이 되지 않기 때문에 클론 하나만(:):before 쓰는 것이 좋다고 볼 수 있다.
css2에서는 콜론이 하나였다가, css3에는 더블클론으로 바뀌었다.

가상선택자 적용이 안되는 경우

  1. 폼(form, input...) 태그와 이미지 태그(img)는 가상선택자가 적용되지 않는다.(이 부분은 지금까지도 몰랐던 사실.. 예시를 만들면서 알게됨)
    가상선택자 이슈
  2. ie7 이하버전은 지원하지 않는다.
  3. ie8 이하버전은 더블콜론(::)이 적용되지 않음

 

 

 

출처: https://green-webdesigner.tistory.com/20

반응형
반응형

Vue.js에서 computed 프로퍼티는 매우 유용하게 사용된다. 그러나 처음 Vue.js를 시작할때 computed와 watch가 모두 반응형이라는 키워드과 관련이 있기 때문에 이 둘을 혼동하곤 했다. Vue.js의 강점을 잘 살려서 코딩을 하기위해 이 두가지 키워드를 잘 알고 있어야 한다.

Computed — 반응형 getter

computed를 한마디로 얘기하자면 “반응형 getter”이다. 아래 예시를 보자. vue.js 공식가이드 문서에 나오는 예시와 동일한 로직을 옮긴것이다.

<template>
  <div>
    <p>원본 메시지: "{{ message }}"</p>
    <p>역순으로 표시한 메시지: "{{ reversedMessage }}"</p>
  </div>
</template>

<script>
export default {
  name: 'test',
  data(){
    return {
      message: '안녕하세요'
    }
  },
  computed: {
    reversedMessage: function () {
      return this.message.split('').reverse().join('')
    }
  }
}
</script>

computed 프로퍼티를 보면 reverseMessage 라는 프로퍼티에 값으로 익명함수가 할당되어있다. computed에 정의하는 이 익명함수는 반드시 값을 리턴하도록 작성되야한다.

getter

computed의 reverseMessage 프로퍼티가 정될때 내부적으로는 Object.defineProperty를 통해 정의되며, 이때 익명함수가 getter로 설정된다. reverseMessage 를 함수가 아니라 일반 객체처럼 사용할 수 있는점과 호출될때만 계산이 이루어지고, 계산결과가 캐싱되는 특성이 생기게 된것은 getter의 특성덕분이다(이는 methods와의 차이를 유발하는 지점이기도 하다). 하지만 바로 이점 때문에 값이 변하게 되어도 캐싱때문에 변경된 값을 인지하지 못하는 단점이 생기게된다.

반응형(reactive)

Vue.js 는 이 단점을 상쇄하고 반응형을 구현하기 위해 특별한 장치를 한다. getter 함수 내에 속한 프로퍼티의 변경여부를 추적하는 것이다.(마이구미님 글 참고) 위 예시에서는 message 를 감시하고 있다가 message의 값이 변경되면 reverseMessage 를 다시 계산한다. 결국, computed는 사용하기 편하고, 자동으로 값을 변경하고 캐싱해주는 아주 끝내주는 “반응형 getter”라 부를 수 있겠다. (반응형은 Computed뿐 아니라 Vue.js 의 전반의 주요한 특징으로 볼 수 있다.)

Watch — 반응형 콜백

변경을 감시(watch)한다는 점 때문에 computed와 watch를 혼동할 수 있다.걱정할 필요는 없다. computed에 비해 watch는 단순하고 이해하기 쉽기 때문이다. watch는 Vue 인스턴스의 특정 프로퍼티가 변경될때 지정한 콜백함수가 실행되는 기능이다. 위 예시를 응용한다면 아래와 같을 것이다.

<template>
  <div>
    <p>원본 메시지: "{{ message }}"</p>
    <p>역순으로 표시한 메시지: "{{ reversedMessage }}"</p>
  </div>
</template>

<script>
export default {
  name: 'test',
  data(){
    return {
      message: '안녕하세요',
      reversedMessage: ''
    }
  },
  watch: {
    message: function (newVal, oldVal) {
      this.reversedMessage = newVal.split('').reverse().join('')
    }
  }
}
</script>

watch를 정의한 부분(17~21)을 보면 message 프로퍼티에 익명함수가 할당되어있다. 이 익명함수가 콜백함수 역할을 할 것이고, message 프로퍼티가 변경되면 변경된 값을 콜백함수의 첫번째 인자로 전달하고, 이전 값을 두번째 인자로 전달하여 실행한다. computed가 새 프로퍼티를 생성하고 그것의 getter 로 익명함수를 설정되는 것과는 달리 watch는 아무 프로퍼티도 생성하지 않고 익명함수는 단순히 콜백함수로의 역할을 한다. watch에 명시된 프로퍼티는 감시할 대상을 의미할 뿐이다.

어떻게 사용할 것인가

  • 위의 예시처럼 인스턴스의 data에 할당된 값들 사이의 종속관계를 자동으로 세팅하고자 할때는 computed로 구현하는것이 좋다. 그러니까 reverseMessage  message 값에 따라 결정되어진다. 이 종속관계가 조금이라도 복잡해지면 watch로 구현할 경우 중복계산이 일어나거나 코드 복잡도가 높아질 것이다. 이는 오류도 더 많이 발생시킬 것이다.
  • watch는 특정 프로퍼티의 변경시점에 특정 액션(call api, push route …)을 취하고자 할때 적합하다.
  • computed의 경우 종속관계가 복잡할 수록 재계산 시점을 예상하기 힘들기 때문에 종속관계의 값으로 계산된 결과를 리턴하는 것 외의 사이드 이펙트가 일어나는 코드를 지양해야한다.
  • 더 쉽게 판단하는 방법: 만약 computed로 구현가능한 것이라면 watch가 아니라 computed로 구현하는것이 대게의 경우 옳다.

참고 문서

출처: https://jeongwooahn.medium.com/vue-js-watch%EC%99%80-computed-%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%99%80-%EC%82%AC%EC%9A%A9%EB%B2%95-e2edce37ec34

반응형

+ Recent posts