파이썬 프로젝트 및 응용/옥션 최저가 항공티켓으로 제주도 여행가기

(프로젝트) 옥션 최저가 항공티켓으로 제주도 여행가기 - 5. 2개월치 금요일 가장싼 티켓 데이터 수집 후 텔레그램으로 보내기

파기차차 2023. 3. 25. 23:37
728x90
반응형
SMALL

ㅁ 개요

 

O 프로그램 소개

 

 - 이번 글은 이전글((프로젝트) 옥션 최저가 항공티켓으로 제주도 여행가기 - 4. 선택한 날짜의 항공 티켓에서 원하는 시간대 및 가장 싼 티켓 정보 찾기)에 이은 6번째 마지막 글로 앞에서 공부했던 내용을 종합하여 2개월치 금요일 가장싼 항공티켓 데이터를 수집 후 나의 텔레그램 봇에게 메시지를 보내는 방법에 대하여 알아보겠습니다.

 

 

 

 

O 완성된 프로그램 실행 화면

 

 

 - 최종 완성된 프로그램의 결과화면은 아래와 같습니다.

 

1.프로그램을 실행하면 아래와 같은 모습으로 실행되며, 아래 프로그램을 실행한 시점은 2월 8일, 따라서 2월~3월의 금요일인 2/10~3/31까지 총 8번에 걸쳐 티켓정보를 수집합니다.

['2023-02-10', '2023-02-17', '2023-02-24', '2023-03-03', '2023-03-10', '2023-03-17', '2023-03-24', '2023-03-31']

 

 

 

 

 

2.아래와 같이 파이썬의 셀레니움으로 크롬 브라우저를 자동으로 띄워 원하는 일자의 티켓정보를 수집하기위해 자동으로 클릭하기 시작합니다.

 

 

 

 

 

3. 파이썬의 크롤링 기법으로 2개월치 데이터를 가져오고 있습니다.

 

 

 

4. 텔레그램 메신저에 해당 일자의 최저가 티켓 정보와 나머지 티켓 정보도 확인할 수 있도록 파일로 보내줍니다.

 

 

 

 

 

5. 파일 탐색기에서 확인한 결과 파일로 티켓의 정보가 잘 저장되고 있는 것을 확인할 수 있습니다.

 

 

 

 

 

6. 2/10일자 파일을 열어보니 파일 내용에 티켓의 세부정보가 잘 저장된 것을 확인 할 수 있습니다.

 

 

 

 

7. 3/31일자까지 모두 자동 수행 후 모든 파일이 탐색기에 저장된 것을 볼 수 있습니다. 다만, 프로그램을 실행한 날짜가 2/8 이므로 그 전 날짜의 티켓인 2/3일자 티켓은 제외하고 나머지 2~3월 금요일의 모든 티켓정보가 저장되었습니다.

 

 

 

 

 

 

 

 

8. 메신저로도 모두 최저가 정보와 모든 티켓정보가 담긴 파일로 잘 수신되었습니다.

 

 

 

 

 

 

 

 

 

 


 

ㅁ 세부 내용

 

O 완성된 소스

 

 

소스 : 1.py

 

import datetime
from datetime import datetime, timedelta, date
from dateutil import relativedelta
from selenium import webdriver # pip install selenium
from selenium.webdriver.common.by import By
import time
from openpyxl import Workbook
import pandas as pd
from selenium.webdriver.common.keys import Keys
from bs4 import BeautifulSoup
import telegram


import pandas as pd


# 텔레그램 설정 부분
bot = telegram.Bot(token='자산의 텔레그램 봇 엑세스 키')


###################################################
# 1. 금요일의 날짜를 구하여 리스트에 담기
###################################################
today = date.today()


#해당 달의 첫째날 구하기
first_day = today.replace(day=1)

this_month = datetime(today.year, today.month, day=1)
next_2month = this_month + relativedelta.relativedelta(months=2)
last_day = next_2month - timedelta(days=1)
print(last_day.strftime("%Y%m%d"))
endDate = last_day.strftime("%Y%m%d")

startDate = datetime.today().strftime("%Y%m%d")

dt_index = pd.date_range(start=startDate, end=endDate)


dt_list = dt_index.strftime("%Y.%m.%d").tolist()


fridays = []

for i in dt_list:
    i = datetime.strptime(i, '%Y.%m.%d')
    if i.weekday() == 4:
        j = str(i)
        fridays.append(j[:10])

print(fridays)
# ['2023-01-06', '2023-01-13', '2023-01-20', '2023-01-27']


###################################################
# 2. 셀레니움으로 옥션 항공티켓 정보 가져오기
###################################################

browser =webdriver.Chrome("chromedriver.exe")


j = 3 # j는 1은 첫째주, 2는 둘째주, 3은 셋째주, 4는 네째주임
l = 1 # 1이면 가는날 창의 완쪽, 2이면 가는날 창의 오른쪽임

for k in fridays:
    time.sleep(2)
    browser.get("http://air.auction.co.kr/au/init/lp/lpMain.do")

    time.sleep(2)
    browser.find_element(By.XPATH,'//*[@id="tour_wrap"]/div[1]/div[1]/a[2]/span[1]').click() # 국내항공

    time.sleep(2)
    browser.find_element(By.XPATH,'//*[@id="tour_wrap"]/div[1]/div[4]/div[2]/div[3]/a[1]').click() # 편도
    time.sleep(2)
    browser.find_element(By.XPATH,'//*[@id="tour_wrap"]/div[1]/div[4]/div[2]/div[4]/div[4]/div[1]/div/span').click() # 가는날
    time.sleep(2)


    browser.find_element(By.XPATH,'//*[@id="ui-datepicker-div"]/div['+str(l)+']/table/tbody/tr['+str(j)+']/td[6]/a').click() # 1/6 일
    time.sleep(1)


    browser.find_element(By.XPATH,'//*[@id="tour_wrap"]/div[1]/div[4]/div[4]/a/span').click() # 검색하기
    time.sleep(15)


    html_souce = browser.page_source
    soup = BeautifulSoup(html_souce, 'html.parser')

    ticket_list = soup.find(class_='simplebar-content').find_all(class_='gt_offer_list_item srpResultLi')
    print(type(ticket_list))
    print(len(ticket_list))

    print("========================================================================")
    print(ticket_list[0])
    print(ticket_list[0].find("div",class_='gt_offer_col gt_offer_company').text.strip())
    print(ticket_list[0].select('div.gt_offer_col.gt_offer_company > img')[0]['alt'],"+++++++++++++")
    print(ticket_list[0].find("div",class_='gt_offer_col gt_offer_time').text.strip())
    print(ticket_list[0].find("div",class_='gt_offer_col gt_offer_level').text.strip())
    print(ticket_list[0].find("div",class_='gt_offer_col gt_offer_vender').text.strip())
    print(ticket_list[0].find("div",class_='gt_offer_col gt_offer_seats2').text.strip())
    print(ticket_list[0].find("div",class_='gt_offer_col gt_offer_price2').text.strip())



    companys = []
    times_ = []
    levels = []
    venders = []
    seats = []
    prices = []

    for i in range(len(ticket_list)):
        time_ = ticket_list[i].find("div",class_='gt_offer_col gt_offer_time').text.strip()
        time_ = time_[0:5]
        dateFormatter = "%H:%M" # 날짜 포멧 맞추기
        mytime = datetime.strptime(time_, dateFormatter)
        first_time = datetime.strptime("17:00", dateFormatter)
        last_time = datetime.strptime("21:00", dateFormatter)


        if mytime > first_time and mytime < last_time:
            company = ticket_list[i].select('div.gt_offer_col.gt_offer_company > img')[0]['alt']
            time_ = ticket_list[i].find("div",class_='gt_offer_col gt_offer_time').text.strip()
            level = ticket_list[i].find("div",class_='gt_offer_col gt_offer_level').text.strip()
            vender = ticket_list[i].find("div",class_='gt_offer_col gt_offer_vender').text.strip()
            seat = ticket_list[i].find("div",class_='gt_offer_col gt_offer_seats2').text.strip()
            price = ticket_list[i].find("div",class_='gt_offer_col gt_offer_price2').text.strip().replace('스마일페이 결제 가능','').replace(',','')

            print(company, time_, level, vender, seat, price)

            companys.append(company)
            times_.append(time_)
            levels.append(level)
            venders.append(vender)
            seats.append(seat)
            prices.append(price)
        else:
            pass


    pd_data = {"항공사" : companys, "출발/도착 시간" : times_, "좌석구분": levels, "판매사": venders, "잔여좌석": seats, "편도1인요금": prices}
    airline_ticket_df = pd.DataFrame(pd_data)
    airline_ticket_df['편도1인요금'] = pd.to_numeric(airline_ticket_df['편도1인요금']) # 이걸 안해주면 시리즈 타입으로 .idxmin()에서 에러 발생, 숫자 타입으로 변경해 줘야 함

    airline_ticket_result = 'airline_ticket_result_'+str(k)+'.xlsx'

    airline_ticket_df.to_excel(airline_ticket_result)
    time.sleep(1)


    try:
        pass
        print(airline_ticket_df.loc[airline_ticket_df["편도1인요금"].idxmin()])
        minticketInfo = airline_ticket_df.loc[airline_ticket_df["편도1인요금"].idxmin()]
        print(airline_ticket_df['편도1인요금'].min())


        # 텔레그램으로 메시지 보내기
        bot.sendMessage(chat_id='본인의 챗id', text='옥션 항공티켓 최저가 정보인 ' +airline_ticket_result+' 파일입니다.\n'+str(minticketInfo))


        # 텔레그램으로 파일 보내기
        bot.send_document(chat_id='본인의 챗id', document=open(airline_ticket_result, 'rb'), filename=airline_ticket_result)
    except:
        pass
        bot.sendMessage(chat_id='본인의 챗id', text=str(k)+' 날짜는 티켓정보가 없습니다.\n(해당 시간대 티켓이 없거나, 기타 다른 이유 등으로 티켓정보를 받아올 수 없습니다.) ')

    print("========================================================================")

    j = j + 1
    if j > 4:
        j = 1 # 네번째 주가 넘어가면 다시 다음월의 첫째주가 되므로 j=1로 초기화 해주고,
        l = 2 # 4주가 넘어가면 다음월 즉, 오른쪽 창에서 선택해야 하므로 l을 2로 바꿔줌

 

 

 - 소스파일을 cmd, 파워쉘 또는 vscode 등에서 아래와 같이 실행하시기 바랍니다.
 

 

 > python 1.py
(실행파일(1.py)과 동일한 위치에 "chromedriver.exe"파일이 있어야 합니다.)
 

 

O 주요 내용

 

1. 소스를 살펴보겠습니다.

 

다른 부분은 이전 글의 소스와 모두 동일하므로 달라진 부분만 설명드리겠습니다.

 

 

날짜 및 텔레그램 관련 모듈을 임포트 해주고, 메신저로 최저가 정보와 모든 티켓의 상세정보를 파일로 보낼 것이기 때문에 텔레그램 봇을 아래와 같이 설정해 줍니다.

 

 

 

2. 날짜를 수기로 넣어주는 것이 아닌, 자동으로 계산하기 위하여 아래와 같은 코드로 자동계산 하도록 코딩합니다.

 

 

 

 

 

3. 2개월치 데이터를 수집할 것이므로 해당월의 주차수와 왼쪽/오른쪽 창을 구분하여 셀레니움이 클릭할 수 있도록 해주어야 합니다.

아래와 같이 j변수는 주차 수를 나타내며, l변수는 가는날 창의 왼쪽(1), 오른쪽(2)을 나타내고, 구분할 수 있도록 설정해 줍니다.

 

 

 

 

 

 

 

 

4. 이전 글에서는 테스트로 티켓 10개만 돌렸으나, 여기서는 모든 티켓을 대상으로 수행해야 하므로 해당일자의 티켓 개수만큼 루프를 돌려줍니다.

 

 

 

 

 

 

 

5. 메신저에서 최저가 티켓의 세부정보를 표시해 주려면 최저가에 해당하는 '편도1인요금' 컬럼의 값을 숫자형으로 변경해 준 후 데이터프레임의 .loc()로 가져와야 합니다. 아래와 같이 표현해 줍니다.

airline_ticket_df['편도1인요금'] = pd.to_numeric(airline_ticket_df['편도1인요금'])

 

그리고 난 후 가져온 티켓 정보를 엑셀 파일로 저장 후 (라인 192~194)

메신저로 최저가 정보와(라인 208)

생성한 엑셀파일을 첨부로 보냅니다.(라인 212)

 

 

 

 

6. 마지막으로 2월이 끝나면(보통 4주차) 다시 3월의 첫번째 주가 시작되므로 주차를 표시하는 j변수가 4보다 크면 1로 초기화 시켜 줍니다.

 

그리고 오션의 티켓을 예매하는 화면이 좌(2월), 우(3월)로 구분되어 있으므로 셀레니움으로 클릭시 이를 구분해 주어야 합니다.

그래서 l변수에 왼쪽 창을 1로, 오른쪽 창을 2로 구분해 주고, 다음월로 넘어갈때(즉, 위의 j변수가 4보다 클때) l변수를 2로 만들어 주었습니다.

 

 

 

 

 

 

 

 

 

 


 

ㅁ 정리

 

O 우리가 배운 내용

 

 

 - 오늘은 선택한 날짜의 모든 티켓 정보 중에서 원하는 시간대(17시~21시)에서 가장싼 티켓의 정보를 필터링하는 방법에 대하여 알아보았습니다.
 
 
 - 오늘 우리가 배운 내용을 간략히 정리해 보면 아래와 같습니다.
 
 > 1.자동으로 해당월의 첫째날을 아래와 같이 구함
#해당 달의 첫째날 구하기
first_day = today.replace(day=1)
this_month = datetime(today.year, today.month, day=1)
next_2month = this_month + relativedelta.relativedelta(months=2)
last_day = next_2month - timedelta(days=1)

startDate = datetime.today().strftime("%Y%m%d")
endDate = last_day.strftime("%Y%m%d")
 
 > 2.주차 및 왼쪽/오른쪽 창 표시에 주의

    j = j + 1
    if j > 4:
        j = 1 # 네번째 주가 넘어가면 다시 다음월의 첫째주가 되므로 j=1로 초기화 해주고,
        l = 2 # 4주가 넘어가면 다음월 즉, 오른쪽 창에서 선택해야 하므로 l을 2로 바꿔줌

 

 

 

O 향후 개선 사항

 

우리 프로그램은 완전하지 않으며, 추가로 아래와 같은 내용의 개선이 필요합니다.

직접 프로그램을 개선하려고 노력해 보시고, 뭔가 작은 것이라도 성취해 보신다면 이런 노력을 통해서 향후 엄청난 스킬업을 경험하실 수 있을 것이라고 확신합니다.

 

 

1.3개월치 티켓정보 수신하기

 -현재는 티켓정보를 2개월치만 받아오고 있습니다. 하지만 더 싼 가격정보를 받기위해서는 더 이전의 티켓정보가 필요합니다. 따라서 3개월 또는 그 이전의 티켓정보를 받아오도록 개선한다면 더 좋은 가격으로 티켓을 구매할 수 있을 것입니다.

 

2.항공사별로 티켓정보 예매하기

 -현재는 항공사 티켓을 예매하는 부분은 하지 않았습니다. 이 예매만 하고 결제는 하지 않아도 되므로 이 부분도 스스로 해보신 다면 실력향상에 크게 도움이 될 것입니다.

(향후 시간이 되면 티케 예매 부분을 추가할 예정입니다.)

 

3.결제까지 해보기

 -현재 자동 결제부분도 하지는 않고 있습니다. 하지만 위의 2번에서 예매한 티켓의 정보가 정확하고, 구매할 만한 가치가 있는 가격대인 경우, 한번 더 확인 후(확인 버튼 클릭 등) 결제까지 자동으로 해주는 기능을 만들어 본다면 더 편리하고, 개발 하면서 본인의 스킬도 몰라보게 향상될 것 입니다.

 

 

 

 

 

 

 

 

오늘은 여기까지이며, 댓글하트는 제가 이글을 지속할 수 있게 해주는 힘이 됩니다.

위의 내용이 유익하셨다면, 댓글과 하트 부탁드립니다.

 

 

 

 

감사합니다.

 

 

 

※ 추가적인 정보는 아래 유튜브 영상에서 해당 내용을 더욱 자세히 보실 수 있습니다.

 

 

 

 

 

 

※ 아래 영상은 기존 프로그램에서 일부 오류 및 수동 작업을 모두 개선하여 완전자동화한 개선 버전입니다.

 

728x90
반응형
LIST