일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Python3
- 인프런파이썬강의
- 인프런오리지널
- 인프런강의
- IOS
- IOS프로그래밍
- 노드JS
- 리프2기
- JS
- 자바스크립트
- SwiftUI
- 프로그래머스
- 토플공부수기
- swift
- 파이썬중급
- 카카오톡채팅봇
- 우리를위한프로그래밍
- rxswift
- 토플
- 웹크롤링
- 교환학생토플
- 파이썬
- uikit
- nodeJS
- 파이썬중급강의
- 유학토플
- 인프런
- 파이썬웹크롤링
- 스위프트
- 인프런파이썬
- Today
- Total
먹고 기도하고 코딩하라
Python Django 카카오톡 학식봇 만들기(3) - 텍스트 파일 파싱해서 메시지로 보내기 본문
이전 글 보기
안녕하세요? 이번 포스팅에서는 저번 포스팅에서 다뤘던 크롤링 코드 파일의 완성판을 한 번 같이 보고 실행 결과인 텍스트 파일을 views 에서 잘 가공하는 방법을 다뤄 보겠습니다.
먼저 가상환경을 활성화하시고 위치가 /home/ubuntu 인 것을 확인하십시오. 다음을 입력합니다.
$ rm test.py
$ cd [base_name]/[app_name]
$ vi scrapy_page.py
# scrapy_page.py
# Import
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from time import sleep
import os
import sys
# Function
def get_options():
options = Options()
options.headless = True
return options
def get_html(driver, url):
driver.get(url)
driver.implicitly_wait(5)
sleep(3) #sleep이 있어야 dateTime class, dietNoteContent id 콘텐츠를 볼 수 있음
html = driver.page_source
return html
def find_table(html):
soup = BeautifulSoup(html, 'lxml')
target_table = soup.find(id='schedule-table')
return target_table
def get_info(target_table):
info_str = target_table.find_all("th")
for i in range(len(info_str)):
info_str[i] = info_str[i].get_text('\n')+'\r\r'
return info_str
def get_meal(target_table):
meal_str = target_table.find_all("td")
for i in range(len(meal_str)):
meal_str[i] = meal_str[i].get_text('\n')+'\r\r'
return meal_str
def go_crawl():
if os.path.exists('week_meal.txt'):
os.remove('week_meal.txt')
if os.path.exists('week_info.txt'):
os.remove('week_info.txt')
url = 'http://www.duksung.ac.kr/diet/schedule.do?menuId=1151'
options = get_options()
driver = webdriver.Firefox(options=options)
try:
html = get_html(driver, url)
except:
sys.exit()
target_table = find_table(html)
info_str = get_info(target_table)
meal_str = get_meal(target_table)
meal_fp = open('week_meal.txt', 'w', encoding='utf-8')
meal_fp.writelines(meal_str)
meal_fp.close()
info_fp = open('week_info.txt', 'w', encoding='utf-8')
info_fp.writelines(info_str)
info_fp.close()
driver.close()
# Main
if __name__ == '__main__':
go_crawl()
go_crawl()을 제외한 함수들은 저번 포스팅에서 순서대로 설명했으므로 건너뛰겠습니다.
일단 week_meal.txt 와 week_info.txt 가 있는지 확인하고 있으면 지웁니다. 물론 새로 덮어쓸 수 있긴 한데 저는 매번 깔끔히 지우고 새로 시작하고 싶어서 이렇게 했습니다.
headless option 을 받고 driver 에 headless option 의 웹 드라이버를 할당해줍니다. 그리고 html 에 드라이버와 url 로 웹 소스 코드를 받아주는데요. 만약 여기서 실패하면 그냥 실행을 종료하도록 했습니다.
그런 다음 우리가 찾는 테이블을 찾고 날짜와 요일, 각 날짜의 식단을 따로 저장하도록 했습니다. 파일 포인터를 열어 날짜 정보와 식단 정보를 따로 텍스트 파일로 저장하고 닫아주고 드라이버까지 close 해 줬습니다.
if __name__ == '__main__' 에 대한 자세한 설명이 여기 있습니다. 파이썬 인터프리터가 최초로 실행한 스크립트 파일의 이름은 '__main__'인데요, 만약 이 파일이 import 된 모듈이 아니라 최초 실행된 스크립트 파일이면 if 다음의 문장을 실행하게 해줍니다. 사실 다른 모듈을 import 한 건 아니라서 그냥 go_crawl() 해 줘도 됩니다.
웹 크롤링을 하고 필요한 정보를 뽑아내 텍스트 파일까지 만들었으면 이제 views 에서 적절히 사용할 수 있게 해야겠습니다.
저는 여기서 고민을 좀 했는데요. 굳이 텍스트 파일을 만들지 않고 go_crawl() 에서 info_str 과 meal_str 을 return 하도록 해서 직접 views 에서 scrapy_page 를 import 해서 쓰면 되지 않을까 하고요. 하지만 그렇게 해보니 카카오톡에서는 응답이 2분 넘도록 오지 않았고 CLI에서는 실행은 됐지만 시간이 오래 걸렸습니다. 그래서 그냥 scrapy_page 를 따로 실행하도록 했습니다. 게다가 views 에서 매번 무겁게 웹크롤링을 하는 작업을 하면 낭비라서 따로 빼 놨습니다.
자 그럼 views 파일을 열어 수정해 봅시다. 다음과 같이 수정합니다.
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from time import localtime
from datetime import datetime
import json
def keyboard(request):
return JsonResponse({
'type': 'text'
})
@csrf_exempt
def message(request):
answer = ((request.body).decode('utf-8'))
return_json_str = json.loads(answer)
return_str = return_json_str['userRequest']['utterance']
wday_arr = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
go_main_button = '처음으로'
today_wday = localtime().tm_wday
meal_path = '/home/ubuntu/haksik_project/haksik/week_meal.txt'
# open에는 full 경로 설정 필수
meal_fp = open(meal_path, 'r', encoding='utf-8')
temp_str = ''
for line in meal_fp.readlines():
temp_str += line
temp_str = temp_str.strip('\n')
meal_list = []
meal_list = temp_str.split('\n\n')
meal_fp.close()
info_path = '/home/ubuntu/haksik_project/haksik/week_info.txt'
info_fp = open(info_path, 'r', encoding='utf-8')
temp_str = ''
for line in info_fp.readlines():
temp_str += line
temp_str = temp_str.strip('\n')
info_list = []
info_list = temp_str.split('\n\n')
info_list = [tmp for tmp in info_list if tmp]
info_fp.close()
for i in range(11):
if meal_list[i] == '-':
meal_list[i] = '\n학식이 없는 날이거나 홈페이지에 등록되지 않았습니다.'
add_text = ''
for j in range(10):
if j < 5:
add_text += '*'
add_text += info_list[6]
add_text += '\n'
else:
add_text += '\n<택1>\n\n*'
add_text += info_list[7]
add_text += '\n'
add_text += meal_list[j]
meal_list[j] = add_text
add_text = ''
s_day = {'mon': meal_list[0], 'tue': meal_list[1], 'wed': meal_list[2],
'thu': meal_list[3], 'fri': meal_list[4], 'every': meal_list[10]}
e_day = {'mon': meal_list[5], 'tue': meal_list[6], 'wed': meal_list[7],
'thu': meal_list[8], 'fri': meal_list[9]}
info = {'mon': info_list[1], 'tue': info_list[2], 'wed': info_list[3],
'thu': info_list[4], 'fri': info_list[5]}
if return_str == '테스트':
return JsonResponse({
'version': "2.0",
'template': {
'outputs': [{
'simpleText': {
'text': info_list[1]+"\n테스트 성공입니다.\n"+meal_list[0]
}
}],
'quickReplies': [{
'label': go_main_button,
'action': 'message',
'messageText': go_main_button
}]
}
})
코드가 좀 많이 길어졌습니다. 하나씩 살펴보겠습니다.
일단 datetime 과 localtime 을 추가로 import 해줬습니다. 사용자의 요청이 들어오는대로 views 가 실행되기 때문에 바로바로 날짜를 잡아내기 위해서입니다.
일단 저는 요일을 나타내는 리스트 wday_arr 을 선언해 날짜를 월~일 순서로 입력해 뒀습니다. 그런 다음 '초기화면'이라는 string 변수를 만들었는데 후에 분기문에서 쓸 것입니다.
week_meal 파일부터 열어 가공해줍니다. 파일 포인터로 open 할 때는 반드시 full path 를 다 적어줘야 하는 것을 잊지 마세요. temp_list 라는 빈 리스트를 만들어 readlines() 하면서 한 줄씩 읽어와 temp_str 에 한꺼번에 다 붙여 버립니다. 그런 다음 '\n'을 토큰으로 strip 을 해주는데요. 원소 앞뒤로 붙은 불필요한 개행을 없앱니다. 그리고 빈 리스트 meal_list 를 만들어 scrapy_page 에서 넣어준 캐리지 리턴 두 개를 기준으로 temp_str 을 split 해서 삽입해줍니다. 그러면 meal_list 에는 총 11개의 원소가 들어가게 됩니다. (월~금 학식, 월~금 교직원식, 비고란의 상시메뉴)
week_info 파일도 마찬가지입니다. 이 때는 리스트 컴프리헨션도 써 봤는데요. 저렇게 쓰면 tmp 가 true, 즉 화이트 스페이스가 아닌 것들만 포함되기 때문에 눈에 보이지 않는 공백들이 리스트에 들어가지 않는다는 장점이 있습니다.
meal_list 에는 학식이 없는 날인 '-'인 원소를 찾아 학식이 없다는 메시지로 바꿔줍니다.
그런 다음 info_list 로 루프를 도는데요. 조금 의아한 구성일 텐데 설명해 드리겠습니다. 일단 loop 를 도는 10은 월~금 학식과 월~금 교직원식 각각 5개 총 10개로 도는 겁니다. 그래서 0~4번째까지는 학식이니 info_list[6]인 '* 학생식당'을 덧붙이는 것이고요. 5~9번째까지는 저희 학교가 학식이 두 갠데 그 중 하나를 선택해서 먹을 수 있어서 <택1>이라고 써준 다음 개행 주고 info_list[7]인 '* 교직원식당'을 덧붙이는 것입니다. 약간 난해한가요? ^^;
그 다음 s_day 부터 info 까지는 학식(상시메뉴 포함)과 교직원식, 날짜와 요일을 가리키는 딕셔너리들입니다. wday_arr 의 리스트 요소들과 같은 이름이죠? 분기문에서 잘 써먹을 겁니다.
일단 간단하게 테스트를 해 보겠습니다. 저는 테스트 문장만 일단 바꿔봤습니다. 날짜와 요일이 나오고 학식을 볼 수 있게요. 여기까지 저장해주고 카카오 i 오픈빌더 홈페이지로 가서 다시 배포를 해준 다음 봇과 대화해봅시다.
신통하게 잘 나오는 모습입니다 ^^
다음 포스팅에는 views 파일을 조금 더 정교하게 고쳐보고 카카오 i 오픈빌더에서 마저 시나리오를 만들고 블록을 만들어서 스킬을 적용해보고 마무리하도록 하겠습니다.
읽어주셔서 감사합니다 ^^
다음 글 보기
Reference
'개발일지' 카테고리의 다른 글
python3 selenium 자바스크립트 함수 작동시켜서 웹크롤링하기 (1) | 2020.03.08 |
---|---|
Python Django 카카오톡 학식봇 만들기(4) - views 완성, 시나리오와 블록에 스킬 적용하기 (1) | 2020.03.07 |
Python Django 카카오톡 학식봇 만들기(2) - Amazon EC2에 firefox 웹드라이버 설치, 동적 웹페이지 크롤링하기 (0) | 2020.03.04 |
웹 소스 코드 보며 beautifulsoup4 문법 익히기 (2) | 2020.03.03 |
Python Django 카카오톡 학식봇 만들기(1) - 동적 웹페이지 크롤링 방법이 정적 웹페이지와 다른 이유 (0) | 2020.03.02 |