매매, 전월세 정보 가져오기
이전 게시글에서 소개했던 "PublicDataReader" 를 활용하여 매매 정보를 불러올 예정이다.'TransactionPrcie' 클래스를 활용한다. 표제부 정보와 달리 용량이 꽤 크기 때문에, 오래된 data는 초반에 전부 다 받아두고 csv로 저장해 둔 뒤 API 호출이 아닌 csv를 불러오는 방식으로 활용 중이다. 아래 코드 블럭에서 month만 수정하여 장기간 다운로드 구현 가능하다. 최신 data는 물건 검색 전 이번 달 data를 검색함하여 업데이트한다.자세한 내용은 Github 참조. https://github.com/WooilJeong/PublicDataReader
from PublicDataReader import TransactionPrice
# 매월 1일을 감안하여 전달, 이번달 다운로드
lastmonth = (datetime.datetime.now() - relativedelta(months=1)).strftime('%Y%m')
month = datetime.datetime.now().strftime('%Y%m')
print('다운로드 받을 월 : ', lastmonth, month)
# 법정동 코드 앞 5자리 (구 단위)
downlist = code_df['구코드'].unique()
# 기존에 저장해둔 매매 list
trade_data = pd.read_csv('trade.csv', encoding='utf-8-sig')
loan_data = pd.read_csv('loan.csv', encoding='utf-8-sig')
api = TransactionPrice(service_key) # service_key는 개별 공공 데이터 포털에서 확인 가능
for d in downlist:
for m in [lastmonth, month]:
# 최신 data를 기존 csv에 추가
pd.concat([trade_data,api.get_data(property_type="아파트", trade_type="매매", sigungu_code=d, year_month=m)], ignore_index= True)
pd.concat([loan_data,api.get_data(property_type="아파트", trade_type="전월세", sigungu_code=d, year_month=m)], ignore_index= True)
trade_data.drop_duplicates().to_csv('trade.csv', index=False, encoding='utf-8-sig')
loan_data.drop_duplicates().to_csv('loan.csv', index=False, encoding='utf-8-sig')
매매, 전월세 정보 전처리
trade_data = trade_data[['지역코드','도로명','법정동','지번','아파트','층','전용면적','년','월','일','거래금액','도로명건물본번호코드','법정동읍면동코드','해제여부']]
trade_data = trade_data[~trade_data['해제여부'].isnull()].drop(columns=['해제여부']) # 취소 제외
trade_data[['전용면적','거래금액']] = trade_data[['전용면적','거래금액']].astype(float)
trade_data['거래금액'] = np.round(trade_data['거래금액'] / 10000,3) # 억 단위로 변경
# datetime type의 거래일자 column 생성
trade_data[['년','월','일']] = trade_data[['년','월','일']].astype(str)
trade_data['거래일자'] = trade_data['년'] + "-" + trade_data['월'] + "-" + trade_data['일']
trade_data['거래일자'] = trade_data['거래일자'].astype('datetime64[ns]')
# 도로명 없는 경우는 등기 전으로 제외 처리
trade_data.dropna(subset=['도로명건물본번호코드'], axis=0, inplace=True)
trade_data['도로명건물본번호코드'] = trade_data['도로명건물본번호코드'].astype(int)
loan_data = loan_data[['지역코드','법정동','지번','아파트','층','전용면적','년','월','일','보증금액','월세금액']]
loan_data[['전용면적','보증금액','월세금액']] = loan_data[['전용면적','보증금액','월세금액']].astype(float)
# datetime type의 거래일자 column 생성
loan_data[['년','월','일']] = loan_data[['년','월','일']].astype(str)
loan_data['거래일자'] = loan_data['년'] + "-" + loan_data['월'] + "-" + loan_data['일']
loan_data['거래일자'] = loan_data['거래일자'].astype('datetime64[ns]')
# 월세 -> 전세 전환율 5.5% 일괄적용
loan_data['전월세'] = np.where(loan_data['월세금액'] == 0, '전세', '월세')
loan_data['보증금액'] = loan_data['보증금액'] + loan_data['월세금액'] * 100 / 5.5 * 12
loan_data['보증금액'] = np.round(loan_data['보증금액'] / 10000,3) # 억 단위로 변경
이후 매매, 전세, 월세를 plt로 chart를 만들 예정이다. 가로 시간 축 설정 위한 거래일자 datetime type column 생성한다. 전처리 과정에서월세 -> 전세 전환율은 5.5% 일괄 적용하여 보증금으로만 차트 생성될 수 있도록 처리했다.
PPT 생성
from pptx.util import Cm, Pt
from pptx.enum.text import PP_ALIGN
from pptx import Presentation
prs = Presentation(pptx="./template.pptx")
layout = prs.slide_layouts[0]
지도, 매매 동향, 사진 등 이미지 동시 확인 위해 엑셀 보단 PPT가 이후 검토하기 편하다.물건 세부 정보 스크래핑 전 빈 ppt 생성하고, 동일한 양식 저장하기 위해 'template.pptx' 라는 템플렛을 미리 제작하여 활용한다.슬라이드 마스터의 가장 상단 layout을 변경해두면 해당 layout으로 새 page를 만들며 물건별로 1page씩 추가 가능하다.
경매 사건 검색을 통한 물건 별 세부 내용 스크래핑
# 물건상세검색 버튼 클릭
driver.find_element(By.XPATH, '경매사건검색 버튼 XPATH 입력').click()
time.sleep(2 * (random.random()+1))
for n, row in aution_item.iterrows():
# 법원 지정
court = Select(driver.find_element(By.ID, 'idJiwonNm'))
court.select_by_value(row['법원'])
# 사건 번호
year = Select(driver.find_element(By.ID, 'idSaYear'))
year.select_by_value(row['사건번호'][:4])
number = driver.find_element(By.ID, 'idSaSer').clear()
number = driver.find_element(By.ID, 'idSaSer').send_keys("value", row['사건번호'][6:])
# 검색 클릭
driver.find_element(By.XPATH, '검색 버튼 XPATH 입력').click()
경매 개별 물건을 검색할 수 있는 '경매사건검색'에서 세부 정보 스크래핑한다..iterrows()로 물건 별로 n 회 실행할 수 있다. 필요한 정보를 순서대로 입력 후 검색 버튼 실행한다
물건 번호에 맞는 물건 상세 조회 이동
# 물건내역 list 불러오기. 다른 표에서도 동일 class_name 활용
item_list = driverCourt.find_elements(By.CLASS_NAME, 'Ltbl_dt')
for i in item_list:
if i.get_property('summary') == '물건내역 표': # 다른 표인경우 pass
if i.find_element(By.XPATH,'./tbody/tr[1]/td[1]').text == row['물건번호']: #동일 물건번호인 경우 물건 상세조회 클릭
i.find_element(By.CSS_SELECTOR, '[alt="물건상세조회"]').click()
time.sleep(2 * (random.random()+1))
break
한 사건에 여러 물건이 있는 경우가 존재한다. 물건에 맞는 물건 상세 조회로 이동하기 위해, 물건번호와 매칭 후 정확한 '물건상세조회' 버튼 클릭 필요하다.
경매 물건 사진 2장 저장
try:
# 사진 저장 위해 임의 사진 클릭
driverCourt.find_element(By.CLASS_NAME, 'photo_td').find_element(By.XPATH, './a').click()
# 팝업 창으로 연결 변경
main = driverCourt.window_handles
driverCourt.switch_to.window(main[1])
# 사진 종류 확인 후 2장 저장
filterdd = Select(driverCourt.find_element(By.ID, 'idPhotoGbncd'))
filterList = [x.text for x in court.options]
for c in range(2):
if filterList: pass # 한개도 없으면 현재사진을 저장
else: # 한개 이상 있으면 새로운 사진을 저장.
if '전경도' in filterList: selectimage = '전경도'
elif '개황도' in filterList: selectimage = '개황도'
elif '내부구조도' in filterList: selectimage = '내부구조도'
elif '관련사진' in filterList: selectimage = '관련사진'
else: selectimage = filterList[-1]
court.select_by_visible_text(selectimage)
filterList.remove(selectimage)
time.sleep(1 * (random.random()+1))
imgUrl = driverCourt.find_element(By.XPATH, '이미지의 XPATH').get_attribute("src")
imgUrl = imgUrl.replace('https', 'http')
urllib.request.urlretrieve(imgUrl, f'./temp/building_image_{c}.jpg')
time.sleep(2 * (random.random()+1))
filterdd = Select(driverCourt.find_element(By.ID, 'idPhotoGbncd'))
# 팝업 종료 후 기존 창으로 복귀
driverCourt.close()
time.sleep(0.5 * (random.random()+1))
driverCourt.switch_to.window(main[0])
time.sleep(1 * (random.random()+1))
isPic = True
except: # 가끔 사진이 없는 경우가 있으며 해당 경우는 예외 처리 함.
isPic = False
임의의 사진을 클릭하여 사진 창으로 이동한다.해당 창에서 전경도 - 개황도 - 내부구조도 - 관련사진 - 클릭한 사진순의 우선순위로 2장을 다운로드한다. 가끔 사진이 없는 경우가 존재하며 이후 ppt에서 에러 방지 위해 isPic이라는 변수로 사진 존재 여부 기록해둔다. 사진은 temp라는 임시폴더에 저장되고, 물건이 반복될때마다 새로운 이미지로 덮어씌워진다.
매각물건명세서 저장
# 물건매각명세서가 있으면 클릭. 없으면 예외 처리를 위한 변수 저장 후 pass
isPapyrus = driverCourt.find_elements(By.CSS_SELECTOR, '[alt="매각물건명세서 팝업"]')
if isPapyrus:
# 실행 후 대기
isPapyrus[0].click()
time.sleep(8 + random.random())
# 파피루스 프로그램 선택
for win in pyautogui.getAllWindows():
if win.title[:7] == 'Papyrus':
Papyrus = pyautogui.getWindowsWithTitle(win.title)[0]
break
if not Papyrus.isActive: Papyrus.activate() # 프로그램 활성화
if not Papyrus.isMaximized: Papyrus.maximize() # 프로그램 최대화
time.sleep(1 * (random.random()+1))
pyautogui.click(749,101) # 전체 맞춤
time.sleep(1 * (random.random()+1))
pyautogui.click(626,101) # 배율 선택
time.sleep(0.5 * (random.random()+1))
pyautogui.hotkey('ctrl', 'a') # 전체 선택
time.sleep(0.1 * (random.random()+1))
pyautogui.typewrite('140', interval=0.1 * (random.random()+1)) # 140 입력
time.sleep(0.1 * (random.random()+1))
pyautogui.hotkey('enter') # 엔터
time.sleep(0.5 * (random.random()+1))
# save screenshot
p = pyautogui.screenshot()
p.save(f'./temp/building_info.png')
# edit screenshot
im = PIL.Image.open(f'./temp/building_info.png')
im_crop = im.crop((720,185,1482,967))
im_crop.save(f'./temp/building_info.png', quality=100)
Papyrus.close()
isinfo = True
else:
isinfo = False
물건매각명세서는 web base가 아닌 papyrus라는 프로그램으로 확인 가능하다. 다만Selenium 이 아닌 pyautogui 라이브러리를 통해 마우스, 키보드 제어를 통해 매각 물건 명세서를 캡처 후 저장하고자 한다.원본을 저장하는 방식이 아닌 원하는 임의의 영역을 캡처하는 방식이라...사실 이 방법이 맞는건지 모르겠다...추후 좋은 방법이 확인되면 변경하고자 한다. 저장을 완료하면 최종적으로 이전으로 돌아와 다음 물건 정보 입력에 대비한다.
이후 목표
- 네이버 지도 저장, 매매 전월세 추이 그래프 저장 예정이다.
- PPT에 원하는 정보 기록 후 최종본 저장한다.
'Python > 파이썬 자동화' 카테고리의 다른 글
[파이썬 자동화] 법원 경매 물건 검색 (마무리) (0) | 2024.06.26 |
---|---|
[파이썬 자동화] 법원 경매 물건 검색 (4) (0) | 2024.06.19 |
[파이썬 자동화] 법원 경매 물건 검색 (2) (0) | 2024.06.05 |
[파이썬 자동화] 법원 경매 물건 검색 (1) (2) | 2024.05.29 |
[파이썬 자동화] Python Selenium 기초 기능 구현 (3) | 2024.05.22 |