Skip to content

Instantly share code, notes, and snippets.

@devxoul
Last active January 24, 2023 08:39
Show Gist options
  • Save devxoul/8546413 to your computer and use it in GitHub Desktop.
Save devxoul/8546413 to your computer and use it in GitHub Desktop.
[Deprecated] 코레일 승차권 검색 및 예매 프로그램. https://github.com/devxoul/korail
# -*- coding: utf-8 -*-
"""
This code won't be updated anymore.
Project has moved to repository: https://github.com/devxoul/korail
"""
import requests
from bs4 import BeautifulSoup
from datetime import datetime
session = requests.session()
class Train(object):
#: 기차 종류
train_type = None
#: 출발역 코드
dep_code = None
#: 출발날짜 (yyyyMMdd)
dep_date = None
#: 출발시각 (hhmmss)
dep_time = None
#: 도착역 코드
arr_code = None
#: 도착 시각
arr_time = None
#: 인원
count = 0
#: 특실 예약가능 여부
first_class = False
#: 일반실 예약가능 여부
general_admission = False
def __repr__(self):
return '[%s] %s~%s(%s~%s) [특실:%d][일반실:%d]' % (
self.train_type.encode('utf-8'),
self.dep_code.encode('utf-8'),
self.dep_time.encode('utf-8'),
self.arr_code.encode('utf-8'),
self.arr_time.encode('utf-8'),
self.first_class,
self.general_admission,
)
def all_stations():
stations = []
for i in range(14):
url = 'http://www.korail.com/servlets/pr.pr11100.sw_pr11111_f1Svt'
params = {
'hidKorInx': i,
}
r = requests.get(url, params=params)
html = r.text.split('<table class="s-view">')[3]
rows = html.split("javascript:putStation('")[1:]
for row in rows:
name = row.split("'")[0]
id = row.split(",'")[1].split("'")[0]
stations.append((id, name))
return stations
def login(id, password, use_phone=False):
"""
:param id: 코레일 멤버십 번호 또는 휴대전화번호
:param password: 비밀번호
:param use_phone: 휴대전화번호 로그인 여부
"""
url = 'https://www.korail.com/servlets/hc.hc14100.sw_hc14111_i2Svt'
data = {
'selInputFlg': '4' if use_phone else '2', # 멤버십번호: 2, 휴대전화로그인: 4
'UserId': id,
'UserPwd': password,
'hidMemberFlg': '1', # 없으면 입력값 오류
}
r = session.post(url, data=data)
if 'w_mem01106' in r.text:
return True
return False
def logout():
url = 'http://www.korail.com/2007/mem/mem01000/w_mem01102.jsp'
session.get(url)
def euckr(s):
"""
문자열을 EUC-KR 유니코드로 변환하여 리턴한다.
"""
return unicode(s, 'utf-8').encode('euc_kr')
def search(dep_code, arr_code, date, time='000000', train='05', count=1):
"""
:param dep_code: 출발역 코드
:param arr_code: 도착역 코드
:param date: 날짜 (yyyyMMdd)
:param time: 시간 (hhmmss)
:param train: 기차 종류
- 00: KTX
- 01: 새마을호
- 02: 무궁화호
- 03: 통근열차
- 04: 누리로
- 05: 전체 (기본값)
- 06: 공학직통
- 09: ITX-청춘
:param count: 인원
"""
url = 'http://www.korail.com/servlets/pr.pr21100.sw_pr21111_i1Svt'
params = {
'txtGoAbrdDt': date,
'txtGoHour': time,
'txtGoStartCode': dep_code,
'txtGoEndCode': arr_code,
'checkStnNm': 'N',
'radJobId': '1', # 직통
'txtPsgCnt1': count,
}
r = session.get(url, params=params)
html = BeautifulSoup(r.text)
error = html.select('.point02')
if error:
print error[0].string.strip()
return
rows = html.select('table.list-view tr')[1:]
trains = []
train_info = r.text.split('new train_info(')[1:]
i = 0
for info in train_info:
obj = info.split(',')
train = Train()
train.train_type = obj[22].strip()[1:-1]
train.dep_code = obj[18].strip()[1:-1]
train.dep_date = obj[24].strip()[1:-1]
train.dep_time = obj[25].strip()[1:-1]
train.arr_code = obj[19].strip()[1:-1]
train.arr_time = obj[27].strip()[1:-1]
train.count = count
td7s = rows[i].select('td[width=7%]')
#특실
img = td7s[0].select('img')
content = img[0] if img else td7s[0].contents[0]
train.first_class = 'yes' in content.__str__()
#일반실
img = td7s[1].select('img')
content = img[0] if img else td7s[0].contents[0]
train.general_admission = 'yes' in content.__str__()
print train.first_class, train.general_admission
trains.append(train)
i += 1
return trains
def reserve(train):
"""
승차권 예약
:param train: `Train` 인스턴스
"""
session.headers = {
'Referer': 'http://www.korail.com/servlets/pr.pr21100.sw_pr21111_i1Svt'
}
url = 'http://www.korail.com/servlets/pr.pr12100.sw_pr12111_i1Svt'
data = {
'txtCompaCnt1': train.count, # 인원
'txtDptRsStnCd1': train.dep_code, # 출발역 코드
'txtDptDt1': train.dep_date, # 출발날짜
'txtDptTm1': train.dep_time, # 출발시각
'txtArvRsStnCd1': train.arr_code, # 도착역 코드
'txtTrnClsfCd1': train.train_type, # 열차 종류 (Train Class Code인듯)
'txtSeatAttCd4': '15', # ??? - 요구속성 (다른 번호는 창측/내측 이런거던데...)
'txtPsgTpCd1': '1', # ??? - 단체예약, 개인예약 구분이라는데...
'txtJobId': '1101', # 1101: 개인예약, 1102: 예약대기, 1103: SEATMAP예약
'txtJrnyCnt': '1', # 환승 횟수 (1이면 편도)
'txtPsrmClCd1': '1', # 1: 일반실, 2: 특실
'txtJrnySqno1': '001', # ???
'txtJrnyTpCd1': '11', # 편도
}
r = session.post(url, data=data)
if 'w_mem01100.jsp' in r.text:
print 'Need login!!'
return False
elif u'홈페이지주소를 잘못 입력하셨습니다.' in r.text:
print u'Referer 오류'
return False
elif u'20분 이내 열차는' in r.text:
print '20분 이내 열차는 예약하실 수 없습니다. 역창구 및 자동발매기를 이용하시기 바랍니다.'
return False
elif u'오류' in r.text:
html = BeautifulSoup(r.text)
error = html.select('.point02')
print error[0].string.strip()
return False
elif 'w_adv03100.gif' in r.text:
print 'Reservation Success!!'
return True
print 'Unhandled Error.'
print r.text
return False
def ticket_ids():
"""
승차권 id 목록 조회
"""
url = 'http://www.korail.com/pr/pr13500/w_pr13510.jsp'
r = session.get(url)
tickets = []
pnrs = r.text.split("new pnr_info( '")
for pnr in pnrs[1:]:
ticket_id = pnr.split("'")[0]
tickets.append(ticket_id)
return tickets
def cancel(pnr_id):
"""
예약취소
:param pnr_id: 예약취소할 승차권 id
"""
url = 'http://www.korail.com/servlets/pr.pr14500.sw_pr14514_i1Svt?'
data = {
'txtPnrNo': pnr_id,
'txtLngScnt': '01',
'txtJrnySqno': '001',
}
r = session.get(url, params=data)
return u'정상적으로 취소가 완료되었습니다.' in r.text
phone = ''
password = ''
stations = all_stations()
dep_code = [station for station in stations if station[1] == u'서울'][0][0]
arr_code = [station for station in stations if station[1] == u'동대구'][0][0]
dep_date = datetime.strftime(datetime.now(), '%Y%m%d')
dep_time = datetime.strftime(datetime.now(), '%H%M%S')
print '서울역:', dep_code
print '동대구역:', arr_code
print 'Logging in...'
r = login(phone, password, True)
if not r:
print 'Login failed.'
exit()
print 'Login succeeded.'
trains = search(dep_code, arr_code, dep_date, dep_time, 2)
for t in trains:
print t
reserve(trains[-1])
tickets = ticket_ids()
for ticket_id in tickets:
if cancel(ticket_id):
print 'Canceled reservation.'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment