20242327 实验四《Python程序设计》实验报告
20242327 2024-2025-2 《Python程序设计》实验四报告
课程:《Python程序设计》
班级: 2423
姓名: 梁瑞丽
学号: 20242327
实验教师:王志强
实验日期:2025年5月14日
必修/选修: 公选课
1.实验内容
Python综合应用:通过爬取12306网站车票信息初步完成查询某地到某地的车票以及完成购票操作
2. 实验过程及结果
(1)实验前准备
安装requests、DrissionPage、pypinyin库,分别用于获取网页内容、网页自动化操作以及汉字拼音转化,为后续的操作打下基础。
同时,查询相关知识,了解如何获取网页信息,尤其如何从庞大的网页信息中的数据提取自己需要的部分以及如何通过代码在指定位置完成输入或者点击的操作。
(2)获取车票数据
①打开12306网站,鼠标右键选择【检查】并在弹出的窗口中选择【窗口】,之后刷新页面后如下:

②将网页的开发者工具中的网络请求头信息中的【URL】地址复制并粘贴在代码内

③完成配置信息headers,将标头中的user-agent、cookie、referer分别写入。
url = f"https://kyfw.12306.cn/otn/leftTicket/queryU?leftTicketDTO.train_date={sj}&leftTicketDTO.from_station={city[gocity]}&leftTicketDTO.to_station={city[tocity]}&purpose_codes=ADULT"
headers ={'user-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
'cookie':"_uab_collina=174511668729622914909282; JSESSIONID=EFABB5D6EAF48174EDDFA97CE1CE2B3C; _jc_save_wfdc_flag=dc; BIGipServerotn=1977155850.24610.0000; BIGipServerpassport=937951498.50215.0000; guidesStatus=off; highContrastMode=defaltMode; cursorStatus=off; route=495c805987d0f5c8c84b14f60212447d; _jc_save_fromStation=%u5317%u4EAC%2CBJP; _jc_save_toStation=%u4E0A%u6D77%2CSHH; _jc_save_fromDate=2025-05-31; _jc_save_toDate=2025-05-31",
'referer':"https://kyfw.12306.cn/otn/leftTicket/init?"}
④向12306发送请求
res = requests.get(url,headers=headers)
⑤将得到的json数据解析使用
具体操作代码如下:
JSON=res.json()
data=JSON['data']['result']
for i in data:
index=i.split('|')#分离函数,将多余字符切掉
page_num +=1
checi=index[3]#车次
start=index[8]#出发时间
over=index[9]#到达时间
time=index[10]#总用时
vip=index[32]#特等座
ydz=index[31]#一等座
edz=index[30]#二等座
dit={
'序号':page_num,
'车次':checi,
'出发时间':start,
'到达时间':over,
'时长':time,
'特等座座位数':vip,
'一等座座位数':ydz,
'二等座座位数':edz,
}
print(dit)
在上述过程中,实现了:
1.从HTTP响应对象res中提取JSON数据,并将其转换为Python字典格式存储在变量JSON中
2.创建包含所有车票信息的列表:从解析后的JSON字典中提取键为'data'的值,然后进一步提取键为'result'的值
3.利用循环遍历车票信息列表,使用split('|')方法将每条车票信息字符串按'|'字符分割成列表index,通过循环打印每个列表的信息序号,将所需要的信息(如发车地目的地等)的序号存储

4.创建一个字典dit,包含车票的所有属性,然后打印该字典。
通过以上步骤,可以查询某两地之间的所有车票如下

(3)实现用户输入出发地和目的地自动查票
1,导入城市代码字符串并转化成字典
*在实际操作中,城市代码数据来源于网络,因此字典中并未包含所有城市
首先新建文件并将文件后缀名改为json,将城市代码以字符串形式粘贴进入如下:

之后将城市代码导入,再将json字符串转化为json字典数据,如下:
k=open('city.json',encoding='utf-8').read()
#将json字符串转化为字典数据
city=json.loads(k)
通过input获取用户出发城市目的城市以及出发时间,另外将URL网址中出发城市目的城市以及时间修改为用户传入的如下:
url = f"https://kyfw.12306.cn/otn/leftTicket/queryU?leftTicketDTO.train_date={sj}&leftTicketDTO.from_station={city[gocity]}&leftTicketDTO.to_station={city[tocity]}&purpose_codes=ADULT"
完成后结果:

(由于本实验报告于实验结束后撰写,故截图中这个过程完成后程序没有立马结束)
(4)实现自动化购票
1.增添票号
在之前的程序中,我们成功获得了某地到某地某一时间的车次,但是如果车次没有序号,用户无法选择购买的车次无法完成购票需求,于是在字典中增添‘序号’栏,并用input传入用户选择,每次循环序号+1即可。
2.导入相关库
from DrissionPage import ChromiumPage#自动化模块
from DrissionPage.common import Actions#导入动作链
from DrissionPage.common import Keys #导入键盘模块
from pypinyin import pinyin,Style #导入拼音模块
这些导入语句分别从不同的Python库中导入了模块,用于实现自动化操作、模拟用户行为以及中文汉字转拼音等功能。
3.自动打开浏览器与目标网址(12306官网)
zdh=ChromiumPage()
#自动打开网页地址
zdh.get('https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E5%8C%97%E4%BA%AC,BJP&ts=%E4%B8%8A%E6%B5%B7,SHH&date=2025-05-31&flag=N,N,Y')
4.元素定位
分析手动购票流程(仅考虑购买单程票),首先需要选择【填入出发地】、【填入目的地】、【填入出发日】,之后需要【点击“查询”】以及【点击“预订”】,点击后,根据是否登录12306官网分已登录和未登录两种情况操作情况也不同,如果是已登录,则只需要选择乘车人支付即可,若未登录,还需要的操作有【填入证件后四位】【点击获取验证码】【填入】【点击确定】。
将以上步骤自动化过程如下:
①首先定位【出发地】【目的地】输入框:通过开发者工具右上角菜单栏上的【元素查找】选择需要的输入框,精准定位输入框代码,复制其中的id内容,如下图为出发地的id:

具体代码实现过程还需要键盘模块的回车键来表示确定(出发地和目的地原理一致 )
如下:
dzl=Actions(zdh)
#元素定位
dzl.move_to('css:#fromStationText').click().type(language(Go_city)) #定位出发地输入框元素
zdh.ele('css:#fromStationText').input(Keys.ENTER)
dzl.move_to('css:#toStationText').click().type(language(To_city)) #定位目的地输入框元素
zdh.ele('css:#toStationText').input(Keys.ENTER)
②同理,定位后续的时间,查询,以及预订并点击(click),值得注意的是时间在网页中存在值,所以定位时需要先清除,另外,【预订】车票元素在查询后发现没有id参数

上翻后在tbody标签内tr标签找到,如下

通过nth-child()选择器搜索,再将查找出的clss里的参数bth72放进后,可以定位到【预订】
zdh.ele('css:#train_date').clear()#清除原有时间
zdh.ele('css:#train_date').input(Sj)#定位出发时间输入框
zdh.ele('css:#query_ticket').click() #定位查询按钮
zdh.ele(f'css:#queryLeftTable tr:nth-child({int(Page_num)*2-1}) .btn72').click()#购票选择均为奇数
③之后,进入if分支,通过CSS选择器 #login_user 定位到登录状态显示的元素,并获取其文本内容。如果文本内容为“登录”,则表示用户未登录。如果用户已经登录(即登录状态不是“登录”),则打印“已登录”信息。
如果用户未登录(这里默认用户是我),将我的手机号以及账户密码通过定位到对应位置后填入,之后同样定位将我的证件号键入,验证码与用户进行交互,用户输入后,使用CSS选择器 #code 定位到验证码输入框,并输入用户输入的验证码。然后使用CSS选择器 #sureClick 定位到确认按钮,并模拟点击操作,提交验证码。
#判断用户是否登录
login=zdh.ele('css:#login_user').text
if login=="登录":
#定位用户登录界面
zdh.ele('css:#J-userName').input("189****9927")
zdh.ele('css:#J-password').input("***********")
zdh.ele("css:#J-login").click()
zdh.ele('css:#id_card').input("****")
zdh.ele('css:#verification_code').click()
yzm = int(input("请输入您的验证码:"))
zdh.ele('css:#code').input(yzm)
zdh.ele('css:#sureClick').click()
else:
print("已登录")
5.拼音转换
在dzl.move_to('css:#fromStationText').click().type("chengshi") 城市输入只允许输入拼音,通过定义language(Chinese)函数,完成将中文转化为拼音。
def language(Chinese):
zw=pinyin(Chinese,style=Style.NORMAL)#使用不带声调的拼音形式
string="".join([t[0]for t in zw])#通过循环遍历提取每个字的拼音组成字符串
return string
(5)合并两部分功能
在函数过程中,分别完成了通过爬虫获取车票信息以及通过CSS选择器定位输入完成购票,接下来,只需要将两部分代码逻辑缝补到一起,就可以实现为用户(我)自动购票。
定义购票函数,将出发地,目的地以及票的序号作为参数,并将输入框内容用参数传递
def buy(Go_city,To_city,Sj,Page_num):
#定义变量名用于自动打开浏览器
zdh=ChromiumPage()
dzl=Actions(zdh)
#自动打开网页地址
zdh.get('https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E5%8C%97%E4%BA%AC,BJP&ts=%E4%B8%8A%E6%B5%B7,SHH&date=2025-05-31&flag=N,N,Y')
#元素定位
dzl.move_to('css:#fromStationText').click().type(language(Go_city)) #定位出发地输入框元素
zdh.ele('css:#fromStationText').input(Keys.ENTER)
dzl.move_to('css:#toStationText').click().type(language(To_city)) #定位目的地输入框元素
zdh.ele('css:#toStationText').input(Keys.ENTER)
zdh.ele('css:#train_date').clear()#清除原有时间
zdh.ele('css:#train_date').input(Sj)#定位出发时间输入框
zdh.ele('css:#query_ticket').click() #定位查询按钮
zdh.ele(f'css:#queryLeftTable tr:nth-child({int(Page_num)*2-1}) .btn72').click()#购票选择均为奇数
#判断用户是否登录
login=zdh.ele('css:#login_user').text
if login=="登录":
#定位用户登录界面
zdh.ele('css:#J-userName').input("dianhua")
zdh.ele('css:#J-password').input("mima")
zdh.ele("css:#J-login").click()
zdh.ele('css:#id_card').input("shenfenzheng")
zdh.ele('css:#verification_code').click()
yzm = int(input("请输入您的验证码:"))
zdh.ele('css:#code').input(yzm)
zdh.ele('css:#sureClick').click()
else:
print("已登录")
之后,在代码中调用buy函数
buy(Go_city=gocity,To_city=tocity,Sj=sj,Page_num=page_num)
(6)实验结果
(7)上传git

3. 实验过程中遇到的问题和解决过程
- 问题1:修改url时城市不能直接传入
- 问题1解决方案:查询资料,用户在go_city输入中文需要与city中的城市代码匹配,中文不能进入网址,用city作为键取它的一个值如city[go_city]可以解决该问题
- 问题2:如果验证码以0开头,0无法被读入会导致无法进行下一步
![]()
- 问题2解决方案:删除验证码部分的‘int’类型转换即可
![]()
- 问题3:安装库的过程遇到问题,在本地终端安装却始终提示缺少库
- 问题3解决方案 :Kimi并没有给出靠谱的做法,但通过b站相关up的教程,我在pycharm中找到了设置->python解释器->点击+安装的方法,经实践,此方法可行有效。
- 问题4:添加buy函数后,始终提示有未定义的变量
- 问题4解决方案:结合c语言的学习经验,函数参数的传递不正确可能导致这样的现象,经检查,我忘记在函数后的内容缩进了o(╥﹏╥)o,尽管这不是一个大的错误,很长一段时间我始终没有察觉。
- 问题5: 通过资料我了解pinyin可以将汉字转化为为拼音(甚至可以带音调),类似这样
zw=pinyin(Chinese,style=Style.NORMAL)
但是在实践过程却无法正常运行(城市无法转化)
- 问题5解决办法:设置断点进行调试,将代码中zw(中文)打印出来,发现打印出的结果并不只是拼音,还包含括号(此处未截图留存o(╥﹏╥)o)
向Kimi寻求解决办法,增添string="".join([t[0]for t in zw])语句遍历提取每个字的拼音组成字符串,测试可以正常提取拼音。 - 问题6 时间处理问题。在网页信息处理时,需要输入出发时间,但由于网页默认含有出发时间,所以会造成出发时间叠罗汉,无法正常购票。
![]()
- 问题6解决办法:多了信息只能删,我就去搜,“怎么样才能删掉网页搜索框里的信息呀?”如下
![]()
我增添命令zdh.ele('css:#train_date').clear(),该问题有效解决。 - 问题7 在测试过程中,由于我多次“购票”,但并不是每次都会把浏览器关掉退出登录让它来操作,所以在已登录的前提下,程序继续执行未登录的输入,会在原来的物理位置尝试键入但无法,最终尽管不影响程序运行,但不符合正常逻辑
- 问题7解决办法:使用if来判断登录出提示的是“登录”还是用户名(即不是登录),从而进行两个分支。
其他(感悟、思考等)
起因是五一期间大家都在感慨票好难抢,演唱会的门票抢不到,12306的票也抢不到,想到python大作业的事情,我大概有了做一个抢票程序的想法,但当时并不觉得自己能真的做出来。在未接触爬虫等内容前,我甚至认为微信自动点赞,获取网页信息,自动点赞朋友圈等行为只是靠代码简直太难,我起码得再修炼三年五年才可能实现,但通过本次实操,我对python的兴趣有增无减。由于12306的登录正好没有人机测试,所以程序几乎没有遇到类似第三方阻拦这样的事情,也没有什么黑客之类的刺激电影画面,整个过程除了电脑温度越来越高几乎没有热血的部分,但最后看到进入购票的页面
我还是很开心。
尽管没有第三方阻拦,程序的编写也并不顺利。密密麻麻的信息真是淹没了我,尤其是“预订”那里没有id,尽管通过搜索找到解决办法,但仍然费了很大功夫,马虎也总是拦着我,城市的字符串由于输入的粘贴等问题出现错误,PyCharm每次报错只告诉我一个错误,又气又无奈;还有缩进,我总是喜欢写分号而总是忘了缩进。
不管怎么样,或许这个程序其实也不能真的抢了谁的票,或许它还有很多很多不足之处,我还是很满意。
课程总结
有句歌词叫什么,“得不到的就更加爱,太容易来的就不理睬。”简直完美刻画我对python课的心理:选课的时候,Python可是炙手可热,花费九牛二虎之力,我得意洋洋,我每分钟打开一次课表看一眼我是不是真的选上了,我一边安慰没选上的同学一遍窃喜着。
但真正上课的时候,恍惚着,我竟然忘记了,我只觉得三节课好长,我只觉得手机怎么会这么好玩,字符串模块还是正则表达式,从我的耳边轻轻吹过,从我的每一个毛孔无聊的略过,亲爱的志强老师,如果您看到了这里,请让我向您真诚的道歉吧,为我每一次的低头道歉,为我每一次的道貌岸然道歉。
如今,又到了“得不到”的时候,结课仓促,我们坐在一起,白板上明晃晃投影着“终于结课了!我跟你们一样的心情”,掌声雷动,Python,从我的生命悄悄路过又安静的谢了幕。
回忆过去的课程,我对Python有了粗浅的认识,由于有c语言相关映射,对于很多简单的输入输出,循环,条件等,我很快能够掌握,至于再深一些,Python所独有的,列表元组字典集合还有正则表达等等,就实在不是三心二意的听课所能完全掌握的了。这对我的实验三实验四完成设置了很大的障碍,所幸经过不懈努力还是完成了。时间过得真的太快,很难想象我的大一就要这样仓促结束,去年这会,我哪里敢想这么远,那些原以为难以逾越的关卡,输输赢赢停停走走,竟然已经全都度过了,不论是成功完成12306的购票,还是高考,还是面试,还是上完所有的Python课,竟然,竟然在时间面前完全都不是问题。
我始终认为评价课程,总结课程,永远绕不开任课老师。亲爱的王老师,该怎么形容您最贴切?初次见面复杂的签到手势,独特的讲义,不走寻常路的互动,猜数游戏,计算器设计……每次实验,您都会一步步引导,我总是手忙脚乱抄写,最后看着您发在群里的源代码陷入沉默。最令我印象深刻的是讲序列元组,讲stock,将各种,课程的每个环节您都努力的和我们产生联系,“××来和我石头剪刀布,输了挂科”“你们说是谁?彭彦祖?好我们写彭彦祖”“还有谁,报别人名字也行!”“连上了吗,连上了和我打电话,喂喂喂?”……不胜枚举,这似乎只是普普通通的小玩笑,但总让我更加感慨生活的美好。
感谢Python程序设计的选修课,感谢亲爱的王志强老师,感谢每一个星期三的晚上。
参考资料
《零基础学Python》





浙公网安备 33010602011771号