# -*- coding: utf-8 -*- """ Created on Thu Jul 8 11:20:51 2021 @author: hs-702 """ # Selenium 이용한 실습 # K-stat (https://stat.kita.net/) 국내통계>>품목수출입>>100개씩보기 import pandas as pd from selenium import webdriver from bs4 import BeautifulSoup import time import shutil import os, sys url = 'https://stat.kita.net/main.screen' driver = webdriver.Chrome('C:/Users/hs-702/Desktop/veroro/chromedriver_win32/chromedriver.exe') # 빈 브라우저 띄움 defalt_next_page_tag_path = "/html/body/div[2]/div[2]/div[2]/div[2]/form/div[4]/div/span/" driver.get(url) # url 접속 from selenium.webdriver.support.ui import Select # 국내통계 링크를 x+path로 접근 driver.find_element_by_xpath("/html/body/div[2]/div[1]/div/div[2]/ul/li[1]/a/img").click() # 픔목수출입 링크를 x+path가 아닌 텍스트로 접근 driver.find_element_by_link_text("품목 수출입").click() # 표시행수선택 driver.find_element_by_xpath("/html/body/div[2]/div[2]/div[2]/div[2]/form/div[1]/div/select").click() select = Select(driver.find_element_by_id('listCount')) # select를 쓰려면 import Select를 해줘야 함 select.select_by_value('100') # select.click()을 하면 'NoneType' object has no attribute 'click' 에러발생 # 조회버튼클릭 driver.find_element_by_xpath("/html/body/div[2]/div[2]/div[2]/div[2]/form/fieldset/div[3]/a/img").click() time.sleep(2) # bs로 parsing bs = BeautifulSoup(driver.page_source,'html.parser') time.sleep(2) total_page = bs.find('span',{'id':'total_count'}).get_text() total_page= int(total_page.replace(',','')) # print(total_page) # 다음페이지로 넘어가기 # 5,291 # /html/body/div[2]/div[2]/div[2]/div[2]/form/div[1]/dl[1]/dd/span # pages = int(total_count)/100 # 현재페이지 HTML속성값: "selected">숫자 # 다음페이지 버튼 상대경로: /html/body/div[2]/div[2]/div[2]/div[2]/form/div[4]/div/span/a[숫자] # 다음페이지 버튼 HTML속성값: # nextpage = driver.find_element_by_class_name('doPagingSearch') >>> 속성값이라서 따로 숫자만 빼서 이와 같이 사용할 수 없음 # 해당 URL에서 파일을 다운받고 특정 폴더에 이동시켜 파일 이름을 규칙에 맞게 순차적으로 변경시키시오 (file_1부터 file_53까지) # shutill.move(path + 'A', path + 'B')>> 파일을 다운 받은 A경로에서 B경로로 파일을 이동하시오 # os.rename(path A, path B) >> 파일이름을 다음과 같이 변경시키시오 (파일확장자까지 써야함) # 다운로드 링크 상대주소 : /html/body/div[2]/div[2]/div[2]/div[2]/form/div[1]/div/a[1] # 다운로드 링크 속성값 : default_path = '/html/body/div[2]/div[2]/div[2]/div[2]/form/div[4]/div/span/a' pages = int((total_page/100)+1) # pages = 53 counter = 0 for page in range(1, pages): if (page % 10) != 0: var = page % 10 time.sleep(2) # 한 페이지에 존재하는 엑셀파일 다운로드 후 shutill 이용해서 폴더 옮기기 driver.find_element_by_xpath("/html/body/div[2]/div[2]/div[2]/div[2]/form/div[1]/div/a[1]").click() time.sleep(3) shutil.move('C:/Users/hs-702/Downloads' + '/K-stat 총괄 .xls', 'C:/Users/hs-702/Desktop/veroro' + '/fileDir') time.sleep(2) # os.rename을 이용해서 파일명을 1부터 53까지 순차적으로 변환하기 file_name = 'kstat_' + str(counter) + '.xls' #time.sleep(3) os.rename('C:/Users/hs-702/Desktop/veroro' + '/fileDir/K-stat 총괄 .xls', 'C:/Users/hs-702/Desktop/veroro' + '/fileDir/' + file_name) #time.sleep(3) counter = counter + 1 #time.sleep(3) # 다음 페이지를 클릭하기 nextpage = driver.find_element_by_xpath(default_path +'[' + str(var) + ']') nextpage.click() #time.sleep(3) else: nextpage = driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[2]/div[2]/form/div[4]/div/a[2]') nextpage.click() time.sleep(3) ''' Xpath의 상대주소 숫자값은 [1]-[9]까지이며 클릭하고 있는 해당 페이지는 a[숫자]대신 strong이다. /html/body/div[2]/div[2]/div[2]/div[2]/form/div[4]/div/span/a[1] /html/body/div[2]/div[2]/div[2]/div[2]/form/div[4]/div/span/strong 다음페이지 화살표의 상대주소 값은 다음과 같다 /html/body/div[2]/div[2]/div[2]/div[2]/form/div[4]/div/a[2] page의 range가 1부터 53까지 간다면 다음과 같은 에러가 뜬다. NoSuchElementException: no such element: Unable to locate element: {"method":"xpath","selector":"/html/body/div[2]/div[2]/div[2]/div[2]/form/div[4]/div/span/a[10]"} (Session info: chrome=91.0.4472.124) 이 문제를 해결하기 위해 1~53까지의 페이지에서 나머지가 1~9까지 나오는 행위를 반복시키는 코드를 짜야한다. ''' # 데이터를 다운받고 폴더와 이름을 바꿀 때, 파일자체가 없거나 파일내용이 없을 때 예외처리가 필요하다. #pd.remove('C:\Users\hs-702\Desktop\veroro\fileDir\kstat_0.xls) >> 잘못된 파일을 삭제한다. #pd.read_excel('C:\Users\hs-702\Desktop\veroro\fileDir\kstat_0.xls') >> 판다스를 활용해서 데이터사이즈를 볼 수 있다. # try, except, pass 문을 사용하게되면 특정 페이지의 파일을 받지 못하는 문제가 발생하게 된다. # 또한 counter+1 이 except 전에 실행되므로 에러발생을 제외하고 파일은 순차적으로 받아지지만 53페이지 파일 중 49개 만 받게 되는 문제가 발생한다. # 실습시간이 부족해서 예외처리 이후의 실습은 선생님 답을 참고하도록 한다. ''' print(temp, shape) shape[0]은 row값, shape[1]은 column값 for page in range(1, pages): if (page % 10) != 0: var = page % 10 time.sleep(2) driver.find_element_by_xpath("/html/body/div[2]/div[2]/div[2]/div[2]/form/div[1]/div/a[1]").click() time.sleep(3) try: temp = pd.read_excel('C:/Users/hs-702/Downloads/K-stat 총괄 .xls') shutil.move('C:/Users/hs-702/Downloads' + '/K-stat 총괄 .xls', 'C:/Users/hs-702/Desktop/veroro' + '/fileDir') time.sleep(2) file_name = 'kstat_' + str(counter) + '.xls' os.rename('C:/Users/hs-702/Desktop/veroro' + '/fileDir/K-stat 총괄 .xls', 'C:/Users/hs-702/Desktop/veroro' + '/fileDir/' + file_name) counter = counter + 1 nextpage = driver.find_element_by_xpath(default_path +'[' + str(var) + ']') nextpage.click() except (FileNotFoundError): pass except temp.shape[0]<5 or temp.shape[1]!=13: pass else: nextpage = driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[2]/div[2]/form/div[4]/div/a[2]') nextpage.click() time.sleep(3) ''' # 다운로드받은 파일을 전처리가 된 하나의 큰 데이터프레임으로 만든다 # 중복되는 헤더정보 제거, K-stat 총괄 column 없애기, unnamed된 column 이름을 바꾼다. # Merge된 column은 DF에서 읽을 수 없다. ''' #시작 디렉터리부터 시작하여 그 하위 모든 디렉터리를 차례대로 방문 temp = os.walk ('C:/Users/hs-702/Desktop/veroro/fileDir') # 파일디렉터리의 idx와 row를 차례대로 프린트해서 보기. for idx, row in enumerate(temp): print(idx) print(row) # print(row)를 해보면 다음과 같은 결과를 얻는다. ''' ('C:/Users/hs-702/Desktop/veroro/fileDir2', [], ['kstat_0.xls', 'kstat_1.xls', 'kstat_10.xls', 'kstat_11.xls', 'kstat_12.xls', 'kstat_13.xls', 'kstat_14.xls', 'kstat_15.xls', 'kstat_16.xls', 'kstat_17.xls', 'kstat_18.xls', 'kstat_19.xls', 'kstat_2.xls', 'kstat_20.xls', 'kstat_21.xls', 'kstat_22.xls', 'kstat_23.xls', 'kstat_24.xls', 'kstat_25.xls', 'kstat_26.xls', 'kstat_27.xls', 'kstat_28.xls', 'kstat_29.xls', 'kstat_3.xls', 'kstat_30.xls', 'kstat_31.xls', 'kstat_32.xls', 'kstat_33.xls', 'kstat_34.xls', 'kstat_35.xls', 'kstat_36.xls', 'kstat_37.xls', 'kstat_38.xls', 'kstat_39.xls', 'kstat_4.xls', 'kstat_40.xls', 'kstat_41.xls', 'kstat_42.xls', 'kstat_43.xls', 'kstat_44.xls', 'kstat_45.xls', 'kstat_46.xls', 'kstat_47.xls', 'kstat_48.xls', 'kstat_49.xls', 'kstat_5.xls', 'kstat_50.xls', 'kstat_51.xls', 'kstat_52.xls', 'kstat_6.xls', 'kstat_7.xls', 'kstat_8.xls', 'kstat_9.xls']) ''' # row[2]는 row의 3번째 element인 kstat_1.xls부터 kstat_52.xls까지의 파일 이름들을 의미한다. # 하지만 character순서로 정렬되어 있어서 0, 1, 10~19, 2, 20~29, 3, 30~39, 4, 40~49, 5, 50~52, 6, 7, 8, 9.xls 순서가 된다. # total에는 read_excel 메서드를 사용해서 파일 경로안의 파일을 업로드함 (파일이름의 string값이 들어간다.) total = [] for row in row[2]: print(row) total.append(pd.read_excel('C:/Users/hs-702/Desktop/veroro/fileDir2/' + row)) # pd.concat는 각각의 DF를 기하학적으로 붙이는 기능을 한다. # concat에서 축정보를 주지 않으면 세로로 붙인다. DF를 붙일 때는 각 DF의 row/column명이 일치해야 한다. # 그래서 total.append하기 전에 column명에 이름을 먼저 주고 같은형식으로 append를 하는 것이 좋다. # test1: total의 index 2, 3 row가 pd.concat로 세로연결되어 있음을 볼 수 있다. test1 = pd.concat([total[1], total[2]]) # test2와 3은 total의 index 2 row의 전처리 전후 과정을 나타낸다. (test3 = 2열부터 끝까지 모든 데이터) test2 = total[1] test3 = test2.iloc[:,1:] # test4는 전처리된 test3의 column명을 코드로 바꾼 것이다. test4 = test3.rename(columns = {'Unnamed: 1': '코드'})