Merge branch 'naverNews' into 'master'
Naver news Naver News 크롤링 결과 See merge request !2
Showing
2 changed files
with
306 additions
and
0 deletions
naverNews/naverNews.md
0 → 100644
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 |
naverNews/naverNews_crawling.py
0 → 100644
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() |
-
Please register or login to post a comment