BRICKSTUDY

데이터 수집 분투기 본문

Data

데이터 수집 분투기

ynrnwa 2024. 12. 1. 10:25

 

안녕하세요. 브릭스스터디의 김바롬입니다.

이번 공유 내용은 제가 크롤링 업무를 진행하면서 경험한 과정을 공유하기 위해 글을 작성했습니다.

 

🚩목적

제가 다니는 자사 앱의 서비스 기능 중 사용자가 장기요양기관을 검색하면 장기요양기관의 정보를 제공하는 기능이 있는데, 이 기능에서 사용하는 데이터가 오래되어 올바른 정보를 제공하지 못하고 있기 때문에 관련 테이블들의 업데이트가 필요했습니다.

 

그래서 공공 데이터포탈에서 제공하는 API를 통해 9개 테이블의 데이터를 업데이트하는 과정에서 발생한 문제점과 해결방법에 대해서 순차적으로 작성하였습니다.

 

 

🛑 Problem 1. 주소와 좌표

API 오퍼레이션 중 일반현황 상세에 대한 데이터가 주소와 좌표에 대한 값이 없었습니다.

 

🔵 Solution 1. 추가적인 파일 데이터의 조인과 API 사용

공공 데이터포탈에서 '국토교통부_행정구역법정코드' 파일데이터를 로드하여 일반현황 상세 테이블의 도로명 코드로 조인을 하여 시도명과 시군구명, 도로명을 구해서 주소를 얻을 수 있었고, 카카오 API를 통해 x,y 좌표의 값을 얻었습니다.

 

  • 주소 : roadNmCd(도로명코드)의 시도 코드를 시도명으로 변환 + roadNmCd(도로명코드) 시군구 코드를 시군구명으로 변환 + roadNmCd(도로명코드) 도로명 번호를 도로명으로 변환 + gunmulMlno(건물 본번) + gunmulSlno(건물 부번) + fl(층)
  • x, y 좌표 : 카카오 API를 통해 요청 매개변수인 주소로 x와 y의 좌표 값을 얻음

 

🛑 Problem 2.  API 오퍼레이션 제공 불가

이전에 제공되었던 사진과 평과 결과에 대한 오퍼레이션이 없어져서 제공기관에 문의했지만 사정상 문제가 있어서 앞으로는 제공을 하지 않는다고 답변을 받았습니다.

 

🔵 Solution 2.크롤링으로 데이터 수집

대체할 수 있는 데이터를 찾다가 제공기관인 국민건강보험공단에서 제공하는 요양기관찾기 서비스를 찾게 되었고 API에서 제공하는 않는 데이터를 다 얻을 수 있어서 크롤링을 통해 데이터를 가져오기로 결정했습니다.

그림(1)
그림(2)

 

🛑 Problem 3. 동적 웹페이지

beautifulsoup을 사용해서 최근년도의 평가결과와 점수를 크롤링 시도를 하였지만 표를 나타내는 부분이 동적으로 데이터를 로드하고 있어 일반적인 HTTP 요청만으로는 데이터를 가져올 수 없었습니다.

 

🔵 Solution 3. Selenium 사용

실제 브라우저를 제어하는 Selenium을 사용해서 데이터가 렌더링된 상태로 가져올 수 있어 표에 있는 평가결과와 점수를 가져올 수 있었습니다.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

url = '{웹 페이지 링크}'

dirver = webdriver.Chrome()
driver.get(url)

wait = WebDriverWait(driver, 10)
element = wait.until(EC.presence_of_element_located((By.TAG_NAME, 'tr')))

search_tag = driver.find_element(By.TAG_NAME, 'div').text
search_id = driver.find_element(By.ID, 'tr').text
search_calss = diver.find_element(By.CLASS_NAME, 'tbalistBody').text
search_xpath = driver.find_element(By.XPATH, '//*[@id="body"]/div/div[2]/div[5]/div/table/tbody/tr[2]/td[1]').text
# 복수형은 find_elements이고 리스트로 반환이 됨

driver.quit()

 

🛑 Problem 4. URL의 쿼리 문자열 구성 문제

https://www.longtermcare.or.kr/npbs/r/a/201/selectLtcoSrchDetail.web?ltcAdminSym=21111000066&adminPttnCd=H31&paymtVltClsfcTypeCd=07&paymtVltClsfcTypeCdSusi=&paymtVltMgmtNo=012023070001&vltMgmtYyyy=2023&aTab=12&pgmId=&rcdCnt=0&paymtVltMgmtNoOld=012019070001&paymtVltMgmtNo2=

위 예시 링크는 평가 정보 탭의 웹 페이지 링크이고 API와 동일하게 요양기관기호(ltcAdminSym)와 기관코드(adminPttnCd)만 변경하면서 url을 확인한 이후에 크롤링을 시도하였는데 데이터가 대부분 null로 가져오는 것을 확인했습니다.

 

🔵 Solution 4. 목록을 크롤링

paymtVltMgmtNo, paymtVltMgmtNoOld, paymtVltMgmtNo2 키에 따라 평가결과에 대한 값을 가져오는 것을 파악하였지만 명확하게 어떤 값으로 서버에 요청하는 것인지 알 수가 없어서 그림(1)과 그림(2)의 사이에 장기요양기관의 목록과 지도를 보여주는 페이지에서 기관별로 상세(그림2) 링크 주소를 크롤링했습니다.

그림(3)

🛑 Problem 5. 페이지 오류

사진 탭에서 사진을 크롤링하는 과정에서도 다음페이지 버튼을 클릭했을 때 오류가 발생하였고 마지막 페이지 버튼만 정상적으로 동작하는 문제가 있었습니다.

그림(4)

🔵 Solution 5. From back to front

해당 문제를 해결하기 위해 사진 탭에서 제일 마지막 페이지로 이동하고 마지막 페이지에서부터 앞으로 이동하면서 사진과 제목의 텍스트를 크롤링하였습니다.

 

🤐비하인드

1. 데이터 시점의 불일치

API로 가져온 장기요양기관의 수와 Solution 4에서 크롤링했던 장기요양기관의 수가 달랐습니다.

이유는 API에서 제공하는 데이터의 시점과 홈페이지에서 제공하는 서비스의 데이터의 시점이 서로 달라서 그 기간 사이에 폐업을 하거나 새로 신설된 장기요양기관이 있어 추가적인 크롤링을 해야 했습니다.

 

2. 지역의 명칭 혹은 행정구역의 변경

전처리나 카카오 API를 통해 좌표 값을 구하는 과정에서 null 값이 많이 발생하였는데, 원인은 행정명이나 행정구역의 변경으로 일치하는 값이 없어서 예외처리를 해야 했습니다. 예를 들면 강원도는 강원특별자치도, 전라북도는 전북특별자치도와 같이 지역의 명칭이 바뀐 경우가 있거나, 부천시의 경우 지역구가 1개의 시로 통합되었다가 다시 3개의 구로 분리된 경우나 군위군이 경상북도 산하에서 대구광역시로 편입된 것처럼 행정구역의 변경이 있었습니다. 

 

 

❗느낀 점

1. 예외처리와 에러로그

Selenium으로 크롤링을 하다 보니 실행시간이 너무 오래 걸렸고 그 과정에서 에러가 발생하거나 최종 결과가 잘못된 경우에 다시 작업을 해야 하는 경우가 많아서 예외처리를 하고 에러가 발생했을 때 해당 장기요양기관기호와 기관코드를 로그로 따로 저장하는 방식을 통해 방지할 수 있었습니다.

 

2. 세상엔 공짜와 완벽한 데이터는 없다...

위와 같이 조건문으로 전처리를 하고 테스트 케이스로 단위 테스트를 했음에도 결국 코드로 해결하지 못한 데이터는 수작업으로 지도에서 검색하면서 직접 데이터를 넣으려고 시도를 하면서 발견한 원인은 주소에 오타가 있거나 도로명번호가 최신화되지 않았기 때문에 전처리 과정에서 주소를 못 찾거나 카카오 API를 통해 좌표 값을 가져올 수 없었던 것이었습니다.

 

 

읽어주셔서 감사합니다.