336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

두번째 프로젝트 순서

1. 프로젝트 주제 정하기

2. 기획 및 데이터 수집, 전처리

3. 데이터 저장(판다스 열/행 관련 정리)

4. 시각화 및 자동화


프로젝트 마무리

매 10분마다 크롤링을 진행하고, 19시가 되면 이슈와 사설을 이메일로 보내준다. 자동화는 코드가 어디있는지 주섬주섬 다시 찾아봐야 한다 ㅠㅠ. 진행을 이슈파트 따로 사설파트 따로 해서, 사설은 어떤 식으로 코드 진행이 됐는지 잘 모르겠지만 나쁘지 않은 결과가 나왔다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
import os
from datetime import datetime
 
base_dir = 'C:/Workspace/project2_final/output/'
os.makedirs(base_dir, exist_ok=True)
 
from wordcloud import WordCloud
wc = WordCloud(font_path=r'C:\Windows\Fonts\MalgunBD.ttf',background_color="white",
               max_words=150, max_font_size=300, width=800, height=800)
cloud = wc.generate_from_frequencies(dict(sum_search))
 
cloud.to_file(base_dir+'IssueKeyWord '+ datetime.today().strftime('%Y%m%d'+ '.png')
 
cs

하루 일정이 마무리 되면 실시간 TOP100개의 키워드와 언급된 게시물 조회수의 비중에 따라 워드클라우드를 생성한다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#### 상위 5개 키워드 ####
 
df = pd.read_excel(path2, header=[0,1], index_col=[0] )
df_col = list(df.columns.levels[0])
 
hit_top5 = []
 
for i in df_col :
    hit_max = df[i,'조회수'].max()
    hit_top5.append(hit_max)
 
hit_dict = dict(zip(df_col,hit_top5)) #딕셔너리 값으로 저장
 
keyword_top5 = sorted(hit_dict, key=hit_dict.get, reverse = True)[:5#탑 5개 keyword 추출
print(keyword_top5)
cs

 



 

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
#### 19시에 코드 보내기
 
import matplotlib.pyplot as plt
import seaborn as sns
 
title_font = {'fontsize'16'fontweight''bold'}
plt.rc('font',family='Malgun Gothic')
plt.figure(figsize=[20,10])
plt.style.use('ggplot')
plt.show(block=False)
plt.pause(1)
plt.close()
 
ymd = today.strftime('%Y-%m-%d'
hms = '19:00:00'
 
if hms == '19:00:00':
    path_png = "C:/Workspace/project2_final/output/graphs"
    if not os.path.isdir(path_png):                                                           
        os.mkdir(path_png)
    
    path_date = f"C:/Workspace/project2_final/output/graphs/{ymd}"
    if not os.path.isdir(path_date):                                                           
        os.mkdir(path_date)    
    
    ##### 그래프 조회수 변동 ######
    for i in keyword_top5 :
        plt.title (f"키워드 : '{i}' 조회수 변동", fontsize=20)
        df.index,df[i,'조회수'].plot( kind='bar')
        #    plt.bar(df.index,df[i,'조회수'])
        #plt.show()
        plt.savefig(f'{path_date}/조회수변동-({i}).png', bbox_inches='tight')
        plt.close()
                 #.plot( kind='bar')
 
    ##### 그래프 키워드 관심도 #####
    for i in keyword_top5 :
        text =f'{i} 키워드 관심도'
        sub_keys = df[i].groupby(['서브키워드'])['조회수'].mean().sort_values()
        plt.title(text, fontdict=title_font, loc='center', pad= 20)
        sub_keys.plot(kind='pie', autopct = '%1.1f%%', shadow = True, startangle=110 )
        #plt.show()
 
        plt.savefig(f'{path_date}/관심도-({i}).png')
        plt.close()
    
    print('그래프 시각화 완료')
cs

19시가 되면 조회수 변동, 키워드에 따른 서브 키워드가 얼마나 변했는지 확인할 수 있다. 이거는 강의실에 와서 발표전 30분 데이터를 취합해서 만든 것이라 서브 키워드 변동도 거의 없고, 조회수 변동폭도 크지 않음을 확인할 수 있다.

 

 

 

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
import smtplib
from email.mime.multipart import MIMEMultipart;
# 메일의 본문 내용을 만드는 모듈
from email.mime.text import MIMEText;
# 메일의 첨부 파일을 base64 형식으로 변환
from email.mime.application import MIMEApplication;
# 메일의 이미지 파일을 base64 형식으로 변환(Content-ID 생성)
from email.mime.image import MIMEImage;
# 메일의 음악 파일을 base64 형식으로 변환(Content-ID 생성)
from email.mime.audio import MIMEAudio;
# 파일 IO
import io;
 
# 메일 서버와 통신하기 전에 메시지를 만든다.
data = MIMEMultipart();
# 송신자 설정
data['From'= "본인의 이메일";
# 수신자 설정 (복수는 콤마 구분이다.)
data['To'= "이메일1","이메일2";
# 메일 제목
data['Subject'= "제목"
with open("C:\\workspace\\project2_final\\mailsource\\hi.png"'rb'as fp:
    img = MIMEImage(fp.read(), Name = "hi.png")
    img.add_header('Content-ID''<hi>')
    data.attach(img)
 
with open("C:\\workspace\\project2_final\\mailsource\\issue.png"'rb'as fp:
    img = MIMEImage(fp.read(), Name = "issue.png")
    img.add_header('Content-ID''<issue>')
    data.attach(img)
    
with open("C:\\workspace\\project2_final\\mailsource\\news.png"'rb'as fp:
    img = MIMEImage(fp.read(), Name = "news.png")
    img.add_header('Content-ID''<news>')
    data.attach(img)    
    
with open("C:\\workspace\\project2_final\\mailsource\\tw.png"'rb'as fp:
    img = MIMEImage(fp.read(), Name = "tw.png")
    img.add_header('Content-ID''<tw>')
    data.attach(img)    
    
with open("C:\\workspace\\project2_final\\mailsource\\fb.png"'rb'as fp:
    img = MIMEImage(fp.read(), Name = "fb.png")
    img.add_header('Content-ID''<fb>')
    data.attach(img)    
 
with open("C:\\workspace\\project2_final\\output\\IssueKeyWord {}.png".format(datetime.today().strftime('%Y%m%d')), 'rb'as fp:
    img = MIMEImage(fp.read(), Name = "wc1.png")
    img.add_header('Content-ID''<wc1>')
    data.attach(img)
    
with open("C:\\workspace\\project2_final\\output\\KeyWord {}.png".format(datetime.today().strftime('%Y%m%d')), 'rb'as fp:
    img = MIMEImage(fp.read(), Name = "wc2.png")
    img.add_header('Content-ID''<wc2>')
    data.attach(img)
    
 
cs

 

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
# Html 형식의 본문 내용 (cid로 이미 첨부 파일을 링크했다.)
 
html = """
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>하루 이슈</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body style="margin: 0; padding: 0;">
 <table align="center" border="0" cellpadding="0" cellspacing="0" width="600" style="border: 1px solid #cccccc;">
 <tr>
  <td align="center" bgcolor="#bdd7ee" style="padding: 40px 0 30px 0;">
 <img src="cid:hi" alt="Creating Email Magic" width="600" height="380" style="display: block;" />
</td>
 </tr>
 <tr>
  <td bgcolor="#ffffff" style="padding: 40px 30px 40px 30px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
 <tr>
  <td font-family: 'Noto Sans KR', sans-serif;>
   <table border="0" cellpadding="0" cellspacing="0" width="100%">
 <tr>
  <td width="260" valign="top" font-family:Noto Sans KR, sans-serif; font-size: 16px; line-height: 20px;"> 
   <h2>커뮤니티 인기 키워드 100</h2>
   <img src="cid:wc1" alt="Creating Email Magic" width="250" height="250" style="display: block;" />
  </td>
  <td style="font-size: 0; line-height: 0;" width="20">
   &nbsp;
  </td>
  <td width="260" valign="top" font-family:Noto Sans KR, sans-serif; font-size: 16px; line-height: 20px;"> 
   <h2>사설 주요 키워드 100</h2>
   <img src="cid:wc2" alt="Creating Email Magic" width="250" height="250" style="display: block;" />
  </td>
 </tr>
</table>
  </td>
 </tr>
 <tr>
  <td style="padding: 20px 0 30px 0; font-family:Noto Sans KR, sans-serif; font-size: 16px; line-height: 20px;">
   <p>오늘 하루도 수고하셨습니다. 하이와 함께 행복한 하루를 마무리하세요!</p>
   <br>
   <br>
  </td>
 </tr>
 <tr>
  <td>
  <table border="0" cellpadding="0" cellspacing="0" width="100%">
 <tr>
  <td width="260" valign="top">
   <table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tr>
     <td bgcolor="#3B89A3" align="center" style="padding: 20px 30px 20px 30px;">
      <img src="cid:issue" alt="" width="50%" height="100" style="display: block;" />
     </td>
    </tr>
    <tr>
     <td style="padding: 10px 0 30px 0; font-family:Noto Sans KR, sans-serif; font-size: 16px; line-height: 20px;"> 
     <h3 style="font-family:Noto Sans KR, sans-serif;">오늘의 커뮤니티 인기 키워드</h3>
      <details><p style="line-height:180%">{1}</p>
       <summary>{0}
       </summary>
      </details>
      <br>
      <details><p style="line-height:180%">{3}</p>
       <summary>{2}
       </summary>
      </details>
      <br>
      <details><p style="line-height:180%">{5}</p>
       <summary>{4}
       </summary>
      </details>
      <br>
      <details><p style="line-height:180%">{7}</p>
       <summary>{6}
       </summary>
      </details>
      <br>
      <details><p style="line-height:180%">{9}</p>
       <summary>{8}
       </summary>
      </details>
     </td>
    </tr>
   </table>
  </td>
  <td style="font-size: 0; line-height: 0;" width="20">
   &nbsp;
  </td>
  <td width="260" valign="top">
   <table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tr>
     <td bgcolor="#3B89A3" align="center" style="padding: 20px 30px 20px 30px;">
      <img src="cid:news" alt="" width="50%" height="100" style="display: block;" />
     </td>
    </tr>
    <tr>
     <td style="padding: 10px 0 30px 0; font-family:Noto Sans KR, sans-serif; font-size: 16px; line-height: 20px;"> 
     <h3 style="font-family:Noto Sans KR, sans-serif;">오늘의 사설 주요 키워드</h3>
      <details><p style="line-height:180%">{11}</p>
       <summary>{10}
       </summary>
      </details>
      <br>
      <details><p style="line-height:180%">{13}</p>
       <summary>{12}
       </summary>
      </details>
      <br>
      <details><p style="line-height:180%">{15}</p>
       <summary>{14}
       </summary>
      </details>
      <br>
      <details><p style="line-height:180%">{16}</p>
       <summary>오늘 자주 등장한 키워드
       </summary>
      </details>
      
     </td>
    </tr>
   </table>
  </td>
 </tr>
</table>
  </td>
 </tr>
</table>
</td>
 </tr>
 <tr>
  <td bgcolor="#bdd7ee" style="padding: 30px 30px 30px 30px;">
 <table border="0" cellpadding="0" cellspacing="0" width="100%">
 <tr>
 <td width="75%"; style="color: #000000; font-family: Arial, sans-serif; font-size: 14px;">
 &reg; 하루이슈, Hi 2021<br/>
 <a href="" style="color: #000000;"><font color="#000000">Unsubscribe</font></a> to this newsletter instantly
</td>
  <td align="right">
 <table border="0" cellpadding="0" cellspacing="0">
  <tr>
   <td>
    <a href="http://www.twitter.com/">
     <img src="cid:tw" alt="Twitter" width="38" height="38" style="display: block;" border="0" />
    </a>
   </td>
   <td style="font-size: 0; line-height: 0;" width="20">&nbsp;</td>
   <td>
    <a href="http://www.facebook.com/">
     <img src="cid:fb" alt="Facebook" width="38" height="38" style="display: block;" border="0" />
    </a>
   </td>
  </tr>
 </table>
</td>
 </tr>
</table>
</td>
 </tr>
</table>
</body>
 
</html>
""".format(keyword_top5[0], articlestr1, keyword_top5[1], articlestr2, keyword_top5[2], articlestr3, keyword_top5[3], articlestr4, keyword_top5[4], articlestr5, sendingKeywords[0], content1, sendingKeywords[1], content2, sendingKeywords[2], content3, dfCSV["text"].tolist())
 
 
msg = MIMEText(html, 'html')
 
 
# 메시지를 확인한다.
# Data 영역의 메시지에 바운더리 추가
data.attach(msg);
print(data);
# 메일 서버와 telnet 통신 개시
server = smtplib.SMTP_SSL('smtp.naver.com',465);
#server = smtplib.SMTP('smtp.gmail.com',587);
# 메일 통신시 디버그
server.set_debuglevel(1);
# 헤로 한번 해주자.(의미 없음)
server.ehlo();
# tls 설정 주문 - tls 587 포트의 경우
#server.starttls();
# 헤로 또 해주자.(의미 없음)
server.ehlo();
# 로그인 한다.
server.login("ID", "P/W!"); # 아이디, 패스워드 입력
# 심심하니 또 헤로 해주자.(의미 없음)
server.ehlo();
# MAIL(송신자) 설정
sender = data['From'];
# RCPT(수신자), 리스트로 보낸다.
# 수신자 추가
receiver = data['To'].split(",");
# # 참조자 추가
# if data['Cc'] is not None:
#     receiver += data['Cc'].split(",");
# # 숨은 참조자 추가
# if data['Bcc'] is not None:
#     receiver += data['Bcc'].split(",");
# 메일 프로토콜 상 MAIL, RCPT, DATA 순으로 메시지를 보내야 하는데 이걸 sendmail함수에서 자동으로 해준다.
server.sendmail(sender, receiver, data.as_string());
# QUIT을 보내고 접속을 종료하고 메일을 보낸다.
server.quit();
 
cs

 

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

두번째 프로젝트 순서

1. 프로젝트 주제 정하기

2. 기획 및 데이터 수집, 전처리

3. 데이터 저장(판다스 열/행 관련 정리)

4. 시각화 및 자동화


데이터를 어떤 식으로 저장할지에 대해서 고민이 많았다. 구글링을 엄청 많이 했는데 잘 나오지 않아서, 영어로도 검색을 많이 했다. 한 열 안에 2개의 정보(멀티인덱스)가 들어가야 했고, 새로운 행이 생성될 때 계속 새로운 열이 생성됐기 때문에 쉽지 않았다. 그래서 판다스(pandas) 관련 개념을 정리해보려고 한다.

 

 

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
#pandas 개념 정리
 
'''
df_review_one_sentence는 컬럼이 titles(제목), reviews(리뷰)로 구성이 됐다.
 
[info]
 #   Column   Non-Null Count  Dtype 
---  ------   --------------  ----- 
 0   titles   3498 non-null   object
 1   reviews  3498 non-null   object
 
[head]
                                             titles                                            reviews
0  13시간 (13 Hours: The Secret Soldiers of Benghazi)  토요일 내리다 황사 몰아치다 일요일 오전 난생 처음 홍대 찾다 파괴 마이클 베이 형...
1    1942: 최정예특수부대 스페츠나츠 (The Dawns Here Are Quiet)  처음 리뷰 킬링타임 용이 땡기다 빠지다 듭니 처음 부분 별로 여군 샤워 커버 해주다...
2                                       33 (The 33)  패트리시아 리건 출연 안토니오 라스 로드 산토 줄리엣 비노 미국 칠레 평점 리뷰 예...
3                          400번의 구타 (The 400 Blows)  달리기 어딘에선 달아나다 모습 기억 남다 처음 만난 우산 가지 오지 내리다 기다 흡...
4                                  45년 후 (45 Years)  압구정 괜찮다 거짓말 괜찮다 괜찮다 괜찮다 살아가다 많다 괜찮다 외치다 괜찮다 괜찮...
 
iloc => 행번호로 선택
loc => 라벨이나 조건으로 선택
'''
 
print(df_review_one_sentence.iloc[0]) # 첫번째 행만 추출 => 제목, 리뷰 다나옴
print(df_review_one_sentence.iloc[0,1]) #첫번째 행, 1번 열  / 요소 1개 뽑기 - 리뷰만 나옴
print(df_review_one_sentence.iloc[0:5,0]) # 1~5번째 행의 제목 5개
print(df_review_one_sentence.loc[:, 'titles']) #컬럼명으로 보겠다 / 모든 제목이 출력된다.
print(df_review_one_sentence['titles'][0]) #타이틀에 0번째

df.iloc[row,col] 숫자로
df.loc['Tom','math'] 컬럼명으로
cs
1
2
3
4
5
6
7
8
9
# enumerate 정리
ls = ['겨울왕국','라이온킹','알라딘']
print(list(enumerate(ls))) #튜플 리스트로 보여줌 / [(0, '겨울왕국'), (1, '라이온킹'), (2, '알라딘')]
for idx,i in enumerate(ls) : #0 겨울왕국, 1 라이온킹, 2 알라딘 출력 / 인덱스 값 같이 보여줌
    print(idx, i)
 
for idx, i in enumerate(ls):
    if i == '라이온킹':
        print(idx)
cs

 

 

 

 

1-4. 파이썬 EDA 데이터 분석 팀 프로젝트 판다스, 시각화 (Matplotlib, Json)

- 첫 프로젝트 글 순서 - 1. 파이썬(python) EDA 데이터분석 주제 정하기 2. 실패한 여기어때 후기 웹스크래핑(web scraping) 3. 데이터 수집 방법 & 데이터 추출, 정제 4. 판다스(pandas) 데이터 처리 / Matpl.

0goodmorning.tistory.com

특정 값을 추출하고 groupbpy를 통해서 묶거나 더하는 것이 필요하다면 이전 코드를 한 번 참조해주세요! 와 근데 지금 다시 보니까 진짜 코드가 엉망진창인 것 같습니다.. 첫번째에 비해 두번째 프로젝트 코드가 훨씬 나아진 것 같고, 현재는 더 나아졌다고 생각이 든다.

이제부터는 안 됐던 코드들을 같이 첨부하려고 합니다. 보시면서 여러분들은 같은 실수는 하지 말아주세요 ㅠㅠㅠ

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
##### 엑셀파일 생성될 때 기본 틀 갖추기 ####
 
import pandas as pd
import numpy as np
from itertools import chain
 
def frame(key_list, day1, day2, path2) :
    
    key_lists = list(chain.from_iterable(zip(key_list,key_list))) #key_list_1로 설정해서 기준을 만들어줘야 함
    multi_key = ['조회수','서브키워드']
    multi_keys = ['조회수','서브키워드'* len(key_list)
 
    range = pd.date_range(f'{day1} 19:00:00',f'{day2} 18:50:00', freq='10min'
    df = pd.DataFrame(columns = [key_lists, multi_keys],index = range)
    
    df.to_csv(f'{path2}', index=False, encoding='UTF-8-sig')
    
    print('저장완료')
cs

처음에는 이런 식으로 먼저 19시부터 익일 18시 50분(def 함수 만들기 전 사진이라 19시까지 들어갔네요. 19시 기준은 퇴근 시간 기준으로 집에 가는 길이나 집에서 하루 이슈를 마무리할 수 있게 잡았습니다.)까지 데이터를 만들어서 저장을 하는 방식인데 고려할 것이 여러개 생겼습니다.

 

 

 

파일을 불러올 경우에 멀티인덱스가 제대로 안 보여지는 것 같습니다. 한강 한강이 병합돼서 하나의 한강 밑에 조회수/ 서브키워드가 있어야 하는데 Unnamed: 0, Unnamed: 2, Unnamed: 4 이런 식으로 생성되는 것을 확인할 수 있습니다. 그리고 시간 마다 새로운 열이 추가되고, 기존 열은 또 사용을 해서 기존 열에 있으면 새로운 열을 생성하는 식의 코드를 짜봤는데 계속 에러가 떠서 포기했습니다. 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
import numpy as np
 
 
df1 = pd.read_excel(path2, header=[0,1], index_col=[0] )
 
 
# 조회수, 서브 키워드 입력
def jos(i, t) :
    df.loc[t][(i,'조회수')] = sum_search[i]
    df.loc[t][(i,'서브키워드')] = sub_key[i]
    
= '2021-05-04 19:20:00' #현재 시간 설정
 
for i in sum_search : 
    if i not in key_list_1 :  # 기준이 되는 key_list 값에 없을 경우 열 추가
        a = [i]
        df = df.join(pd.DataFrame(columns=pd.MultiIndex.from_product([a, multi_key]),index=df.index))
        jos(i, t)
    else :
        jos(i, t)
        
df.to_csv(path2)
cs

이런 식으로 진행을 하려고 했는데 조회만 추가가 되고 서브키워드는 추가가 안 되는 문제가 발생했습니다.

 

 

 

 

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
import pandas as pd
import numpy as np
 
#복제
from itertools import chain
key_lists = list(chain.from_iterable(zip(key_list,key_list))) #key_list_1로 설정해서 기준을 만들어줘야 함
multi_key = ['조회수','서브키워드']
multi_keys = ['조회수','서브키워드'* len(key_list)
 
# 조회수, 서브 키워드 입력
def jos(i, time) :
    df.loc[time][(i,'조회수')] = sum_search[i]
    df.loc[time][(i,'서브키워드')] = sub_key[i]
    
# generate time series index
range = pd.date_range('05-03-21''05-04-21', freq='10min')              # if문 7~ +1 6시50분 시간을 기준으로 삼아서
df = pd.DataFrame(columns = [key_lists, multi_keys],index = range)
 
time = "2021-05-03 00:10:00" #현재 시간 설정
 
 
 
for i in sum_search : 
    if i not in key_lists :
        a = [i]
        df = df.join(pd.DataFrame(columns=pd.MultiIndex.from_product([a, multi_key]),index=df.index))
        jos(i, time)
    else :
        jos(i, time)
df
cs

코드를 바꿔서 진행했는데 이제는 조회수와 서브키워드에 채워지는 것을 확인할 수 있습니다. 그런데 또 시간이 바뀌고 새로운 데이터를 크콜링하게 되면 오류가 나가너 코드가 복잡해져서 방법을 더 찾았습니다.

 

 

 

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
#### 행 생성 ####
import pandas as pd
import numpy as np
from itertools import chain
 
#복제
key_lists = list(chain.from_iterable(zip(key_list,key_list))) #멀티 인덱스를 위해서 [a,b,c] => [a,a,b,b,c,c] 로 만들어줌
multi_keys = ['조회수','서브키워드'* len(key_list)
 
# 조회수, 서브 키워드 입력
def hit_sub(i, time) :
    df1.loc[time][(i,'조회수')] = sum_search[i]
    df1.loc[time][(i,'서브키워드')] = sub_key[i]
    
# generate time series index
time = nowDatetime    #현재 시간 설정
 
df1 = pd.DataFrame(columns = [key_lists, multi_keys],index = [time])
 
for i in sum_search : 
    hit_sub(i, time)
    
#### 다음 행에 추가 ####
df = pd.read_excel(path2, header=[0,1], index_col=[0] )   #number 해결
df = df.append(df1, sort=False)    #컬럼 수가 다를 때 NaN 값을 넣고 행 추가
cs

if 문을 사용하지 않고 멀티 인덱스가 있는 상황에서, 새로운 행과 열이 생성되고, 기존 열은 유지되는 함수

=> append를 사용하시면 됩니다!!!

 

 

 

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
#subject 디렉토리내 파일 삭제하기
 
import shutil
 
pathTest = r"C:/Workspace/project2_final/output/temp"
 
try:
    shutil.rmtree(pathTest)
except OSError as e:
    print(e)
else:
    print("The directory is deleted successfully")
 
 
#### 새로운 파일 생성 ###
 
import os
from datetime import datetime, date, time, timedelta
import datetime
import openpyxl
import pickle
 
#디렉토리 폴더 생성
path = "C:/Workspace/project2_final/output"
if not os.path.isdir(path):                                                           
    os.mkdir(path)
 
 
today =datetime.datetime.now()
 
hms = today.strftime('%H:%M:%S'#시분초
ymd = today.strftime('%Y-%m-%d'#년월일  - 오늘 날짜
yesterday = (today - datetime.timedelta(1)).strftime('%Y-%m-%d'#어제날짜
 
wb = openpyxl.Workbook()
 
if hms < '19:00:00':
    file_name = yesterday
    path2 = f"C:/Workspace/project2_final/output/{file_name}.xlsx"
    
    if not os.path.isfile(path2):  
        wb.save(path2)
        print('파일 생성완료1')
 
    else :
        print('파일을 작업중입니다....')
 
else : 
    file_name = ymd
    path2 = f"C:/Workspace/project2_final/output/{file_name}.xlsx"
    
    if not os.path.isfile(path2):  
        wb.save(path2)
        print('파일 생성완료2')
 
    else :
        print('파일을 작업중입니다....')
 
cs

이런 식으로 특정 시간이 되면 새로운 폴더와 파일을 생성 하는 코드입니다.

 

 

 

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

두번째 프로젝트 순서

1. 프로젝트 주제 정하기

2. 기획 및 데이터 수집, 전처리

3. 데이터 저장(판다스 열/행 관련 정리)

4. 시각화 및 자동화


각종 커뮤니티를 모두 크롤링하기에는 시간도 없고 벅차서, 이슈링크라는 싸이트에서 이미 친절하게 각종 커뮤니티를 크롤링해주고 있어서 이슈링크 싸이트를 이용하였다. 봇을 이용하여 글들을 긁어와주는 걸로 보인다. 

 

하지만 확인 결과, 오늘의 이슈태그 Top5커뮤니티 베스트 키워드들은 실제로 다수의 사람들이 관심이 있는 것이 아니었다. 분석하기로는 얼마나 커뮤니티에서 자주 언급되는지에 따라서 순위가 올라가는 것으로 보인다.

 

그래서 다수의 사람들이 관심이 있는 키워드와 이슈거리를 어떻게 하면 찾을 수 있을까 생각하면서 파이썬 프로젝트를 진행했다.

 

 

 

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
#현재 시간 설정
from datetime import datetime, date, time, timedelta
 
now = datetime.now()
nowDatetime = now.strftime('%Y-%m-%d %H:%M')
print(nowDatetime)
 
 
#### 이슈 빼오기 #####
import requests
from bs4 import BeautifulSoup
 
url = 'https://www.issuelink.co.kr/community/listview/all/3/adj/_self/blank/blank/blank'
headers = {'user-agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'}
html_rank = requests.get(url, headers=headers).text
soup_rank = BeautifulSoup(html_rank, 'lxml')
keywords = soup_rank.select('div.ibox.float-e-margins > div > table > tbody > tr > td > a')
 
 
key_list = []
for k in keywords:
    keyword = k.text
    key_list.append(keyword)
 
###### 7시를 기준으로 기준 리스트를 하나 만들어야 함 / datetime에서 시간 분만 빼와서 if로 비교
 
key_list.pop(0)
print(key_list)
cs

 

 

 

예측을 한 것인데 실제로 확인을 해보니 총조회수가 높지 않지만 많이 언급될 수록 상위권에 있는 것으로 확인이 됐다. 이제 이 과정을 코드로 시행.

 

 

 

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
### 이슈 실제 조회수 ###
 
from bs4 import BeautifulSoup
import requests
import urllib
import operator
 
sum_list = []
search_list = key_list
 
def sum(search) :
    
    url = f'https://www.issuelink.co.kr/community/listview/read/3/adj/_self/blank/{search}'
    headers = {'user-agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'}
    r = requests.get(url, headers=headers)
    soup = BeautifulSoup(r.text, 'html.parser')
 
    hits = soup.select('span.hit')
    
    sum=0
    
    for hit in hits :
        sum += int(hit.text.replace(',',''))
    
    sum_list.append(sum)  
    print('*',end='')
    
for search in search_list :
    sum(search)
 
#조회수, 키워드 합치기
sum_search = dict(zip(key_list,sum_list))
 
#조회수 순으로 정렬
#a = sorted(sum_search.items(), key=lambda x:x[1], reverse = True)
 
print()
print(sum_search)
cs

이슈가 되는 키워드를 검색했을 때, 각 커뮤니티별로 얼만큼의 조회수를 보이고 있는지 확인을 했다. 문제는 각 사람들마다 성향이 있어서 특정 싸이트를 보여주면, 극도로 싫어하는 사람들이 있다. 그래서 커뮤니티 글 중에 조회수가 높은 글을 보여주기에는 애매해서, 이를 네이버 뉴스로 보여주려고 했다.

 

다만 네이버 뉴스를 예로 들었을 때, 당시 '브레이브걸스' 로션은 알테니 스킵을 아재 팬들이 몰라서 이슈가 됐었다. 하지만 당시 브레이브걸스 자체가 가지고 있는 인기가 있어서, 브레이브걸스를 뉴스에서 검색해도 이 알테니 스킵이라는 이슈와는 다른 이슈가 검색이 될 수도 있다. 그래서 서브키워드를 같이 추출하기 위해서 형태소 분석을 했다.

 

 

 

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
#### 키워드 제목 추출 탐30 개별로 ####
 
from bs4 import BeautifulSoup
import requests
import urllib
import os
 
#디렉토리 폴더 생성
path = "./subject"
if not os.path.isdir(path):                                                           
    os.mkdir(path)
 
keyword_list=[]
 
def subject(search) :
    url = f'https://www.issuelink.co.kr/community/listview/all/3/adj/_self/blank/{search}'
    headers = {'user-agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'}
    r = requests.get(url, headers=headers)
    soup = BeautifulSoup(r.text, 'html.parser')
 
    sub = soup.select('span.title')
    
    keyword_list.clear()
    
    for i in sub :
        split_string = i.get_text().split(' [',1)
        substring = split_string[0]    
        keyword_list.append(substring)
        
    with open(f'./subject/{search}.txt','w', encoding = 'utf-8'as file :
        file.writelines(keyword_list)
    
    print('**', end="")
            
    
for search in search_list :
    subject(search)
 
print()
print('완료')
cs

 

 

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
#### 키워드 형태소 카운팅 ####
 
""" 형태소 분석기
    명사 추출 및 빈도수 체크
    python [모듈 이름] [텍스트 파일명.txt] [결과파일명.txt]
"""
 
import sys
from konlpy.tag import Twitter
from collections import Counter
 
 
def get_tags(text, ntags=50):
    spliter = Twitter()
    nouns = spliter.nouns(text)
    count = Counter(nouns)
    return_list = []
    for n, c in count.most_common(ntags):
        temp = {'tag': n, 'count': c}
        return_list.append(temp)
    return return_list
 
 
def main(search):
    # 분석할 파일
    noun_count = 50
    # count.txt 에 저장
    open_text_file = open(f'./subject/{search}.txt''r',-1,"utf-8")
    # 분석할 파일을 open 
    text = open_text_file.read() #파일을 읽습니다.
    tags = get_tags(text, noun_count) # get_tags 함수 실행
    open_text_file.close()   #파일 close
    open_output_file = open(f"./subject/{search}-count.txt"'w',-1,"utf-8")
    # 결과로 쓰일 count.txt 열기
    for tag in tags:
        noun = tag['tag']
        count = tag['count']
        open_output_file.write('{} {}\n'.format(noun, count))
    # 결과 저장
    open_output_file.close() 
 
for search in search_list :
    main(search)
    
print('완료')
 
cs

당시에 형태소 분석을 제대로 다루지 못해서 구글을 통해서 검색을 했다. 그리고 제목보다는 서브키워드가 중요하다고 생각하여서, 제목은 잠시 파일에 저장하고 시간이 지나면 삭제하는 식의 과정을 진행했다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
from bs4 import BeautifulSoup
 
sub_key = {}
 
for i in sum_search :
      
    with open(f'C:/Workspace/project2_final/output/temp/{i}-count.txt','r', encoding = 'utf-8'as file :
        data = str(file.readlines()[1])
 
    split_string = data.split(' ',1
    substring = split_string[0]           #빈도수 제거 
    #print(substring)
    
    sub_key[i] = substring
    
    
print(sub_key)
cs

 

 

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
#네이버 검색, 키워드 서브키워드 
import requests
from bs4 import BeautifulSoup
 
art_lists = []
 
def search(key, b) :
    
    art_list = [b]
    
    #url = f'https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=1&ie=utf8&query={key}'
    url = f'https://search.naver.com/search.naver?where=news&sm=tab_jum&query={key}'
    html = requests.get(url).text
    soup = BeautifulSoup(html, 'lxml')
    
    for i in range(2) :
        news = soup.select('div.info_group > a:nth-of-type(2)')[i].attrs["href"]
        art_list.append(news)
    
    art_lists.append(art_list)
    
= sorted(sum_search.items(), key=lambda x:x[1], reverse = True#value 값 기준으로 정렬, 상위 5개 키워드
 
 
for i in range(5) :
    b= a[i][0]     #정렬 후 dic -> list 함수로 변환돼서 [i][0]으로 빼옴 
    #print(b)
    
    with open(f'C:/Workspace/project2_final/output/temp/{b}-count.txt','r', encoding = 'utf-8'as file :
        data = str(file.readlines()[1])
 
    split_string = data.split(' ',1
    substring = split_string[0]           #빈도수 제거 
    #print(substring)
    
    key = b + " " + substring
    search(key, b)
    
print(art_lists)
print(sum_search.items())
 
cs

+ Recent posts