必威体育Betway必威体育官网
当前位置:首页 > IT技术

12306验证码识别初尝试(1)

时间:2019-08-11 16:43:40来源:IT技术作者:seo实验室小编阅读:52次「手机版」
 

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登录提示非法请求怎么办?

小编使用智行火车票、网易火车票等抢火车票登录12306账号,输入了正确的账号、密码和验证码的情况下,居然提示&ldquo;非法请求&rdquo

中国铁路客户服务中心---http://www.12306.cn

中国铁路客户服务中心---http://www.12306.cn

飞猪抢票成功率高吗?和12306哪个更好?

飞猪抢票成功率高吗。飞猪,原名阿里旅行,是阿里巴巴旗下,一个专攻旅游、机票、酒店预定住宿等服务的平台,自从上线以来,也确实收获了一

用深度学习破解12306图片验证码,识别率超96%- web效果

12306最新版图片验证码已被破解已是公开的秘密。比如市面上那么专业多抢票软件,早就破解了这个图片验证码。那他们到底是怎么实现

分享到:

栏目导航

推荐阅读

热门阅读