• 【爬虫】8.1. 使用OCR技术识别图形验证码


    使用OCR技术识别图形验证码

    图形验证码是最早出现的验证方式,现在依然很常见,一般由4位左右的字母或者数字组成。本章节使用的网站时https://captcha7.scrape.center/,这个网站的验证码相对来说比较平整,没有过多的干扰线和干扰点,文字也没有大幅度的变形和旋转,因此比较好作为案例进行分析,对于这类验证码,可以使用OCR技术识别。参考书籍依然是Python3网络爬虫开发实战(第三版)。

    1. OCR技术

    OCR,即Optical Character Recognition,中文叫做光学字符识别,是指使用电子设备(例如扫描仪和数码相机)检查打印再纸上的字符,通过检查暗、亮的模式确定字符形状,然后使用字符识别方法将形状转化位计算机文字。现在OCR技术已经广泛应用于生产活动中,如文档识别,证件识别,字幕识别,文档搜索等。当然用来识别本节所述的图形验证码也没有问题。

    2. 准备工作

    再本节的学习中需要导入tesserocr库,这个库的安装需要参考https://setup.scrape.center/tesserocr.另外,还需要安装Selenium、Pillow、Numpy和retrying库用来模拟登录、处理图像和重试操作,可以使用pip3工具安装这些库。 安装好这些库就可以开始了。

    2.1. tesserocr安装异常

    如果安装异常的话就换一个,可以参照我的,我用的库不是上面的,而是pytesseract,我觉得两者差别不大

    • 打开tesseract下载的网页 tesseract,下载最后一个(应该是)tesseract-ocr-w64-setup-v5.3.0.2.221214这个版本,接着就是安装,安装过程中自己记好自己安装在哪里!!!然后就是选择语言包,建议不要全选会下载很慢。
    • 将你记下来的安装路径的整个文件地址给添加到环境变量中去。
    • 接着python安装pytesseract,找到pytesseract.py文件,打开并找到tesseract_cmd这个变量(大约在30行左右)将里面的值修改为tesseract.exe文件的地址(这个文件在你一开始记下的文件地址里面,查找文件夹就找到了,不用进其他的文件夹,注意转义字符)。
    • 搞定上述之后在cmd窗口运行tesseract --list-langs可以看到你下载的语言包。
    • 重启,然后运行你的示例代码就行了,如果还不可以,那你去看其他下载教程

    3. 验证码图片爬取

    这个网页使用JavaScript渲染出来的,所以我们进行爬取的时候使用selenium自动化测试工具。

    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from PIL import Image
    from io import BytesIO
    import time
    
    def demo():
        browser = webdriver.Chrome()
        browser.get("https://captcha7.scrape.center")
        time.sleep(3)
        captcha = browser.find_element(By.CSS_SELECTOR,"#captcha")
        image = Image.open(BytesIO(captcha.screenshot_as_png))
        image.show()
    
    if __name__ == "__main__":
        demo()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这里使用了我很少见的BytesIO,这是一个类,它的功能是读取二进制数据流,而图片就是二进制数据流;还有就是captcha.screenshot_as_png这部分的功能就是将当前页面的内容捕获为一张图像,以bytes二进制数据保存;最后调用image的show方法来显式验证码的图像。

    4. 无障碍识别测试

    首先我们选用两张图片来进行测试,第一张是有换行和明显空格,第二张是一张验证码。
    在这里插入图片描述
    在这里插入图片描述

    我们运行下面代码:

    import pytesseract
    from PIL import Image
    image1 = Image.open("tesseract_tt1.png")
    result1 = pytesseract.image_to_string(image1)
    image2 = Image.open("tesseract_tt2.png")
    result2 = pytesseract.image_to_string(image2)
    print(result1, end= '')
    print("=========")
    print(result2, end= '')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    Demons
    Lin
    Ss ZzTU
    =========
    2034
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我们可以看到在输出SZTU这部分时候出现了SsZz这样大小写都输出的情况,这是因为pytesseract库在识别大小写字母时候很难准确识别出大小写,你可以采取其他办法来执行,这里就不列出来。

    5. 错误识别

    我选取到了一张图片,如下所示:
    在这里插入图片描述

    import pytesseract
    from PIL import Image
    image = Image.open("error.png")
    result = pytesseract.image_to_string(image)
    print(result, end= '')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    04-8 d.
    
    • 1

    可以看到这个输出结果明显不是我们想要的,这是因为OCR识别技术是通过检查暗、亮的模式确定字符形状,不是我们想当然的用脑子来看。所以,我们需要做一些额外处理,把干扰信息去掉,我们观察发现,图片里哪些造成干扰的点,其颜色大多比文本的颜色更浅,因此可以通过颜色将干扰点去掉。首先将保存的图片转化为数组,看一下维度:

    from PIL import Image
    import numpy as np
    image = Image.open("error.png")
    print(np.array(image).shape)
    print(image.mode)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    (38, 112, 4)
    RGBA
    
    • 1
    • 2

    从结果上可以看出,这个图片其实是一个三维数组,38和112代表图片的高和宽,4则是每个像素点的表示向量,那为什么是4呢?因为最后一维是一个长度为4的数组分别表示R(红)G(绿)B(蓝)A(透明度),即一个像素点由4个数字表示。那为什么是RGBA而不是RGB或者其他的呢?因为image.mode是RGBA,即由透明通道的真彩色。

    mode属性定义了图片的类型和像素的位宽,一共由9种类型:

    • 1:像素用1位表示,Python中表示为True或False,即二值化。
    • L:像素用8位表示,取值位0-255,表示灰度图像,数字越小,颜色越黑。
    • P:像素用8位表示,即调色板数据。
    • RGB:像素用3X8位表示,即真彩色。
    • RGBA:像素用4X8位标识,即有透明通道的真彩色。
    • CMYK:像素用4X8位表示,即印刷四色模式。
    • YCbCr:像素用3X8位表示,即彩色视频格式。
    • I:像素用32位整型表示。
    • F:像素用32位浮点型表示。

    为了方便处理,可以把RGBA转化位更简单的L,即把图片转化位灰度图像。往图片对象的convert方法中传入L即可,代码如下表示:

    image = image.convert('L')
    image.show()
    
    • 1
    • 2

    我们选择把图片转化位灰度图像,然后根据阈值删除图片上的干扰点,成功识别出验证码,代码如下:

    from PIL import Image
    import numpy as np
    
    image = Image.open("error.png")
    image = image.convert('L')
    threshold = 90
    array = np.array(image)
    array = np.where(array> threshold, 255, 0)
    image = Image.fromarray((array.astype('uint8')))
    # image.show()
    result = pytesseract.image_to_string(image)
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这里先将变量threshold赋值位50.它代表灰度的阈值。接着将图片转化位Numpy数组,利用Numpy的where方法对数组进行筛选和处理,其中将灰度大于阈值的图片的像素设置为255表示白色,否则为0,表示黑色。

    6. 识别实战:

    import time
    import re
    import pytesseract
    from selenium import webdriver
    from io import BytesIO
    from PIL import Image
    from retrying import retry
    from selenium.webdriver.support.wait import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.common.exceptions import TimeoutException
    from selenium.webdriver.common.by import By
    import numpy as np
    
    
    def preprocess(image):
        image = image.convert('L')
        array = np.array(image)
        array = np.where(array > 105, 255, 0)
        image = Image.fromarray(array.astype('uint8'))
        return image
    
    
    @retry(stop_max_attempt_number=10, retry_on_result=lambda x: x is False)
    def login():
        browser.get('https://captcha7.scrape.center/')
        browser.find_element(By.CSS_SELECTOR, '.username input[type="text"]').send_keys('admin')
        browser.find_element(By.CSS_SELECTOR, '.password input[type="password"]').send_keys('admin')
        captcha = browser.find_element(By.CSS_SELECTOR,'#captcha')
        image = Image.open(BytesIO(captcha.screenshot_as_png))
        image = preprocess(image)
        image.show()
        captcha = pytesseract.image_to_string(image)
        print(captcha)
        captcha = re.sub('[^A-Za-z0-9]', '', captcha)
        browser.find_element(By.CSS_SELECTOR, '.captcha input[type="text"]').send_keys(captcha)
        browser.find_element(By.CSS_SELECTOR, '.login').click()
        try:
            WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.XPATH, '//h2[contains(., "登录成功")]')))
            time.sleep(5)
            browser.close()
            return True
        except TimeoutException:
            return False
    
    
    if __name__ == '__main__':
        browser = webdriver.Chrome()
        login()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    7. 参数设置

    在使用 pytesseract 时,你可以使用以下参数:

    1. lang: 这个参数用于指定 OCR 使用的语言。默认为 ‘eng’,表示英文。如果你的验证码是英文的,那么你可以保持这个默认值。如果验证码是其他语言的,你需要指定相应的语言代码。例如,中文的语言代码是 ‘chi_sim’。
    2. config: 这个参数用于指定 tesseract 的配置文件。你可以使用它来调整 OCR 的行为。例如,你可以设置 tesseract 只识别数字和大写字母。
    3. nice: 这个参数用于指定 OCR 的质量。值的范围是 0-3,0 表示最快但质量最低,3 表示最慢但质量最高。默认值是 0。如果你的验证码很难识别,你可能需要将这个值设为 3。

    这些参数可以在调用 pytesseract.image_to_string 时通过关键字参数的方式指定。例如:

    captcha = pytesseract.image_to_string(image, lang='chi_sim', config='--psm 10', nice=3)
    
    • 1

    另外,你也可以使用 pytesseract.image_to_data 函数,它比 image_to_string 更灵活。image_to_data 函数返回一个包含了 OCR 结果的数据结构,你可以从这个数据结构中提取你需要的信息。例如,你可以提取每个单词的置信度,然后只保留置信度高的单词。

    还有其他的识别技巧可以学习,这里给出CSDN博客我觉得挺好的一篇:

    借助Tesseract-OCR进行文本检测(1)

    借助Tesseract-OCR进行文本检测(2)

  • 相关阅读:
    Sentinel 规则持久化,基于Redis持久化【附带源码】
    机器学习实战-系列教程7:SVM分类实战2线性SVM(鸢尾花数据集/软间隔/线性SVM/非线性SVM/scikit-learn框架)项目实战、代码解读
    Python进阶:btc逆向-数据采集-JS逆向
    LeetCode - 解题笔记 - 214 - Shortest Palindrome
    计算机网络课程期末大总结
    《基于FPGA的数字信号处理》专栏的导航与说明
    layui之会议OA系统4.0
    基于 Markdown 与 Git 的知识管理系统
    每日一题 2558. 从数量最多的堆取走礼物(简单,heapq)
    v-if、v-for、列表的过滤与排序、强制绑定class和style、收集表单信息
  • 原文地址:https://blog.csdn.net/likinguuu/article/details/132767318