12306验证码识别
首先,贴代码(尚未完成,仅供参考):
# -*- coding: utf-8 -*-
"""
@author: Steve
"""
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
# 导入Keys 模块
from selenium.webdriver.common.keys import Keys
from splinter.browser import Browser
from time import sleep
from PIL import Image
from PIL import ImageFilter
#import urllib #这是Python2的用法
#import urllib2 #这是python2的用法
import urllib.request
import re
import json
import ssl
import traceback
import time, sys
import winsound
if hasattr(ssl, '_create_unverified_context'):
ssl._create_default_https_context = ssl._create_unverified_context
UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) APPleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36"
#pic_url = "https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=login&rand=sjrand&0.21191171556711197"
def get_img(pic_url):
resp = urllib.request.urlopen(pic_url)
raw = resp.read()
with open("./tmp.jpg", 'wb') as fp:
fp.write(raw)
return Image.open("./tmp.jpg")
def get_sub_img(im, x, y):
assert 0 <= x <= 3
assert 0 <= y <= 2
WITH = HEIGHT = 68
left = 5 + (67 + 5) * x
top = 41 + (67 + 5) * y
right = left + 67
bottom = top + 67
return im.crop((left, top, right, bottom))
def baidu_stu_lookup(im):
url = "http://stu.baidu.com/n/image?fr=html5&needRawImageUrl=true&id=WU_FILE_0&name=233.png&type=image%2Fpng&lastModifiedDate=Mon+Mar+16+2015+20%3A49%3A11+GMT%2B0800+(CST)&size="
im.save("./query_temp_img.png")
raw = open("./query_temp_img.png", 'rb').read()
url = url + str(len(raw))
req = urllib.request.Request(url, raw, {'content-Type': 'image/png', 'User-Agent': UA})
resp = urllib.request.urlopen(req)
resp_url = resp.read() # return a pure url
url = "http://stu.baidu.com/n/searchpc?queryImageUrl=" + urllib.request.quote(resp_url)
req = urllib.request.Request(url, headers={'User-Agent': UA})
resp = urllib.request.urlopen(req)
html = resp.read()
return baidu_stu_html_extract(html)
def baidu_stu_html_extract(html):
# pattern = re.compile(r'<script type="text/javascript">(.*?)</script>', re.DOTALL | re.MULTILINE)
pattern = re.compile(r"keywords:'(.*?)'")
#pattern = pattern.decode('utf-8') # https://blog.csdn.net/zoulonglong/article/details/78547191
html = html.decode('utf-8')
matches = pattern.findall(html)
if not matches:
return '[UNKNOWN]'
json_str = matches[0]
json_str = json_str.replace('\\x22', '"').replace('\\\\', '\\')
# print json_str
result = [item['keyword'] for item in json.loads(json_str)]
return '|'.join(result) if result else '[UNKNOWN]'
def ocr_question_extract(im):
# [email protected]:madmaze/pytesseract.git
global pytesseract
try:
import pytesseract
except:
print("[ERROR] pytesseract not installed")
return
im = im.crop((127, 3, 260, 22))
im = pre_ocr_processing(im)
im.show()
return pytesseract.image_to_string(im, lang='chi_sim').strip()
def pre_ocr_processing(im):
im = im.convert("RGB")
width, height = im.size
white = im.filter(ImageFilter.BLUR).filter(ImageFilter.MaxFilter(23))
grey = im.convert('L')
impix = im.load()
whitepix = white.load()
greypix = grey.load()
for y in range(height):
for x in range(width):
greypix[x, y] = min(255, max(255 + impix[x, y][0] - whitepix[x, y][0],
255 + impix[x, y][1] - whitepix[x, y][1],
255 + impix[x, y][2] - whitepix[x, y][2]))
new_im = grey.copy()
binarize(new_im, 150)
return new_im
def binarize(im, thresh=120):
assert 0 < thresh < 255
assert im.mode == 'L'
w, h = im.size
for y in range(0, h): # for y in xrange(0, h): # xrange只用于python 2
for x in range(0, w): # for x in xrange(0, w): # xrange只用于python 2
if im.getpixel((x, y)) < thresh:
im.putpixel((x, y), 0)
else:
im.putpixel((x, y), 255)
class huoche(object):
"""docstring for huoche"""
#dr = webdriver.Chrome() #此命令可以成功打开chrome
driver_name = ''
executable_path = ''
# 用户名,密码
username = u"[email protected]"
passwd = u"xxxx"
# cookies值,需自行寻找
#starts = u"%u6B66%u6C49%2CWHN" # 武汉
#ends = u"%u9EBB%u57CE%2CMCN" # 麻城
starts = u"%u5317%u4EAC%2CBJP"
ends = u"%u9A7B%u9A6C%u5E97%u897F%2CZLN"
# 时间格式2018-01-19
dtime = u"2019-01-16"
# 车次,选择第几趟,0则从上之下依次点击
order = 0
###乘客名
users = [u"王笨","李莎莎"]
##席位
xb = u"二等座"
pz = u"成人票"
"""网址"""
ticket_url = "https://kyfw.12306.cn/otn/leftTicket/init"
login_url = "https://kyfw.12306.cn/otn/login/init"
initmy_url = "https://kyfw.12306.cn/otn/view/index.html"
buy = "https://kyfw.12306.cn/otn/confirmPassenger/initDc"
login_url = 'https://kyfw.12306.cn/otn/login/init'
def __init__(self):
self.driver_name = 'chrome'
self.executable_path = 'C:\Python27\chromedriver.exe'
def login(self):
self.driver.visit(self.login_url)
#sleep(9)
# 填充密码
self.driver.fill("loginUserDTO.user_name", self.username)
# sleep(1)
self.driver.fill("userDTO.password", self.passwd)
print(u"等待验证码,自行输入...")
#验证码部分
# 定位到要右击的元素
#qqq = self.driver.driver.find_element_by_xpath("//*[@id='loginForm']/p/ul[2]/li[4]/p/p/p[3]").text
pic = self.driver.driver.find_element_by_xpath("//*[@id='loginForm']/p/ul[2]/li[4]/p/p/p[3]/img")
#sleep(3)
pic_src = pic.get_attribute('src')
# image = pic.find_element_by_tag_name("img")
#pic_src = pic.value_of_css_property('background-color')
#pic_src = pic.getText()
print('url is: %s' % (pic_src))
#print(pic.get_attribute("alt"))
# 对定位到的元素执行鼠标右键操作
#qqq2 = ActionChains(self.driver).context_click(qqq).perform()
#ActionChains(self.driver).contextClick(qqq).sendKeys(Keys.ARROW_DOWN).build().perform()
#actionChains = ActionChains(self.driver)
#actionChains.context_click(qqq).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform()
#action = ActionChains(self.driver)
#action.move_to_element(qqq) # 移动到该元素
#action.context_click(qqq) # 右键点击该元素
#action.send_keys(Keys.ARROW_DOWN) # 点击键盘向下箭头
#action.send_keys('v') # 键盘输入V保存图
#action.perform() # 执行保存
im = get_img(pic_src)
print('OCR Question: ',ocr_question_extract(im))
for y in range(2):
for x in range(4):
im2 = get_sub_img(im, x, y)
result = baidu_stu_lookup(im2)
print(y, x), result
while True:
if self.driver.url != self.initmy_url:
sleep(1)
else:
break
def start(self):
self.driver = Browser(driver_name=self.driver_name, executable_path=self.executable_path)
self.driver.driver.set_window_size(1400, 1000)
self.login() # 此Login指上面的login函数
# sleep(1)
self.driver.visit(self.ticket_url) # 打开12306界面
try:
print(u"购票页面开始...")
# sleep(1)
# 加载查询信息
self.driver.cookies.add({"_jc_save_fromStation": self.starts})
self.driver.cookies.add({"_jc_save_toStation": self.ends})
self.driver.cookies.add({"_jc_save_fromDate": self.dtime})
self.driver.reload() # 重新加载cookies
# 添加车次类型。放在加载之后,因为实测发现比如勾选"GC-高铁/城际"checkbox后,什么都不干页面就会自动变化
l = ['GC-高铁/城际', 'D-动车'] # 在列表里可以去掉不需要的车次类型
for i in l:
btn = self.driver.find_by_text(i)
btn.click()
i = 0 # 上面的for循环使用了i,这里如果不清零,下面因为也用到了i,会报错。
count = 0
if self.order != 0:
while self.driver.url == self.ticket_url: # 判断是否成功跳转到目标页面
self.driver.find_by_text(u"查询").click()
count += 1
print(u"循环点击查询... 第 %s 次" % count)
# sleep(1)
try:
self.driver.find_by_text(u"预订")[self.order - 1].click()
except Exception as e:
print(e)
print(u"还没开始预订")
continue
else:
while self.driver.url == self.ticket_url:
self.driver.find_by_text(u"查询").click()
count += 1
print(u"循环点击查询... 第 %s 次" % count)
# sleep(0.8)
try:
for i in self.driver.find_by_text(u"预订"):
i.click()
sleep(1)
except Exception as e:
print(e)
print(u"还没开始预订 %s")
continue
print(u"开始预订...")
# sleep(3)
# self.driver.reload()
sleep(1)
print(u'开始选择用户...')
for user in self.users:
self.driver.find_by_text(user).last.click()
print(u"提交订单...")
sleep(1)
# self.driver.find_by_text(self.pz).click()
# self.driver.find_by_id('').select(self.pz)
# # sleep(1)
# self.driver.find_by_text(self.xb).click()
# sleep(1)
self.driver.find_by_id('submitOrder_id').click()
# print u"开始选座..."
# self.driver.find_by_id('1D').last.click()
# self.driver.find_by_id('1F').last.click()
sleep(1.5)
print(u"确认选座...")
self.driver.find_by_id('qr_submit_id').click()
# 连续发出提示音
while True:
winsound.Beep(300, 1000)
except Exception as e:
print(e)
cities = {'成都': '%u6210%u90FD%2CCDW',
'重庆': '%u91CD%u5E86%2CCQW',
'北京': '%u5317%u4EAC%2CBJP',
'广州': '%u5E7F%u5DDE%2CGZQ',
'杭州': '%u676D%u5DDE%2CHZH',
'宜昌': '%u5B9C%u660C%2CYCN',
'郑州': '%u90D1%u5DDE%2CZZF',
'深圳': '%u6DF1%u5733%2CSZQ',
'西安': '%u897F%u5B89%2CXAY',
'大连': '%u5927%u8FDE%2CDLT',
'武汉': '%u6B66%u6C49%2CWHN',
'上海': '%u4E0A%u6D77%2CSHH',
'麻城': '%u9EBB%u57CE%2CMCN',
'临沂': '%u4E34%u6C82%2CLVK',
'驻马店西': '%u9A7B%u9A6C%u5E97%u897F%2CZLN'}
if __name__ == '__main__':
huoche = huoche()
#huoche.starts = cities[sys.argv[1]]
#huoche.ends = cities[sys.argv[2]]
#huoche.dtime = sys.argv[3]
huoche.start()
首先,我在寻找验证码图片的url上花费了不少时间,终于在别人的基础上写出了正确的代码:
注意这个img:
结果如下:
上述代码的健壮性不够好,我的感觉是大概运行五次出一次问题,打印不出url,因为没有找到。
又出现了这个问题:
解决方法:原因是python 2的程序用在了python 3中。
python 2中的写法是:
python 3中得这样写:
然后就进入了12306验证码识别的第一个核心阶段:识别提示文字,例如:
在上面的程序中有这样一句报错:
因此在网上找到了pytesseract安装的正确方法:
第一步,安装pytesseract。这应该是我的火车票抢票程序安装的第一个东西:
第二步:安装Tesseract-OCR.(安装包下载地址与安装方法:https://jingyan.baidu.com/article/219f4bf788addfde442d38fe.html)
下载过程中,系统提醒我有个包没下载(好像是equ),照着下文的提示下载并放到了下文所说的地址:
https://github.com/tesseract-ocr/tessdata
放到我的这个地址:
D:\Program Files (x86)\Tesseract-OCR\tessdata
下载并安装之后,发现Tesseract并未自动注册路径,因此在Path中加入了Tesseract的路径,然后就可以在CMD中输入tesseract了:
并且注册了TESSDATA_PREFIX这个系统变量(路径是安装,值为: D:\Program Files (x86)\Tesseract-OCR\tessdata)这个必须注册,并且值是…\tessdata,不然会报错找不到chi_sim等等!:
我的pytesseract.py的路径为:
C:\Users\AdMinistrator\AppData\local\Programs\Python\Python37\Lib\site-packages\pytesseract
打开pytesseract.py后,将tessract_cmd='tesseract’改为正确的路径(注意斜杠是右斜杠,而不是上面的左斜杠):
最后按照此文中的方法,截图验证了tesseract已经安装成功(https://jingyan.baidu.com/article/219f4bf788addfde442d38fe.html):
然后,pycharm中必须install pytesseract才能import pytesseract:
安装完毕,试验的时候发现print了奇怪的字符,再在cmd中试验,发现tesseract基本识别不出12306验证码中的中文字符,网上查询说需要训练。哈哈,还是躲不过人工智能这一步啊。此外,截图也有问题,明显没有截取完全:
从此,我的悲催生活就开始了。。。主要是查询发现,如果要训练,需要下载jtessboxeditorfx来生成tesseract-orc的字典,务必选择带FX的版本,才支持中文字符编辑。但是这个jtessboxeditorfx实在是不好用,因为我使用过250张12306的验证码截图,每张截图中的每个字都得在jtessboxeditorfx一点一点地手工修改,光修改就花了我六个小时,而这六个小时过去后,发现训练结果仍然很差。 我十分想找到更好的训练方法来训练tesseract,如果不行,就只能另寻其他方法了。
jTessBoxEditor的作用:tesseract生成的 “.box”文件中列出了每个字符在图片上的位置以及内容, jTessBoxEditor的作用就是用来调整每个字符在图片上的位置以及内容的。(参考:https://www.cr173.com/soft/432051.html)
我开始以为训练是很容易的,就只截取了五张图来训练:
旧版的jTessBoxEditor只能处理后缀为 “.tif” 的图片,而2.0版本就可以处理 GPEG、PNG、BItmap等几乎所有格式的图片,不再需要对样本进行 “.tif” 格式的转化了。
jTessBoxEditor或jTessBoxEditorFX无需安装,双击文件夹中的train.BAT即可打开,将五张图片(jpg格式即可)–Tools–Merge TIFF成了一张:mjorcen.normal.expfinal.tif
将最终得到的tif文件转换为box文件,可见只识别出了一张:
此时我以为编辑box文件(把第四张图的内容(红框括起来的那部分)拷贝三次,注意最后一列的编号要变),然后在jtessboxeditor中操作后就ok了,没想到修改后还是不行(https://blog.csdn.net/dcrmg/article/details/78233459; https://wenku.baidu.com/view/6caccb76a31614791711cc7931b765ce05087aa7.html):
保存后,此时再用jTessEditorFX打开,竟然可以看到前三张都有框了(修改之前都没有),哈哈!:
此时必须修改character,X,Y,W,H。后面四项都无法直接输入,而必须要一点一点地改变,慢慢享受吧!
新建文件font_properties,并输入normal 0 0 0 0 0:
生成train文件,不知道这一步为什么前三页还是empty page,这里不修改了,后面用250张图片修改时加入了psm参数,就不会出现empty page了:
执行下列命令,生成字符集文件:
先执行第一条命令,再执行第二条命令,生成字典数据:
生成之后手工修改 clustering 过程生成的 4 个文件(inttemp、pffmtable、normproto、shapetable)的名称为 [lang].xxx。这里改为 mjorcen.inttemp、mjorcen.pffmtable、mjorcen.normproto、mjorcen.shapetable。
在终端执行此命令,合并数据文件:
将生成的 mjorcen.traineddata” 语言包文件拷贝到 tessdata 目录下,就可以用它来进行中文字符识别了。
现在试验一下,识别"茶几”这两个字,生成是生成了,但是字不正确:
再用tesseract自带的语言包试验一下,发现效果比自己训练出来的好,哈哈:
因此接下来多找点图片来训练,就刷新12306,得到了251张图片。
照着以下步骤做(主要是添加了psm参数,下面可以看到psm参数的巨大作用):
1.jTessEditorFX–Tools–Merge Tiff,将251张jpg文件合并成一个tif文件。
2.makebox。cmd中进入tif文件所在目录,并输入以下命令:
tesseract mjorcen.normal.exp0.tif mjorcen.normal.exp0 -l chi_sim --psm 7 batch.nochop makebox
3.jTessEditorFX–Box Editor–Open,打开tif文件,一张一张地把每个框都修改好。有一张图片实在修改不好,就放弃了。
4.新建文件font_properties(注意没有后缀!它没有文件类型,不是txt文件!),并输入normal 0 0 0 0 0
5.cmd中生成train文件:mjorcen.normal.exp0.tif mjorcen.normal.exp0 -l chi_sim --psm 7 nobatch box.train
6.cmd中生成字符集文件:unicharset_extractor mjorcen.normal.exp0.box
7.生成字典数据(先执行第一条命令,再执行第二条命令):
mftraining -F font_properties -U unicharset -O mjorcen.unicharset mjorcen.normal.exp0.tr
cntraining mjorcen.normal.exp0.tr
8.手工修改上面生成的 4 个文件(inttemp、pffmtable、normproto、shapetable)的名称为 [lang].xxx。这里改为 mjorcen.inttemp、mjorcen.pffmtable、mjorcen.normproto、mjorcen.shapetable.
9.cmd中合并数据文件: combine_tessdata mjorcen
10.将生成的 mjorcen.traineddata” 语言包文件拷贝到 tessdata 目录下,就可以用它来进行中文字符识别了。
试验一下:
cmd中输入 tesseract mjorcen.normal.exp0.jpg 1.txt -l mjorcen --psm 7
结果:
1.排风机档案袋 未能识别
2.档案袋 识别成 桃漏袋
3.药片 识别成 药片
4.茶几 识别成 茶几绿
5.拖把 识别成 跑把
6.狮子 识别成 独子口
7.订书机 识别成 灯书机
8.中国结 识别成 中口国结
可见识别效果非常之差,得想办法提高tesseract的中文识别效果,不行的话,只能弃用tesseract用其他的方法了。
在此说明一下psm的重要作用:
psm参数极为重要,上述第二步由于有psm参数,大获成功!makebox时成功率为100%,每一张都没有empty page了!
用jTessEditorFx打开来编辑box:
在没用psm参数时,第一张图片根本一个框都没有,这样的话,jTessEditorFX的"Insert”根本没法用,即一旦一个框都没有,那这张图片就废了,但是用了psm之后,所有的图片都有框!所以用jTessEditorFx打开可以来编辑所有图片的box
相关阅读
12306提示操作频率过快怎么办 12306提示您的操作频率
12306提示您的操作频率过快怎么办?当你登录12306官网准备购票时,发现页面提示您的操作频率过快。刷新了N次,仍是这样的提示。小编方
小编使用智行火车票、网易火车票等抢火车票登录12306账号,输入了正确的账号、密码和验证码的情况下,居然提示“非法请求&rdquo
中国铁路客户服务中心---http://www.12306.cn
中国铁路客户服务中心---http://www.12306.cn
飞猪抢票成功率高吗。飞猪,原名阿里旅行,是阿里巴巴旗下,一个专攻旅游、机票、酒店预定住宿等服务的平台,自从上线以来,也确实收获了一
用深度学习破解12306图片验证码,识别率超96%- web效果
12306最新版图片验证码已被破解已是公开的秘密。比如市面上那么专业多抢票软件,早就破解了这个图片验证码。那他们到底是怎么实现