准备新开一个系列,名字就叫 《我也来爬一爬12306》。
本来想得比较简单,就是写一个爬网页和接口数据的研究和实现过程,后来发现事情没有那么简单,可能涉及很多的概念、技术、思维过程和处理方式,恐怕一篇文章是装不下的,只能是系列文章。此外,为了使这个过程更加清晰有趣,笔者打算使用一种"日拱一卒"的方式来进行表述,就有了本系列按照日期来步进式阐述的方式。
本文作为系列的开篇,就是逻辑上第一天的内容。
基本构思
在第一天里,我们的主要任务是进行系统框架的总体构思。主要包括基本目标,原则和设定,和实现用的基本技术框架。
目标
笔者认为可以把本次活动的定位成为一次探索性的技术研究和实践。为此设定的基本的目标是,编写一个或者一系列的脚本化程序,从公开的火车业务信息网站或者系统中(其实就是12306),获取中国铁路的车站、车次和运行时刻信息。并写入关系型数据库系统,作为后续研究和应用的基础。
所以,本工具(集)的输入就是 12306.cn;输出是一个包含车站、车次和时刻表信息,并可以进行关联数据查询的数据库文件。
基本设定和原则
基于简单、使用和灵活性的考虑,设计的信息获取工具集(爬虫)的设定和限制如下:
- 所有信息和数据均来源于12306官方网站
- 最好不需要进行用户的认证或者交互,可以由脚本完全自动完成操作
- 工具集是脚本化的,并命令行方式执行,方便使用、调试和调整
- 工具集可以跨平台使用,易于移植和重新部署
- 数据存储和查询,支持标准SQL,最后可以体现为文件形式,独立工作
- 考虑一般的错误和例外处理,但不需要达到生产级别
技术框架
基于以上的基本构思和设定,笔者为这次实践选择的基本技术框架如下:
- 应用平台: Nodejs
笔者原来考虑是不是要用Python的(学习和了解的目的),但后来考虑到熟悉和了解的程度,特别是需求和场景比较简单,还是选择了Nodejs,后来的代码可以证实这个过程确实好像比较适合。
- HTTP客户端: Nodejs内置的HTTP模块
在后面的内容中,我们将会看到,这个工具集的实现过程,主要基于两类操作,HTML页面(内容文件)和API调用,所以HTTP客户端是其功能的核心。还是同样的原因,笔者并没有使用扩展的HTTP请求库,而是直接使用了Nodejs内置的HTTP模块,并且进行了简单的扩展和封装。
- 数据库系统: SQLite
考虑到场景和需求比较简单,SQL操作的标准和便利性,以及部署和移植的方便性,笔者的选择是应用最为广泛的本地文件型关系数据库系统SQLite。配合nodejs的SQLite模块使用,访问SQLite数据库不需要安装和配置任何外部的驱动或者程序,可以直接使用代码和程序文件来操作SQLite文件和内容的数据。
- 开发操作系统: Windows
Windows是应用最广泛的应用开发和工作平台。如果没有特别的模块和代码,nodejs也可以很好的运行在Windows之上,特别是在开发过程,相关操作也比较熟悉,开发效率可以得到保证。
- 开发命令行环境: PowerShell
对于Web应用开发和程序运行而言,Windows的一般问题就是它没有提供一个类Linux或者标准的命令行环境。但这个问题对于Nodejs应用程序而言,在Node环境配置完成后,可能的影响应该不大,所以我们在开发测试时,选择的命令行环境就是Windows内置的命令行环境PowerShell。
- 浏览器模拟器
一般的爬虫程序,特别是针对网页的爬虫,都需要使用一个浏览器模拟的程序,可以模拟使用浏览器进行网页访问,内容解析,甚至包括脚本程序的执行。但经过笔者进行的简单研究,发现在当前的任务中,应该可以不使用到如此复杂的功能,只需要一个简单的XML解析模块就可以了。基于简单方便的考虑,就没有使用这个技术。
程序项目架构
前面已经提到,本项目的核心程序,基本上就是一个nodejs程序项目。在这个项目中,为了方便开发的组织工作,初步规划了以下几个主要模块:
- 配置信息和库
考虑到通用性和共用性,笔者使用一个库文件,来实现大部分通用的操作功能,主要包括网络操作、数据库操作、通用计算等等。
同时在这个文件中,还包括了整个项目的一些配置信息,遇到需要调整的情况,可以通过修改配置信息,而部署修改程序的方式,就可以适应一些新的应用场景。
- 入口文件
每个nodejs项目都有一个默认执行的入口文件,作为程序统一的执行调用方式,而不是碎片化的执行方式。随后,程序还可以方便的调整和配置,来使用命令行来控制程序和功能的执行,这些都需要在入口程序文件中实现。
- 程序步骤
在后面的实现中,我们将会看到,本项目其实是由几个前后有一定逻辑关系的步骤构成的。因此笔者为每个步骤或者功能项目都独立设计了代码模块。然后在入口文件中进行组织和调用。
关于为什么要这样组织,笔者认为在这里解释其实是不太直观的。在后面几天的编程过程中,我们将会看到基于此框架的程序代码和文件的组织,并进一步充分理解这样规划和设计的原因。大体上就是方便开发的组织、程序功能实现和测试调试。
这样,笔者的nodejs项目的基础结构如下:
shell
tl_lib.js -- 功能库文件,主要配置信息
tl0.js -- 入口程序,阶段文件加载和调用
tl1.js -- 第一阶段, 车站信息
tl2.js -- 第二阶段, 车次信息
tl3.js -- 第三阶段, 列车信息
tl4.js -- 第四阶段, 时刻信息
package.json -- 项目配置文件
关于各个阶段实现的特性功能,会在系列项目后续文章中逐步展开阐述。
火车相关业务概念
本实践操作涉及12306相关的应用和数据,在构建应用之前,笔者觉得有必要了解一下中国铁路和火车相关的业务概念和特性,这样在设计数据结构、访问和操作数据、构建数据之间关系的过程,就更符合实际情况。
幸运的是,作为普通中国国民,一般都有购买火车票、乘车旅行的经历,作为应用层面的理解,一般还是有一定基础的。下面笔者就尝试针对本次的实践过程,总结和列举铁路和火车相关的概念。
- 车站
中国铁路网络(包括高铁)是由车站和运营线路构成的。一趟客运列车,从起点站发出,途经各个铁路线路,中途停靠这些线路上的车站(途经站),最后到达终点站。其实,在铁路系统中,对每个车站,都有固定的名称,也有对应的编码,通常是三位英文字符,过去可能是在电报系统当中的代码,现在可以作为车站的编码。如北京站的电报码是BJP。在本次任务中,我们也使用电报码作为车站的标识。
- 车次
我们乘坐的客运列车,通常使用车次来进行标识。在现有的铁路系统中,使用一个字母前缀+数组,来对车次进行编码。笔者记得在比较早的时候,客车的编码就是一个简单的数字。比如上大学的时候,假期来往学校和家所在地的 43/44次,75/76次(有人能猜到是哪里吗?)。那时的规则好像是100次以内应该是特快,500次以内是直快,后面是慢车。而且好像由于没有信息化系统,这个编码,在不同的路局中,可能是有冲突的,但在那个时候,这并不是一个很大的问题。后来铁路大发展了车次可能有几千个,在加上网络化售票,就进行了重新的规划和编码,形成了现在类型代码+数字的编码形式。
- 时刻表
时刻表,就是指一趟特定的车次,在始发站的发车时间,在各中间站的到达和发车时间,以及终点到站时间,当然时间都是计划时间,构成的一个车站-时间的表格。
- 列车编码
需要注意的是,实际的列车运营,可能有一些比较复杂的情况。比如同一趟车次的运营时刻可能会发生变化,同一趟车次,在不同的日期,发站或者到站会进行延申而不同的情况。所以,所有的实际的时刻表,都是和实际发车日期关联的,在内部也会使用一个单独的代码,而不是之间使用车次(可以看成是车次的实例)。这个编码,就是列车的编码。
在中国铁路体系和运行系统当中,还有一些值得注意的细节:
- 线路类型
中国铁路的客运线路,其实基本上有两个网络,一个是动车和高铁专用的客运线路网络,一个是传统的客货混用的线路网络,传统的慢车、快车、直达快车等等,一般都是跑在这个网络上的。这两个网络可能在节点上有交叉的情况,比如在某些车站,两个线路都会通过,甚至列车可以在不同的线路上转换,但一般是比较特殊的情况,并不普遍。
所以,逻辑上而言,一个车站可能是不同类型线路上的节点。但我们从现有的信息中,无法直接从代码或者信息中,分辨车站在这个方面的特征。
- 车次类型 在比较早的时候,中国列车都采用数字编码,不同级别的车次,主要体现在数字的大小和长度,越小的车次一般越重要。但后来铁路大发展后,车次数量太多,所以现在通常使用字母前缀+数字的方式,来进行编码。
它的一般编码规则如下,G-高铁,D-动车,K-快车,Z-直达快车,C-城际列车,S-市域列车...
其实,中国铁路的货车,也是有其代码和班次的,但通常和我们熟悉的客运关系不大,而且非常复杂,这里不进行讨论。
- 车次变换
中国铁路对车次的编码,是有一定的规范的。一般而言,以北京作为中心,在线路内,上行就是朝向北京的运行方向,列车车次是偶数;下行(远离北京)是奇数。所以,同一趟车,在运行的时候,去程和回程,一般有两个车次,让人们可以方便的辨析列车运行的方向。还有更复杂的情况,就是在中途要转换上下行的时候,这个车次可能会发生多次变换,就使一趟列车,可能有多个车次,而且理论上没有上限。
这个问题,显然给数据的处理带来了不便。笔者理解这可能是一个历史遗留问题,在没有信息系统和网络的时代,信息规模也很小,这种设计确实方便管理。但在信息时代,只能通过信息系统来进行管理,而且可以很快速的进行各种处理,这种方式就显得有点没有必要的复杂了。
小结
本系列文章,构思了使用一套命令行工具和脚本程序,来访问12306官方网站,获取相关车站、车次和时刻等相关数据的,并且构建关系数据的操作过程。
在第一天,我们并没有立刻入手开展实际的工作,而是先熟悉了一下相关业务,确立的一些基本设定和工作的原则,并建立了基本的技术框架和项目框架。
然后,在后续的几天中,我们会将问题分解成为几个阶段,逐步深入分析和解决每个阶段的具体问题,并建立起对应的信息处理能力。