Python爬虫学习笔记-第十四课(Tesseract简介+多线程)

1. Tesseract的安装与使用

1.1 Tesseract简介

有时候阻碍程序爬虫的正是在登录或者请求数据时的图形验证码。因此,笔者这里简单介绍⼀种能将图片翻译成文字的技术。将图片翻译成文字⼀般被称为光学文字识别(Optical Character Recognition),简写为OCR。
目前,实现OCR的库不是很多,特别是开源的。因为这块存在⼀定的技术壁垒(需要大量数据的支撑、要求有一定的机器学习、深度学习算法知识等),并且如果做得好就具有很高的商业价值。这里介绍⼀个比较优秀的图像识别开源库:Tesserat。
Tesseract是目前公认最优秀、最准确的开源OCR库。其具有很高的识别度,也具有很高的灵活性,它可以通过训练识别任何字体。

1.2 Tesseract的安装

  • 下载安装包:
    Windows系统安装,在以下链接下载安装包:https://github.com/tesseract-ocr/
    具体安装过程如下:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    这里可以选是否下载额外的语言数据,大家根据自己的需求定,笔者这就不下载了。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在Python中使用Tesseract前,需先进行安装conda install pytesseract -n xxx

  • 设置环境变量:
    安装完成后,如果想要在命令行中使用Tesseract,那么应该设置环境变量。一般,Mac和Linux在安装的时候就默认已经设置好了。在Windows下需要把tesseract.exe所在的路径添加到PATH环境变量中。
    在这里插入图片描述
    为方便使用,还有⼀个需要设置的环境变量是:添加训练的数据文件路径。
    在这里插入图片描述

  • 验证安装:
    进⼊cmd,输入命令tesseract --version查看版本,正常运行则安装成功。
    在这里插入图片描述

1.3 Tesseract的使用

在命令行中使用Tesseract:
在这里插入图片描述
验证码图片如下:
在这里插入图片描述
结果保存到当前工作路径,自动保存为txt文本,其中内容如下:

7 3 6 4

在Python中使用Tesseract识别图形验证码:

import pytesseract
from PIL import Image # 是python图像处理库

# 指定tesseract的驱动
pytesseract.pytesseract.tesseract_cmd = r'D:\Program Files\Tesseract-OCR\tesseract.exe'
# 指定tesseract的训练数据的路径
tessdata_dir_config = r'--tessdata-dir "D:\Program Files\Tesseract-OCR\tessdata"'
# 从本地导入图片
image = Image.open('demo.jpg')
# 输出结果
print(pytesseract.image_to_string(image, lang='eng', config=tessdata_dir_config))

运行结果:
在这里插入图片描述

需要注意的是,Tesseract对于一些简单的图片识别率比较高,而对于一些比较复杂的图片验证码也是无能为力的。

2. 多线程的快速入门

2.1 多线程基本概念

多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。多线程是在同一时间需要完成多项任务时而出现的产物。
在日常生活中的许多事情是同时进行的,比如开车的时候,手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的。
爬虫为什么要学习多线程:为了提高爬虫的效率。

2.2 多线程的引出

通常情况下,代码是按顺序执行的:

import time
def sing():
    for i in range(3):
        print('John is singing.')
        time.sleep(1)

def dance():
    for i in range(3):
        print('John is dancing.')
        time.sleep(1)

if __name__ == '__main__':
    sing()
    dance()

运行结果:
在这里插入图片描述
上述结果显示,先完成唱歌任务,再执行跳舞任务,不符合期望的唱歌跳舞任务同时进行。

如何创建线程:

  1. 通过函数,使用Threading模块中的Thread类,在这个类中有一个target参数需要传递一个函数对象,而这个函数里面就是多线程的逻辑
  2. 通过类的方式,这个类需要继承threading.Thread,并实现(重写)run(),在run方法中实现多线程的逻辑。

以上两种方式都可以实现多线程,一般情况下我们会选择第二种方式,因为当多线程的逻辑较为复杂时,采用面向对象的方式编写代码会方便后续的维护以及复用

代码需求:创建多线程,线程里执行print语句。

import time
import threading

def demo():
    print('Hello, this is child thread.')

if __name__ == '__main__':
    for i in range(5):
        # 每执行一次循环,就创建一个线程
        t = threading.Thread(target=demo)
        t.start()
        time.sleep(1)

运行结果:
在这里插入图片描述

3. 通过函数创建线程

3.1 主线程与子线程

一般情况下,main()方法中执行的是主线程,Thread()创建的线程称为子线程。
示例代码:

import time
import threading

def demo():
    for i in range(5):
        print('Hello, this is child thread.')
        time.sleep(1)

if __name__ == '__main__':
    t = threading.Thread(target=demo)
    t.start() # 开始执行子线程
    print('Hello, this is main thread.')

运行结果:
在这里插入图片描述
上述结果中,主线程的print语句结束后,程序并没有退出,而是等到demo的for循环结束后再退出,说明主线程会等待子线程执行结束之后,主线程再结束。

如果希望子线程执行结束后,再执行主线程,可以使用join()方法。

if __name__ == '__main__':
    t = threading.Thread(target=demo)
    t.start() # 开始执行子线程
    t.join() # 等待子线程结束,再执行后续代码
    print('Hello, this is main thread.')

运行结果:
在这里插入图片描述
join()方法的作用是等待子线程执行结束后,再让主线程继续执行。

如果希望主线程执行结束就退出,而不想等待子线程,可以使用setDaemon()方法。

if __name__ == '__main__':
    t = threading.Thread(target=demo)
    t.setDaemon(True) # 不会等待子线程
    t.start() # 开始执行子线程
    print('Hello, this is main thread.')

运行结果:
在这里插入图片描述
现在,可以实现一边唱歌、一边跳舞的逻辑了:

import time
import threading

def sing():
    for i in range(3):
        print('John is singing.')
        time.sleep(1)

def dance():
    for i in range(3):
        print('John is dancing.')
        time.sleep(1)

def main():
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)
    t1.start()
    t2.start()

if __name__ == '__main__':
    main()

运行结果:
在这里插入图片描述

3.2 查看线程的数量

使用threading.enumerate()查看当前线程的数量,该方法返回的是所有当前存活的线程对象的列表。
示例代码:

import threading
def demo1():
    for i in range(5):
        print('This is demo1:%d' % i)

def demo2():
    for i in range(5):
        print('This is demo2:%d' % i)

def main():
    t1 = threading.Thread(target=demo1)
    t2 = threading.Thread(target=demo2)
    t1.start()
    t2.start()
    # 打印当前存活的线程
    print(threading.enumerate())

if __name__ == '__main__':
    main()

运行结果:
在这里插入图片描述
主线程打印的结果,列表里只有两个线程对象,分别为主线程和子线程2,原因是子线程1已经执行完毕,看不到想要的效果。

为了更好地看出线程存活的情况下,现做如下改进:

  1. 每个子线程执行时加入延时;
  2. 第二(一)个子线程增加循环次数;
  3. 主线程添加循环,查看线程存活数量。

改进后的代码:

import threading
import time

def demo1():
    for i in range(3):
        print('This is demo1:%d' % i)
        time.sleep(1)

def demo2():
    for i in range(6):
        print('This is demo2:%d' % i)
        time.sleep(1)

def main():
    t1 = threading.Thread(target=demo1)
    t2 = threading.Thread(target=demo2)
    t1.start()
    t2.start()
    # 打印当前存活的线程
    while True:
        if len(threading.enumerate()) == 1:
            # 列表里只有一个线程对象,说明只剩下主线程
            print(threading.enumerate())
            break
        print(threading.enumerate())
        time.sleep(1)

if __name__ == '__main__':
    main()

运行结果:
在这里插入图片描述

3.3 验证子线程的创建与执行

现在,仔细地探讨一下子线程的创建与执行。
示例代码:

import time
import threading

def demo():
    for i in range(3):
        print('This is child thread:  %d' % i)
        time.sleep(1)

def main():
    # 每一步执行时都打印存活的线程,验证线程的创建和执行
    print(threading.enumerate())
    t_obj = threading.Thread(target=demo)
    print(threading.enumerate())
    t_obj.start()
    print(threading.enumerate())

if __name__ == '__main__':
    main()

运行结果:
在这里插入图片描述
main()中第一个print语句显示当前只有主线程,第二个print语句在threading.Thread(target=demo)语句之后,但是结果显示仍然只有主线程,直到调用start()方法后,才显示有两个线程。所以,start()方法才是真正地创建并执行子线程。其实, threading.Thread(target=demo)的作用类似于向系统注册一个回调函数,目的是告诉子线程的执行逻辑。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值