从传感器到推理端:VLA 机器人 TCP 通信与 msgpack 序列化深度解析
2026/5/25 1:45:32
在网络应用中,验证码是防止自动化攻击的重要手段。12306作为中国铁路售票系统,其验证码设计尤为复杂,从早期的数字字母组合,到后来的图片点击,再到如今的智能验证,一直在与自动化脚本进行着"军备竞赛"。
12306抢票项目的验证码处理模块,完整实现了从验证码获取、保存到识别的全流程,支持手动输入和自动识别两种方式,是学习Python验证码处理的绝佳范例。本文将深入分析12306项目的验证码处理逻辑,探讨验证码识别的技术要点和实践经验。
12306项目的验证码处理主要由两个核心文件组成,形成了完整的处理链路:
获取验证码 -> 保存图片 -> 识别验证码 -> 转换坐标 -> 提交验证inter/GetPassCodeNewOrderAndLogin.py:负责验证码图片的获取与保存inter/GetRandCode.py:负责验证码的识别(手动/自动)和坐标转换该模块负责从12306服务器下载验证码图片,支持登录和订单两种场景。
# -*- coding=utf-8 -*-fromconfig.urlConfimporturlsdefgetPassCodeNewOrderAndLogin(session,imgType):""" 下载验证码 :param session: 会话对象,包含HTTP客户端和URL配置 :param imgType: 下载验证码类型,login=登录验证码,其余为订单验证码 :return: 验证码图片二进制数据或False(失败时) """try:# 根据场景选择对应的URLifimgType=="login":url=session.urls["getCodeImg"]else:url=session.urls["codeImgByOrder"]# 添加随机数参数,防止缓存url="{0}?{1}".format(url,"r={0}".format(session.httpClint.send(session.urls["randCode"])))# 发送请求获取验证码response=session.httpClint.opener.open(url,timeout=5)result=response.read()returnresultexceptExceptionase:print(u"获取验证码失败: {0}".format(e))returnFalse该模块实现了验证码的识别功能,支持手动输入和自动识别两种方式,并将识别结果转换为12306服务器要求的坐标格式。
# -*- coding=utf-8 -*-fromPILimportImagefromconfig.ticketConfimport_get_yamlfromdamatuCode.ruokuaiimportRClienttry:raw_input# Python 2exceptNameError:# Python 3raw_input=inputdefgetRandCode(is_auto_code,auto_code_type,result):""" 识别验证码 :param is_auto_code: 是否自动识别 :param auto_code_type: 自动识别类型(1:打码兔,2:若快) :param result: 验证码图片二进制数据 :return: 验证码坐标字符串或空字符串(失败时) """try:# 保存验证码图片到文件try:withopen('./tkcode.png','wb')asimg:img.write(result)print(u"验证码图片已成功保存到 ./tkcode.png")exceptExceptionase:print(u"保存验证码图片失败: {0}".format(e))return""ifis_auto_code:# 自动识别逻辑ifauto_code_type==1:print(u"打码兔已关闭, 如需使用自动识别,请使用若快平台 auto_code_type == 2")returnifauto_code_type==2:# 调用若快打码平台APIrc=RClient(_get_yaml()["auto_code_account"]["user"],_get_yaml()["auto_code_account"]["pwd"])Result=rc.rk_create(result,6113)# 6113是12306验证码类型if"Result"inResult:returncodexy(Ofset=",".join(list(Result["Result"])),is_raw_input=False)else:if"Error"inResultandResult["Error"]:print(u"打码平台错误: {0}, 请登录打码平台查看-http://www.ruokuai.com/client/index?6726".format(Result["Error"]))return""else:# 手动输入逻辑try:# 尝试打开验证码图片img=Image.open('./tkcode.png')print(u"正在打开验证码图片...")img.show()print(u"验证码图片已打开,请查看并输入")exceptExceptionase:print(u"打开验证码图片失败: {0}".format(e))print(u"请手动双击根目录下的 tkcode.png 文件查看验证码")# 调用手动输入函数returncodexy()exceptExceptionase:print(u"验证码处理异常: {0}".format(e))importtraceback traceback.print_exc()return""defcodexy(Ofset=None,is_raw_input=True):""" 获取验证码坐标 :param Ofset: 自动识别结果或None(手动输入时) :param is_raw_input: 是否手动输入 :return: 验证码坐标字符串 """ifis_raw_input:# 显示坐标提示print(u""" ***************** | 1 | 2 | 3 | 4 | ***************** | 5 | 6 | 7 | 8 | ***************** """)print(u"验证码分为8个,对应上面数字,例如第一和第二张,输入1, 2")print(u"如果是linux无图形界面,请使用自动打码,is_auto_code: True")print(u"如果没有弹出验证码,请手动双击根目录下的tkcode.png文件")Ofset=raw_input(u"输入对应的验证码: ")# 标准化输入格式Ofset=Ofset.replace(",",",")select=Ofset.split(',')# 坐标映射post=[]offsetsX=0# X坐标offsetsY=0# Y坐标forofsetinselect:ifofset=='1':offsetsY=77offsetsX=40elifofset=='2':offsetsY=77offsetsX=112elifofset=='3':offsetsY=77offsetsX=184elifofset=='4':offsetsY=77offsetsX=256elifofset=='5':offsetsY=149offsetsX=40elifofset=='6':offsetsY=149offsetsX=112elifofset=='7':offsetsY=149offsetsX=184elifofset=='8':offsetsY=149offsetsX=256else:passpost.append(offsetsX)post.append(offsetsY)# 转换为12306要求的坐标格式randCode=str(post).replace(']','').replace('[','').replace("'",'').replace(' ','')print(u"验证码识别坐标为{0}".format(randCode))returnrandCoderaw_input/input函数| 处理方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 手动输入 | 准确率高、无成本、无需依赖第三方服务 | 效率低、需要人工干预、不适合长时间运行 | 调试阶段、自动识别失败时 |
| 自动识别 | 效率高、无需人工干预、适合长时间运行 | 需要付费、准确率受平台影响、依赖网络 | 正式抢票、批量操作、无人值守 |
# 优化前:直接保存二进制数据withopen('./tkcode.png','wb')asimg:img.write(result)# 优化后:添加图片验证和压缩fromPILimportImagefromioimportBytesIOtry:# 验证图片完整性img=Image.open(BytesIO(result))img.verify()# 重新打开并保存,可选择压缩img=Image.open(BytesIO(result))img.save('./tkcode.png',optimize=True,quality=90)print(u"验证码图片已成功保存到 ./tkcode.png")exceptExceptionase:print(u"验证码图片无效: {0}".format(e))return""# 优化前:单次调用Result=rc.rk_create(result,6113)# 优化后:添加重试机制max_retries=3foriinrange(max_retries):try:Result=rc.rk_create(result,6113)if"Result"inResult:returncodexy(Ofset=",".join(list(Result["Result"])),is_raw_input=False)exceptExceptionase:print(u"自动识别重试 {0}/{1} 失败: {2}".format(i+1,max_retries,e))time.sleep(1)# 优化前:多个if-elif分支ifofset=='1':offsetsY=77offsetsX=40# ... 其他分支# 优化后:使用字典映射,更简洁高效coord_map={'1':(40,77),'2':(112,77),'3':(184,77),'4':(256,77),'5':(40,149),'6':(112,149),'7':(184,149),'8':(256,149)}ifofsetincoord_map:offsetsX,offsetsY=coord_map[ofset]12306项目的验证码处理模块展示了Python在验证码处理方面的强大能力,从图片获取、保存到识别、坐标转换,形成了完整的处理链路。通过学习这个项目,我们可以掌握以下核心技术:
随着AI技术的发展,验证码识别技术也在不断演进,从早期的OCR识别,到如今的深度学习模型,识别准确率不断提高。然而,12306的验证码也在不断升级,从简单的图片点击,到如今的多轮验证,形成了一场持续的"攻防战"。
对于开发者来说,掌握验证码处理的核心技术,不仅可以应对12306这样的特定场景,还可以将这些技术应用到其他需要验证码处理的场景中。无论是手动输入还是自动识别,关键是根据实际需求选择合适的处理方式,并不断优化和改进。
希望本文对你理解Python验证码处理有所帮助,祝你在验证码处理的道路上越走越远!
参考资料: