专栏首页Python爬虫数据分析挖掘你的车票,我来负责!!!

你的车票,我来负责!!!

第一次写爬虫,咱从入门级——12306车票爬取 开始

我们要爬取的信息是https://www.12306.cn/index/上的车票信息

当我们选择出发地和目的地,点击查询可以获得相关的车票信息,我们现在要将这些信息使用Python爬取

假如我输入出发地为武汉,目的地为广州,则查询结果如下

程序效果如下:

找到车票数据的传输链接

按下F12打开Google浏览器的开发者模式,找到Network–>XHR,里面可以找到带有车票数据的链接,如下图所示(如果没有可以刷新一下页面)

我们选定这个query?leftTicket,展开data-resul,可以看到下图所示的内容

也可以看到这个链接为 https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2020-10-13&leftTicketDTO.from_station=WHN&leftTicketDTO.to_station=GZQ&purpose_codes=ADULT

又换了几个出发地和到达地尝试一下,发现变化的参数只有train_date、from_station、to_station,即日期,出发地,到达地

但我们发现出发地和到达地都是简称,那么怎样获得站名简称呢

刷新一下网页,在all中发现了这个文件station_name,语义上就能知道了,嘿嘿

获取站点名与简称的字典

下面就开始写代码了,首先有几个库是需要装的,要是没装,可以先安装一下

python -m pip install prettytable,selenium,requests

stations.py

import re
import requests
 
url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9161'
requests.packages.urllib3.disable_warnings()
response = requests.get(url, verify=False)
stations = re.findall(u'([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text)
stations=dict(stations)
print(stations)#此处print出字典检查一下看看有没有问题

通过stations文件,我们就可以获取站名简称

运行结果如下

构造请求链接获取车票信息

在获取到简称之后,就可以构造链接了,观察他的请求结果,发现是类似这样的结果

我们要获取到result中的结果,同时将含有列车停运的结果去除

get_tickets()函数

def get_tickets(froms,tos,date):
  '''
  froms:出发地
  tos:到达地
  date:日期
  '''
    froms = stations[froms]#转换名称为简称
    tos = stations[tos]
    #这里是做了一个动态获取cookie,不懂的可以看看我的其他文章,要是我还没写就百度找一下
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('--headless')
    #此处做了一个后台打开浏览器的设置
    browser = webdriver.Chrome(chrome_options=chrome_options)
    browser.get('https://www.12306.cn/index/index.html')
    time.sleep(3)#这个等待3秒是为了防止网页还没加载完就下一步了,保证后续获取到cookie
    #以下是获取cookie并组成完整的cookie
    Cookie = browser.get_cookies()
    strr = ''
    for c in Cookie:
        strr += c['name']
        strr += '='
        strr += c['value']
        strr += ';'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36',
        'Cookie':strr
        }
    print(headers)#检查是否成功获取cookie,也可以写一个循环来检查,我懒就没写了
    browser.quit()#退出后台的浏览器,不退出会占内存的
    #构建链接
    request_url = 'https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT'.format(date,froms,tos)
    #发送请求
    response = json.loads(requests.get(request_url,headers=headers).text)
    #获取结果
    result = response['data']['result']
    new_list = []
    for item in result:
        if not '列车停运' in item:
            new_list.append(item)
        else:
            pass
    return new_list

解析获得的信息

获取的信息如下

4GLHFQ1iKtT3Vp45aqKC%2FVUQBEU3j%2FzFH9kyv5dCX4llmfRhfLGohfEoVO1gjw8WlJ5tEO0dOPei%0AAGI4tWQqXNh5xXY36JbwJX6RcD%2Bv5NVZCazQrnb19tpLjHVzMvjr7wsOKh2%2FdWnro4EemxrbvzgX%0ACwPsdRj6hDo11vd4sntVbyDbtcIo1fnLznDNxhvpPWYuDOPASjrdGJxhFNH4HdvU5wRXM80sW2W8%0AEfjkh%2BVn%2Fi1d%2F397mYXxQyW6ehJqrvi6enFnZZ6bM4IsjeRxbXnUx0rJGu68uPYciccN%2F%2BCdZIjz%0ADFs7tA%3D%3D|预订|87000K129500|K1295|YIJ|GZQ|WCN|GZQ|00:45|13:28|12:43|Y|SOotAvHbzmywXdrxTntAfWPwToY356wSaQMbGaZVww4OpCRF3M1IwMThCX4%3D|20201011|3|J1|26|33|1|0||||1|||无||1|20|||||403010W0|4311|1|0||4042550001302385000110138500201013853000||||||1|0

可以看得出来是由 | 符号进行分割的,数了一下后有46个内容,我们用正则表达式来提取有用的信息 decrypt()函数

def decrypt(string):
    string = ''.join(string)#传过来的是list类型,我们把它转成str
    reg = re.compile('.*?\|预订\|.*?\|(.*?)\|.*?\|.*?\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|.*?\|.*?\|.*?\|.*')
    result = re.findall(reg,string)[0]
    return result

这个正则表达式只取了信息里前36个里的部分数据,因为后10个没啥作用,“(.*?)”这种即为取用,不带括号的为不取用,自己多看,对照着一个一个看,看多了就懂了。

获取用户输入的信息

get_info()函数

def get_info():
    fw = input("请输入出发地>>>")
    tw = input("请输入目的地>>>")
    st = input("请输入出发时间;格式:(年-月-日)(默认为今日日期)>>>>")
    if st == '':
        st = datetime.date.today()
        return fw,tw,st
    else:
        today = datetime.date.today()
        date = str(today).split('-')
        list = st.split('-')
        if int(list[0]) < int(date[0]) or int(list[0]) > int(date[0]):
            exit("输入的年份不在我的查询范围之内")
        else:
            if int(list[1]) < int(date[1]) or int(list[1]) > int(date[1])+1:
                exit("你输入的月份不在我的查询范围之内")
            else:
                if int(list[2]) < int(date[2]):
                    exit("你输入的日期不在我的查询范围之内")
                else:
                    if int(list[1]) < 10 and int(list[1][0]) != 0:
                        list[1] = '0' + list[1]
                    if int(list[2]) < 10 and int(list[2][0]) != 0:
                        list[2] = '0' + list[2]
                    return fw,tw,list[0] + '-' + list[1] + '-' + list[2]

这个函数很简单,没啥说的

运行

header = ["车次","出发站","到达站","出发时间","到达时间","历时","商务座","一等座","二等座","高级软卧","软卧","动卧","硬卧","软座","硬座","无座"]
if __name__ == '__main__':
    pt = PrettyTable()
    pt.field_names = header
    [fw,tw,st]=get_info()
    trainlist=get_tickets(fw,tw,st)
    new_dict = {v: k for k, v in stations.items()}
    for item in trainlist:
        result=list(decrypt(item))
        result[1] = new_dict[result[1]]
        result[2] = new_dict[result[2]]
        results = [result[0],result[1],result[2],result[3],result[4],result[5],result[-1],result[-2],result[-3],result[-12],result[-10],result[-6],result[-5],result[-8],result[-4],result[-7]]
        pt.add_row(results)
    print(pt)

这里使用了prettytable优化输出的内容,其中result就是解析后的车票信息了

以下为整个项目的内容,一共分两个文件stations.py 和tickets.py,stations.py在前面已经贴出来了,这里就不再重复。 tickets.py

from stations import stations
from prettytable import PrettyTable
from selenium import webdriver
import datetime
import requests
import json
import re
import time
header = ["车次","出发站","到达站","出发时间","到达时间","历时","商务座","一等座","二等座","高级软卧","软卧","动卧","硬卧","软座","硬座","无座"]
def get_tickets(froms,tos,date):
    froms = stations[froms]
    tos = stations[tos]
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('--headless')
    browser = webdriver.Chrome(chrome_options=chrome_options)
    browser.get('https://www.12306.cn/index/index.html')
    time.sleep(3)
    Cookie = browser.get_cookies()
    strr = ''
    for c in Cookie:
        strr += c['name']
        strr += '='
        strr += c['value']
        strr += ';'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36',
        'Cookie':strr
        }
    print(headers)
    browser.quit()
    request_url = 'https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT'.format(date,froms,tos)
    response = json.loads(requests.get(request_url,headers=headers).text)
    result = response['data']['result']
    new_list = []
    for item in result:
        if not '列车停运' in item:
            new_list.append(item)
        else:
            pass
    return new_list

def get_info():
    fw = input("请输入出发地>>>")
    tw = input("请输入目的地>>>")
    st = input("请输入出发时间;格式:(年-月-日)(默认为今日日期)>>>>")
    if st == '':
        st = datetime.date.today()
        return fw,tw,st
    else:
        today = datetime.date.today()
        date = str(today).split('-')
        list = st.split('-')
        if int(list[0]) < int(date[0]) or int(list[0]) > int(date[0]):
            exit("输入的年份不在我的查询范围之内")
        else:
            if int(list[1]) < int(date[1]) or int(list[1]) > int(date[1])+1:
                exit("你输入的月份不在我的查询范围之内")
            else:
                if int(list[2]) < int(date[2]):
                    exit("你输入的日期不在我的查询范围之内")
                else:
                    if int(list[1]) < 10 and int(list[1][0]) != 0:
                        list[1] = '0' + list[1]
                    if int(list[2]) < 10 and int(list[2][0]) != 0:
                        list[2] = '0' + list[2]
                    return fw,tw,list[0] + '-' + list[1] + '-' + list[2]

def decrypt(string):
    string = ''.join(string)
    reg = re.compile('.*?\|预订\|.*?\|(.*?)\|.*?\|.*?\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|.*?\|.*?\|.*?\|.*')
    result = re.findall(reg,string)[0]
    return result

if __name__ == '__main__':
    pt = PrettyTable()
    pt.field_names = header
    [fw,tw,st]=get_info()
    trainlist=get_tickets(fw,tw,st)
    new_dict = {v: k for k, v in stations.items()}
    for item in trainlist:
        result=list(decrypt(item))
        result[1] = new_dict[result[1]]
        result[2] = new_dict[result[2]]
        results = [result[0],result[1],result[2],result[3],result[4],result[5],result[-1],result[-2],result[-3],result[-12],result[-10],result[-6],result[-5],result[-8],result[-4],result[-7]]
        pt.add_row(results)
    print(pt)

文章结束!!!

欢迎关注公众号:Python爬虫数据分析挖掘

本文分享自微信公众号 - Python爬虫数据分析挖掘(zyzx3344),作者:李运辰

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-10-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 你真的会看博客???来看看怎么回事

    通过分析我的博客列表网页代码,提取出每篇文章的链接。 我的博客列表url为:https://blog.csdn.net/xiaoma_2018/article...

    孤独的明月
  • Python网络爬虫(实践篇)

    读取内容常见的3种方式,其用法是: file.read()读取文件的全部内容,并把读取到的内容赋给一个字符串变量 file.readlines()读取文件的全部...

    孤独的明月
  • Python爬虫之抓取某东苹果手机评价

    本文实现的爬虫是抓取京东商城指定苹果手机的评论信息。使用 requests 抓取手机评论 API 信息,然后通过 json 模块的相应 API 将返回的 JSO...

    孤独的明月
  • 测试面试题集-Python列表去重

    ITester软件测试小栈
  • 宋宝华:LEP ( Linux 易用剖析器 ) 是什么,为什么以及怎么办 ( 1 )

    LEP是 Linuxer 之 LEP 项目组(Barry Song,Mac Xu,陈松等以及陈莉君老师)正在致力于打造的一个开源项目,本文是 LEP 文档《 L...

    Linuxer
  • Selenium Webdriver Desired Capabilities

    前言 我们每一个的selenium测试都应该在指定的环境下运行,这个环境可以是web浏览器、移动设备、移动模拟器等等。 那怎么来指定我们的selenium测试脚...

    苦叶子
  • 服务化配置的另一种可能

    项目背景 项目是给内部团队用的,也算是业务场景较为复杂的系统,这种系统较于互联网C端产品,用户量不大,QPS峰值不会太高,但业务会比较复杂,业务变动比较频繁。 ...

    春哥大魔王
  • 一分钟系列----springboot之Validator校验

    我相信每个做开发的都听过这句“永远不要相信用户的输入”,因此后台需要对用户的每个输入项都做校验:手机号、用户名、密码、邮箱、身份证号······这时候就需要hi...

    小尘哥
  • 从SAP最佳业务实践看企业管理(27)-SD-定价策略的威力

    无论是在当今低迷的景气中力求生存,或在未来景气复苏时鸿图大展,「按交易订价」的策略都是重要的关键利器 很少有什么时候,降价的压力像现在这般沉重。这股压力有一部份...

    SAP最佳业务实践
  • 01更换源

    修改sources.list文件,将http://archive.ubuntu.com和http://security.ubuntu.com替换成http://...

    Remember_Ray

扫码关注云+社区

领取腾讯云代金券