Open API

[Python] 병원명으로 위도와 경도 나타내기 (Open API 네이버 검색)

Jerry_K 2024. 4. 2. 12:14
 

[Python] 공공데이터 가져오기 (공공데이터포털 - 전국 응급의료기관 정보)

📚공공데이터 활용하기 공공데이터를 사용해서 할 수 있는 것은 정말 많다. 공공데이터 기반 웹/앱 서비스도 가능하다. 그래서 이번 포스팅은 공공데이터를 가져와서, 내가 원하는 정보를 출력

fishking.tistory.com

(지난 포스팅 참고)

🧐 포스팅 설명

지난번에 파이썬 Nominatim 라이브러리를 통해서 병원명을 입력하면 위도,경도가 출력되 코드를 작성해봤다. 

 

근데 이게 살짝 위치도 좀 틀리고,  몇몇 병원명의 도로명은 None으로 나타낸다.

Nominatim 라이브러리는 영국에서 만들어졌다는데, 한국의 지도를 정확하게 나타내는게 무리일지도 ...

여기에 해결방법은 구체적인 도로명을 적어주는 것이다.

 

그래서 어떻게 할까 고민을 해봤고, 가장 먼저 떠올랐던거는 크롤링이였다.

나는 selenium을 정말 싫어하기 때문에 당연히 requests로 먼저 시도했다. 

(selenium은 동적으로 하기때문에 뭐든 다 크롤링을 할 수 있지만, 한편으로 느리고 변수가 많다...)

 

만능인 network에서 curl을 copy하여 해보려했지만 이게 위도와 경도를 파라미터에 입력을 해줘야한다.

내가 위도와 경도를 위해 도로명 주소가 필요한데 도로명 주소를 구하려면 위도와 경도가 필요한 것이다 !?

 

아무리 시도해도 requests로는 안될 것 같아서 결국 포기하고 selenium으로 시도를 했다.근데 역시 selenium은 너무 불안정하다. 코드를 작성했는데 잘 됐다가 안됐다를 반복했다. (컴퓨터 환경도 selenium 크롤링에 영향을 많이 미친것 같다.)

 

selenium도 도저히 아닌것 같아서, 지도 opne api를 이리저리 살펴보다, 가장 나에게 필요한 open api를 찾았다. 

 

그것은 바로  네이버 검색 API !!   

이 API만 있으면 굳이 Nominatim을 쓸 필요없다.

 

요약 : 위도 경도를 위해 도로명 주소를 구하려했고, 크롤링에 실패해서 네이버 검색 Open API 를 사용

 

(포스팅 맨 아래에 selenium으로 한 코드도 남기겠다. iframe을 통해 했기때문에 이곳 저곳 도움이 될 듯 하다.)  

 

 

🔍목차

1. 네이버 검색 API 신청
2.
검색 API로 위도와 경도 나타내기
+ @  Selenium 으로도 해보기 


🖊️포스팅 목표

hospital_data.csv
0.02MB

 

지난 포스팅에서 구한 병원명을  "지역" 데이터 프레임에 merge를 해주고 hospital_data.csv로 만들었다.

이번 포스팅에서는 그 hopital_data.csv를 쓸 것이다.

 

여기서 내가 필요한 것은 병원별 위도와 경도이다. 

예를들어 서울대학교를 파라미터로 주면 (37.579715 , 126.999017)   이렇게 위도와 경도를 리턴 해주는 것이다.

(나중에 folium에 위치 표현을 하기위해 필요함)

 

여러시도를 해본 결과  네이버 검색 api가 가장 좋았고, 이 방법을 통해 병원 이름으로 위도와 경도를 알아내보자.


📚 병원명으로 위도 경도 나타내기

 

📕네이버 검색 API 신청

네이버 API 신청은 간단하게 가능하다. 

 

https://developers.naver.com/apps/#/register

 

애플리케이션 - NAVER Developers

 

developers.naver.com

 

여기에 들어가서 어플리케이션 이름 작성하고, 

사용 API에 "검색" 을 선택하고, 

입력하라는 것들을 다 입력하면 된다. 

 

 

 

그러면 이렇게 Client IDClient Secret을 받게된다. 

key들을 따로 저장해두면 된다.  

 

 

https://developers.naver.com/docs/serviceapi/search/blog/blog.md#%EB%B8%94%EB%A1%9C%EA%B7%B8

 

검색 > 블로그 - Search API

검색 > 블로그 블로그 검색 개요 개요 검색 API와 블로그 검색 개요 검색 API는 네이버 검색 결과를 뉴스, 백과사전, 블로그, 쇼핑, 웹 문서, 전문정보, 지식iN, 책, 카페글 등 분야별로 볼 수 있는 API

developers.naver.com

 

이게 네이버 검색 API 개발 가이드이다. 

여기에서 요청 URL , 필수 파라미터, 요청 예시, 응답 예시들을 꼭 살펴보자. 

참고로 검색 API에서는 요청에 따라 

뉴스, 백과사전, 블로그, 쇼핑, 웹 문서, 전문정보, 지식iN, 책, 카페글 등 분야별 볼 수 있다.

 

 

📕 검색 API로 위도와 경도 나타내기

import requests
import pandas as pd
headers = {
    'X-Naver-Client-Id': 'Client-Id',
    'X-Naver-Client-Secret': 'Client-Secret',
}

params = {
    'query': "강북삼성병원",
    'display': '10',
    'start': '1',
    'sort': 'random',
}

response = requests.get('https://openapi.naver.com/v1/search/local.json', params=params, headers=headers)
search = response.json()

 

 

headers에 좀 전에 발급  Client IDClient Secret를 입력한다. 

필수파라미터는 query이고 나머지는 선택하면 된다.

요청 url은 json형태와 xml 형태가 있는데 json 형태가 훨씬 편하기 때문에 json 형태로 가져왔다.

 

그렇게 request를 하고 json 형태로 출력하면 위와 같은 출력이 나온다. 

딕셔너리 형태의 출력이 나왔으니 이제 원하는 key 값을 찾아 출력하면 된다.

 

search["items"][0]

 

보통은 첫 번째로 나온 검색값이 가장 정확하기 때문에 첫번째꺼를 인덱싱 해주었다.

이제 원하는 key값을 이용해 value 만 출력하면 된다.

 

내가 원하는 것은 위도와 경도인데, 따로 확인을 해보니 mapx,mapy가 각각 경도와 위도였다.

위도와 경도는 각도이기때문에 저런 숫자가 나올 수 없기 때문에 각각 100,10의 자리로 만들어주자.

 

float(search["items"][0]["mapx"][:3] + "." + search["items"][0]["mapx"][3:])
float(search["items"][0]["mapy"][:2] + "." + search["items"][0]["mapy"][2:])

 

우리나라 내에서 위도와 경도의 자리수가 바뀔일은 없기때문에

경도는 3자리까지 위도는 2자리까지 인덱싱하고 조합을 적절한 경도와 위도로 변환시켜줬다.

 

이렇게 병원명만으로 위도와 경도를 나타내주었다. 

 

df = pd.read_csv("hospital_data.csv")

road_address = []
road_mapx = []
road_mapy = []

headers = {
    'X-Naver-Client-Id': 'Client-Id',
    'X-Naver-Client-Secret': 'Client-Secret',
}

for hospital in df["hospital"] :    
    params = {
        'query': hospital,
        'display': '10',
        'start': '1',
        'sort': 'random',
    }

    print(f"[{hospital}] - ", end='   ' )
    
    response = requests.get('https://openapi.naver.com/v1/search/local.json', params=params, headers=headers)
    search = response.json()
    
    try :
        address = search["items"][0]["roadAddress"]
        mapx = float(search["items"][0]["mapx"][:3] +"." +  search["items"][0]["mapx"][3:]) # 경도
        mapy = float(search["items"][0]["mapy"][:2] +"." +  search["items"][0]["mapy"][2:]) # 위도
        
        road_address.append(address)
        road_mapy.append(mapy)
        road_mapx.append(mapx)
        
        print(f"{address} - ({mapy,mapy})")
    
    except : 
        print("위치 데이터 없음")
        road_address.append(None)
        road_mapy.append(None)
        road_mapx.append(None)

 

 

이 코드는 hospital_data의 전체 병원명으로 각각의 위도와 경도를 나타낸다.

앞에 설명한 코드에 print랑 append 몇 개만  붙여준게 전부이다. 

 

가끔가다 위치 데이터가 없다고 하는데

이런 경우는 네이버에 검색을 해도 위치 정도가 안뜨는 경우이다. 

이런거는 Nan값으로 대체했다. 

 


🗒️ Selenium 으로도 해보기 

이것은 좀 불안정해서 비추이지만,

나중에 다른 것을 크롤링 할때 참고해도 좋을 것 같기 때문에 기록으로 간단히 남겨둔다.

 

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from webdriver_manager.chrome import ChromeDriverManager
chrome_options = Options()
chrome_options.add_experimental_option("detach",True)
service = Service(executable_path=ChromeDriverManager().install())  # Chrome driver 자동 업데이트

 

여기까지는 반 의무적으로 해준다. 

browser = webdriver.Chrome(service=service, options=chrome_options)

browser.get("https://map.naver.com")  # 네이버 지도 사이트
browser.implicitly_wait(10) 

road_address = []

for key in df["hospital"]:
    search = browser.find_element(By.CSS_SELECTOR,"input.input_search")  # 지도 검색창 
    
    time.sleep(2)
    search.send_keys(key)  # 검색창에 key 값 입력
    time.sleep(1)
    
    
    search.send_keys(Keys.ENTER) # 검색창 enter
    time.sleep(2)
    
    try: 
    	# 네이버 지도는 iframe으로 나눠져 있기 때문에 이렇게 frame을 switch 해줘야 한다.
        browser.switch_to.frame('searchIframe')  
       
        # searchIframe이 활성화 되게 빈 공간 클릭
        browser.find_element(By.CSS_SELECTOR,"#_pcmap_list_scroll_container").click()
        time.sleep(1)

		# key 검색 후 병원을 클릭 (새로운 iframe 오픈)
        browser.find_element(By.CSS_SELECTOR,"#_pcmap_list_scroll_container>ul>li:nth-of-type(1)>.qbGlu .ouxiq>a").click()
        time.sleep(1)
	
    	# 기본 브라우져로 다시 스위치 
        browser.switch_to.default_content()
    
    except :     	
        print(f"{key}는 특별한 경로")  # 예외 상황 출력
        
    
    # key 검색 후 병원을 클릭 후 생긴 iframe으로 스위치;
    browser.switch_to.frame('entryIframe')
    time.sleep(1)
    
    # key로 입력한 병원의 도로명 주소를 text로 만들어줌
    address = browser.find_element(By.CSS_SELECTOR,"span.LDgIH").text
    print(address)
    
    # road_address 리스트에 저장해주기 
    road_address.append(address)
    browser.switch_to.default_content()
    time.sleep(1)
    
    # 다음 반복을 위하여 key로 입력한 값 초기화 
    browser.find_element(By.CSS_SELECTOR,".btn_clear").click()
    
    """ 
    browser.execute_script("arguments[0].value = '';", search) 
    이 방법 key로 입력한 값을 초기화 할 수 있다 .
    참고로 clear()로는 값 지우기 잘 안됨
    """

 

자세한 코드 설명은 주석을 통해 달아두었다 .


🤓 마무리 

이렇게 내가 원하는 위도와 경도를 얻었다.

그럼에도 불구하고, 결측값들이 꽤나 존재한다... 

이것들은 네이버 위치 검색을 해도 위치가 뜨지않기 때문이다.

 

따로 내가 위치를 서칭하여 결측값을 넣을지, 아니면 그냥 제거를 할 지를 고민하고 있다.

 

다음 포스팅에서는 어느정도 전처리 된 데이터를 통해 

folium으로 위치를 나타내고, 정보를 marker에 넣어보는 것을 해볼 생각이다.