대망의 번거로운 짓거리의 시작...
뉴스 가져오는 함수에 프레임 생성을 넣고, 버튼을 누르면 다시 사라지는 메커니즘을 구현해야 한다.
거기에 버튼 또한 반복적으로 만들어야 하니 귀찮지 아니할 수 없다.
정리해보자면, 스크래핑 함수 자체는 동일하게 만들 것이지만,
버튼을 누르는 순간 변수 생성, 인터페이스에 추가, 링크 객체를 생성하며 거기에 링크 부여
를 해야 한다...
우선 함수를 정의해주고, 링크를 편하게 만들기 위해 클래스를 만들어주자.
def scrape_headline_news(): #오늘의 뉴스 스크래핑해오기
global todays_news
todays_news.delete(0,END) #우선 기존 내용 삭제
todays_news.insert(END, '[오늘의 뉴스]')
todays_news.insert(END, '')
lines = [] #기사가 들어갈 리스트
links = [] #링크가 들어갈 리스트
url = 'https://news.naver.com'
soup = create_soup(url)
news_list = soup.find('ul', attrs={'class':'hdline_article_list'}).find_all('li', limit=5) #limit은 검색갯수 제한
for idx, news in enumerate(news_list, start=1):
title = news.find('a').get_text().strip()
link = url + news.find('a')['href']
lines.append('{}.'.format(idx)+' '+title) #기사의 제목을 lines 리스트에 추가
links.append(link) #링크를 links 리스트에 추가
[todays_news.insert(END, line) for line in lines] #한줄 for문으로 lines 의 line 모두 추가
함수는 이런 식으로 만들어줄 것이고, 링크는 links 변수가 결정되어야 설정할 수 있으니 그 정의는 아래쪽에 하자.
링크가 들어갈 프레임을 먼저 정해주면 되는데, 링크 프레임이 이미 존재한다면 삭제해야 하기 때문에
함수 바깥쪽에 link_frame_exists라는 다소 직관적인 이름의 변수를 정의해주겠다.
처음 1회의 버튼 클릭에만 프레임이 없을 것이므로 False로 설정하고
버튼을 한 번이라도 누르면 True가 되는 방향으로 설정하자.
headline_frame_exists = False #헤드라인 뉴스의 프레임이 기존에 존재하는가? 거짓
def scrape_headline_news():
global todays_news, headline_frame_exists
#------------!!!!은 이미 전에 작성한 코드들!------------------
!!!!todays_news_link_frame = LabelFrame(todays_news_frame, text='뉴스 링크')
!!!!todays_news_link_frame.pack(pady=7, padx=7, ipady=5, ipadx=5)
!!!!if headline_frame_exists == True:
todays_news_link_frame.destroy() #이미 존재한다면 프레임 통째로 삭제
!!!!else:
headline_frame_exists = True #처음 1회라면 변수를 '존재함'으로 설정
todays_news.delete(0,END)
todays_news.insert(END, '[오늘의 뉴스]')
todays_news.insert(END, '')
lines = []
links = []
url = 'https://news.naver.com'
soup = create_soup(url)
news_list = soup.find('ul', attrs={'class':'hdline_article_list'}).find_all('li', limit=5) #limit은 검색갯수 제한
for idx, news in enumerate(news_list, start=1):
title = news.find('a').get_text().strip()
link = url + news.find('a')['href']
lines.append('{}.'.format(idx)+' '+title)
links.append(link)
[todays_news.insert(END,line) for line in lines]
이렇게 느낌표를 붙인 줄을 추가해주면 된다. 이러면 버튼을 연속으로 눌러도 링크 프레임이 계속해서 생기지 않을 것이다.
링크를 만드는 방법은 생각보다는 어렵지 않다.
일단 레이블을 만들고, 그것들을 bind함수를 이용해 <Button-1>(마우스 좌클릭)과 lambda를 이용한 함수로 링크를 열으라고 명령해주면 된다.
bind 함수는 bind(이벤트, 이벤트 시 실행시킬 함수) 꼴로 넣어주면 작동한다.
def callback(url):
webbrowser.open_new(url)
a = Label(link_frame, text=text, fg=blue, cursor='hand2')
a.bind('<Button-1>', lambda x: callback(Link))
이런 느낌? 하지만 Label을 5개씩 만들어줘야 하기 때문에
(위에서 뉴스 제목을 5개씩만 가져오도록 했으므로)
오늘의 뉴스 + 스포츠 뉴스
총 10개의 버튼을 이렇게 일일이 해줘야 한다니...
이럴때 쓰라고 빵틀이 있는 게 아닌가. 클래스를 이용하여 link를 정의해보자.
class moveto :
def __init__(self, link_frame, text, idx, links):
#인자는 링크가 들어갈 프레임, 링크 제목, links의 몇번째 항인지, links 리스트
self = Label(link_frame, text=text, fg='blue', cursor='hand2') #객체를 Label로 정의
link = links[idx] #링크는 함수 내에서 정의한 links 의 idx번째 항
self.pack(padx=5, pady=5)
self.bind('<Button-1>', lambda e:callback(link)) #Label의 링크화
이러면 tkinter에서 링크를 만들 수 있다.
그럼 이놈을 아까 만든 link_frame 안에만 만들어주면 되시겠다.
어차피 얘들은 프레임이 사라지면 같이 사라지므로
(destroy 함수는 프레임 내부의 모든 것을 같이 삭제한다.)
만드는 것만 신경써주면 될 것 같다.
함수의 제일 마지막에 이것을 더해주면 된다.
moveto1 = moveto(todays_news_link_frame, '1번 기사 링크', 0, links)
moveto2 = moveto(todays_news_link_frame, '2번 기사 링크', 1, links)
moveto3 = moveto(todays_news_link_frame, '3번 기사 링크', 2, links)
moveto4 = moveto(todays_news_link_frame, '4번 기사 링크', 3, links)
moveto5 = moveto(todays_news_link_frame, '5번 기사 링크', 4, links)
최종적인 함수는
def scrape_headline_news():
global todays_news, headline_frame_exists
todays_news_link_frame = LabelFrame(todays_news_frame, text='뉴스 링크')
todays_news_link_frame.pack(pady=7, padx=7, ipady=5, ipadx=5)
if headline_frame_exists == True: #프레임 존재하면 삭제
todays_news_link_frame.destroy()
else: #프레임 존재하지 않으면 존재함으로 바꿈
headline_frame_exists = True
todays_news.delete(0,END)
todays_news.insert(END, '[오늘의 뉴스]')
todays_news.insert(END, '')
lines = []
links = []
url = 'https://news.naver.com'
soup = create_soup(url)
news_list = soup.find('ul', attrs={'class':'hdline_article_list'}).find_all('li', limit=5) #limit은 검색갯수 제한
for idx, news in enumerate(news_list, start=1):
title = news.find('a').get_text().strip()
link = url + news.find('a')['href']
lines.append('{}.'.format(idx)+' '+title)
lines.append(' ') #공백 만들기
links.append(link)
[todays_news.insert(END,line) for line in lines]
moveto1 = moveto(todays_news_link_frame, '1번 기사 링크', 0, links)
moveto2 = moveto(todays_news_link_frame, '2번 기사 링크', 1, links)
moveto3 = moveto(todays_news_link_frame, '3번 기사 링크', 2, links)
moveto4 = moveto(todays_news_link_frame, '4번 기사 링크', 3, links)
moveto5 = moveto(todays_news_link_frame, '5번 기사 링크', 4, links)
같은 방법으로 스포츠 뉴스 스크래핑도 만들어주면 끝.
전체 코드는
import webbrowser
from tkinter import *
from bs4 import BeautifulSoup
import requests
import datetime
headline_frame_exists = False
sports_frame_exists = False
class moveto :
def __init__(self, link_frame, text, idx, links):
self = Label(link_frame, text=text, fg='blue', cursor='hand2')
link = links[idx]
self.pack(padx=5, pady=5)
self.bind('<Button-1>', lambda e:callback(link))
def create_soup(url):
headers = {'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'}
res= requests.get(url, headers=headers)
res.raise_for_status()
soup = BeautifulSoup(res.text, 'lxml')
return soup
def callback(url):
webbrowser.open_new(url)
def scrape_weather():
global weather_news
try:
weather_news.delete(0,END)
url = 'https://search.naver.com/search.naver?where=nexearch&sm=top_sug.asiw&fbm=0&acr=1&acq=%EC%84%9C%EC%9A%B8+%EB%82%A0%EC%94%A8&qdt=0&ie=utf8&acir=1&query=%EC%84%9C%EC%9A%B8+%EB%82%A0%EC%94%A8'
soup = create_soup(url)
#날씨, 어제보다 00도 높아요
cast = soup.find('p', attrs={'class':'cast_txt'}).get_text()
#현재 00도 (최고, 최저)
cur_temp = soup.find('p', attrs={'class' : 'info_temperature'}).get_text().replace('도씨', '')
max_temp = soup.find('span', attrs={'class':'max'}).get_text()
min_temp = soup.find('span', attrs={'class':'min'}).get_text()
#강수확률
morning_rainrate = soup.find('span', attrs={'class':'point_time morning'}).get_text().strip()
afternoon_rainrate = soup.find('span', attrs={'class':'point_time afternoon'}).get_text().strip()
#미세먼지
dust = soup.find('dl', attrs={'class':'indicator'})
pm10 = dust.find_all('dd')[0].get_text() #미세먼지
pm25 = dust.find_all('dd')[1].get_text() #초미세먼지
#출력
lines = [cast,'현재 {} (최저{} / 최고 {})'.format(cur_temp, min_temp, max_temp),
'오전 {} / 오후 {}'.format(morning_rainrate, afternoon_rainrate),
'미세먼지 {}'.format(pm10),
'초미세먼지 {}'.format(pm25)]
weather_news.insert(END, str(datetime.datetime.today().strftime("%Y-%m-%d")+': 오늘의 날씨입니다.'))
for line in lines:
weather_news.insert(END, line)
weather_news.insert(END, '')
except Exception as e:
err = '오류가 발생하였습니다.'
weather_news.insert(END, err)
weather_news.insert(END, e)
def scrape_headline_news():
global todays_news, headline_frame_exists
todays_news_link_frame = LabelFrame(todays_news_frame, text='뉴스 링크')
todays_news_link_frame.pack(pady=7, padx=7, ipady=5, ipadx=5)
if headline_frame_exists == True:
todays_news_link_frame.destroy()
else:
headline_frame_exists = True
todays_news.delete(0,END)
todays_news.insert(END, '[오늘의 뉴스]')
todays_news.insert(END, '')
lines = []
links = []
url = 'https://news.naver.com'
soup = create_soup(url)
news_list = soup.find('ul', attrs={'class':'hdline_article_list'}).find_all('li', limit=5) #limit은 검색갯수 제한
for idx, news in enumerate(news_list, start=1):
title = news.find('a').get_text().strip()
link = url + news.find('a')['href']
lines.append('{}.'.format(idx)+' '+title)
lines.append(' ')
links.append(link)
[todays_news.insert(END,line) for line in lines]
moveto1 = moveto(todays_news_link_frame, '1번 기사 링크', 0, links)
moveto2 = moveto(todays_news_link_frame, '2번 기사 링크', 1, links)
moveto3 = moveto(todays_news_link_frame, '3번 기사 링크', 2, links)
moveto4 = moveto(todays_news_link_frame, '4번 기사 링크', 3, links)
moveto5 = moveto(todays_news_link_frame, '5번 기사 링크', 4, links)
def scrape_sports_news():
global sports_news, sports_news_frame, sports_frame_exists
sports_news_link_frame = LabelFrame(sports_news_frame, text='스포츠 링크')
sports_news_link_frame.pack(side='right', pady=7, padx=7, ipady=5, ipadx=5)
if sports_frame_exists == True:
sports_news_link_frame.destroy()
else:
sports_frame_exists = True
sports_news.delete(0, END)
sports_news.insert(END, '[스포츠 뉴스]')
sports_news.insert(END, '')
lines = []
links = []
url = 'https://sports.news.naver.com/index.nhn'
soup = create_soup(url)
news_list = soup.find('ul', attrs={'class':'today_list'}).find_all('li', limit=5)
for idx, news in enumerate(news_list, start=1):
title = news.find('strong').get_text().strip()
link = 'https://sports.news.naver.com' + news.find('a')['href']
lines.append('{}. {}'.format(idx, title))
lines.append(' ')
links.append(link)
[sports_news.insert(END, line) for line in lines]
moveto1 = moveto(sports_news_link_frame,'1번 기사 링크', 0, links)
moveto2 = moveto(sports_news_link_frame, '2번 기사 링크', 1, links)
moveto3 = moveto(sports_news_link_frame, '3번 기사 링크', 2, links)
moveto4 = moveto(sports_news_link_frame, '4번 기사 링크', 3, links)
moveto5 = moveto(sports_news_link_frame, '5번 기사 링크', 4, links)
def scrap():
scrape_weather() #날씨 가져오기
scrape_headline_news() #헤드라인 뉴스 정보 가져오기
scrape_sports_news()
root = Tk()
root.title('오늘의 이슈')
root.resizable(False, False)
btn_frame = Frame(root)
btn_frame.pack()
scrap_btn = Button(btn_frame, text='뉴스 가져오기', width=10, command=scrap)
scrap_btn.pack(padx=5, pady=5)
weather_news_frame = LabelFrame(root, text='오늘의 날씨', width=50)
weather_news_frame.pack(pady=5, padx=5)
weather_news = Listbox(weather_news_frame, width=50)
weather_news.pack(fill='x', side='top', pady=10, padx=10)
todays_news_frame = LabelFrame(root, width=50, text='오늘의 뉴스')
todays_news_frame.pack(side='top',pady=5, padx=5)
todays_news = Listbox(todays_news_frame, width=50)
todays_news.pack(side='left', fill='x', padx=10, pady=10)
sports_news_frame = LabelFrame(root, text='스포츠 뉴스')
sports_news_frame.pack(side='top',padx=5, pady=5)
sports_news= Listbox(sports_news_frame, width=50)
sports_news.pack(side='left', fill='x', padx=10, pady=10)
root.mainloop()
굉장히 길다 ...
잘 돌아가는 것을 볼 수 있다.
만든 지 조금 된 코드라서 날씨가 안 가져와지는데(네이버의 코드 변경이 있을 수 있음)
저것만 손보면 될 듯!
귀찮으니 보수는 나중에 하겠다.
이번 프로젝트를 하면서 난관은 링크의 레이블을 만들었다 지웠다 하는 일이었는데
boolean 값을 설정해줘서 처리했다 !
실행 시 처음에만 기존 레이블이 없을 테니깐..
토글식으로 boolean값을 이용하는 방법을 체득한 것 같아서 좋구만.
'Programming > Projects' 카테고리의 다른 글
[Python] 화면 녹화 프로그램 1.-opencv, tkinter (0) | 2021.09.30 |
---|---|
[Python] 계산기 만들기 - tkinter (0) | 2021.09.30 |
[Python] 인스타그램 좋아요 매크로 업그레이드- opencv (0) | 2021.09.30 |
[Python] 인스타그램 좋아요 매크로-selenium (1) | 2021.09.30 |
[Python] 뉴스 스크래핑 프로그램 만들기 (1) (0) | 2021.09.19 |
댓글