김건

Merge branch 'naverNews' into 'master'

Naver news

Naver News 크롤링 결과

See merge request !2
1 +1. Data 받아오기
2 + 1) selenuim을 이용하여 웹페이지에서 데이터를 검색
3 + 2) 원하는 URL 입력받는다
4 + 3) headless하게 구현하기 위해 chrome option 적용하여 driver 생성
5 + 4) naverNews는 댓글 영역 하단 부 '더보기'를 지속적으로 눌러줘야하므로
6 + driver의 find_element_by_css_selector함수로 해당 class인
7 + u_cbox_btn_more을 페이지가 끝날 때까지 돌림
8 + 5) 위의 과정에서 얻은 페이지 소스를 beautifulSoup을 이용하여, find_all을 통해 {사용자ID, 댓글, 작성시간}의 데이터를 각각 raw하게 뽑음. (naverNews의 제한적인 특징으로 사용자ID 뒤 4자리는 비공개처리됨)
9 +
10 +2. 사용할 DataSet으로 가공
11 + 1) 리스트 형태로 각각 nicknames(사용자ID), comments(댓글), times(작성시간)을 뽑아냄
12 + 2) 세 리스트에서 짝을 이루는 쌍을 dictionary형태로 {사용자ID, 댓글, 작성시간} 다음과 같이 저장
13 + 3) 저장된 dictionary list(info_dic)을 최종 결과 리스트인 naverNewsList에 저장한다.
14 +
15 +3. 함수 구현
16 + 1) KEYWORD 기반 검색 기능
17 + 2) 가장 자주 나온 단어 검색 기능
18 + 3) ID 기반 검색 기능
19 + 4) 시간 대별 검색 기능
20 + 등 여러 함수 구현 예정
21 +
22 +=> 수정사항
23 +
24 + data를 get하여 정제하는 파일을 모듈로 분리해 내어 list형태로 저장된 데이터셋을 반환하여
25 + main 에서 사용할 수 있도록 한다. 이 후 main에서 리스트를 받아와 url을 입력받아 데이터를
26 + 받아오는 방식으로 사용한다. 이 후, keyword기반, id기반, 시간대 기반 검색 함수를 구현하였고
27 + 시간대별 검색 함수의 기능 보강과 가장 자주 나온 단어 검색 기능을 추가 구현할 예정이다.
28 +
29 +* 4차 수정사항
30 +
31 + 기존파일의 분리 관리 시, import관련 오류 문제 해결 완료(하나의 파일로 관리)
32 + 사용자 UI의 틀을 구축해놓았고, 곧바로 함수별 추가 세부 구현 예정
33 +
34 +* 5차 수정사항
35 +
36 + 1) 네이버 댓글공간엑서 받아온 날짜 정보를 YYYY-MM-DD형식으로 바꿈. ('방금 전, 몇 분 전, 몇 시간 전, 몇 일 전'의 경우를 처리하기 위해 dateTime과 timeDelta 모듈을 활용하여
37 + 현재 날짜를 기준으로 계산하여 YYYY-MM-DD로 저장될 수 있도록
38 + 코드 추가)
39 + 2) 시간대별로 (시작시간, 끝시간)을 입력하여 그 시간에 해당하는 기사를 출력해주는 함수 구현
40 +
41 + 가장 자주 많이 나온 단어 검색과 MATPLOTLIB을 활용한 시각적 표현 구현 예정
42 +
43 +* 6차 수정사항
44 +
45 + konlpy를 활용한 명사 추출 및 단어 빈도수가 많으 순대로 사용자가 입력한 limit만큼 출력해주는 함수 구현 완료
...\ No newline at end of file ...\ No newline at end of file
1 +from selenium import webdriver
2 +from selenium.common import exceptions
3 +from bs4 import BeautifulSoup
4 +from datetime import datetime, timedelta
5 +from konlpy.tag import Twitter
6 +from collections import Counter
7 +import time
8 +
9 +
10 +def getData(url):
11 + ## chrome option걸기 (headless하게 웹 크롤링 수행하기 위해<웹페이지 안보이게 하기>)
12 + options = webdriver.ChromeOptions()
13 + #options.add_argument('headless')
14 + #options.add_argument("disable-gpu")
15 + #_url = "https://entertain.naver.com/ranking/comment/list?oid=144&aid=0000642175" # 크롤링할 URL
16 + _url = url # 크롤링할 URL
17 + webDriver = "C:\\Users\\user\\Desktop\\chromedriver_win32\\chromedriver.exe" # 내 웹드라이버 위치
18 + driver = webdriver.Chrome(webDriver,chrome_options=options)
19 + #driver = webdriver.Chrome(webDriver)
20 + driver.get(_url)
21 + pageCnt = 0
22 + driver.implicitly_wait(3) # 페이지가 다 로드 될때까지 기다리게함
23 + try:
24 + while True: # 댓글 페이지 끝날때까지 돌림
25 + #driver의 find_element_by_css_selector함수로 '네이버 뉴스'의 댓글 '더보기' 버튼을 찾아서 계속 클릭해준다(끝까지)
26 + driver.find_element_by_css_selector(".u_cbox_btn_more").click()
27 + pageCnt = pageCnt+1
28 +
29 + except exceptions.ElementNotVisibleException as e: # 페이지가 끝남
30 + pass
31 +
32 + except Exception as e: # 다른 예외 발생시 확인
33 + print(e)
34 +
35 + pageSource = driver.page_source # 페이지 소스를 따와서
36 + result = BeautifulSoup(pageSource, "lxml") # 빠르게 뽑아오기 위해 lxml 사용
37 +
38 + # nickname, text, time을 raw하게 뽑아온다
39 + comments_raw = result.find_all("span", {"class" : "u_cbox_contents"})
40 + nicknames_raw = result.find_all("span", {"class" : "u_cbox_nick"})
41 + times_raw = result.find_all("span", {"class" : "u_cbox_date"})
42 +
43 + # nickname, text, time 값 만을 뽑아내어 리스트로 정리한다
44 + comments = [comment.text for comment in comments_raw]
45 + nicknames = [nickname.text for nickname in nicknames_raw]
46 + times = [time.text for time in times_raw]
47 +
48 + naverNewsList = []
49 +
50 + for i in range(len(comments)):
51 + info_dic = {'userID' : nicknames[i], 'comment' : comments[i], 'time' : times[i]}
52 + naverNewsList.append(info_dic)
53 +
54 + return naverNewsList
55 + #driver.quit()
56 +
57 +from time import sleep
58 +
59 +def print_cList(c_List) :
60 + for item in c_List :
61 + print(item)
62 +
63 +def search_by_author(c_List,user_ID) :
64 + result_List = []
65 + for item in c_List :
66 + #print(item['userID'])
67 + if ( user_ID in item['userID']) :
68 + result_List.append(item)
69 + return result_List
70 +
71 +def search_by_keyword(c_List,keyword) :
72 + result_List = []
73 + for item in c_List :
74 + #print(item['comment'])
75 + if ( keyword in item['comment']) :
76 + result_List.append(item)
77 + return result_List
78 +
79 +def refine_time(c_List): # 시간에서 몇일 전, 몇 분 전, 방금 전 등의 형태를 YYYY.MM.DD로 바꿔준다
80 + now = datetime.now()
81 +
82 + for item in c_List:
83 + if (item['time'].find('전') != -1): # ~~전이 있으면
84 + if (item['time'].find('일 전') != -1): # ~일 전이라면
85 + _day = -(int)(item['time'][0]) # 몇 일전인지에 대한 정수형 변수
86 + tempTime = now + timedelta(days=_day)
87 + item['time'] = str(tempTime)
88 + item['time'] = item['time'][0:10]
89 + continue
90 + elif (item['time'].find('시간 전') != -1):
91 + _index = item['time'].index('시')
92 + _time = -(int)(item['time'][0:_index]) # 몇 시간 전인지에 대한 정수형 변수
93 + tempTime = now + timedelta(hours = _time)
94 + item['time'] = str(tempTime)
95 + item['time'] = item['time'][0:10]
96 + continue
97 + elif (item['time'].find('분 전') != -1):
98 + _index = item['time'].index('분')
99 + _minute = -(int)(item['time'][0:_index]) # 몇 분 전인지에 대한 정수형 변수
100 + tempTime = now + timedelta(minutes = _minute)
101 + item['time'] = str(tempTime)
102 + item['time'] = item['time'][0:10]
103 + continue
104 + elif (item['time'].find('방금 전') != -1):
105 + tempTime = now
106 + item['time'] = str(tempTime)
107 + item['time'] = item['time'][0:10]
108 + continue
109 + else:
110 + item['time'] = item['time'][0:10]
111 + continue
112 +
113 +
114 +
115 +
116 +
117 +def search_by_time(c_List,startTime, endTime) :
118 + result_List = []
119 +
120 + startYear = int(startTime[0:4])
121 +
122 + if (int(startTime[5]) == 0): # 한자리의 월일 때
123 + startMonth = int(startTime[6])
124 + else:
125 + startMonth = int(startTime[5:7])
126 +
127 + if (int(startTime[8]) == 0): # 한자리의 일일 때
128 + startDay = int(startTime[9])
129 + else:
130 + startDay = int(startTime[8:10])
131 +
132 +
133 +
134 + endYear = int(endTime[0:4])
135 +
136 + if (int(endTime[5]) == 0): # 한자리의 월일 때
137 + endMonth = int(endTime[6])
138 + else:
139 + endMonth = int(endTime[5:7])
140 +
141 + if (int(endTime[8]) == 0): # 한자리의 일일 때
142 + endDay = int(endTime[9])
143 + else:
144 + endDay = int(endTime[8:10])
145 +
146 + for item in c_List:
147 + itemYear = int(item['time'][0:4])
148 +
149 + if (int(item['time'][5]) == 0): # 한자리의 월일 때
150 + itemMonth = int(item['time'][6])
151 + else:
152 + itemMonth = int(item['time'][5:7])
153 +
154 + if (int(item['time'][8]) == 0): # 한자리의 일일 때
155 + itemDay = int(item['time'][9])
156 + else:
157 + itemDay = int(item['time'][8:10])
158 +
159 + if (itemYear >= startYear and itemYear <= endYear):
160 + if (itemMonth >= startMonth and itemMonth <= endMonth):
161 + if(itemDay >= startDay and itemDay <= endDay):
162 + result_List.append(item)
163 +
164 + return result_List
165 +
166 +def printMostShowed(c_List,limit):
167 + temp = ""
168 + result = ""
169 + for item in c_List:
170 + temp = str(item['comment']) + " "
171 + result = result + temp
172 +
173 + sp = Twitter()
174 +
175 + nouns = sp.nouns(result)
176 +
177 + _cnt = Counter(nouns)
178 +
179 + tempList = []
180 + repCnt = 0
181 +
182 + for i,j in _cnt.most_common(limit):
183 + print(str(repCnt+1)+'. '+str(i)+" : "+str(j))
184 + repCnt += 1
185 +
186 +def printResult(c_List):
187 + for i in range(0,len(c_List)):
188 + print(c_List[i])
189 +
190 +def main ():
191 + ## 시작화면
192 +
193 + _star = '*'
194 + print(_star.center(30,'*'))
195 + print('\n')
196 + headString = '< Naver News Crawling >'
197 + print(headString.center(30,'*'))
198 + print('\n')
199 + print(_star.center(30,'*'))
200 +
201 +
202 + # 검색하고자 하는 url을 입력받는다
203 + _url = input('검색하고자 하는 url을 입력해주세요: ')
204 + print('comment_list를 가져오는 중.....')
205 + cList = getData(_url)
206 + refine_time(cList)
207 + #printMostShowed(cList,10)
208 + print('\n')
209 + print('comment_list를 다 가져왔습니다!')
210 +
211 + while(True):
212 + print('***********************************')
213 + print('1.닉네임 기반 검색')
214 + print('2.키워드 기반 검색')
215 + print('3.작성시간 기반 검색')
216 + print('4.자주 나타난 단어 출력')
217 + menu = input('메뉴를 입력해주세요: ')
218 +
219 + if(menu == str(1)):
220 + print('***********************************')
221 + inputID = input('검색할 닉네임 앞 4자리를 입력해주세요(전 단계로 가시려면 -1을 입력해주세요): ')
222 + if(inputID == str(-1)):
223 + continue
224 + _result = search_by_author(cList,inputID)
225 + printResult(_result)
226 + print(_result)
227 + elif(menu == str(2)):
228 + print('***********************************')
229 + inputKW = input('검색할 키워드를 입력해주세요(전 단계로 가시려면 -1을 입력해주세요): ')
230 + if(inputKW == str(-1)):
231 + continue
232 + _result = search_by_keyword(cList,inputKW)
233 + printResult(_result)
234 + elif(menu == str(3)):
235 + print('***********************************')
236 + print('전 단계로 돌아가시려면 -1을 입력해주세요')
237 + startTime = input('검색할 시간대의 시작일을 입력해주세요(YYYY-MM-DD): ')
238 + endTime = input('검색할 시간대의 마지막 일을 입력해주세요(YYYY-MM-DD): ')
239 +
240 + if(startTime == str(-1) or endTime == str(-1)):
241 + continue
242 +
243 + _result = search_by_time(cList,startTime,endTime)
244 + printResult(_result)
245 + elif(menu == str(4)):
246 + print('***********************************')
247 + inputLimit = input('상위 몇 개 까지 보고 싶은지 입력하세요(1~20): ')
248 + while(True):
249 + if (int(inputLimit) <= 0 or int(inputLimit) > 20):
250 + inputLimit = input('상위 몇 개 까지 보고 싶은지 입력하세요(1~20): ')
251 + else:
252 + break
253 +
254 + printMostShowed(cList,int(inputLimit))
255 + else:
256 + print('잘못된 입력입니다')
257 + continue
258 +
259 +
260 +
261 +main()