20241309实验四《Python程序设计》实验报告
课程:《Python程序设计》
班级: 2413
姓名: 梅良谦
学号:20241309
实验教师:王志强
实验日期:2025年5月14日
必修/选修: 公选课
一、实验内容
(1)可选择的实验内容:
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
例如:编写从社交网络爬取数据,实现可视化舆情监控或者情感分析。
例如:利用公开数据集,开展图像分类、恶意软件检测等
例如:利用Python库,基于OCR技术实现自动化提取图片中数据,并填入excel中。
例如:爬取天气数据,实现自动化微信提醒
例如:利用爬虫,实现自动化下载网站视频、文件等。
例如:编写小游戏:坦克大战、贪吃蛇、扫雷等等
(2)我选择的实验内容:实现12306自动查询余票
迈入大学以后,12306的使用次数逐渐增多,无论是开学、放假抑或是旅游时,脑海中总是会浮现一个问题:有票吗?而无论是手机的铁路12306app还是电脑上的网站,查询起来总是有些许的不方便,而且容易出错,比如本人五一假期时原本打算去找高中同学嗨皮,但是因为错误输入了出发日期导致没有买到票,就很难受,整好想到python的最后一次实验,于是想要用python的爬虫相关知识设计一个12306的自动查询余票的程序。
二、 实验过程及结果
(一)、实验前的准备
1.pythoncharm安装requests库用于模拟浏览器向url地址发送请求

2.了解开发者工具的相关知识
3.获取各个城市对应的三字码的json文件,如下图:

4.考虑到用户的使用体验,安装prettytable模块,用于美化输出结果

(二)、实验实现的过程
1.获取车次信息:(爬虫)
通过浏览器抓包分析,车次信息在哪个url里面,通过开发者工具实现抓包,F12打开开发者工具,选择网络

点击网页查询按钮,得到返回的数据结果和标头信息


模拟浏览器对url地址发送请求
导入request模块,确定请求链接,发送请求,代码如下:
#导入数据请求模块
#导入数据请求模块
import requests
#确定请求链接
url = 'https://kyfw.12306.cn/otn/leftTicket/queryU?leftTicketDTO.train_date=2025-06-06&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=HFH&purpose_codes=ADULT'
#模拟伪装
headers = {'Cookie':'_uab_collina=172708454695846491222171; JSESSIONID=164EA4674060E17C470B28D05BC17B5E; _jc_save_wfdc_flag=dc; _jc_save_fromStation=%u5317%u4EAC%2CBJP; _jc_save_toDate=2025-06-06; guidesStatus=off; highContrastMode=defaltMode; cursorStatus=off; route=495c805987d0f5c8c84b14f60212447d; BIGipServerotn=1591279882.50210.0000; BIGipServerpassport=887619850.50215.0000; _jc_save_toStation=%u5408%u80A5%2CHFH; _jc_save_fromDate=2025-06-06',
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0'}
#发送请求
response = requests.get(url=url,headers=headers)
print(response)
打印response出现下面情况说明发送请求成功:

response.json()获取响应json字典数据
2.解析获取到的数据,提取想要的车次信息
字典取值,返回车次相关信息,如下图

为了提取到车次,出发时间等我想要得到的数据,决定先用split进行分割,得到列表后通过索引位置获取我们想要的信息,代码和结果如下:
for i in response.json()['data']['result']:
#分割,再用列表取值
print(i)
index = i.split('|')
a = 0
for j in index:
print(j,a,sep='--')
a +=1
print(index)
break

根据索引位置得到想要的车次的相关信息的代码和结果如下图:


2.实现查询功能
读取json文件得到的是字符串数据,将其转化为字典数据便于根据键值对取值,紧接着输入出发的城市、到达的城市以及耗时,并将这些参数替换到url中,实现查询功能,完整代码和结果截图如下图:
import json
f = open('city.json',encoding='utf-8')
json_data =json.loads(f.read())
chufacity = input('请输入出发城市:')
daodacity = input('请输入到达城市:')
data = input('出发时间:')
import requests
#确定请求链接
url = f'https://kyfw.12306.cn/otn/leftTicket/queryU?leftTicketDTO.train_date={data}&leftTicketDTO.from_station={json_data[chufacity]}&leftTicketDTO.to_station={json_data[daodacity]}&purpose_codes=ADULT'
#模拟伪装
headers = {'Cookie':'_uab_collina=172708454695846491222171; JSESSIONID=8BDFA5C38B37A367093477D4BDEE327F; _jc_save_wfdc_flag=dc; _jc_save_fromStation=%u5317%u4EAC%2CBJP; guidesStatus=off; highContrastMode=defaltMode; cursorStatus=off; _jc_save_toStation=%u5408%u80A5%2CHFH; BIGipServerotn=1524171018.24610.0000; BIGipServerpassport=988283146.50215.0000; route=6f50b51faa11b987e576cdb301e545c4; _jc_save_fromDate=2025-06-07; _jc_save_toDate=2025-06-07',
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0'}
#发送请求
response = requests.get(url=url,headers=headers)
for i in response.json()['data']['result']:
index = i.split('|')
checi = index[3]
chufatime1 = index[8]#出发时间
daodatime2 = index[9]#到达时间
usetime = index[10]#用时
tedengzuo = index[32]
yidengzuo = index[31]
erdengzuo = index[30]
yingwo = index[28]
yingzuo = index[29]
wuzuo = index[26]
ruanwo = index[23]
dit = {'车次': checi,
'出发时间': chufatime1,
'到达时间': daodatime2,
'用时': usetime,
'特等座': tedengzuo,
'一等座': yidengzuo,
'二等座': erdengzuo,
'软卧': ruanwo,
'硬卧': yingwo,
'硬座': yingzuo,
'无座': wuzuo,
}
print(dit)

3.美化输出结果
导入prettytable模块,实例化一个tb对象,最后的代码和输出结果如下:
import json
f = open('city.json',encoding='utf-8')
json_data =json.loads(f.read())
chufacity = input('请输入出发城市:')
daodacity = input('请输入到达城市:')
data = input('出发时间:')
import requests
#确定请求链接
import prettytable as pt#导入prettytable模块,让打印的表格更加美观
url = f'https://kyfw.12306.cn/otn/leftTicket/queryU?leftTicketDTO.train_date={data}&leftTicketDTO.from_station={json_data[chufacity]}&leftTicketDTO.to_station={json_data[daodacity]}&purpose_codes=ADULT'
#模拟伪装
headers = {'Cookie':'_uab_collina=172708454695846491222171; JSESSIONID=8BDFA5C38B37A367093477D4BDEE327F; _jc_save_wfdc_flag=dc; _jc_save_fromStation=%u5317%u4EAC%2CBJP; guidesStatus=off; highContrastMode=defaltMode; cursorStatus=off; _jc_save_toStation=%u5408%u80A5%2CHFH; BIGipServerotn=1524171018.24610.0000; BIGipServerpassport=988283146.50215.0000; route=6f50b51faa11b987e576cdb301e545c4; _jc_save_fromDate=2025-06-07; _jc_save_toDate=2025-06-07',
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0'}
#发送请求
response = requests.get(url=url,headers=headers)
tb = pt.PrettyTable()
tb.field_names = ['车次',
'出发时间',
'到达时间',
'用时',
'特等座',
'一等座',
'二等座',
'软卧',
'硬卧',
'硬座',
'无座',]
for i in response.json()['data']['result']:
index = i.split('|')
checi = index[3]
chufatime1 = index[8]#出发时间
daodatime2 = index[9]#到达时间
usetime = index[10]#用时
tedengzuo = index[32]
yidengzuo = index[31]
erdengzuo = index[30]
yingwo = index[28]
yingzuo = index[29]
wuzuo = index[26]
ruanwo = index[23]
dit = {'车次': checi,
'出发时间': chufatime1,
'到达时间': daodatime2,
'用时': usetime,
'特等座': tedengzuo,
'一等座': yidengzuo,
'二等座': erdengzuo,
'软卧': ruanwo,
'硬卧': yingwo,
'硬座': yingzuo,
'无座': wuzuo,
}
tb.add_row([checi,chufatime1, daodatime2,usetime, tedengzuo,yidengzuo, erdengzuo, ruanwo, yingwo,yingzuo, wuzuo,])
print(tb)

(三)、程序运行视频
视频录制和程序编写完成不在同一天因为失败太多次导致12306不给我发登录验证码了
三、实验改进
在完成上述的查票程序后,我突然想到12306本身的查票系统已经比较便捷,上述编写的程序可能用处没有想象中那么大,只是减少了打开网页和各个网页点击的步骤,因此我想到能否让程序自动运行浏览器,完成12306的账号登录和车次查询呢,如果可以实现,那么将会避开购票时繁琐的登录和查票环节(比较账号和密码十分容易遗忘),为此我又进行了下面的实验:
(一)、实验前的准备(学习了相关知识,下面只选取了程序中用到的)
1. Selenium 相关库
webdriver:Selenium 的核心模块,用于控制浏览器驱动。
By:提供多种元素定位方式。
WebDriverWait + EC:显式等待模块,用于智能等待页面元素加载或状态变更。
Keys:模拟键盘操作(用于后续的回车操作)。
2.time 库
用于添加固定等待时间,确保网页能够顺利加载完成。
3.显式等待原理
WebDriverWait(driver, timeout, poll_frequency=0.5):
timeout:最长等待时间(秒)
poll_frequency:检查条件的间隔时间(默认 0.5 秒)
until(method, message=''):持续检查
4.常见等待条件
element_to_be_clickable(locator):元素可点击。
5.定位器
(By.CSS_SELECTOR, '#J-userName'):使用 CSS 选择器定位元素,#J-userName 表示 ID 为 J-userName 的元素。
(二)、实验过程
1.导入各种库
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
2.驱动浏览器并打开12306登录界面
driver = webdriver.Edge()
driver.get('https://kyfw.12306.cn/otn/resources/login.html')
3.利用开发者工具获取元素id实现定位

4.模拟用户进行点击,输入等操作
try:
# 打开12306登录页面
driver.get('https://kyfw.12306.cn/otn/resources/login.html')
username_input = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '#J-userName'))
)
username_input.send_keys('17355018398')
driver.find_element(By.CSS_SELECTOR, '#J-password').send_keys('mlq061015')
driver.find_element(By.CSS_SELECTOR, '#J-login').click()
card_input = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '#id_card'))
)
card_input.send_keys('6016')
driver.find_element(By.CSS_SELECTOR, '#verification_code').click()
5.考虑到用户登录12306需要手机验证码,因此要求用户输入验证码,顺便输入出发城市,到达城市和到达日期
yanzhengma = input('输入手机验证码')
chufacity = input('请输入出发城市:')
daodacity = input('请输入到达城市:')
data = input('出发时间:')
driver.find_element(By.CSS_SELECTOR, '#code').send_keys(yanzhengma)
driver.find_element(By.CSS_SELECTOR, '#sureClick').click()
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#link_for_ticket'))
)
print("登录成功!")
time.sleep(5)
完成登录
6.点击车票预定,进行选中,清空选择框内的内容,重新输入,回车操作
driver.find_element(By.CSS_SELECTOR, '#link_for_ticket').click()
time.sleep(5)
driver.find_element(By.CSS_SELECTOR, '#fromStationText').click()
driver.find_element(By.CSS_SELECTOR, '#fromStationText').clear()
driver.find_element(By.CSS_SELECTOR, '#fromStationText').send_keys(chufacity)
driver.find_element(By.CSS_SELECTOR, '#fromStationText').send_keys(Keys.ENTER)
driver.find_element(By.CSS_SELECTOR, '#toStationText').click()
driver.find_element(By.CSS_SELECTOR, '#toStationText').clear()
driver.find_element(By.CSS_SELECTOR, '#toStationText').send_keys(daodacity)
driver.find_element(By.CSS_SELECTOR, '#toStationText').send_keys(Keys.ENTER)
driver.find_element(By.CSS_SELECTOR, '#train_date').click()
driver.find_element(By.CSS_SELECTOR, '#train_date').clear()
driver.find_element(By.CSS_SELECTOR, '#train_date').send_keys(data)
driver.find_element(By.CSS_SELECTOR, '#train_date').send_keys(Keys.ENTER)
driver.find_element(By.CSS_SELECTOR, '#query_ticket').click()
实现查询功能
7.设计异常情况处理的代码
except Exception as e:
print(f"登录失败: {e}")
finally:
input("按回车键关闭浏览器...")
driver.quit()
(三)、实现代码和运行视频截图如下:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
driver = webdriver.Edge()
try:
# 打开12306登录页面
driver.get('https://kyfw.12306.cn/otn/resources/login.html')
username_input = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '#J-userName'))
)
username_input.send_keys('17355018398')
driver.find_element(By.CSS_SELECTOR, '#J-password').send_keys('mlq061015')
driver.find_element(By.CSS_SELECTOR, '#J-login').click()
card_input = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '#id_card'))
)
card_input.send_keys('6016')
driver.find_element(By.CSS_SELECTOR, '#verification_code').click()
yanzhengma = input('输入手机验证码')
chufacity = input('请输入出发城市:')
daodacity = input('请输入到达城市:')
data = input('出发时间:')
driver.find_element(By.CSS_SELECTOR, '#code').send_keys(yanzhengma)
driver.find_element(By.CSS_SELECTOR, '#sureClick').click()
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#link_for_ticket'))
)
print("登录成功!")
time.sleep(5)
driver.find_element(By.CSS_SELECTOR, '#link_for_ticket').click()
time.sleep(5)
driver.find_element(By.CSS_SELECTOR, '#fromStationText').click()
driver.find_element(By.CSS_SELECTOR, '#fromStationText').clear()
driver.find_element(By.CSS_SELECTOR, '#fromStationText').send_keys(chufacity)
driver.find_element(By.CSS_SELECTOR, '#fromStationText').send_keys(Keys.ENTER)
driver.find_element(By.CSS_SELECTOR, '#toStationText').click()
driver.find_element(By.CSS_SELECTOR, '#toStationText').clear()
driver.find_element(By.CSS_SELECTOR, '#toStationText').send_keys(daodacity)
driver.find_element(By.CSS_SELECTOR, '#toStationText').send_keys(Keys.ENTER)
driver.find_element(By.CSS_SELECTOR, '#train_date').click()
driver.find_element(By.CSS_SELECTOR, '#train_date').clear()
driver.find_element(By.CSS_SELECTOR, '#train_date').send_keys(data)
driver.find_element(By.CSS_SELECTOR, '#train_date').send_keys(Keys.ENTER)
driver.find_element(By.CSS_SELECTOR, '#query_ticket').click()
except Exception as e:
print(f"登录失败: {e}")
finally:
input("按回车键关闭浏览器...")
driver.quit()
视频录制和程序编写完成不在同一天因为失败太多次导致12306不给我发登录验证码了
四、实验中遇到的问题
本次实验的过程真的遇到了很多很多问题,即使提前学习了相关的知识,浏览了相关内容的视频,在真正编写代码的过程中还是漏洞百出,以至于这次实验拖了这么长时间才提交,一方面是因为我的准备时间没有把握好,学习前置的知识用的时间过长,另一方面则是因为编写代码的过程频频出错,甚至现在的这份实验报告只能算是一份残缺版吧,因为本来是打算给第一个查票程序设计一个精美的图形界面的,但是最后运行以后总是无法得到想要的效果,我尝试过用TKinter和pYQt5,但是都没有把图形界面很好的呈现出来,可能是因为功夫还没有学到家吧,我也没有打算随便做一个简陋的图形界面交到实验里,不然这么多天也是白学了,所以还是决定先把图形界面删掉,等暑假的时候再给补上,下面是一些遇到的问题(很多很多,这里只举三例代表一下):
问题一:获取网站信息时无法成功获取;
解决方案:发现是因为12306网站有反爬,需要模拟用户。
问题二:不知道如何从获取的信息中截取想要的信息;
解决方案:通过观看b站的视频,想到分割得到列表后用索引来提取想要的信息。
问题三:在进行第二个实验时,发现程序运行时浏览器一打开后立马关闭;
解决方案:询问ai后得知是因为程序运行完后自动结束了。
五、实验感想
这次实验是我第一次尝试运用所学的知识去解决生活中的问题,真正的让知识服务于生活,无论是一开始选择实验内容时的迷茫,担心自己能力达不到无法完成,还是实验过程中一次又一次的程序报错和运行结果出错,抑或是最后实验完成程序运行成功的喜悦和激动,我认为都是我这一学期的python课程的一种收获,相信有了这一次的实践,以后我也会多多思考能否用python去解决生活中的各种各样的问题,感觉第一次头一次真正进入了互联网的世界!
六、课程总结
一学期的python课程转眼间就落下了帷幕,依稀记得当初上学期期末选课的时候,在众多的课程中看到了这门python公选课,觉得无论如何也要学一学,不然容易跟不上时代啥的,也希望能学到一些真正实用的知识,在朋友面前装一装也挺好的,就是在这种既可笑又有看可取之处(个人认为)的想法下,我拉着几个人一起选了王老师的这门python课程,事实证明,当初的选择是有几分智慧的,一学期的课程下来我确实学到了很多,不仅是因为王老师幽默轻松的课堂氛围让我有亦师亦友的感觉,更是因为我感觉到python确实是一门很有趣的课程,虽然折磨人的时候也特别折磨人,但是是属于那种打一棒子给个甜枣的类型,不得不说程序运行成功的“甜枣”确实很有吸引力。当然在学习的过程中也十分感谢王老师对我的关心和指导,尤其印象深刻的是这学期的中旬,那时候因为院团学的工作比较多,耽误了课程的学习,中途几次做实验做不明白,去找王老师请教的时候被您当场看出了我操作的生疏,告诫我要好好学习,于是我向您解释了我学习落后的原因,当时的我正在被院团学工作和学习两者之间的关系所困扰,您明确的告诉我要先把学习抓好,大学生的本职任务就是学习。事后我认真思考了老师的话,最终将重心慢慢的偏移到学业上,也逐渐跟上了课程,但是院团学的工作我也没有放下,我发现专注学习后工作的效率也提升了不少,可能是因为学习使得我更加专注,不容易被外界打扰吧,总之我自认为勉强做到了学习与工作的平衡,十分感谢王老师在我迷茫时的指导。如果以后还有王老师的选修课,还是希望能跟王老师再续师生情。最后也是搬上最后一节课的图片,希望自己以后脚踏实地,不断奋斗,未来繁花似锦,也祝愿王老师以后工作顺利,幸福美满。借用我的桌面的一句话,希望日子和我们都一直熠熠生辉


浙公网安备 33010602011771号