小白教程:Python爬取12306车票信息以及后期数据处理_python爬取12306火车票信息-程序员宅基地

技术标签: python  

步骤一

寻找想爬取数据的来源网站(即12306),直接进入官网就可以了https://kyfw.12306.cn

步骤二

因为我们是爬取车票信息,所以直接点击单程车票查询就可了
F12启动chrome开发者模式,然后按照下面图片里的步骤操作

在这里插入图片描述

步骤三:
然后就会发现一个请求,接下来,我们点击这条请求,查看请求信息,可以发现包括Headers,Proview,Responsr,Cookies,Timing等信息,以及GET请求的url

在这里插入图片描述
下面是请求网络路径(url),经过多次,不同车站的搜索,我们可以发现,里面参数发生改变的只有,始发地、终点站、出发时间等(但是地名都是大写字母简称)

在这里插入图片描述

步骤四
接下来,我们就部署链接,先把站点简称爬取到本都(我是爬取到本地,然后顺便转存到tomcat服务器,为后面检索关键词、制作词云图做准备)

'''
	文件名:_get_station.py
	如果先复制我的代码,请命名与我一致,因为后面文件里要引用这个模块
'''
#引用request模块 -- 网络请求模块
import requests
#引用re模块 -- 正则表达式
import re
#引用shutil模块,进行文件复制转存,直接存储在tomcat服务器中
import shutil
#引用esayGui模块,制作弹窗
import easygui as ea
#Function() -- 获取12306站点信息
def get_station():
    '''
        @url:12306站点信息存储文件地址(即网络服务器文件目录)
    	:return: station
    '''
    url = 'https://www.gn720.com/file/station_name.js'
    #爬取数据,并以txt问本形式返回
    response = requests.get(url).text
    #用正则表达式匹配出name和referred(站点列表和简称列表)
    name = re.findall(r'.*?\|(.*?)\|.*?\|.*?\|.*?\|.*?',response)
    referred = re.findall(r'.*?\|.*?\|(.*?)\|.*?\|.*?\|.*?',response)
    #用zip打包信息,将信息打包成由元组构成的列表,在转成字典存储到本地文件里
    '''
    @zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
    '''
    station = dict(zip(name,referred))
    '''
    1.以二进制形式打开新的文件,本地有文件就打开,没有就创建一个新的
    2.向文件写入获取内容
    3.关闭文件流
    '''
    file = open('station.txt','w',encoding='utf-8')
    file.write(str(station))
    file.close()
    #将文件转存到tomcats服务器中
    returnString = shutil.move('C:\\Desktop\\Desktop\\实训\\Demo\\_Python_12306\\station.txt','G:\\tomcat8\\apache-tomcat-8.5.38\\webapps\\_localhostServer_')
    #弹窗提示:站点信息爬取完成,并且已经存储在本地
    ea.msgbox(msg='----------------12306站点信息已经爬取完成----------------\n\n'
                  '---------------------成功上传tomcat---------------------\n\n'
                  'Path:http://127.0.0.1:9999/_localhostServer_/station.txt\n\n\n'
                  '-----------------稍后可查看关键词、词云图----------------\n\n',title='运行提示!',ok_button='查看关键字检索')
    #@return:
    return station

这段代码可以直接复制运行,因为不涉及到其他模块、文件的引用,如果你只想存储到本地,或者没有tomcat,记得把转存到服务器部分的代码注释掉~

运行上面代码,在当前文件夹里面应该会出现一个文本文件station.txt,打开,里面是所有站点名称,以及简称构成的字典key为站点中文名称,value为简称,像这样子:

在这里插入图片描述

'''
	这个station.txt文件的作用:
		1.稍后,我们会用这个字典,匹配出我们输入的汉字站点,转成简称,作为请求参数;
		2.拿到响应数据,由于站点信息是我们看不懂的英文简称,用其转成中文,再做回显;
'''

步骤五

下面我们就可以构造链接了,要一下这个请求的响应结果:
可以发现响应结果的形式是这样的:(如下)

在这里插入图片描述

步骤六

构造链接,抓数据

'''
	文件名:_get_tickets.py
'''
#以用request模块
import requests
#引用json模块
import json
#取得编码模块
from urllib.parse import urlencode
'''
    @headers:request请求的请求头
    请求信息:
    F12,启动开发者模式,在参数里就可以找到请求头信息
'''
headers = {
    
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36',
    'Cookie': 'JSESSIONID=85ABACBDDEC5EF0D3F4390E49C235DCD; BIGipServerotn=569377290.24610.0000; BIGipServerpool_passport=183304714.50215.0000; RAIL_EXPIRATION=1584746738988; RAIL_DEVICEID=c_HPEh5qqB0-onW7FlqB5a2T-w9tiHZ95ePILEBaXLQ3Nj84j7a4PV1ezmRs7O57oEVHFp3JcbAEi_s3qJb_bqey5sGYiQ-RmKrzrZ0wzbndDLKGidKjF1l5UZ4FjwqSTdhbaSx8ds-5RgV-KxQrm0mINenavAb3; route=c5c62a339e7744272a54643b3be5bf64; _jc_save_fromStation=%u5317%u4EAC%2CBJP; _jc_save_fromDate=2020-03-17; _jc_save_toDate=2020-03-17; _jc_save_wfdc_flag=dc; _jc_save_toStation=%u5929%u6D25%2CTJP'
}
'''
    @data_dict:key_value:
    经过多次的请求测试,发现get参数改变的都是出发地,到达地,出发时间
    所以确定字典有三个键值对
    即:fromwhere,towhere,startime
'''
def get_tickets(fromwhere,towhere,startime):
    #数据字典
    data = {
    
        'leftTicketDTO.train_date': startime,
        'leftTicketDTO.from_station': fromwhere,
        'leftTicketDTO.to_station': towhere,
        'purpose_codes': 'ADULT',
    }
    #构造请求链接:地址 + 参数 (startime,fromwhere,wowhere)
    request_url = 'https://kyfw.12306.cn/otn/leftTicket/query?' + urlencode(data)
    #利用json.loads()将其他(这里是文本类型)类型的数据转化成Python类型(这里是转成字典)
    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:dict{}
    return new_list

别慌!!! 有些小伙伴们看到代码是不是有点懵,我来说一下这里面乱七八糟的东西怎么来的,走着~!

解释1:headers :这个是网络请求的请求头,这里面大致包括'User-Agent''Cookie',这东西在这:

User-Agent:
在这里插入图片描述
Cookie:
在这里插入图片描述
解释2:urlencode()方法

接受参数形式为:[(key1, value1), (key2, value2),...] 和 {'key1': 'value1', 'key2': 'value2',...}
返回的是形如key2=value2&key1=value1字符串。

解释3:下面字典中键的名字怎么来的

data = {
    
    'leftTicketDTO.train_date': startime,
    'leftTicketDTO.from_station': fromwhere,
    'leftTicketDTO.to_station': towhere,
    'purpose_codes': 'ADULT',
}

把鼠标悬停在这,你就看见了~
在这里插入图片描述
步骤七:

这个_get_tickets.py写完,打印一下返回值:

在这里插入图片描述
乍一看有点懵,仔细看,在“预定”之后其实就是我们想要的东西
所以接下来,我们直接用正则表达式,把我们想要的东西搞下来

'''
	文件名:Decrypt.py
'''
#对数据进行解码
#引用re模块 -- 正则表达式
import re
def decrypt(string):
    #指定匹配规则
    reg = re.compile('.*?\|预订\|.*?\|(.*?)\|(.*?)\|(.*?)\|.*?\|.*?\|(.*?)\|(.*?)\|(.*?)\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|.*?\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|.*?\|.*?\|.*?\|.*')
    '''
        正则匹配
        [0]:匹配所有数据
    '''
    result = re.findall(reg,string)[0]
    #返回匹配结果
    return result

步骤八:

最后一步就是通过main方法,按照一定的逻辑,调用上面的三个函数,或者再加上一下回显修饰

这里其实可以有很多信息,如:各种座位的余票,可不可以预定等;但这里我解码出六项数据,做回显,【车次】,【始发地】,【终点站】,【出发时间】,【到达时间】,【历时】,如果你需要其他的,可以自己加上去。

这里代码简单,不解释啦,直接贴上~

'''
	文件名:mian.py
'''
from _get_station import get_station
from _get_tickets import get_tickets
from GUI import inputInfo
from Decrypt import decrypt
from texttable import Texttable
import easygui as g
import sys
import datetime

#获取信息函数
def get_message():
    _inputInfo = inputInfo()

    fw = _inputInfo[0]
    tw = _inputInfo[1]
    st = _inputInfo[2]

    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]
message = get_message()

def run():
    #获取站点信息
    station_name = get_station()
    g.msgbox(msg='request Static : request success.\n\n'
                 '数据表生成成功',title='运行提示',ok_button='打印数据表')
    try:
        #result_list = []
        fromwhere = station_name[message[0]]
        towhere = station_name[message[1]]
        startime = message[2]
        #get_tickets()获得车票信息列表
        tickets = get_tickets(fromwhere,towhere,startime)
        print('===============================================================================')
        print('carNum'.center(8, '-'), end='')
        print('{0:{1}^9}'.format('始发站', chr(12288)), end='')
        print('{0:{1}^9}'.format('目的地', chr(12288)), end='')
        print('{0:{1}^9}'.format('sTime', chr(12288)), end='')
        print('{0:{1}^9}'.format('aTime', chr(12288)), end='')
        print('{0:{1}^9}'.format('bTime', chr(12288)))
        print('===============================================================================')

        for item in tickets:
            #利用decrypt()函数解码result(网络请求里的result),解码后生成列表,存储到result中
            result = list(decrypt(item))
            #遍历站点信息字典,存储到new_dict新字典里
            new_dict = {
    v: k for k, v in station_name.items()}
            # ----------result_test-----------#
            #结果:
            #list列表:
            #['G102', 'AOH', 'VNP', '06:26', '12:29', '06:03',  '有', '有', '13']
            #print(result)
            #-----------new_dict_test----------#
            #结果:
            #dict字典:
            #{'VAP': '北京北', 'BOP': '北京东'...}
            #print(new_dict)
            '''
                将出发地、到达地的简称转换成中文
            
            '''
            #result列表准备就绪:
            result[1] = new_dict[result[1]]
            result[2] = new_dict[result[2]]

            rightWidth = 8
            print(result[0].center(rightWidth, '-'),end = '')
            print('{0:{1}^9}'.format(result[1], chr(12288)),end = '')
            print('{0:{1}^9}'.format(result[2], chr(12288)),end = '')
            print('{0:{1}^9}'.format(result[3], chr(12288)),end = '')
            print('{0:{1}^9}'.format(result[4], chr(12288)),end = '')
            print('{0:{1}^9}'.format(result[5], chr(12288)))
            print('-------------------------------------------------------------------------------')
    except KeyError as k:
        print("I can't find the city %s"%k)
if __name__ == '__main__':
    run()

按照我的步骤一步一步,肯定是可以运行的,亲测有效

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_47298749/article/details/108541819

智能推荐

什么是内部类?成员内部类、静态内部类、局部内部类和匿名内部类的区别及作用?_成员内部类和局部内部类的区别-程序员宅基地

文章浏览阅读3.4k次,点赞8次,收藏42次。一、什么是内部类?or 内部类的概念内部类是定义在另一个类中的类;下面类TestB是类TestA的内部类。即内部类对象引用了实例化该内部对象的外围类对象。public class TestA{ class TestB {}}二、 为什么需要内部类?or 内部类有什么作用?1、 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。2、内部类可以对同一个包中的其他类隐藏起来。3、 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。三、 内部类的分类成员内部_成员内部类和局部内部类的区别

分布式系统_分布式系统运维工具-程序员宅基地

文章浏览阅读118次。分布式系统要求拆分分布式思想的实质搭配要求分布式系统要求按照某些特定的规则将项目进行拆分。如果将一个项目的所有模板功能都写到一起,当某个模块出现问题时将直接导致整个服务器出现问题。拆分按照业务拆分为不同的服务器,有效的降低系统架构的耦合性在业务拆分的基础上可按照代码层级进行拆分(view、controller、service、pojo)分布式思想的实质分布式思想的实质是为了系统的..._分布式系统运维工具

用Exce分析l数据极简入门_exce l趋势分析数据量-程序员宅基地

文章浏览阅读174次。1.数据源准备2.数据处理step1:数据表处理应用函数:①VLOOKUP函数; ② CONCATENATE函数终表:step2:数据透视表统计分析(1) 透视表汇总不同渠道用户数, 金额(2)透视表汇总不同日期购买用户数,金额(3)透视表汇总不同用户购买订单数,金额step3:讲第二步结果可视化, 比如, 柱形图(1)不同渠道用户数, 金额(2)不同日期..._exce l趋势分析数据量

宁盾堡垒机双因素认证方案_horizon宁盾双因素配置-程序员宅基地

文章浏览阅读3.3k次。堡垒机可以为企业实现服务器、网络设备、数据库、安全设备等的集中管控和安全可靠运行,帮助IT运维人员提高工作效率。通俗来说,就是用来控制哪些人可以登录哪些资产(事先防范和事中控制),以及录像记录登录资产后做了什么事情(事后溯源)。由于堡垒机内部保存着企业所有的设备资产和权限关系,是企业内部信息安全的重要一环。但目前出现的以下问题产生了很大安全隐患:密码设置过于简单,容易被暴力破解;为方便记忆,设置统一的密码,一旦单点被破,极易引发全面危机。在单一的静态密码验证机制下,登录密码是堡垒机安全的唯一_horizon宁盾双因素配置

谷歌浏览器安装(Win、Linux、离线安装)_chrome linux debian离线安装依赖-程序员宅基地

文章浏览阅读7.7k次,点赞4次,收藏16次。Chrome作为一款挺不错的浏览器,其有着诸多的优良特性,并且支持跨平台。其支持(Windows、Linux、Mac OS X、BSD、Android),在绝大多数情况下,其的安装都很简单,但有时会由于网络原因,无法安装,所以在这里总结下Chrome的安装。Windows下的安装:在线安装:离线安装:Linux下的安装:在线安装:离线安装:..._chrome linux debian离线安装依赖

烤仔TVの尚书房 | 逃离北上广?不如押宝越南“北上广”-程序员宅基地

文章浏览阅读153次。中国发达城市榜单每天都在刷新,但无非是北上广轮流坐庄。北京拥有最顶尖的文化资源,上海是“摩登”的国际化大都市,广州是活力四射的千年商都。GDP和发展潜力是衡量城市的数字指...

随便推点

java spark的使用和配置_使用java调用spark注册进去的程序-程序员宅基地

文章浏览阅读3.3k次。前言spark在java使用比较少,多是scala的用法,我这里介绍一下我在项目中使用的代码配置详细算法的使用请点击我主页列表查看版本jar版本说明spark3.0.1scala2.12这个版本注意和spark版本对应,只是为了引jar包springboot版本2.3.2.RELEASEmaven<!-- spark --> <dependency> <gro_使用java调用spark注册进去的程序

汽车零部件开发工具巨头V公司全套bootloader中UDS协议栈源代码,自己完成底层外设驱动开发后,集成即可使用_uds协议栈 源代码-程序员宅基地

文章浏览阅读4.8k次。汽车零部件开发工具巨头V公司全套bootloader中UDS协议栈源代码,自己完成底层外设驱动开发后,集成即可使用,代码精简高效,大厂出品有量产保证。:139800617636213023darcy169_uds协议栈 源代码

AUTOSAR基础篇之OS(下)_autosar 定义了 5 种多核支持类型-程序员宅基地

文章浏览阅读4.6k次,点赞20次,收藏148次。AUTOSAR基础篇之OS(下)前言首先,请问大家几个小小的问题,你清楚:你知道多核OS在什么场景下使用吗?多核系统OS又是如何协同启动或者关闭的呢?AUTOSAR OS存在哪些功能安全等方面的要求呢?多核OS之间的启动关闭与单核相比又存在哪些异同呢?。。。。。。今天,我们来一起探索并回答这些问题。为了便于大家理解,以下是本文的主题大纲:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JCXrdI0k-1636287756923)(https://gite_autosar 定义了 5 种多核支持类型

VS报错无法打开自己写的头文件_vs2013打不开自己定义的头文件-程序员宅基地

文章浏览阅读2.2k次,点赞6次,收藏14次。原因:自己写的头文件没有被加入到方案的包含目录中去,无法被检索到,也就无法打开。将自己写的头文件都放入header files。然后在VS界面上,右键方案名,点击属性。将自己头文件夹的目录添加进去。_vs2013打不开自己定义的头文件

【Redis】Redis基础命令集详解_redis命令-程序员宅基地

文章浏览阅读3.3w次,点赞80次,收藏342次。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。当数据量很大时,count 的数量的指定可能会不起作用,Redis 会自动调整每次的遍历数目。_redis命令

URP渲染管线简介-程序员宅基地

文章浏览阅读449次,点赞3次,收藏3次。URP的设计目标是在保持高性能的同时,提供更多的渲染功能和自定义选项。与普通项目相比,会多出Presets文件夹,里面包含着一些设置,包括本色,声音,法线,贴图等设置。全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,主光源和附加光源在一次Pass中可以一起着色。URP:全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,一次Pass可以计算多个光源。可编程渲染管线:渲染策略是可以供程序员定制的,可以定制的有:光照计算和光源,深度测试,摄像机光照烘焙,后期处理策略等等。_urp渲染管线