剑客
关注科技互联网

Python实现12306车票查询

今天准备给师傅一块去应急响应,票都定好了,发现忘带身份证了,MDZZ…所以我就这样错失了一次宝贵的机会…更™可恶的是我去退票,竟然给我说没有身份证退不了。。。我特么的要是有身份证,我还退你大爷啊。。。

所以想搞它,一想,算了,凭我这本事搞12306还是别装逼,所以就有了这个脚本。。。

源代码如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
'''
@Author:joy_nick
@博客:http://byd.dropsec.xyz/
'''
import urllib2
import json
import smtplib
import time
import codecs
from email.mime.text import MIMEText
import ssl

# 记录日志
def log(content):
 t = time.strftime('%Y-%m-%d %H:%M:%S')
 f = codecs.open('watcher.log', 'a', 'utf-8')
 f.write('[%s]%s/n' % (t, content))
 f.close()

# 发送邮件
def send_mail(content):
 to_list=['1048834050@qq.com'] 
 mail_host = 'smtp.163.com' 
 mail_user = '15565978325' 
 mail_pass = 'abcABC12345678' 
 mail_postfix = '163.com' 
 me = "TicketsWatcher"+"<"+mail_user+"@"+mail_postfix+">"
 msg = MIMEText(content,_subtype='plain',_charset='gb2312')
 msg['Subject'] = 'There are some tickets you need.'
 msg['From'] = me
 msg['To'] = ";".join(to_list)
 server = smtplib.SMTP()
 server.connect(mail_host)
 server.ehlo()
 server.starttls()
 server.login(mail_user,mail_pass)
 server.sendmail(me, to_list, msg.as_string())
 server.close()
 log('sent mail successfully')

try:
 # 请求地址根据实际要抓取的页面修改,参数包括日期、出发站、到达站
 ssl._create_default_https_context = ssl._create_unverified_context
 resp = urllib2.urlopen("https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate=2016-10-11&from_station=NJH&to_station=SHH")
 #print resp
 result = resp.read()
 #print result
 data = json.loads(result)
 datas = data['data']['datas']
 print datas
 for d in datas:
 if d['station_train_code'] == 'T135': 
 content = 'tickes for hard seat of %s: %s' % (d['station_train_code'], d['yz_num'])
 log(content)
 if unicode(d['yz_num']) != u"无":
 send_mail(content) 
 break
except Exception, e:
 content = 'somethings wrong with the program:/n' + str(e)
 log(content)
 send_mail(content)

测试结果:

Python实现12306车票查询
Python实现12306车票查询

说明:

脚本分为三部分:

  • 记录日志
  • 发送邮件
  • 车票信息捕捉

记录日志会在脚本目录下生成一个 watch.log 文件,这个主要是得结合实时捕捉数据,你可以定时也可以使用 crontab ,这里我就没在加了(还有十个网站没测呢,政府网站真是尼玛啊,谁有比较好的经验望大牛们不吝分享,不说了都是泪…)

发送邮件部分主要用了 smtplib和email 库,具体代码为:

to_list=['xxx@qq.com'] #接收通知的邮箱
mail_host = 'smtp.163.com' #设置服务器
mail_user = 'xxx' #替换为发件邮箱用户名,不带@后面的
mail_pass = 'xxx' #替换为发件邮箱口令
mail_postfix = '163.com' #发件箱的后缀
msg = MIMEText(content,_subtype='plain',_charset='gb2312')

第一个参数就是邮件正文,第二个参数是MIME的subtype,传入’plain’,最终的MIME就是’text/plain’,最后设置编码为gb2312,不过为了兼容性,你可以使用 utf-8 ,具体的过程函数我就不解释了。

信息抓取部分,很简单就是把数据变为数组,从数组中匹配信息。这里我遇到一个问题: urllib2.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)>
这个错误信息主要是因为: SSL: CERTIFICATE_VERIFY_FAILED Python 升级到 2.7.9 之后引入了一个新特性,当使用urllib.urlopen打开一个 https 链接时,会验证一次 SSL 证书。而当目标网站使用的是自签名的证书时就会抛出一个 urllib2.URLError: 的错误消息.

解决方案:

import ssl
import urllib2
ssl._create_default_https_context = ssl._create_unverified_context
print urllib2.urlopen("https://www.111cn.net/").read()

附:

smtp协议的基本命令包括:

HELO 向服务器标识用户身份
MAIL 初始化邮件传输 mail from:
RCPT 标识单个的邮件接收人;常在MAIL命令后面,可有多个rcpt to:
DATA 在单个或多个RCPT命令后,表示所有的邮件接收人已标识,并初始化数据传输,以.结束
VRFY 用于验证指定的用户/邮箱是否存在;由于安全方面的原因,服务器常禁止此命令
EXPN 验证给定的邮箱列表是否存在,扩充邮箱列表,也常被禁用
HELP 查询服务器支持什么命令
NOOP 无操作,服务器应响应OK
QUIT 结束会话
RSET 重置会话,当前传输被取消
MAIL FROM 指定发送者地址
RCPT TO 指明的接收者地址

SMTP会话的流程:

  1. ehlo
  2. auth login
  3. mail from
  4. rcpt to
  5. data
  6. quit

上面说的是最普通的情况,但是现在好多企业邮件都是安全邮件的,就是通过SSL发送的邮件,这个怎么发呢?SMTP对SSL安全邮件的支持有两种方案,一种老的是专门开启一个465端口来接收ssl邮件,另一种更新的做法是在标准的25端口的smtp上增加一个 starttls 的命令来支持。

这个很简单, smtplib 里就有这个方法,叫 smtplib.starttls() 。当然,不是所有的邮件系统都支持安全邮件的,这个需要从 ehlo 的返回值里来确认,如果里面有 starttls ,才表示支持。

注意:

以上的代码为了方便我都没有判断返回值,严格说来,是应该判断一下返回的代码的,在smtp协议中,只有返回代码是2xx或者3xx才能继续下一步,返回4xx或5xx的,都是出错了。

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址