一、写在前面

我在以前写过一次12306网站的爬虫,当时实现了模拟登录和查询车票,但是感觉还不太够,所以对之前的代码加以修改,还实现了一个订购车票的功能。

二、主要思路

在使用Selenium做模拟登录12306网站的时候,需要将登录成功后的Cookie保存下来,这个Cookie在后面是必需的。然后就是在12306网站上查票订票,同时使用Fiddler软件进行抓包,通过分析得到订票所需的十多个请求,只要依次发送这些请求,在请求成功之后就能够订到票。

三、模拟登录

之前的代码已经基本实现了模拟登录的功能,但是还没法得到想要的Cookie,所以需要对之前的代码进行改进。虽然Selenium模块提供了get_cookies()方法,但是使用这个方法得到的是当前会话的Cookie,也就是Selenium开启的浏览器中当前页面的Cookie,这个Cookie和本地浏览器中的Cookie是不同的。如下是从本地Chrome中拷贝的Cookie,其中以_jc_save开头的字段都是之前查询车票的记录,而其余字段都是生成的:JSESSIONID=A318817EEE594DE954CE352761DF4CD7;

_jc_save_fromStation=%u6B66%u6C49%2CWHN;

_jc_save_wfdc_flag=dc;

_jc_save_toStation=%u4E0A%u6D77%2CAOH;

RAIL_EXPIRATION=1560095439082;

RAIL_DEVICEID=P2wunHEkKFe9MgTM56h-NxsWiIGNkK6JLCOVaG0DHzRm-RxYa7YnDwftPoumiZ0wL7GPsQ93YBHRHgMgB_GLWwZ9Vb65tNiVuwaIOytW8lVG7B1KopI4pSyUr1u06RWpKPhvExBg3FA7ed87WxO3E-68Wg-hXZLl;

_jc_save_fromDate=2019-06-30;

_jc_save_toDate=2019-06-06;

_jc_save_showIns=true;

route=495c805987d0f5c8c84b14f60212447d;

BIGipServerotn=300941834.24610.0000;

BIGipServerpool_passport=250413578.50215.0000

下面是使用Selenium模块的get_cookies()方法得到的Cookie,可以看到和浏览器中的Cookie有很大不同,缺少了很多字段:[{'domain': 'kyfw.12306.cn', 'httpOnly': False, 'name': 'JSESSIONID', 'path': '/otn', 'secure': False, 'value': '672BAF8C694C50C49D3EFFCF9913A745'},

{'domain': 'kyfw.12306.cn', 'httpOnly': False, 'name': 'route', 'path': '/', 'secure': False, 'value': 'c5c62a339e7744272a54643b3be5bf64'},

{'domain': 'kyfw.12306.cn', 'httpOnly': False, 'name': 'BIGipServerotn', 'path': '/', 'secure': False, 'value': '1139802634.24610.0000'}]

解决办法是使用add_cookie()方法向Selenium开启的Chrome中添加Cookie,达到模拟本地浏览器的效果,最终就能登录成功。在登录成功之后,要获取此时的Cookie,除了使用get_cookies()方法或者get_cookie()方法,还可以使用如下语句:cookie = browser.execute_script("return document.cookie;")

不过为了验证是否真的登录成功了,还需要进行一下测试,验证是否登录成功的方法如下代码,这段代码会发送一个请求,请求的结果中包含了是否登录信息(即is_login)和用户名等信息:

def get_name(self):

"""获取用户姓名:return:"""

url = "https://kyfw.12306.cn/otn/login/conf"

res = requests.post(url, headers=self.headers)

is_login = res.json()['data']['is_login']

if is_login == 'Y':

self.name = res.json()['data']['name']

print("欢迎用户:{}".format(self.name))

else:

print("未登录!请先登录。")

四、订购车票

由于查询车票之前就已经做过了,所以这里就不再赘述。这里就说查询车票之后的操作,首先是在12306网站上查余票,然后选择一个车次点击预订,就会跳转到如下页面:

在这个页面上可以选择乘客、选择座位类型,然后再提交订单。这里虽然我们可以使用开发者工具然后刷新页面来抓包,但是为了避免遗漏掉某些请求,所以我选择使用Fiddler软件抓包,最终经过分析实践得到12个请求,其url和对应的含义如下图所示:

这里我并不打算把所有的请求都说一遍,我会将几个重要的请求拿出来描述,这些请求所使用的headers都是一样的,其中包含了登录后的Cookie,如果Cookie失效就会导致订票失败。

1.初始化页面

首先是initDc这个请求,在这个请求的结果中包含了后面请求所必需的一个参数--token(如下图),获取的方法也比较简单,可以直接使用正则表达式进行匹配:

初始化页面获取token的代码如下:

# 初始化,获取token值

def init_dc():

global token

url = "https://kyfw.12306.cn/otn/confirmPassenger/initDc"

data = {

"_json_att": ""

}

res = requests.post(url, headers=headers, data=data)

result = re.findall(" var globalRepeatSubmitToken = '(.*?)';", res.text)

# print(result)

if len(result):

token = result[0]

else:

raise Exception("Error init")

2.提交车票预订信息

其次是提交车票预订信息,在Fiddler中点击Inspectors,然后选择WebForms,可以看到如下图所示信息,其中包含了出发城市、目的城市、出发日期等:

需要注意的是secretStr这个加密字符串,其来源于查询车票时的结果,在结果中每一条车次信息中都包含了一个字符串,不过这两个字符串并不完全一样。如下图所示就是两个字符串的对比,要得到加密字符串只需要使用unquote()方法:

3.确认订单信息

在选择完车次、座位类型、乘客之后会生成一个订单,然后就会发送一个确认订单信息的请求,其中包含了很多重要的信息。这里我放上该部分的代码:

# 确认订单信息

def check_order_info(name, uid, mobile, type_id):

# 商务座,一等座,二等座,软卧,硬卧,硬座

type_str = ["9,0,1,", "M,0,1,", "O,0,1,", "4,0,1,", "3,0,1,", "1,0,1,"][type_id]

url = "https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo"

data = {

"_json_att": "",

"bed_level_order_num": "000000000000000000000000000000",

"cancel_flag": "2",

"oldPassengerStr": name + ",1," + uid + ",1_",

"passengerTicketStr": type_str + name + ",1," + uid + "," + mobile + ",N",

"REPEAT_SUBMIT_TOKEN": token,

"randCode": "",

"tour_flag": "dc",

"whatsSelect": "1"

}

res = requests.post(url, headers=headers, data=data)

# print(res.text)

这个方法包含了四个参数,name、uid和mobile分别表示乘客的姓名、身份证号和电话号码,这三个值都是在获取乘客信息时得到的,第四个参数是座位类别id。在这个请求携带的参数中有一个REPEAT_SUBMIT_TOKEN,这就是前面说过的token,由于我已经将token设置为了全局变量,所以这里就不用作为参数传到方法里了。要注意的是每个座位类别对应的字符是不同的,我通过在页面上选择元素得到了每个座位类型对应的字符,最后生成一个列表,然后通过改变座位类别id就能完成选择座位类别的功能。

4.提交预订请求

在确认订单之后就是提交预订请求,还是在Fiddler软件中找到这个请求,然后查看其携带的参数,如下图所示:

其中包含了车次编码、出发站编码、目的站编码、token等信息,这些编码信息都可以在查询车票的结果中得到,需要注意的是train_date,可以看到这是一个日期信息,而且是一个格林威治标准时间,要得到这个时间可以使用如下方法,这就能将日期转变成格林威治标准时间:train_date = datetime.datetime.strptime(train_date, "%Y-%m-%d").date()

this_date = train_date.strftime("%a+%b+%d+%Y")

5.检查提交状态

在提交预订请求之后,需要检查提交状态,这个请求包含了很多参数,其中一些参数的值都包含在提交预订请求的结果中,除此之外这些参数还有乘客姓名、身份证号、乘客电话、token等。这个请求返回的结果中有一个submitStatus,需要提取出来,该值表明了提交是否成功。该部分的代码如下所示:

# 检查提交状态

def confirm(key_check, left_ticket, passenger_name, passenger_id, passenger_mobile, location, type_id):

# 商务座,一等座,二等座,软卧,硬卧,硬座

type_str = ["9,0,1,", "M,0,1,", "O,0,1,", "4,0,1,", "3,0,1,", "1,0,1,"][type_id]

url = "https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue"

data = {

"choose_seats": "",

"dwAll": "N",

"key_check_isChange": key_check,

"leftTicketStr": left_ticket,

"oldPassengerStr": passenger_name + ",1," + passenger_id + ",1_",

"passengerTicketStr": type_str + passenger_name + ",1," + passenger_id + "," + passenger_mobile + ",N",

"purpose_codes": "00",

"randCode": "",

"REPEAT_SUBMIT_TOKEN": token,

"roomType": "00",

"seatDetailType": "000",

"train_location": location,

"whatsSelect": "1",

"_json_att": "", }

res = requests.post(url, headers=headers, data=data)

try:

js = json.loads(res.text)

status = js["data"]["submitStatus"]

# print(status)

return status

except Exception as e:

print(e)

raise Exception("Confirm Error!")

6.排队等待

当我们的订单提交成功之后,就需要排队等待了,此时会发送一个请求,该请求中携带了一个时间戳参数(random),如下图所示:

这是一个十三位的时间戳,在Python中可以使用 int(time() * 1000) 得到十三位时间戳。需要注意的是排队等待的结果是不确定的,正确的结果如下图所示:

其中有一个orderId,这个值是我们需要的。如果返回的结果中不包含orderId,就需要重新发送请求。

7.请求预订结果

在得到orderId之后,就可以请求预订结果了,请求无误的话就能够成功订到票了。下图是在Fiddler软件中截到的图,其中EF73361481就是前面得到的orderId:

五、运行结果

  下图是在Pycharm中的运行截图,在登录成功之后查询余票,将查询的结果显示出来:

查询车票之后就是预订车票,需要输入车次名称、座位类别和选择乘客,然后提交订单,最终成功订到火车票。

订票成功之后,进入12306网站进行查看,可以看到成功订到票了, 如下图所示:

python12306爬虫_【Python3爬虫】最新的12306爬虫相关推荐

  1. 为什么用python写爬虫_老猿为什么写Python爬虫教程

    对于"爬虫", 或许你只是听说过,或许已经有所了解.无论怎样,你可能有过这样的困惑: + 学了爬虫不知道怎么挣钱? + 技术不知道如何进阶? + 遇到问题不知道找谁交流? 十多年前 ...

  2. 【Python3爬虫】最新的12306爬虫

    一.写在前面 我在以前写过一次12306网站的爬虫,当时实现了模拟登录和查询车票,但是感觉还不太够,所以对之前的代码加以修改,还实现了一个订购车票的功能. 二.主要思路 在使用Selenium做模拟登 ...

  3. java 有没有类似于 requests 爬虫_广州地震?用 Python 爬虫带你了解地震

    这周看到了两个地震新闻,一个是广东广州增城的,另一个是台湾的,因为本人身在广州,而且对于广佛这边的来说地震其实挺突然的对于我们来说,基本在广佛这边很少出现地震,所以我看到广州增城地震也挺吃惊,然后就萌 ...

  4. python深度爬虫_总结:常用的 Python 爬虫技巧

    用python也差不多一年多了,python应用最多的场景还是web快速开发.爬虫.自动化运维:写过简单网站.写过自动发帖脚本.写过收发邮件脚本.写过简单验证码识别脚本. 爬虫在开发过程中也有很多复用 ...

  5. cve 爬虫_好用的Google漏洞爬虫:Google Mass Explorer

    *本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担. 这是一款基于谷歌搜索引擎的自动化爬虫. 爬虫介绍 爬虫大体机制就是:先进行一次谷歌 ...

  6. python极客学院爬虫_学习极客学院多线程爬虫课程的收获

    昨天开始了极客学院<XPath与多线程爬虫>课程的学习,主要涉及到XPath和requests的使用,在测试过程中出现了很多问题,经过不断摸索以及前辈们的帮助,现将经验总结如下: 1. P ...

  7. python简单网络爬虫_【Python】 简单网络爬虫实现

    介绍网络爬虫(英语:网络爬虫),也称为网络蜘蛛(蜘蛛)是一个Web机器人用于自动浏览万维网.其目的通常是为了编译web索引.\u2014\u2014维基百科web爬虫程序可以节省他们访问的页面,以便搜 ...

  8. 手机写python爬虫_零基础开始写Python爬虫心得

    零基础学习爬虫,坑确实比较多,总结如下: 1.环境配置,各种安装包.环境变量,对小白太不友好: 2.缺少合理的学习路径,上来 Python.HTML 各种学,极其容易放弃: 3.Python有很多包. ...

  9. 如何用python实现爬虫_如何用python实现网络爬虫原理?

    对于一个网络爬虫,如果要按广度遍历的方式下载,它就是这样干活的: 1.从给定的入口网址把第一个网页下载下来 2.从第一个网页中提取出所有新的网页地址,放入下载列表中 3.按下载列表中的地址,下载所有新 ...

最新文章

  1. 零基础的你还在纠结怎么学习Python编程吗?
  2. python格式化字符串漏洞_Python新型字符串格式漏洞分析及解决方案
  3. 视频质量评价:挑战与机遇
  4. 知道无人驾驶的网络安全有多重要吗?英国政府都决定插手开发了
  5. ubuntu12.10下安装mysqlworkbench出现“Dependency is not satisfiable: libctemplate0”问题的解决方案...
  6. Firefox开发者版本终于支持Windows 64位
  7. MySQL安装 MySQL5.7.10免安装版配置,mysql5.7.10免安装版
  8. 评:网瘾不是问题 两代人文化冲突是根本
  9. GoJS图表组件简介
  10. cad插件_CAD插件自动标注安装教程
  11. ios12完美深色模式插件_让所有网站开启深色模式,试试这个浏览器插件
  12. IDEA打包jar包详尽流程
  13. 1110_win10专业版官方原版镜像64位
  14. ceph osd heartbeat 分析
  15. 设计模式——工厂方法模式
  16. 51单片机寻迹小车完整程序
  17. 微信停止为苹果服务器,在苹果开发者政策限制下微信终于宣布将在近期为iOS版微信增加暗黑模式-...
  18. 自媒体人如何在千氪实现月收入过万?
  19. Gitlab Custom_hooks集代码规则注释校验脚本(pre-receive)
  20. Unity3D 5.5 Baked view问题

热门文章

  1. C语言符号-取余\取模运算
  2. 生信分析--入门实践一条龙的CWL中文教程
  3. 清理buff/cache
  4. 《Graph Learning》| 第一章:缤纷的图世界
  5. Linux运行C语言程序
  6. 百度脑图相关操作及快捷键
  7. java笛卡尔积算法实现
  8. vue-cli element-ui打包报错Unexpected token: operator ()
  9. jupyter插件安装
  10. Keepalived-一主多备原理及实践