前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >趣玩爬虫 | 12306火车车次信息爬取分析

趣玩爬虫 | 12306火车车次信息爬取分析

作者头像
啃饼思录
发布2019-05-28 19:50:57
5.7K0
发布2019-05-28 19:50:57
举报

点击上方蓝色字关注 [ 啃饼思录 ]~

12306火车车次信息爬取分析

本篇我们要进行的是12306火车车次信息的爬取分析。都说12306是目前反爬措施最强的网站,的确如此。博主于2017年专门研究过如何爬取并进行了购票分析,费了很大功夫终于成功地抢到了票,但是很不幸,没过多久12306就进行了大改版,写过的爬虫代码几乎成为了摆设。这里只是爬取车次信息,并不进行购票操作,后续可能会出专门的教程介绍这一块,本篇文章的重点不在于此。

分析与爬取过程

车站名称信息爬取

12306网址:https://www.12306.cn/index/,由于我们爬取的是车次信息,因此首先需要知道所有的车站名称:

通过观察,很容易发现出发地和到达地都是一个弹窗,由JS控制。我们思考一下,如果想要爬取所有的车站名称,是不是爬取这个JS弹窗内的信息就可以?是的,我们就是这样做的。首先我们需要登录账号,接着进行下面的操作。我们按F12或者直接开启开发者模式,先在出发地随便选一个车站,然后观察network处的变化,我们发现了一个名为station_name_v10026.js的文件,就是这样:

接着我们就点击这个js文件,页面就跳转到:https://www.12306.cn/index/script/core/common/station_name_v10026.js,我们使用fe助手对代码进行格式化,发现这个就是车站名称控件:

接下来就是将里面的车站名称都爬取下面。先点击这里:汉字 Unicode 编码范围,我们发现一般车站都是常见字,因此使用4E00-9FA5区间的unicode编码即可。我们可以使用([\u4e00-\u9fa5]+)\|([A-Z]+)这样的正则匹配条件来获取我们需要的诸如北京北': 'VAP'等。相应的代码如下:

代码语言:javascript
复制
import requestsimport re

headers = {    'Cookie': '',    'Host': 'www.12306.cn',    'Upgrade-Insecure-Requests': '1',    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'}def getStationName():
    # 爬取12306网站所有车站名称信息
    url = 'https://www.12306.cn/index/script/core/common/station_name_v10026.js'  # 车站信息控件
    r = requests.get(url)
    pattern = '([\u4e00-\u9fa5]+)\|([A-Z]+)'  # 正则匹配规则
    result = re.findall(pattern, r.text)
    stationName = dict(result)  # 所有车站信息,转换为字典
    print(stationName)    # return stationNamegetStationName()

运行结果如下(部分信息):

代码语言:javascript
复制
{'北京北': 'VAP', '北京东': 'BOP', '北京': 'BJP', '北京南': 'VNP'}

两站之间火车票信息查询

在前面我们查询到了所有的车站名称,接下来我们就是查询两站之间火车票的信息了。我们随意输入两个城市,上海和天津,时间更是随意,如下所示:

我们注意一下此时url栏的变化,此时url变为:

我们尝试输入北京到上海,再次确认一下url的变化:

说明后面的flag=N,N,Y是一个常量,如果去掉发现页面就不能正常显示。 也就是说我们需要传入三个参数:date(日期),from_station(出发站),to_station(到达站)。我们需要构造的url应该是这样: https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=from_station&ts=to_station&date=date&flag=N,N,Y 通过观察发现,其实不带车站的字母代号也是可以查询到票务信息的:

所以你可以简化前面爬取的内容,但是我这里就不了,因为我个人比较喜欢这种方式。

接下来就是构建查询的代码了,结合上面查询到的车站信息,我们就能得到如下代码:

代码语言:javascript
复制
import requestsimport re

headers = {    'Cookie': '',    'Host': 'www.12306.cn',    'Upgrade-Insecure-Requests': '1',    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'}def getStationName():
    # 爬取12306网站所有车站名称信息
    url = 'https://www.12306.cn/index/script/core/common/station_name_v10026.js'  # 车站信息控件
    r = requests.get(url)
    pattern = '([\u4e00-\u9fa5]+)\|([A-Z]+)'  # 正则匹配规则
    result = re.findall(pattern, r.text)
    stationName = dict(result)  # 所有车站信息,转换为字典
    # print(stationName)
    return stationName
text =getStationName()def get_query_url(text, date, from_station, to_station):
    # 构建用于查询列车车次信息的url
    # 参数:日期,出发地,到达地
    # key为车站名称, value为车站代号

    date = date
    from_station = from_station+","+text[from_station]
    to_station = to_station+","+text[to_station]    # 新的url
    url = (        "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&"
        "fs={}"
        "&ts={}"
        "&date={}"
        "&flag=N,N,Y"
    ).format(from_station, to_station, date)
    print(url)
get_query_url(text,'2019-04-20','上海','天津')

该程序运行结果为:

代码语言:javascript
复制
https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=上海,SHH&ts=天津,TJP&date=2019-04-20&flag=N,N,Y

拿着这个url去输入栏访问一下,发现可以正常访问了,页面显示票务信息。

获取火车票详情

现在我们就是获取刚才链接页面出现的票务详情信息了,我们需要明确获取的字段信息:车次、出发站、到达站、出发时间、到达时间、历时、商务/特等座、一等座、二等座、软卧、硬卧、硬座和无座等。我们拿到刚才生成的url,访问一下(开启开发者模式):

我们发现有一个query,然后右边就是我们查询的结果,因此我们可以从这个结果中取出信息:

代码语言:javascript
复制
https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2019-04-20&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=TJP&purpose_codes=ADULT

看到这里,你或许会明白,我们其实构造的url最后就是以这个url的查询结果的形式返回,那么我们可不可以直接构造这个url呢?答案是可以的,那么我们对上面的代码进行修改一下:

代码语言:javascript
复制
def get_query_url(text, date, from_station, to_station):
    # 构建用于查询列车车次信息的url
    # 参数:日期,出发地,到达地
    # key为车站名称, value为车站代号

    date = date
    from_station = text[from_station]
    to_station = text[to_station]    # 新的url
    url = (        "https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}"
        "&leftTicketDTO.from_station={}"
        "&leftTicketDTO.to_station={}"
        "&purpose_codes=ADULT"
    ).format(date, from_station, to_station)
    print(url)    return url

运行结果如下:

代码语言:javascript
复制
https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2019-04-20&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=TJP&purpose_codes=ADULT

截图就是这样:

也就说结果是一个json形式的dict,我们先根据data这个key取出全部信息,再根据result这个key取出车次信息。图中密密麻麻的就是我们需要的车次信息:

从这个信息中遍历一下,就能得到我们需要的信息,0代表某一辆车的信息,再从0这个一行中取出我们需要的字段信息即可:

我们发现每个字段都是被|给分割的,因此只需要统计这个|的位置就能取出相应的信息。相应的代码如下:

代码语言:javascript
复制
import requests


url="https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2019-04-20&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=TJP&purpose_codes=ADULT"r =requests.get(url)
all_trains = r.json()['data']['result']  # 获取所有车次信息for one_train in all_trains:  # 遍历取出每辆车的信息
    data_list = one_train.split('|')
    train_number = data_list[3]  # 车次
    from_station_code = data_list[6]  # 出发站代号
    from_station_name = '上海' # 出发站名称
    to_station_code = data_list[7]  # 到达站代号
    to_station_name = '天津' # 到达站名称
    go_time = data_list[8]  # 出发时间
    arrive_time = data_list[9]  # 到达时间
    cost_time = data_list[10]  # 历时
    special_class_seat = data_list[32] or '--'  # 商务/特等座
    first_class_seat = data_list[31] or '--'  # 一等座
    second_class_seat = data_list[30] or '--'  # 二等座
    soft_sleep = data_list[23] or '--'  # 软卧
    hard_sleep = data_list[28] or '--'  # 硬卧
    hard_seat = data_list[29] or '--'  # 硬座
    no_seat = data_list[26] or '--'  # 无座
    print(train_number,from_station_code,from_station_name,to_station_code,to_station_name,go_time,arrive_time,cost_time,special_class_seat,first_class_seat,second_class_seat,soft_sleep,
          hard_sleep,hard_seat,no_seat)

from_station_name,to_station_name这两个先这样用,这里贴出来就是先测试看看数据是否抓取准确,然后再和前面的链接进行拼接,运行结果如下(部分):

代码语言:javascript
复制
G108 AOH 上海 TIP 天津 07:22 12:45 05:23 5 有 有 -- -- -- --G1232 AOH 上海 TXP 天津 07:34 13:45 06:11 无 无 有 -- -- -- --G120 AOH 上海 TIP 天津 07:51 12:49 04:58 16 有 有 -- -- -- --G112 AOH 上海 TIP 天津 08:05 13:24 05:19 无 有 有 -- -- -- --G212 AOH 上海 TXP 天津 08:42 14:26 05:44 有 有 有 -- -- -- --G1258 AOH 上海 TXP 天津 09:08 14:34 05:26 -- 19 有 -- -- -- --

可以看到数据吻合,这样我们就完成了本篇文章的要求。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 啃饼思录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 分析与爬取过程
    • 车站名称信息爬取
      • 两站之间火车票信息查询
        • 获取火车票详情
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档