查询余票接口
打开12306官网,并进入余票查询页面,同时开启chrome浏览器F12控制台,以北京到上海为例,搜索2018年10月1日的余票信息,点击搜索按钮,可以在控制台发送了一条GET请求,请求结果以json字符串的形式放回,里面有查询到的余票信息。
通过python-requests来请求一下这个接口,比较意外的是这个接口并没有校验header信息,因此我们不用添加header。在测试中发现虽然12306官网主页提供了证书下载,但是这个https请求并不是必须校验证书,将requests方法中的verify参数设置为False取消校验也能正常返回结果。
import requests
url = 'https://kyfw.12306.cn/otn/leftTicket/queryA?leftTicketDTO.train_date=2018-10-01&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=SHH&purpose_codes=ADULT'
requests.packages.urllib3.disable_warnings()
r = requests.get(url,verify=False).json()
yupiao = r['data']['result']
print(yupiao)
返回的json每一个元素就是一条火车线路,包含始发站代码,终点站代码,发车时间,每种座位的余票等信息,通过” | “分隔,需要注意的是每种座位的余票数量与网页展示的位置前后顺序并不是一一对应的,这个花了不少时间来确定每个对应关系。
将上一步返回结果放入循环中,每一个元素按照” | “符号分割,并从分割后的列表中依次取出需要的内容(城市代码,时间,车次,作为余票等信息)。
import requests
from tabulate import tabulate
#查询余票
def query():
url = 'https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2018-06-05&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=SHH&purpose_codes=ADULT'
requests.packages.urllib3.disable_warnings()
r = requests.get(url,verify=False).json()
yupiao = r['data']['result']
table_header = ['车次','始发站','终点站','始发时间','到站时间','历时','商务特等座','一等座','二等座','高级软卧','软卧','动卧','硬卧','软座','硬座','无座'] #表格头
table_data = [] #数据添加至列表,作为表格的主体内容
for i in yupiao:
a = i.split('|') #分割字符串,a是个列表
is_null = a[0] #判断是否有票
train_code = a[3] #车次
start_city_code = a[4] #始发站城市代码
end_city_code = a[5] #终点站城市代码
start_time = a[8] #终点站开始时间
end_time = a[9] #终点站到达时间
total_time = a[10] #总共历时
shangwutedeng = a[32] #商务特等座
yideng = a[31] #一等座
erdeng = a[30] #二等座
gaojiruanwo = a[21] #高级软卧
ruanwo = a[23] #软卧
dongwo = a[33] #动卧
yingwo = a[28] #硬卧
ruanzuo = a[24] #软座
yingzuo = a[29] #硬座
wuzuo = a[26] #无座
#添加到元组
yupiao_tuple = (train_code,start_city_code,end_city_code,start_time,end_time,total_time,shangwutedeng,yideng,erdeng,gaojiruanwo,ruanwo,dongwo,yingwo,ruanzuo,yingzuo,wuzuo)
table_data.append(yupiao_tuple)
print(tabulate(table_data,headers=table_header,tablefmt='grid'))
在上面代码中使用了tabulate库,它可以把结果输出成易读的表格样式
参数化准备
再回头看之前请求的URL,这个GET请求有四个请求参数,分别代表如下:
leftTicketDTO.train_date --> 乘车日期
leftTicketDTO.from_station --> 始发站代码(例如北京北:VAP)
leftTicketDTO.to_station --> 终点站代码(例如上海虹桥:AOH)
purpose_codes --> 车票类型(普通票:ADULT,学生票:0X00)
第一个和第四个参数好填写,始发站和终点站代码我们现在并没有对应的表,不知道每个站点对应的代码,在网站上找一下。我们在页面上手动修改始发站和终点站的时候发现并没有发送新的https请求,可以猜测所有的站点信息应该是在一个js文件里面,然后通过页面的js方法来选择不同的站名,现在F12控制台切换至JS模块下,刷新这个余票查询页面。
python 发送邮件
import smtplib
from email.mime.text import MIMEText
# 第三方 SMTP 服务
mail_host = "smtp.163.com" # SMTP服务器
mail_user = "username" # 用户名
mail_pass = "passwd" # 密码
sender = 'user@163.com' # 发件人邮箱(最好写全, 不然会失败)
receivers = ['to_someone@qq.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
content = '邮件内容!'
title = '邮件主题' # 邮件主题
message = MIMEText(content, 'plain', 'utf-8') # 内容, 格式, 编码
message['From'] = "{}".format(sender)
message['To'] = ",".join(receivers)
message['Subject'] = title
try:
smtpObj = smtplib.SMTP_SSL(mail_host, 465) # 启用SSL发信, 端口一般是465
smtpObj.login(mail_user, mail_pass) # 登录验证
smtpObj.sendmail(sender, receivers, message.as_string()) # 发送
print("mail has been send successfully.")
except smtplib.SMTPException as e:
print(e)
注意: 如果出现此以下报错信息,看下邮件主题是否为test,网易不允许这个也是奇葩
smtplib.SMTPDataError: (554, b'DT:SPM 163 smtp10,DsCowAA3h9_QbgZXI9_fCQ--.713S2 1460039376,please see http://mail.163.com/help/help_