Selenium을 이용해 카카오맵 크롤링하기

박상철·2022년 4월 27일
5

Crawling

목록 보기
2/2

이전 포스팅에서 네이버 검색 API를 사용하여 식당 정보를 가져오는 크롤링을 해보았다. 이 API를 이용하는 방법으로는 식당의 전화번호, 주소, category, link밖에 못 가져온다는 한계가 있었다. 내가 원하는 정보를 얻기 위해서 구글링을 해보니 흔히 크롤링에 Selenium과 BeautifulSoup 패키지가 사용됨을 알게 되었고 나는 Selenium을 선택하였다.

먼저 내가 사용한 모듈들이다

import openpyxl
import os
import sys
import time
import json
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

관악구 열린데이터 광장에서 식당 목록이 담긴 데이터셋을 가져왔고 '관악구레스토랑.xlsx'로 저장하였다.
다음은 해당 데이터셋에서 행단위로 데이터를 가져와 지역과 식당명을 ex) ㅇㅇ동 ㅁㅁ식당 형식으로 data List에 저장하는 코드이다.

filename = '관악구레스토랑.xlsx'
restaurant_file = openpyxl.load_workbook(filename)
restaurants = restaurant_file.worksheets[0]
data = []

for restaurant in restaurants.iter_rows(min_row=3):
    address = restaurant[16].value
    if address is not None:
        data.append([
            restaurant[16].value.split('(')[1].split(')')[0],
            restaurant[18].value
        ])

print(data[0])

selenium을 사용하기 위해선 browser driver를 설치해야한다.
나는 chromedriver를 설치하였고 chromedriver를 버전에 맞게 자동으로 설치하는 방법은 따로 포스팅하겠다.
다음은 설치한 chromedriver로 크롤러를 만드는 방법이다.

options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
driver = webdriver.Chrome('크롬드라이버경로',options=options)

아래는 크롤러에 검색할 url을 전달하는 코드이다.
driver.implicitly_wait() 메소드를 통해 웹페이지의 load 최대시간을 설정할수 있다.

for i in data:
    keyword = i[0] + ' ' + i[1]
    print(keyword, end=" ")
    try:
        content = {}
        kakao_map_search_url = f"https://map.kakao.com/?q={keyword}"
        driver.get(kakao_map_search_url)
        driver.implicitly_wait(time_to_wait=3)

아래는 카카오맵을 검색하여 찾은 식당리스트에 첫번째를 클릭하여 들어가는 코드이다.
send_keys(Keys.ENTER)는 click()과 같은 결과를 같지만 click() 메소드가 반영이 안될때가 있다는 글들보고 send_keys를 사용했다.
드라이버에 창을 변경하고 싶을때는 다음과 같이 driver.switch_to.window() 메소드를 사용하면 된다.

newlink = driver.find_element(by='xpath',value = '//*[@id="info.search.place.list"]/li[1]/div[5]/div[4]/a[1]').send_keys(Keys.ENTER)
driver.switch_to.window(driver.window_handles[-1])

아래는 식당의 평점, 영업 시간, 메뉴를 가져오는 코드이다.
element를 가져오고 싶을때는 driver.find_element 메소드를 사용하며 다음 표에 있는 방식들로 가져올 수 있다.
xpath와 css_selector는 개발자 도구를 켜서 해당 태그를 클릭 후 복사 -> (xpath,css_selector)로 복사로 값을 가져올 수 있다.
요소를 모두 가져오고 싶을때는 find_elements처럼 복수형으로 쓰면 된다.

class tag driver.find_element(By.CLASS_NAME, 'class name')
ID tag driver.find_element(By.NAME, 'name')
Name driver.find_element(By.ID, 'id name')
XPATH driver.find_element(By.XPATH, 'xpath')
CSS_SELECTOR driver.find_element(By.CSS_SELECTOR, 'css selecto')
rateNum = driver.find_element(by='xpath',value = '//*[@id="mArticle"]/div[1]/div[1]/div[2]/div/div/a[1]/span[1]').text

Timelist = driver.find_element(by=By.CLASS_NAME, value = 'list_operation')
periodTime = Timelist.find_elements(by=By.CLASS_NAME, value = 'txt_operation')
detailTime = Timelist.find_elements(by=By.CLASS_NAME, value = 'time_operation')

menus_dic = {}
menulist = driver.find_element(by=By.CLASS_NAME, value = 'list_menu')
menus = menulist.find_elements(by=By.CLASS_NAME, value = 'loss_word')
menus_price = menulist.find_elements(by=By.CLASS_NAME, value = 'price_menu')
for a,b in zip(menus,menus_price):
	menus_dic[a.text] = b.text

다음은 찾은 요소들을 화면에 출력하고 content 딕셔너리에 저장하는 코드이다
영업 시간은 휴무일만 적거나 브레이크 타임만 적거나 너무 다양하게 적어놔서 구체적인 숫자로 된 시간이 있을 경우에만 가져오도록 했다.
모든 과정이 끝나면 아까 열어 놓은 창은 닫고 원래 창으로 돌아가도록 했다.

print("평점 " + rateNum)
content["평점"] = rateNum

if detailTime:
	time = []
    for i in periodTime:
        time.append(i.text)
        print(i.text)
    content["영업 시간"] = time
else:
    print("시간 정보 없음")
    content["영업 시간"] = "시간 정보 없음"

for i in menus_dic:
    print("key: {}, value: {}".format(i, menus_dic[i]))
content["메뉴"] = menus_dic
        
json_data[keyword] = list(content.items())
driver.close()
driver.switch_to.window(driver.window_handles[0]);

json으로 저장하는 방법은 네이버 API 크롤링 포스팅에서 설명하였으니 생략한다

전체 코드

import openpyxl
import os
import sys
import time
import json
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

엑셀에서 데이터 추출(형식 : ㅇㅇ동 ㅁㅁ식당)
filename = '관악구레스토랑.xlsx'
restaurant_file = openpyxl.load_workbook(filename)
restaurants = restaurant_file.worksheets[0]
data = []

for restaurant in restaurants.iter_rows(min_row=3):
    address = restaurant[16].value
    if address is not None:
        data.append([
            restaurant[16].value.split('(')[1].split(')')[0],
            restaurant[18].value
        ])

print(data[0])

json_data = {}
file_path = "./restaurants.json"

options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
driver = webdriver.Chrome('크롬드라이버 경로',options=options)

for i in data:
    keyword = i[0] + ' ' + i[1]
    print(keyword, end=" ")
    try:
        content = {}
        kakao_map_search_url = f"https://map.kakao.com/?q={keyword}"
        driver.get(kakao_map_search_url)
        driver.implicitly_wait(time_to_wait=3)

        newlink = driver.find_element(by='xpath',value = '//*[@id="info.search.place.list"]/li[1]/div[5]/div[4]/a[1]').send_keys(Keys.ENTER)
        driver.switch_to.window(driver.window_handles[-1])

        rateNum = driver.find_element(by='xpath',value = '//*[@id="mArticle"]/div[1]/div[1]/div[2]/div/div/a[1]/span[1]').text

        Timelist = driver.find_element(by=By.CLASS_NAME, value = 'list_operation')
        periodTime = Timelist.find_elements(by=By.CLASS_NAME, value = 'txt_operation')
        detailTime = Timelist.find_elements(by=By.CLASS_NAME, value = 'time_operation')

        menus_dic = {}
        menulist = driver.find_element(by=By.CLASS_NAME, value = 'list_menu')
        menus = menulist.find_elements(by=By.CLASS_NAME, value = 'loss_word')
        menus_price = menulist.find_elements(by=By.CLASS_NAME, value = 'price_menu')
        for a,b in zip(menus,menus_price):
            menus_dic[a.text] = b.text

        print("평점 " + rateNum)
        content["평점"] = rateNum

        if detailTime:
            time = []
            for i in periodTime:
                time.append(i.text)
                print(i.text)
            content["영업 시간"] = time
        else:
            print("시간 정보 없음")
            content["영업 시간"] = "시간 정보 없음"

        for i in menus_dic:
            print("key: {}, value: {}".format(i, menus_dic[i]))
        content["메뉴"] = menus_dic

        json_data[keyword] = list(content.items())
        driver.close()
        driver.switch_to.window(driver.window_handles[0]);

    except Exception as e1:
        print("정보 없음")
        driver.switch_to.window(driver.window_handles[0]);
        pass

    print('\n')

with open(file_path, 'w', encoding='utf-8') as file:
    json.dump(json_data, file, indent=4)
driver.close()
profile
운동하는 개발자

0개의 댓글