-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlivecrawler.py
211 lines (150 loc) · 7.27 KB
/
livecrawler.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import json
import requests
import argparse
import os
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import ElementNotInteractableException
from bs4 import BeautifulSoup
import pandas as pd
import masldb
# 크롬 드라이버 옵션 설정
options = webdriver.ChromeOptions()
options.add_argument('headless') # 브라우저 창 안 뜨도록 프로그램 상에서만 크롤링 동작
options.add_argument('lang=ko_KR') # 언어는 한국어
# 크롬 드라이버 호출
driver_path = "chromedriver" # 다운로드 : https://chromedriver.chromium.org/downloads
driver = webdriver.Chrome(os.path.join(os.getcwd(), driver_path), options=options)
# 2개 브라우저 필요 (구글맵, 카카오맵)
driver.execute_script('window.open("about:blank", "_blank");')
driver.execute_script('window.open("about:blank", "_blank");')
tabs = driver.window_handles
sleep(3)
# 구글맵
driver.switch_to.window(tabs[0])
driver.get('https://www.google.co.kr/maps/@37.597491,126.9258391,14z')
sleep(3)
# 카카오맵
driver.switch_to.window(tabs[1])
driver.get('https://map.kakao.com/')
def area_crawler(area, facility):
# 크롬 드라이버는 전역 변수
global driver
# 빈 데이터프레임 생성 : https://shydev.tistory.com/29
df = pd.DataFrame(columns=['fac_id', 'fac_type', 'fac_name', 'fac_addr', 'fac_geo_lat', 'fac_geo_lng'])
# # 구글에 검색할 "위도값, 경도값"
# geo_keyword = str(lat_center) + ", " + str(lng_center)
# 4초 대기
driver.implicitly_wait(4)
# 검색 함수 실행
df_total = search(area, facility, df)
# 웹드라이버 셧 다운
driver.quit()
print("크롤링 완료")
def search(area, facility, df):
global driver
# sleep(4)
# driver.switch_to.window(tabs[0])
# df_total = pd.DataFrame(columns=['fac_id', 'fac_type', 'fac_name', 'fac_addr', 'fac_geo_lat', 'fac_geo_lng'])
# # xPath 로 검색창 태그 추출
# search_area = driver.find_element_by_xpath('//*[@id="searchboxinput"]')
# sleep(2)
# # 검색어 입력
# search_area.send_keys(keyword)
# sleep(2)
# # 검색 Enter
# driver.find_element_by_xpath('//*[@id="searchbox-searchbutton"]').send_keys(Keys.ENTER)
# # 검색된 정보가 있는 경우에만 탐색
# sleep(3)
# html = driver.page_source
# soup = BeautifulSoup(html, 'html.parser')
# addr = soup.select('#pane > div > div.widget-pane-content.scrollable-y > div > div > div:nth-of-type(8) > div > div.section-info-line > span.section-info-text > span.widget-pane-link')[0].text
# addr_data = addr.split(" ")
# addr = " ".join(addr_data[1:3])
df_temp = list_crawler(area, facility, df)
driver.switch_to.window(tabs[0])
sleep(3)
# 한 페이지 목록을 받아서 크롤링 하는 함수
def list_crawler(addr, facility, df):
keyword = addr + " " + facility
driver.switch_to.window(tabs[1])
sleep(3)
# xPath 로 검색창 태그 추출
search_area = driver.find_element_by_xpath('//*[@id="search.keyword.query"]')
# 검색어 입력
search_area.send_keys(keyword)
# 검색 Enter
driver.find_element_by_xpath('//*[@id="search.keyword.submit"]').send_keys(Keys.ENTER)
sleep(1)
next_button = driver.find_element_by_xpath('//*[@id="info.search.place.more"]')
next_button_class = next_button.get_attribute('class')
if next_button_class == "more":
xPath_next_button = '//*[@id="info.search.place.more"]'
driver.find_element_by_xpath(xPath_next_button).send_keys(Keys.ENTER)
else:
pass
sleep(3)
try:
while True: # 페이지들 크롤링이 전부 끝날 때까지 계속 [다음] 버튼으로 넘어감
for i in range(1, 6):
# 한 덩어리에는 5개의 페이지가 존재 (1페이지 to 5페이지 / 6페이지 to 10페이지 / .. etc.)
xPath_page = '//*[@id="info.search.page.no' + str(i) + '"]'
driver.find_element_by_xpath(xPath_page).send_keys(Keys.ENTER)
sleep(1)
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
# 한 페이지에 검색된 시설 리스트
fac_lists = soup.select('.placelist > .PlaceItem')
# 광고에 따라서 index 조정 필요 - 광고칸은 목록에서 4번째마다 등장
for i, place in enumerate(fac_lists):
if i >= 3:
i += 1
# 시설 이름
fac_name = place.select('.head_item > .tit_name > .link_name')[0].text
# 시설 주소
fac_addr = place.select('.info_item > .addr > p')[0].text
addr_list = fac_addr.split(" ")
addr_list = addr_list[:4]
fac_addr = " ".join(addr_list)
# 시설 카테고리
fac_type = place.select('.head_item > span')[0].text
# 매장 위도 경도
fac_geo_lat, fac_geo_lng = getGeoCode(fac_addr)
print('시설명:', fac_name, '| 시설주소: ', fac_addr, '| 시설분류:', fac_type)
print('위도:', fac_geo_lat, '| 경도:', fac_geo_lng)
print('----------------------------------------------------------------------')
# 데이터프레임에 데이터 .append
df = df.append(pd.DataFrame([['-', fac_type, fac_name, fac_addr, fac_geo_lat, fac_geo_lng]], columns=['fac_id', 'fac_type', 'fac_name', 'fac_addr', 'fac_geo_lat', 'fac_geo_lng']))
# [다음] 버튼의 클래스 속성 값이 next 이면 계속 넘어가고, 아니면(next disabled) 크롤링 종료
next_button = driver.find_element_by_xpath('//*[@id="info.search.page.next"]')
next_button_class = next_button.get_attribute('class')
if next_button_class == "next":
xPath_next_button = '//*[@id="info.search.page.next"]'
driver.find_element_by_xpath(xPath_next_button).send_keys(Keys.ENTER)
else:
break
except ElementNotInteractableException as ni:
print('No More Result', ni)
except Exception as e:
print("Error", e)
finally:
search_area.clear()
# 웹드라이버 임시 종료 - 종료하면 세션 만료로 오류 발생해서 주석 처리
# driver.close()
return df
# KAKAO MAP API 연동하여 위도 경도 받아오는 함수
def getGeoCode(address):
# 주소 정보 입력
url = 'https://dapi.kakao.com/v2/local/search/address.json?query={}'.format(address)
# KAKAO REST API 토큰 인증 - 2021/3/15 8:25 토큰 갱신 (2달간 유효)
headers = {"Authorization": "KakaoAK 6f3c5c2ae909068ed7155b2e79237b82"}
# url 로 위경도 정보 호출
result = json.loads(str(requests.get(url, headers=headers).text))
# 위경도 정보 호출 실패 시 위도 경도 값 각각 "-" 값으로 대체
if result['documents'] == []:
return "-", "-"
else:
match_first = result['documents'][0]['address']
# y 좌표(위도), x 좌표(경도) 반환
return float(match_first['y']), float(match_first['x'])