2019. 9. 5. 23:08 ㆍ개발 이야기/python
오늘은 뉴스 크롤러를 만들어 볼 건데요.
이 블로그를 참고했습니다.
먼저 텔레그램 봇이 필요한데요
텔레그램 앱을 설치하신 후에!
/newbot
이라고 치시면 안내에 따라 어렵지 않게 만드실 수 있습니다!
그러므로 봇 만드는 것은 건너뛰고요!
저는 일단 특정 카테고리에 있는 뉴스의 새로운 기사를 보고 싶었는데요
그래서 각각 카테고리 별로 분류를 해서 작성했습니다.
먼저 카테고리 별 URL을 딕셔너리를 이용해서 담았는데요
if __name__ == "__main__":
# https://www.boannews.com/media/s_list.asp?skind=5 취약점 경고 및 보안 업데이트
# https://www.boannews.com/media/s_list.asp?skind=6 정책
# https://www.boannews.com/media/list.asp?mkind=1 security
# https://www.boannews.com/media/list.asp?mkind=3 defense
urls = {1: 'https://www.boannews.com/media/s_list.asp?skind=5',
2: 'https://www.boannews.com/media/s_list.asp?skind=6',
3: 'https://www.boannews.com/media/list.asp?mkind=1',
4: 'https://www.boannews.com/media/list.asp?mkind=3'}
executeCrawler(urls)
이렇게 urls라는 딕셔너리에 시리얼 넘버와 함께 담았습니다 이유는 시리얼 넘버도
사용하기 때문인데요
다음은 실행 부분입니다
def executeCrawler(urls):
for key in urls:
crawler(urls.get(key), key)
실행 부분의 함수는 URL KEY를 가지고 총 4번 실행되게끔 했는데요
이유는 DB를 사용하지 않고 txt 파일을 가지고 데이터를 저장하는데
추후에 용량이 커졌을 때 조금이나마 검색 속도가 더 빠르지 않을까 하는 생각에 분류를 해봤습니다.
다음은 실행 부분의 함수가 실질적으로 실행시키는 크롤러 부분!
def crawler(url, key):
news_link = []
reponse = requests.get(url)
html = reponse.text
soup = BeautifulSoup(html, 'html.parser') # news_area
url = '/media/view.asp'
idx = 'idx='
kind = 'kind'
for link in soup.find_all('a', href=True):
notices_link = link['href']
if notices_link.find(url) != -1 and notices_link.find(idx) != -1 and notices_link.find(kind) != -1:
split_notices_link = notices_link.split('&')[0]
if split_notices_link not in news_link and 'idx' in split_notices_link:
news_link.append(split_notices_link)
newlink = compareLink(news_link)
chatBot(newlink, key)
파라미터를 받아 URL과 매핑되는 key를 전달하면서
URL을 이용해 request를 보내고 받은 응답에
a 태그를 모두 뽑아낸 후 '/media/view/asp'와 'idx'등이 포함된 URL을 제외하고는 모두 저장하지 않았습니다!
이유는
a 태그의 href 부분을 모두 가져오게 되면 저런 식으로 쓸모없는 부분도 굉장히 많은데요!
물론 처음에 가져올 때 잘 가져올 수도 있지만 저는 간략하게 짜다 보니 이런 식으로 작성을 했고요!
여기서 제가 필요한 부분만 조건문을 이용해 가져왔습니다.
제가 필요한 해당 카테고리의 a 태그 안의 주소는 저렇게 /media/view.asp, idx와 page 등이 혼재했고 나머지는 그렇지 않은 부분들이 많이 있었습니다.
또한
split_notices_link = notices_link.split('&')[0]
를 이용해서 제가 가져온 URL
/media/view.asp?idx=81972&page=1&mkind=3&kind=2
위와 같은 부분을
하기와 같이 절삭해서 추렸고
/media/view.asp?idx=81972
절삭 후에! 중복되는 부분들은
if split_notices_link not in news_link and 'idx' in split_notices_link:
news_link.append(split_notices_link)
위의 분기문을 통해 절삭 후에 news_link 리스트에 담았습니다!
이제 비교 부분
def compareLink(newslink):
newlink = []
if os.path.exists(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt'):
with open(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt') as f:
link_list = f.readlines()
for printer in newslink:
if printer + '\n' not in link_list:
newlink.append(printer)
else:
for printer in newslink:
newlink.append(printer)
with open(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt', 'a') as f:
for tempwrite in newlink:
print(tempwrite)
f.write(tempwrite + '\n')
return newlink
카테고리를 key(1~4)로 나누어 구분하여 총 4개의 txt 파일로 분리를 했는데요!
중복으로 날라오는 버그로 수정했습니다. compare.txt한개로 비교!
newlink 리스트를 만들고!
newlink = []
if os.path.exists(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt'):
with open(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt') as f:
link_list = f.readlines()
for printer in newslink:
if printer + '\n' not in link_list:
newlink.append(printer)
else:
for printer in newslink:
newlink.append(printer)
key에 맞는 txt가 있다면 파일을 열어서 파일 안에 주소가 있는지 확인을 하고
없으면! newlink에 append 시켜주었고
파일이 없다면!(최초 실행 시 혹은 파일을 지운 경우) 모두 newlink에 append 되도록 작성을 했습니다.
with open(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt', 'a') as f:
for tempwrite in newlink:
print(tempwrite)
f.write(tempwrite + '\n')
return newlink
그리고 newlink에 담긴 주소를 compare txt 파일에 추가한 후 리턴을 해줍니다!
그리고 def crawler의 newlink 변수로 리턴 값을 던져 준 후 chaBot 함수의 파라미터로 전달해줍니다!
newlink = compareLink(news_link)
chatBot(newlink, key)
마지막 chatBot 함수는 전달받은 키를 카테고리명으로 치환해
메시지를 날려줍니다
def chatBot(newlink, key):
if key == 1:
category = '취약점 경고 및 보안 업데이트'
elif key == 2:
category = '정책'
elif key == 3:
category = 'security'
else:
category = 'defense'
if newlink.__len__() != 0:
bot = telegram.Bot('토큰!!!!!!!!!!!!')
for newlisk_list in newlink:
bot.sendMessage(
chat_id=쳇_아이디!!!, text='★보안뉴스\n카테고리 : '
'{category}\nURL : {URL}'.format(category=category,
URL='\nhttps://www.boannews.com' + newlisk_list)
여기서 챗봇을 생성해 받은 토큰을 제 위치에 넣어주고
chat_id를 넣어주어야 하는데요!
저 같은 경우에는 채널을 하나 생성해서 그 채널에만 새로운 뉴스를 전달해주고자 채널의 id를 넣어줬습니다.
채널 아이디는 어떻게 알 수 있을까요?
먼저 메뉴의 채널 만들기를 통해서 채널을 만들어 준다음에
https://api.telegram.org/botㅁㅁㅁㅁㅁ:ㅠㅠㅠㅠㅠㅠ형태의토큰/getUpdates
봇을 만들고 얻은 토큰을 이용해서 상위 주소에 기입을 하고 chrome이나 IE를 통해 주소창에 넣어줍니다.
그러면!
빨간색으로 찍찍 그어진 부분처럼 id가 나오는데요 그 부분을
if newlink.__len__() != 0:
bot = telegram.Bot('TOKEN')
for newlisk_list in newlink:
bot.sendMessage(
chat_id=-여기!!!!여기!!!!, text='★보안뉴스\n카테고리 : '
'{category}\nURL : {URL}'.format(category=category,
URL='\nhttps://www.boannews.com' + newlisk_list))
여기라고 써놓은 부분에 기입해줍니다.
그리고 돌려보면!
python main.py
이렇게 잘 동작하는 것을 확인할 수 있습니다!!
그런데 계속해서 새로운 뉴스를 원하신다면
지속적으로 돌아야겠죠?
많은 방법이 있지만 웹 형태로 서비스를 올리기엔 너무 오버스러운 면이 있기 때문에
저는 그냥 crontab으로 5분마다 크롤링을 하도록 했는데요!
이렇게 설정했습니다
간단히 설명해드리자면!!
crontab -e 를 누르신 후에
*/5 * * * * /usr/bin/python /path/to/main.py
왼쪽에서부터 분 시간 주 달 인데요
저는 분 부분에 /5를 해서 5분마다로 설정했습니다.
*/3을 하면 3 분마 다고요! 어렵지 않게 설정하실 수 있습니다
crontab 설정마저 끝나면 진짜 끝!
소스코드는 여기에!
https://github.com/bourbonkk/BoanNewsCrawler
'개발 이야기 > python' 카테고리의 다른 글
# PYTHON PANDAS 특정 행 이하 제거 (1) | 2021.11.11 |
---|---|
[회귀분석] COVID-19 확산 지표와 교통 통계량 데이터를 이용한 회귀분석 과정 (0) | 2021.07.04 |
# anaconda 64bit 설치 후 32bit python 사용하기 (0) | 2020.09.02 |
비동기 B2C 서버 구축하기 - Gunicorn, Celery, rabbitMQ (7) | 2019.11.23 |
python 아나콘다 사용 방법(pycharm ssh interpreter) (0) | 2019.08.06 |