前言

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。

作者:TM0831

PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取

python免费学习资料以及群交流解答点击即可加入


具体步骤

一、登录

登录功能是通过使用selenium实现的,用到了超级鹰来识别验证码。没有超级鹰账号的先注册一个账号,充值一点题分,然后把下载这个Python接口文件,再在里面添加一个use_cjy的函数,以后使用的时候传入文件名就可以了(验证码类型和价格可以在价格体系查看):

1 def use_cjy(filename):
2     username = ""  # 用户名
3     password = ""  # 密码
4     app_id = ""  # 软件ID
5     cjy = CJYClient(username, password, app_id)  # 用户中心>>软件ID
6     im = open(filename, 'rb').read()  # 本地图片文件路径
7     return cjy.PostPic(im, 9004)  # 9004->验证码类型

然后进入12306的登录页面,网址为https://kyfw.12306.cn/otn/login/init,可以看到有一个像下面这样的验证码:

要破解这个验证码,第一个问题是怎么得到这个验证码图片,我们可以很轻松的找到这个验证码图片的链接,但是如果用requests去请求这个链接,然后把图片下载下来,这样得到的图片和网页上的验证码图片是不同的,因为每次请求都会刷新一次验证码。所以需要换个思路,比如先把网页截个图,然后我们可以知道验证码图片在网页中的位置,然后再根据这个位置,把截图相应的位置给截取出来,就相当于把验证码图片从整个截图中给抠出来了,这样得到的验证码图片就和网页上的验证码一样了。相关代码如下:

 1 # 定位到验证码图片2 captcha_img = browser.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')3 location = captcha_img.location4 size = captcha_img.size5 # 写成我们需要截取的位置坐标6 coordinates = (int(location['x']), int(location['y']),7                int(location['x'] + size['width']), int(location['y'] + size['height']))8 browser.save_screenshot('screen.png')9 i = Image.open('screen.png')
10 # 使用Image的crop函数,从截图中再次截取我们需要的区域
11 verify_code_image = i.crop(coordinates)
12 verify_code_image.save('captcha.png')

现在已经得到了验证码图片了,下一个问题是怎么识别?点触验证码识别起来有两个难点,一个是文字识别,要把图上的鞭炮文字识别出来,第二点是识别图片中的内容,比如上图就要把有鞭炮的图片识别出来,而这两个难点利用OCR技术都很那实现,因此选择使用打码平台(比如超级鹰)来识别验证码。对于上面这个图,在使用超级鹰识别之后会返回下面这个结果:

{'pic_id': '6048511471893900001', 'err_no': 0, 'err_str': 'OK', 'md5': 'bde1de3b886fe2019a252934874c6669', 'pic_str': '117,140'}

其中pic_str对应的值就是有鞭炮的图片的坐标位置(如果有多个坐标,会用“|”进行分隔),我们对这个结果进行解析,把坐标提取出来,再利用selenium模拟点击就可以了,相关代码如下:

 1 # 调用超级鹰识别验证码2 capture_result = use_cjy('captcha.png')3 print(capture_result)4 # 对返回的结果进行解析5 groups = capture_result.get("pic_str").split('|')6 points = [[int(number) for number in group.split(',')] for group in groups]7 for point in points:8     # 先定位到验证图片9     element = WebDriverWait(browser, 20).until(
10         EC.presence_of_element_located((By.CLASS_NAME, "touclick-bgimg")))
11     # 模拟点击验证图片
12     ActionChains(browser).move_to_element_with_offset(element, point[0], point[1]).click().perform()
13     sleep(1)

二、查询

带有车票信息的ajax接口很容易找到,格式也是标准的json格式,解析起来会方便不少

但是爆保存车票的字符串很复杂,我们先把第一条信息打印出来看看,以下是部分信息:

'hH0qeKPBgl0X0aCnrtZFyBgzqydzV45U2M1r%2F32FsaPHeb7Mul00sIb7y9W%2B6df1tUdDGCxqdVs8%0Aw2VodSjdXjUQ2uNdwFprKdVK9iaW60Wj2jKpNKaViR4ndlBCjsYB0SIF
QR0pLksy7HDP0KcaoLe4%0A4RW6zRcscO7SRNJZOsF%2Fxj3Ooq76lzzdku3Uw957yjLFyf7ikixOaC%2FAOrLAwCc7y0krRpKJbSn3%0ApBsY%2F%2Fok%2Bmg2xNhXapoCPIt4w0p9',  这段字符是随机生成的,过几秒就回失效。'39000D30280G',  列车编号'D3028',  车次'HKN',  始发站'AOH',  终点站'HKN',  出发站'AOH',  目的站'07:31',  出发时间'13:06',  到达时间'05:35',  总耗时'Y',  Y表示可以购票,N表示不可以'20181111',  日期
后面基本都是座位的余票信息了。

对于提到的列车站点代码,可以通过请求这个链接,通过得到JS脚本中的station_names变量获取,对应的站点以@字符分隔,相关代码如下:

1 # 请求保存列车站点代码的链接
2 res1 = requests.get("https://kyfw.12306.cn/otn/resources/js/framework/station_name.js")
3 # 把分割处理后的车站信息保存在station_data中
4 self.station_data = res1.text.lstrip("var station_names ='").rstrip("'").split('@')
 1 # 返回车站英文缩写2  def get_station(self, city):3     for i in self.station_data:4         if city in i:5             return i.split('|')[2]6 7 # 返回车站中文缩写8 def get_city(self, station):9     for i in self.station_data:
10         if station in i:
11             return i.split('|')[1]

由于ajax接口有了一点变化,所以我对之前的代码做了一点修改,在输入数据的部分:

1 # 需要按2018-01-01的格式输入日期,不然会出现错误
2 d = input("请输入日期(如:2018-01-01):")
3 f = self.get_station(input("请输入您的出发站:"))
4 t = self.get_station(input("请输入您的目的站:"))
5 url = "https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}" \
6       "&leftTicketDTO.to_station={}&purpose_codes=ADULT".format(d, f, t)

完整代码

CJYDemo.py

import requests
from hashlib import md5class CJYClient(object):def __init__(self, username, password, soft_id):self.username = usernameself.password = md5(password.encode('utf8')).hexdigest()self.soft_id = soft_idself.base_params = {'user': self.username,'pass2': self.password,'softid': self.soft_id,}self.headers = {'Connection': 'Keep-Alive','User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',}def PostPic(self, im, codetype):"""im: 图片字节codetype: 题目类型 参考 http://www.chaojiying.com/price.html"""params = {'codetype': codetype,}params.update(self.base_params)files = {'userfile': ('ccc.jpg', im)}r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,headers=self.headers)return r.json()def ReportError(self, im_id):"""im_id:报错题目的图片ID"""params = {'id': im_id,}params.update(self.base_params)r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)return r.json()def use_cjy(filename):username = ""  # 用户名password = ""  # 密码app_id = ""  # 软件IDcjy = CJYClient(username, password, app_id)  # 用户中心>>软件IDim = open(filename, 'rb').read()  # 本地图片文件路径return cjy.PostPic(im, 9004)  # 9004->验证码类型

test.py


import json
import requests
from PIL import Image
from time import sleep
from .CJYDemo import use_cjy
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ECheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1"" Safari/537.1"
}class TrainUser:def __init__(self, username, password):self.username = usernameself.password = passwordself.cookie = ""self.station_data = ""# 登录12306def login(self):browser = webdriver.Chrome()browser.get("https://kyfw.12306.cn/otn/login/init")browser.find_element_by_xpath('//*[@id="username"]').send_keys(self.username)sleep(2)browser.find_element_by_xpath('//*[@id="password"]').send_keys(self.password)sleep(2)captcha_img = browser.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')location = captcha_img.locationsize = captcha_img.size# 写成我们需要截取的位置坐标coordinates = (int(location['x']), int(location['y']),int(location['x'] + 2 * size['width']), int(location['y'] + 2 * size['height']))browser.save_screenshot('screen.png')i = Image.open('screen.png')# 使用Image的crop函数,从截图中再次截取我们需要的区域verify_code_image = i.crop(coordinates)verify_code_image.save('captcha.png')# 调用超级鹰识别验证码capture_result = use_cjy('captcha.png')print(capture_result)# 对返回的结果进行解析groups = capture_result.get("pic_str").split('|')points = [[int(number) for number in group.split(',')] for group in groups]for point in points:# 先定位到验证图片element = WebDriverWait(browser, 20).until(EC.presence_of_element_located((By.CLASS_NAME, "touclick-image")))# 模拟点击验证图片ActionChains(browser).move_to_element_with_offset(element, point[0] - 110, point[1] - 90).click().perform()sleep(3)browser.find_element_by_xpath('//*[@id="loginSub"]').click()sleep(5)if browser.current_url not in ["https://kyfw.12306.cn/otn/login/init", "https://kyfw.12306.cn/otn/login/init#"]:print("登录成功!")else:print("登录失败,请重试!")# 显示可购车票信息def show_ticket(self):# 请求保存列车站点代码的链接res1 = requests.get("https://kyfw.12306.cn/otn/resources/js/framework/station_name.js")# 把分割处理后的车站信息保存在station_data中self.station_data = res1.text.lstrip("var station_names ='").rstrip("'").split('@')# 需要按2018-01-01的格式输入日期,不然会出现错误d = input("请输入日期(如:2018-01-01):")f = self.get_station(input("请输入您的出发站:"))t = self.get_station(input("请输入您的目的站:"))url = "https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}" \"&leftTicketDTO.to_station={}&purpose_codes=ADULT".format(d, f, t)res2 = requests.get(url)result = json.loads(res2.text)['data']['result']seat_data = [(32, "商务座"), (31, "一等座"), (30, "二等座"), (26, "无座"), (23, "软卧"), (28, "硬卧"), (29, "硬座")]for i in result:i = i.split('|')info = {"车次": i[3], "出发日期": i[13], "始发站": self.get_city(i[4]), "终点站": self.get_city(i[7]),"出发站": self.get_city(i[6]), "目的站": self.get_city(i[5]), "出发时间": i[8], "到达时间": i[9],"总耗时": str(int(i[10][:i[10].index(":")])) + "小时" + str(int(i[10][i[10].index(":") + 1:])) + "分钟","商务座": '', "一等座": '', "二等座": '', "无座": '', "软卧": '', "硬卧": '', "硬座": ''}for j in range(7):if i[seat_data[j][0]] == "有" or i[seat_data[j][0]].isdigit():info[seat_data[j][1]] = i[seat_data[j][0]]else:del info[seat_data[j][1]]print(info)# 返回车站英文缩写def get_station(self, city):for i in self.station_data:if city in i:return i.split('|')[2]# 返回车站中文缩写def get_city(self, station):for i in self.station_data:if station in i:return i.split('|')[1]if __name__ == '__main__':u = TrainUser(input("请输入您的用户名:"), input("请输入您的密码:"))u.login()u.show_ticket()

python实现登录12306网站查看火车票信息相关推荐

  1. Python使用selenium模拟滑块验证登录12306网站 实测可用

    Python使用selenium模拟登录12306网站(根据12306的更新编写的2022年4月最新方法)实测可用 from selenium import webdriver from time i ...

  2. python+splinter实现12306网站刷票并自动购票流程

    python+splinter实现12306网站刷票并自动购票流程 通过python+splinter,实现在12306网站刷票并自动购票流程(无法自动识别验证码). 此类程序只是提高了12306网站 ...

  3. python12306自动抢票为什么进入个人中心,python自动登录12306并自动点击验证码完成登录的实现源代码...

    以下代码可自动登录12306 - 包括输入用户名密码以及自动识别验证码并点击验证码登陆.该源码需要稍作修改: 把  username.send_keys('xxxxxxx')  中的  xxxxxx ...

  4. python爬虫登录12306失败_Python网络爬虫(selenium模拟登录12306网站)

    一.通过selenium自动登录12306官网 1.1 超级鹰打码平台API,创建chaojiyin.py文件 #!/usr/bin/env python#coding:utf-8 importreq ...

  5. python 爬取12306网站车次信息

    12306网站爬取车次信息 本想着可以做一个类似于查询详细车次信息的小工具,但是不尽人意,12306网站爬取的车次信息加密了,研究了一个下午也没有研究出其中全部的信息解密: 爬取的信息如下(举一个例子 ...

  6. 1.python自动化登录12306

    python selenium 自动化登录12306 1.下载依赖包 pip install selenium # 自动化必须的包 2.下载驱动 # 下载驱动 https://npm.taobao.o ...

  7. 模拟登录12306网站

    首先用火狐浏览器进入12306网站的登录界面,F12获取登录界面的url地址. 再获取验证码图片的链接及其验证码校正所post的数据 最后获取用户名和密码的登录链接及其post的数据 # coding ...

  8. 12306网站秒杀火车票 谁是幕后推手?

    [IT168 评论]大概是有英雄情结的缘故,小时候看铁道游击队,就很崇拜那些扒火车的英雄行为.如今,扒火车早已不再是英雄行为,相反是犯罪行为.警察叔叔告诫我们要做到排队守纪.于是,大冬天排长队买火车票 ...

  9. Andriod的Http请求获取Cookie信息并同步保存,使第二次不用登录也可查看个人信息...

    Android使用Http请求登录,则通过登录成功获取Cookie信息并同步,可以是下一次不用登录也可以查看到个人信息, 注:如果初始化加载登录,可通过缓存Cookie信息来验证是否要加载登录界面.C ...

最新文章

  1. 会话(cookie的使用,路径和Session的工作原理,使用)
  2. 如何将项目上传到GitHub
  3. 非关系型数据库mongodb的打开与连接
  4. boost::mp11::mp_or相关用法的测试程序
  5. boost::hana::append用法的测试程序
  6. 【BZOJ 1096】[ZJOI2007]仓库建设
  7. 【转】UITableView详解(UITableViewCell
  8. java u003_我在B站学编程 DAY-003 JAVA基础概念和语法
  9. bzoj4330:JSOI2012 爱之项链
  10. ML《集成学习(二)Boosting之Adaboosting》
  11. linux查看vnc服务加密修复,VNC远程管理Linux服务器安全指导
  12. Java 代理访问http
  13. 苹果电脑如何读写ntfs格式磁盘
  14. 悟透delphi 第五章 包
  15. redis集群原理(简版)
  16. drupal 电商网站_为什么小型企业应该为电子商务网站选择Drupal
  17. package.json bin的作用
  18. 上海落户计算机水平毕业研究生,2021应届生落户上海打分标准,部分毕业生可直接落户上海...
  19. IMAX [生活时尚]
  20. HTML中视频默认显示自定义图片

热门文章

  1. 学高数b能考计算机吗,经验 | 辞职考研上岸中南计算机
  2. 终于把Python弄明白了!
  3. Ocp证书的价值以及拥有它的意义!
  4. 写字板能制作html,写字板的制作方法
  5. 说服别人的“经典”小故事
  6. 元宇宙产业链5大领域公司和项目盘点
  7. 全栈开发工程师修炼指南
  8. JavaBean、MVC模式、Servlet、Servlet生命周期:5个阶段
  9. 将纸质的电话号码导入到手机通讯录(二):使用网页版QQ同步助手将电话号码导入到手机通讯录...
  10. 【转】一个老员工的离职忠告,7年职场经历的感悟