빅데이터 국비 교육

[아이티윌 빅데이터 52기] LAB 13 | 파이썬 파이널 과제 | OpenAPI 연동 실습 (선생님 버전)

datahaseo 2025. 11. 7. 12:51

[아이티윌 빅데이터 52기] LAB 13 | 파이썬 파이널 과제 |  OpenAPI 연동 실습 (선생님 버전)

모든 과제에 대한 저작권은 아이티윌 이광호 강사님께 있습니다

 

 

 

 

 

 

 

 

 

활용할 데이터 셋

브이월드 API레퍼런스

서울교통공사_역주소 및 전화번호> 데이터셋> 공공데이터 | 서울열린데이터광장

 

 

<풀이 순서>

 

STEP1. 패키지 참조
STEP2. CSV 파일 읽어오기

STEP 3. Open API 데이터 요청 스펙 확인

STEP4. 위경도를 조회하는 함수 정의

STEP.5-1 위경도 변환하기 | 동기 방식

STEP.5-2 위경도 변환하기 | 비동기 방식

STEP.6 변환 결과 저장하기

 

 

 

 

STEP1. 패키지 참조

import requests
from pandas import DataFrame
from concurrent import futures


STEP2. CSV 파일 읽어오기

#서울 열린 데이터 광장 - 서울 교통공사 역주소 및 전화번호 최근 데이터셋을 다운받아 r 읽기 모드로 데이터를 가져온다
with open("서울교통공사_역주소 및 전화번호_20250318.csv","r",encoding='euc-kr') as f:
  #데이터 세트 객체를 한 행씩 읽는 readlines() 메서드를 활용하고, 해당 결과를 csv_list 에 담는다
  csv_list=f.readlines()

print(csv_list[:5])

 

STEP 3. Open API 데이터 요청 스펙 확인

OPEN API 를 연결할 때의 작업 항목들 살펴보면,
OPEN API 요청 정보를 확인하고, SESSION 객체로 URL 에 접속하여 데이터를 가져오는 것.

 

#요청 url
url = "https://api.vworld.kr/req/address?"
key = "생략"

#요청 파라미터 (명세서 확인)
params = {
	"service": "address",
	"request": "getcoord",
	"crs": "epsg:4326",
	"address": "판교로 242",
	"format": "json",
	"type": "road",
	"key": key
}
#웹에 데이터 요청하기
with requests.Session() as session:

  #세션 객체에 웹 브라우저 정보 (UserAgent) 주입 (웹서버가 파이썬 프로그램을 정상적인 웹 브라우저로 여기도록)
  session.headers.update({"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"
                          })
  

  r=session.get(url,params=params)    #get 메서드로 접근하고, 파라미터 값 쿼리 스트링 형태로 전달
  print(r.url)

  if r.status_code !=200:
    msg ="[%d Error] %s 에러가 발생함" % (r.status_code,r.reason)
    raise Exception(msg)

print(r) # HTTP 통신 상태 확인
#요청 테스트 확인
mydict = r.json()
mydict

 

#위도와 경도 추출

point=mydict["response"]["result"]["point"]
print(point["x"],point["y"])

 

 

STEP4. 위경도를 조회하는 함수 정의

위경도를 조회하는 함수 정의
위에서 테스트로 확인한 과정을 함수로 정의

def get_point(addr,type ="road"):
  #요청 url
  url = "https://api.vworld.kr/req/address?"
  key = "1C1C7AC9-4A35-3854-BFE7-D91FAEDD655E"

  #요청 파라미터 (명세서 확인)
  params = {
    "service": "address",
    "request": "getcoord",
    "crs": "epsg:4326",
    "address": addr,
    "format": "json",
    "type": type,
    "key": key
  }


#웹에 데이터 요청하기
  with requests.Session() as session:

    #세션 객체에 웹 브라우저 정보 (UserAgent) 주입 (웹서버가 파이썬 프로그램을 정상적인 웹 브라우저로 여기도록)
    session.headers.update({"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"
                            })
    

    r=session.get(url,params=params)    #get 메서드로 접근하고, 파라미터 값 쿼리 스트링 형태로 전달
    print(r.url)

    if r.status_code !=200:
      msg ="[%d Error] %s 에러가 발생함" % (r.status_code,r.reason)
      raise Exception(msg)

    mydict = r.json()
    point=mydict["response"]["result"]["point"]
      
    return (point["x"],point["y"])

 

 

 

STEP.5-1 위경도 변환하기 | 동기 방식

- OPEN API 를 활용하기 위해서는 주소지 정보를 넣어 경도와 위도 값을 반환해야하고,
해당 과정을 get_point() 라는 함수로 정의한 상황. 이 함수를 쓰려면 주소 정보를 get_point() 함수에 파라미터로 전달해야 하므로, 우선 csv_list 에서 주소 정보를 추출한다. 


- 추출한 주소 정보를 get_point() 함수에 넣으면 경도와 위도 정보가 나오는데, 만약 기본값으로 세팅한 type=road 에서 변환이 되지 않았다면 , 지번 주소로 재시도 하는 예외 처리를 걸어준다


- 만약 지번 주소에서도 예외로 처리되면 그때는 위도와 경도에 None 값을 넣어준다


- 기존에 존재하던 csv_list 데이터의 각 항목들에 위도와 경도를 추가해주고, 이 결과를 비워진 리스트 resultset에 넣어주면 리스트 내에 각 데이터들이 또 다른 리스트 형태로 존재하는 2차원 구조의 데이터가 완성된다

size = len(csv_list)
resultset=[]
for i,v in enumerate(csv_list[1:]): #제목을 건너뛰기 위해 1부터
  print("%d/%d 진행중..."%(i+1,size))
  items=v.strip().split(",")

  try:
    lat,lon = get_point(items[5])
  
  except Exception as e :
    try:
      lat,lon =get_point(items[6],type="parcel")

    except Exception as e2:
      lat =None
      lon=None

  items.append(lat)
  items.append(lon)
  resultset.append(items)


resultset

STEP.5-2 위경도 변환하기 | 비동기 방식

- 비동기식 작업은 반복해서 실행해야하는 함수가 정의되어 있으면, worker 들이 스레드에서 해당 함수 작업을 하는 프로세스를 빈 리스트에 넣어주고, 나중에 그 프로세스들에 대한 결과 for 구문으로 탐색해면 빠르게 작업된 결과물들에 접근할 수 있게 된다


- 여기서는 위에서 정의한 get_point 함수를 일거리로 보내주고, 그 작업을 processes 리스트에 넣어준다


- processes 리스트를 result() 메서드로 탐색하면 결과값을 lat 과 lon 으로 받아온다


- 새롭게 추가된 lat 과 lon 은 csv_list 문자열을 요소별로 items 리스트로 만든 다음 해당 리스트에 append 해주고, append 된 리스트 결과를 resultset 에 하나씩 차곡차곡 쌓아준다

size = len(csv_list)
resultset=[]
processes=[] # 비동기 작업 프로세스를 저장할 리스트


with futures.ThreadPoolExecutor(max_workers=30) as executor:

  for i,v in enumerate(csv_list[1:]):     #제목을 건너뛰기 위해 1부터
    print("%d/%d 진행중..."%(i+1,size))
    items=v.strip().split(",")

    pro=executor.submit(get_point,items[5])
    processes.append(pro)   #위에서 준비한 리스트에 저장


  for i,p in enumerate(processes) :   #비동기 프로세스가 실행되는 동안 발생하는 예외에 대비하기 위한 처리 

    try:
      lat,lon = p.result()
    
    except Exception as e :
      try:
        lat,lon =get_point(items[6],type="parcel")

      except Exception as e2:
        lat =None
        lon=None

    items=csv_list[i+1].strip().split(",")
    items.append(lat)
    items.append(lon)
    resultset.append(items)


resultset

 

STEP.6 변환 결과 저장하기

- 리스트 안에 리스트 형식으로 각 행들이 쌓여져 있는 상황,resultset 리스트를 탐색해주고, 각 리스들의 값을 순서대로 딕셔너리에 반복해서 넣어준다. 이 딕셔너리를 비워져있는 리스트에 넣고, 해당 결과물을 데이터 프레임으로 바꿔주면 엑셀 형태로 다운받을 수 있게 된다

new_resultset = []

for r in resultset:
    item_dict = {
        "역번": r[0],
        "역번호": r[1],
        "호선": r[2],
        "역명": r[3],
        "역전화번호": r[4],
        "도로명주소": r[5],
        "지번주소": r[6],
        "위도": r[7],
        "경도": r[8]
    }

    new_resultset.append(item_dict)

df = DataFrame(new_resultset)
df.to_excel("지하철역.xlsx")
df