임신 육아 종합포털 아이사랑(https://www.childcare.go.kr/cpin/main1.jsp) > 어린이집 찾기 > 경상남도에 있는 모든 어린이집 정보 크롤링하기

 

크롤링할 정보: 기관명 / 설립유형 / 주소 / 제공서비스

 

 

 

1. 크롤링하려면 사이트의 주소가 필요

확인해야하는 사이트는 '요약'과 '기본현황' 두 개의 사이트이며 각 사이트의 주소는

>요약: http://info.childcare.go.kr/info/pnis/search/preview/SummaryInfoSlPu.jsp?flag=YJ&STCODE_POP='유치원코드'
>기본현황: http://info.childcare.go.kr/info/pnis/search/preview/BasisPresentConditionSlPu.jsp?flag=GH&STCODE_POP='유치원코드'

 

이며 유치원코드는 지역코드(5자리)+고유번호(6자리)로 이루어짐

# 시군구 코드: [48120,48121,48123,48125,48127,48129,48170,48220,48240,48250,48270,48310,48330,48720,48730,48740,48820,48840,48850,48860,48870,48880,48890]

# 고유번호는 규칙도 모르겠고 중간에 폐지된 시설도 있지만 세자리를 넘어가는 코드는 없는 것으로 보임

 

 

 

2. 해당 규칙에 따라 다음과 같이 url을 얻는 코드 작성

# 경상남도의 모든 시군구 코드 리스트
gn_cd_list=['48120','48121','48123','48125','48127','48129','48170','48220','48240','48250','48270','48310',
            '48330','48720','48730','48740','48820','48840','48850','48860','48870','48880','48890']

# 각 시군구 코드를 주소에 적용
for gn_cd in range(gn_cd_list):
    # 주소가 문자형이므로 세자리의 고유번호를 000~999 형태로 넣고 싶었지만 000을 0으로 자동으로 바꾸기에
    # 자리수에 따라 0~9, 10~99, 100~999 세번의 for문 사용
    
    for i in range(10):
        # 기관명과 주소를 확인할 수 있는 '요약' 사이트의 url
        name_juso_url = 'http://info.childcare.go.kr/info/pnis/search/preview/SummaryInfoSlPu.jsp?flag=YJ&STCODE_POP=%s00000%d'%(gn_cd,i)
        # 설립유형과 제공 서비스를 확인할 수 있는 '기본현황' 사이트의 url
        type_service_url='http://info.childcare.go.kr/info/pnis/search/preview/BasisPresentConditionSlPu.jsp?flag=GH&STCODE_POP=%s00000%d'%(gn_cd,i)
    
    for i in range(10,100):
        name_juso_url = 'http://info.childcare.go.kr/info/pnis/search/preview/SummaryInfoSlPu.jsp?flag=YJ&STCODE_POP=%s0000%d'%(gn_cd,i)
        type_service_url='http://info.childcare.go.kr/info/pnis/search/preview/BasisPresentConditionSlPu.jsp?flag=GH&STCODE_POP=%s0000%d'%(gn_cd,i)
    
    for i in range(100,1000):
        name_juso_url = 'http://info.childcare.go.kr/info/pnis/search/preview/SummaryInfoSlPu.jsp?flag=YJ&STCODE_POP=%s000%d'%(gn_cd,i)
        type_service_url='http://info.childcare.go.kr/info/pnis/search/preview/BasisPresentConditionSlPu.jsp?flag=GH&STCODE_POP=%s000%d'%(gn_cd,i)
 

 

 

3. 사이트에서 f12를 눌러 개발자 도구 확인

오른쪽 마우스 사용이 불가능한 사이트라 직접 화살표를 눌러가며 기관명, 주소, 설립유형, 제공서비스의 경로를 찾아 XPath를 복사

 

복사한 XPath를 fromstring(request.get(url).text).xpath('XPath')문에 적용하여 해당 정보 크롤링

참고로, 복사한 XPath문 맨 뒤에 '/text()'를 붙여줘야 XPath 주소에 해당하는 부분의 텍스트를 출력할 수 있음

 

import requests
import lxml.html
from lxml.html import fromstring

# 기관명의 XPath를 통해 사이트 내의 기관명 크롤링
name_li= fromstring(requests.get(name_juso_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table/tbody/tr[1]/td[1]/text()')

# 주소의 XPath를 통해 사이트 내의 주소 크롤링
juso_li = fromstring(requests.get(name_juso_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table/tbody/tr[10]/td/text()')

# 설립유형의 XPath를 통해 사이트 내의 설립유형 크롤링
type_li=fromstring(requests.get(type_service_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table[1]/tbody/tr[1]/td[2]/text()')

# 제공 서비스의 XPath를 통해 사이트 내의 제공 서비스 크롤링
service_li=fromstring(requests.get(type_service_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table[1]/tbody/tr[3]/td[1]/text()')

print(name_li)
print(juso_li)
print(type_li)
print(service_li)

### 실행 결과 ###
['\n              BNK창원어린이집\n              \n            ']
['\n                    \t(51366)\n                    \t경상남도 창원시 마산회원구 양덕로 135\xa0(양덕동)\n\t\t\t\t\t\t']
['직장']
['일반']

이름과 주소에서 필요없는 띄어쓰기와 '\n', '\t', '\xa0' 등의 문자가 나타나므로 replace(' ' ,''), replace('\n','') 등으로 없애기

 

 

4. 전체 코드

import requests
from lxml.html import fromstring
import pandas as pd

# 각 정보를 크롤링해와서 넣어 놓을 리스트
name=[]    # 기관명
juso=[]    # 주소
type=[]    # 설립유형
service=[] # 제공서비스

# 경상남도 시군구 코드
gn_cd_list=['48120','48121','48123','48125','48127','48129','48170','48220','48240','48250','48270','48310',
            '48330','48720','48730','48740','48820','48840','48850','48860','48870','48880','48890']

# 경상남도의 각 시군구 코드+한 자리수 코드(000~009)/두 자리수 코드(010~099)/세 자리수 코드(100~999)의 주소 크롤링
for gn_cd in gn_cd_list:
    # 한 자리수 코드(000~009)
    for i in range(10):
        name_juso_url = 'http://info.childcare.go.kr/info/pnis/search/preview/SummaryInfoSlPu.jsp?flag=YJ&STCODE_POP=%s00000%d'%(gn_cd,i)
        type_service_url='http://info.childcare.go.kr/info/pnis/search/preview/BasisPresentConditionSlPu.jsp?flag=GH&STCODE_POP=%s00000%d'%(gn_cd,i)
        name_li= fromstring(requests.get(name_juso_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table/tbody/tr[1]/td[1]')
        juso_li = fromstring(requests.get(name_juso_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table/tbody/tr[10]/td/text()')
        type_li=fromstring(requests.get(type_service_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table[1]/tbody/tr[1]/td[2]')
        service_li=fromstring(requests.get(type_service_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table[1]/tbody/tr[3]/td[1]')
        name.append(name_li[0].text.replace('\xa0','').replace('\t','').replace('\n','').replace('  ',''))  # 필요없는 단어 제외후 리스트에 추가하여 저장
        juso.append(juso_li[0].replace('\xa0','').replace('\t','').replace('\n','').replace('  ',''))       # 필요없는 단어 제외후 리스트에 추가하여 저장
        type.append(type_li[0].text)                                                                        # 리스트에 추가하여 저장
        service.append(service_li[0].text)                                                                  # 리스트에 추가하여 저장

    # 두 자리수 코드(010~099)
    for j in range(10,100):
        name_juso_url = 'http://info.childcare.go.kr/info/pnis/search/preview/SummaryInfoSlPu.jsp?flag=YJ&STCODE_POP=%s0000%d'%(gn_cd,j)
        type_service_url='http://info.childcare.go.kr/info/pnis/search/preview/BasisPresentConditionSlPu.jsp?flag=GH&STCODE_POP=%s0000%d'%(gn_cd,j)
        name_li= fromstring(requests.get(name_juso_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table/tbody/tr[1]/td[1]')
        juso_li = fromstring(requests.get(name_juso_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table/tbody/tr[10]/td/text()')
        type_li=fromstring(requests.get(type_service_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table[1]/tbody/tr[1]/td[2]')
        service_li=fromstring(requests.get(type_service_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table[1]/tbody/tr[3]/td[1]')
        name.append(name_li[0].text.replace('\xa0','').replace('\t','').replace('\n','').replace('  ',''))  # 필요없는 단어 제외후 리스트에 추가하여 저장
        juso.append(juso_li[0].replace('\xa0','').replace('\t','').replace('\n','').replace('  ',''))       # 필요없는 단어 제외후 리스트에 추가하여 저장
        type.append(type_li[0].text)                                                                        # 리스트에 추가하여 저장
        service.append(service_li[0].text)                                                                  # 리스트에 추가하여 저장

    # 세 자리수 코드(100~999)
    for k in range(100,1000):
        name_juso_url = 'http://info.childcare.go.kr/info/pnis/search/preview/SummaryInfoSlPu.jsp?flag=YJ&STCODE_POP=%s000%d'%(gn_cd,k)
        type_service_url='http://info.childcare.go.kr/info/pnis/search/preview/BasisPresentConditionSlPu.jsp?flag=GH&STCODE_POP=%s000%d'%(gn_cd,k)
        name_li= fromstring(requests.get(name_juso_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table/tbody/tr[1]/td[1]')
        juso_li = fromstring(requests.get(name_juso_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table/tbody/tr[10]/td/text()')
        type_li=fromstring(requests.get(type_service_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table[1]/tbody/tr[1]/td[2]')
        service_li=fromstring(requests.get(type_service_url).text).xpath('//*[@id="popWrap2"]/div/div/div/table[1]/tbody/tr[3]/td[1]')
        name.append(name_li[0].text.replace('\xa0','').replace('\t','').replace('\n','').replace('  ',''))  # 필요없는 단어 제외후 리스트에 추가하여 저장
        juso.append(juso_li[0].replace('\xa0','').replace('\t','').replace('\n','').replace('  ',''))       # 필요없는 단어 제외후 리스트에 추가하여 저장
        type.append(type_li[0].text)                                                                        # 리스트에 추가하여 저장
        service.append(service_li[0].text)                                                                  # 리스트에 추가하여 저장
    print(gn_cd) # 몇번째 시군구까지 작업을 완료했는지 알아보기 위함

# 각 리스트를 하나의 데이터프레임으로 생성
df=pd.DataFrame({'기관명':name,'주소':juso,'설립유형':type,'제공서비스':service})
df.drop(df[df['기관명']==''].index,inplace=True)  # 위에서 고유코드가 없었던 경우도 빈 행으로 데이터 프레임에 포함되었기 때문에 빈 행 제거
df.reset_index().iloc[:,1:]

# 마지막으로 생성한 데이터프레임을 csv파일로 저장
df.to_csv('어린이집_크롤링.csv',encoding='cp949')

 

각 어린이집의 고유코드에 대한 정보를 알지 못해서 무작정 000~999까지 넣어 크롤링하여 실제 어린이집 보다 많은 수의 어린이집이 크롤링됨(임신육아종합포털에 올라와있지 않은 어린이집도 있는 듯)

지오코딩이란?

고유명칭(주소나 산, 호수의 이름 등)을 가지고 위도, 경도의 좌표를 얻는 것

 

카카오 api를 이용하여 지오코딩 하기

1. Kakao Developers 사이트(https://developers.kakao.com/)에 회원가입 후 로그인

 

2. 내 애플리케이션 > 애플리케이션 추가(사진 필요X, 앱 이름, 사업자명은 아무거나)

이렇게 한 뒤 생성된 애플리케이션에 들어가보면 다음과 같이 키가 발급되어 있음

키는 메모장에 복사해두기

 

 

3. 내 애플리케이션 > 제품 설정 > 카카오 로그인 > 활성화 상태 On

 

 

4. 내 애플리케이션 > 제품 설정 > 카카오 로그인 > 맨 아래에 Redirect Url 추가

각자 사용환경에 맞게 등록(주피터 노트북의 경우 http://localhost:8888로 등록)

 

 

5. 코드 받기

https://kauth.kakao.com/oauth/authorize?client_id=REST API키&redirect_uri=http://localhost:8888&response_type=code&response_type=code

각자 발급받았던 REST API 키와 사용환경에 맞게 등록한 Redirect Url 주소를 알맞게 넣어 주소창에 입력

개인정보 동의를 누르면 자신의 개발환경 창으로 접속

주소창에는 http://localhost:8888/tree?code=코드값 의 형태로 코드가 함께 나타남

 

 

6. 토큰 받기

https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id=REST API키&redirect_uri=http://localhost:8888&code=드값

발급받은 REST API, 개발환경에 맞는 Redirect Url, 발급받은 코드를 각각 알맞게 붙여서 주소창에 입력

위와 같이 {"access token":"~", "token_type":"bearer", "refresh_token":"~",...}등 토큰이 발급됨

토큰은 기억할 필요X

 

 

7. 지오코딩 코드 작성

import json
import requests
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

REST_API='발급받은 Rest Api 키'

def Geocoding(주소):
    url = 'https://dapi.kakao.com/v2/local/search/address.json?query=' + 주소
    headers = {"Authorization": "KakaoAK "+ REST_API}
    result = json.loads(str(requests.get(url, headers=headers).text))
    match_first = result['documents'][0]['address']
    return float(match_first['y']), float(match_first['x'])
    
Geocoding('지오코딩 하고 싶은 주소')

# 결과는 해당 주소의 (위도,경도)로 반환됨

 

 

8. 엑셀 파일에 입력된 주소 여러 개를 통째로 지오코딩하기(+없거나 변경된 주소에 대한 대처)

import os
import lxml.html
from lxml.html import fromstring
import pandas as pd
from bs4 import BeautifulSoup

# 주소가 입력되어 있는 엑셀파일 불러오기
df=pd.read_csv('엑셀파일명')

# 위도와 경도가 입력될 열 생성
df['latitude']=0
df['longitude']=0

# 지오코딩 함수는 위의 함수 그대로 사용

# 엑셀의 주소 변환 작업 시작
for i in range(len(df)):
    try:
        try:
            df['latitude'][i]=Geocoding(df['주소'][i])[0]     # (위도, 경도)로 반환되는 결과 중 위도 부분
            df['longitude'][i]=Geocoding(df['주소'][i])[1]    #(위도, 경도)로 반환되는 결과 중 경도 부분

        # 주소가 변경되었다면 주소정보누리집에서 주소를 검색하여 새로운 주소를 크롤링
        except:
            juso=df['주소'][i].replace(' ','+')
            url=f'https://www.juso.go.kr/support/AddressMainSearch.do?firstSort=none&ablYn=N&aotYn=N&fillterHiddenValue=&searchKeyword={juso}&dsgubuntext=&dscity1text=&dscounty1text=&dsemd1text=&dsri1text=&dssan1text=&dsrd_nm1text=&searchType=HSTRY&dssearchType1=road&dscity1=&dscounty1=&dsrd_nm_idx1=%EA%B0%80_%EB%82%98&dsrd_nm1=&dsma=&dssb=&dstown1=&dsri1=&dsbun1=&dsbun2=&dstown2=&dsbuilding1='
            res=requests.get(url)
            p=fromstring(res.text)
            li=p.xpath('//*[@id="list1"]/div[2]/span[2]')

            # 크롤링한 변경된 주소에서 불필요한 부분 삭제 후 원래 주소 자리에 대체
            df['주소'][i]=li[0].text.replace('\r','').replace('\t','').replace('\n','')
            df['latitude'][i]=getLatLng(df['LTNO_ADDR'][i])[0]
            df['longitude'][i]=getLatLng(df['LTNO_ADDR'][i])[1]
    
    # 주소가 변경된 것이 아니라 카카오맵에 없는 것이라면 위 방법으로도 오류를 해결할 수 없으므로
    # 일단 위도와 경도 모두 0으로 대체 후 따로 확인해보기
    except:
        df['latitude'][i]=0
        df['longitude'][i]=0

하루에 할 수 있는 주소 개수에 제한이 있어서 49000개~50000개 정도만 되는 것 같음

+ Recent posts