오지's blog

진료행위정보서비스 크롤러 개발중 해결한 selenium을 이용한 년월 선택문제 본문

개발노트/Python

진료행위정보서비스 크롤러 개발중 해결한 selenium을 이용한 년월 선택문제

오지구영ojjy90 2021. 7. 4. 21:29
728x90
반응형

공공데이터포털 내 건강보험심사평가원에서 제공하는 진료행위정보서비스 API가 있다 그러나 2013년 이후는 모두 0이 나오길래 이상하여 직접 기관에 전화하여 확인하니 애석하게도 2013년 이후의 데이터는 안넣고 있고 기약은 없지만 내년즈음 서비스를 개편하여 제공하려고 계획은 하고 있다고 한다. 그걸 세월아 내월아 기다릴수 없어 방법을 찾아본 결과 직접 심평원에서 제공하고 있는 보건의료빅데이터개방시스템에서 직접 엑셀파일을 다운로드 하는 크롤러를 개발하는 방법밖에 없었다. 그리고 나서 엑셀파일을 직접 데이터베이스에 넣는 방식으로 데이터 구축을 해야 했다.

그러나 나와 같은 문제가 발생하여 어떤 훌륭한 분이 크롤러를 개발해 두었다. 문제는 이 크롤러가 개발된지 오래되어 deprecate된것이 있고 시작 날짜는 지정되는데 끝날짜는 지정되지 않는 문제가 있었다.

그래서 끝날짜를 지정하도록 수정하고 싶었는데 제대로 되지 않았다. 다음은 그 문제의 코드이다.

 

                    ### 검색 시작 날짜 선택
                    # ext-gen1645 > table > tbody > tr > td > div:nth-child(4) > div > input:nth-child(2)
                    date_to = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR,"#ext-gen1645 > table > tbody > tr > td > div:nth-child(4) > div > input:nth-child(2)")))
                    date_to.click()

                    # ## 정규표현식으로 id 찾기 (엘리먼트의 id값이 날자를 기준으로 페이지 로드 마다 계속해서 바뀜)
                    html = driver.page_source
                    soup = BeautifulSoup(html, 'html.parser')
                    id_css = re.search(r'monthpicker_\d+', str(soup))

                    ## 달력에서 년도 선택
                    year_css = '#'+ id_css.group() +' > div > select > option:nth-child(9)'
                    year = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, year_css)))
                    year.click()

                    ## 달력에서 월 선택
                    month_css = '#' + id_css.group() + ' > table > tbody > tr:nth-child(3) > td:nth-child(2)'
                    month = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, month_css)))
                    month.click()


                    ### 검색 끝 날짜 선택
                    # ext-gen1645 > table > tbody > tr > td > div:nth-child(4) > div > input:nth-child(4)
                    date_from = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR,"#ext-gen1645 > table > tbody > tr > td > div:nth-child(4) > div > input:nth-child(4)")))
                    date_from.click()


                    ## 정규표현식으로 id 찾기 (엘리먼트의 id값이 날자를 기준으로 페이지 로드 마다 계속해서 바뀜)
                    html = driver.page_source
                    soup = BeautifulSoup(html, 'html.parser')
                    id_css_from = re.search(r'monthpicker_\d+', str(soup))

                    # ## 달력에서 년도 선택
                    # # monthpicker_0009401117271277718 > div > select > option:nth-child(10)
                    year_css = '#'+ id_css_from.group() +' > div > select > option:nth-child(10)'
					# 아래부분에서 exception발생
					year = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, year_css)))
                    year.click()
                    
                    ## 달력에서 월 선택
                    month_css = '#' + id_css_from.group() + ' > table > tbody > tr:nth-child(3) > td:nth-child(2)'
                    month = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, month_css)))
                    month.click()

                    ## 조회 버튼
                    driver.implicitly_wait(120)
                    search_btn = driver.find_element_by_class_name("dt-btn-search")
                    driver.execute_script("arguments[0].click();", search_btn)
                    driver.implicitly_wait(10)

 

문제는 끝년도 선택하는데 웨이팅과정에서 exception이 발생했다. 

# 아래부분에서 exception발생
year = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, year_css)))
year_css가 첫번째 달력을 가리키고 있는데 두번째 달력의 년도를 클릭하려고 하니 에러가 발생한 것이였다.

 

이에, bs4를 통해서 monthpicker_ 로 시작하는 id를 찾고  두가지가 있으니 배열과 dict형을 통해서 monthpicker_ 뒤의 숫자값을 get하는 과정을 통해 해결하였다.

다음은 문제를 해결하여 끝년도와 월이 선택되는 코드이다.

 

 

                   ### 검색 시작 날짜 선택
                    # ext-gen1645 > table > tbody > tr > td > div:nth-child(4) > div > input:nth-child(2)
                    date_to = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR,"#ext-gen1645 > table > tbody > tr > td > div:nth-child(4) > div > input:nth-child(2)")))
                    date_to.click()

                    # ## 정규표현식으로 id 찾기 (엘리먼트의 id값이 날자를 기준으로 페이지 로드 마다 계속해서 바뀜)
                    html = driver.page_source
                    soup = BeautifulSoup(html, 'html.parser')
                    mpid = soup.find_all(id=lambda x: x and x.startswith('monthpicker_'))
                    print(mpid[0].attrs['id'])
                    print(mpid[1].attrs['id'])

                    ## 달력에서 년도 선택
                    year_css = '#'+ mpid[0].attrs['id'] +' > div > select > option:nth-child(6)'
                    year = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, year_css)))
                    year.click()

                    ## 달력에서 월 선택
                    month_css = '#' + mpid[0].attrs['id'] + ' > table > tbody > tr:nth-child(3) > td:nth-child(2)'
                    month = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, month_css)))
                    month.click()


                    ### 검색 끝 날짜 선택
                    # ext-gen1645 > table > tbody > tr > td > div:nth-child(4) > div > input:nth-child(4)
                    date_from = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR,"#ext-gen1645 > table > tbody > tr > td > div:nth-child(4) > div > input:nth-child(4)")))
                    date_from.click()

                    # ## 달력에서 년도 선택
                    # # monthpicker_0009401117271277718 > div > select > option:nth-child(10)
                    year_css = '#'+ mpid[1].attrs['id'] +' > div > select > option:nth-child(10)'
                    year = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, year_css)))
                    year.click()

                    ## 달력에서 월 선택
                    month_css = '#' + mpid[1].attrs['id'] + ' > table > tbody > tr:nth-child(3) > td:nth-child(2)'
                    month = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, month_css)))
                    month.click()

                    ## 조회 버튼
                    driver.implicitly_wait(120)
                    search_btn = driver.find_element_by_class_name("dt-btn-search")
                    driver.execute_script("arguments[0].click();", search_btn)

위코드의 핵심은 monthpicker_ 로 시작하는 id값을 찾는 과정으로 다음과 같다.

mpid = soup.find_all(id=lambda x: x and x.startswith('monthpicker_'))
이를 통해 css든 xpath든 각 요소에 접근할수 있다.                    

 

refereneces

https://github.com/code-sonya/crawling

 

code-sonya/crawling

건강보험심사평가원(http://www.hira.or.kr/main.do) 데이터 크롤링 하기 - code-sonya/crawling

github.com

 

https://stackoverflow.com/questions/42195472/using-regular-expression-in-find-all-of-beautifulsoup

 

Using regular expression in find_all of Beautifulsoup

I was trying to scrape tumblr archive, the div class tag looks like given in picture The class starts with "post post_micro", I tried using regular expression but failed soup.find_all(class_=re.

stackoverflow.com

 

 

 

Comments