1. 多线程:同步完成多项任务,多线程就像火车的每节车厢,进程则是火车。如同时下载多张图片的多线程爬虫应用。
2. 多线程之--------threading模块。
2.1 threading模块实现函数,类的多线程执行。
#encoding:utf-8
#time 和threading是Python内置模块,无需单独下载
import time
import threading
def coding():
for x in range(3): #range(3)表示从0开始生成0,1,2整数
print("%x正在写代码"% threading.current_thread()) # threading.current_thread()查看当前执行的线程的名字。
time.sleep(1)
def draming():
for x in range(3):
print("%x正在画图"%x)
time.sleep(1) #延迟一秒执行
def main():
#1. 传统的执行函数的同步方式:先执行coding 3秒后执行draming函数,一共需要6秒。
#coding()
#draming()
#2. 多线程执行任务-----函数写法,同时执行coding和draming函数,一共需要3秒
t1=threading.Thread(target=coding)
t2=threading.Thread(target=draming)
t1.start()
t2.start()
print(threading.enumerate()) #查看正在运行的线程数量,包括main(主线程),t1(子线程),t2(子线程)一共3个线程。
if __name__=="__main__":
main()
class CodingThread(threading.Thread):
def run(self):
for x in range(3):
print("写代码")
time.sleep(1)
class DrawingThread(threading.Thread):
def run(self):
for x in range(3):
print("画图")
time.sleep(1)
def main():
#33. 多线程执行任务--------继承类写法,同时执行CodingThread类的run方法和DrawingThread类的run方法,一共需要3秒
t1=CodingThread()
t2=DrawingThread()
t1.start()
t2.start()
if __name__=="__main__":
main()
2.2 多线程的全局变量的共享问题:多线程都是在一个进程中执行,因此在进程中的全局变量在该进程下的线程中都是可共享,这就会造成一个问题,因为线程执行是无须的,如果在多线程中同时修改一个全局变量,执行的先后无须,就会出现全局变量的值结果数据错误。解决:锁机制,以解决多线程的全局变量共享问题。
#encoding:utf-8
#多线程之---------锁机制
#多线程之---------全局变量共享问题
import threading
VAL=0 #声明一个全局变量
gLock=threading.Lock() #实例化一个多线程锁机制
def add_val():
global VAL #在子作用域中修改全局变量,需要先声明该作用域是全局变量
for x in range(10000000):
VAL+=1
print("执行结果%s"%VAL) #在没有开启锁机制下,2次线程执行打印的结果是不太固定的,因为存在多线程下全局变量共享的问题,如当VAL=10时,一个线程在执行VAL+=1时,另外一个线程也许
#同时也在执行VAL+=1,最后VAL的结果,是11不是12.
def add_val():
global VAL
gLock.acquire() #开启锁机制
for x in range(10000000):
VAL+=1
gLock.release() #关闭锁机制, 在开启-关闭锁机制这段线程里,其他与之相关的线程会暂停,等其执行完,解锁锁机制后,才会继续执行,没有开启锁机制的部分,代码不受到锁机制影响。
print("执行结果%s"%VAL)
def main():
for x in range(2): #实例化2个多线程执行
t=threading.Thread(target=add_val)
t.start()
if __name__=="__main__":
main()
2.3 lock版的生产者和消费者模式:在爬虫中的应用,如生产者模式(多线程,从网页爬取数据)->全局变量->消费者模式(多线程,处理和筛选数据)。缺点:通过while 死循环,很消耗内存cpu。
#encoding:utf-8
import time
import threading
import random
MYMONEY=1000
gLock=threading.Lock() #实例化一个全局 Lock线程锁
gTimes=0
class Producer(threading.Thread): #生产者模式
def run(self):
global MYMONEY
global gTimes
while True:
money=random.randint(100,1000) #随机生成100~1000的数据
gLock.acquire() #开启线程锁
if gTimes>=10:
gLock.release()
break
MYMONEY+=money
gTimes+=1
print("%s生产者模式,生产了%s元钱,剩余%s元"%(threading.current_thread(),money,MYMONEY))
gLock.release()
time.sleep(1)
class Consumer(threading.Thread): #消费者模式
def run(self):
global MYMONEY
global gTimes
while True:
money=random.randint(100,1000)
gLock.acquire()
if MYMONEY>= money:
MYMONEY-=money
print("%s消费者模式,消费了%s元钱,剩余%s元"%(threading.current_thread(),money,MYMONEY))
else:
if gTimes>=10: #当生产次数超过10次时,且剩余不足时,无法进行中断消费者模式的循环
gLock.release()
break
print("%s消费者模式,准备消费%s元钱,剩余%s元,不足"%(threading.current_thread(),money,MYMONEY))
gLock.release()
time.sleep(1)
def main():
for x in range(3):
t=Producer()
t.start()
for x in range(5):
t=Consumer()
t.start()
if __name__=="__main__":
main()
2.4 condition版的生产者和消费者模式:在爬虫中的应用,如生产者模式(多线程,从网页爬取数据)->全局变量->消费者模式(多线程,处理和筛选数据)。优点:与lock版相比,采用阻塞模式,性能优化,消耗内存少。
#encoding:utf-8
import time
import threading
import random
MYMONEY=1000
gCondition=threading.Condition() #实例化一个全局 Condition线程锁
gTimes=0
class Producer(threading.Thread): #生产者模式
def run(self):
global MYMONEY
global gTimes
while True:
money=random.randint(100,1000) #随机生成100~1000的数据
gCondition.acquire() #开启线程锁
if gTimes>=10:
gCondition.release()
break
MYMONEY+=money
gTimes+=1
print("%s生产者模式,生产了%s元钱,剩余%s元"%(threading.current_thread(),money,MYMONEY))
gCondition.notify_all() #唤醒并开启所有等待(阻塞状态)下的线程的acquire(线程锁),必须在release之前。gCondition.notify() 唤醒某个阻塞状态下的线程,默认是第一个。
gCondition.release()
time.sleep(1)
class Consumer(threading.Thread): #消费者模式
def run(self):
global MYMONEY
global gTimes
while True:
money=random.randint(100,1000)
gCondition.acquire()
while MYMONEY< money:
if gTimes>10:
gCondition.release()
return
print("%s消费者模式,准备消费%s元钱,剩余%s元,不足"%(threading.current_thread(),money,MYMONEY))
gCondition.wait() #线程锁阻塞状态, 即关闭线程锁,
MYMONEY-=money
print("%s消费者模式,消费了%s元钱,剩余%s元"%(threading.current_thread(),money,MYMONEY))
gCondition.release()
time.sleep(1)
def main():
for x in range(3):
t=Producer()
t.start()
for x in range(5):
t=Consumer()
t.start()
if __name__=="__main__":
main()
2.4 Queue线程安全队列:遵循先进先出的原则。
#encoding:utf-8
# Queue安全队列,py内置模块,无需下载。队列:先进先出,像是排队买票。栈:是先进后出,像是仓库存取。
from queue import Queue
import time
q=Queue(4) #实例化一个最大存储4个元素的安全队列。
q.put(1) #向实例化的安全queue队列中放入一个 '1'元素
print(q.qsize()) #查看队列中一共有多少个元素
print(q.empty()) #判断队列是否为空,即元素个数是否为0
print(q.full()) #判断队列是否满了,即达到最大存储个数的队列。
print(q.get()) #取出队列中下标最开始的位置的一个元素。取出之后,队列中就没有改元素了。
print(q.get/pust(block=True)) #block默认true表示,取出这个api是否是阻塞的,当取出时,里面没有元素了,就会阻塞,当put放入时,已经到达最大存储,也默认阻塞。
def set_val(q):
index=0
while True:
q.put(index) #参数block默认=true阻塞,Queue队列达到最大存储值,就会在此阻塞
index+=1
time.sleep(3)
def get_val():
while True:
val=q.get() #参数block默认=true阻塞,Queue队列已经为空,就会在此阻塞
print(val)
def main():
q=Queue(4)
t1=threading.Thread(target=set_val,args=[q]) #多线程传参args是一个列表或字典
t2=threading.Thread(target=get_val,args=[q])
t1.start()
t2.start()
if __name=="__main__":
main()
3.案例 :
3.1 同步的方法(传统的方法),爬取下载表情图到本地文件中。
#encoding:utf-8
#同步下载网上的表情图片,即下载完一张图片,才继续下载另外一张,耗时很长。
import requests
from lxml import etree
from urllib import request
import re
import os
def parse_page(url):
headers={
"user-agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
}
res=requests.get(url,headers=headers)
text=res.text
html=etree.HTML(text)
imgs=html.xpath("//div[@class='page-content text-center']//img[@class !='gif']")
for img in imgs:
img_url=img.get("data-original") #get表示从img标签中获取某个属性的值。
alt=img.get("alt")
alt=re.sub(r'[\??\.!,。!\*]','',alt) #将alt值中的特殊符号通过正则替换为''空字符。
suffix=os.path.splitext(img_url)[1] #将文件图片路径安装后缀名前面的'.'切割成元祖,如'http://img/2.png'切割成('http://img/2','.png')
filename=alt+suffix
request.urlretrieve(img_url,"images/"+filename) #将图片下载在images文件夹下
def main():
for x in range(1,1001):
url="http://www.doutula.com/photo/list/?page=%s"%x
parse_page(url)
if __name__=="__main__":
main()
3.2 异步方式(生成者和消费者模式多线程),下载表情图到本地。
#encoding:utf-8
#异步方法(生成者和消费者模式多线程),下载表情包图片到本地。生成者:每个页获取的元素集合是页面队列的元素,存放多线程安全队列queue中。消费者:每个表情图是图片队列的一个元素,在从中取出来。
import requests
import threading
from lxml import etree
from queue import Queue
from urllib import request
import re
import os
class Procuder(threading.Thread): #生产者类,继承多线程父类
headers={
"user-agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
}
def __init__(self,page_queue,img_queue,*args,**kwargs): #重新构建Procuder类的init并继承多线程父类的init
super(Procuder,self).__init__(*args,**kwargs)
self.page_queue=page_queue
self.img_queue=img_queue
def run(self):
while True:
if self.page_queue.empty():
break
url=self.page_queue.get() #从队列中获取元素
self.parse_page(url)
def parse_page(self,url):
res=requests.get(url,headers=self.headers)
text=res.text
html=etree.HTML(text)
imgs=html.xpath("//div[@class='page-content text-center']//img[@class !='gif']")
for img in imgs:
img_url=img.get("data-original") #get表示从img标签中获取某个属性的值。
alt=img.get("alt")
alt=re.sub(r'[\??\.!,。!]','',alt) #将alt值中的特殊符号通过正则替换为''空字符。
suffix=os.path.splitext(img_url)[1] #将文件图片路径安装后缀名前面的'.'切割成元祖,如'http://img/2.png'切割成('http://img/2','.png')
filename=alt+suffix
self.img_queue.put((img_url,filename)) #存放元素到队列
class Consumer(threading.Thread):
def __init__(self,page_queue,img_queue,*args,**kwargs): #重新构建Consumer类的init并继承多线程父类的init
super(Consumer,self).__init__(*args,**kwargs)
self.page_queue=page_queue
self.img_queue=img_queue
def run(self):
while True:
if self.img_queue.empty() and self.page_queue.empty() :
break
img_url,filename=self.img_queue.get() #解构,获取列队中的元祖元素,依次赋值给img_url,filename
request.urlretrieve(img_url,"image/"+filename) #将图片下载在已经手动创建好的image文件夹下
def main():
page_queue=Queue(100)
img_queue=Queue(1000) #最大队列容量可以大一点,避免等待
for x in range(1,101):
url="http://www.doutula.com/photo/list/?page=%s"%x
page_queue.put(url)
for x in range(5): #创建5个生成者
t=Procuder(page_queue,img_queue)
t.start()
for x in range(5): #创建5个消费者
t=Consumer(page_queue,img_queue)
t.start()
if __name__=="__main__":
main()
4. GIL全局解释器锁:python自带的解释器Cpthon(c语言写的解释器),无法真正实现同时利用多核cpu,同一时刻只能运行一个线程,为了保证同一时刻只有一个线程运行,所有Cpython解释器中存在一个GIL全局解释器锁。从而使得其运行原理是:一个cpu在极短时间(几乎可以忽略不计的时间)内来回在多个线程直接不停的切换,从视觉上达到,感觉同时在运行多个线程的效果。
5. 爬虫获取ajax数据的2种方式:
方式 | 优点 | 缺点 |
直接request请求ajax接口,获取数据 | 代码少,性能高,通过json.load(data)解析为字典 | 容易被识别是爬虫,且对参数加密会比较麻烦识别。 |
selenium | 直接模拟人类操作浏览器操作行为(如点击,请求,输入,删除...),爬虫更稳定 | 代码多,性能低 |
6. selenium+chromedriver:通过直接模拟人类操作浏览器操作行为(如点击,请求,输入,删除...)获取动态数据,chromedriver是chrome浏览器的驱动程序,不同浏览器驱动程序不同。中文文档:https://python-selenium-zh.readthedocs.io/zh_CN/latest/6.页面对象/。
6.1 下载selenium:pip install selenium或者pip.exe install selenium。
6.2 安装chromedriver:下载后,放到不需要权限的纯英文目录下即可。如在http://chromedriver.storage.googleapis.com/index.html?path=2.0/中下载chromedriver_win32.zip。window64位也下载这个。
并将其chromedriver.exe放入一个不需要权限的纯英文目录下即可。
#encoding:utf-8
# 下载selenium和chromedriver之后
from selenium import webdriver
driver_path=r"D:\chromedriver.exe" #找到下载的chromedriver.exe保存的文件路径,记得\要转义。
driver=webdriver.Chrome(executable_path=driver_path) #实例化一个Chrome浏览器的类的驱动程序,如果是其他浏览器如火狐就webdriver.Firefox()
driver.get("https://www.baidu.com") #通过驱动程序,实现打开chrome浏览器的百度页面,没有返回值
driver.close() #关闭之前打开的页面
driver.quite() #退出整个浏览器
print(driver.page_source) #返回整个页面的代码信息
6.3 selenium定位元素的几种方法:返回时webElement类型的标签。
#encoding:utf-8
from selenium import webdriver
from lxml import etree
from selenium.webdriver.common.by import BY
#注意:所有的driver.find_element_by_xxx()或driver.find_elements_by_xxx()可以写成driver.find_element(BY.XXX,'')或driver.find_elements(BY.XXX,'')。xxx要全大写。
driver_path=r"D:\chromedriver.exe" #找到下载的chromedriver.exe保存的文件路径,记得\要转义。
driver=webdriver.Chrome(executable_path=driver_path)
driver.get("https://www.baidu.com/") #通过驱动程序,实现打开chrome浏览器的百度页面,没有返回
#(一)通过xpath定位元素,因为xpath是c语言写的,效率比较高,但是无法操作浏览器行为,如点击,输入内容,打开/关闭浏览器.....
html=etree.HTML(driver.page_source) #返回整个页面的代码信息,通过xpath查看元素信息
tags=html.xpath("//div")
#(二)通过selenium定位元素,因为是python语言写的,所有效率低,但是可以模拟人操作浏览器的行为,如点击,输入内容,打开/关闭浏览器.....
#selenium的几种定位(查找)元素的方式。find_element_by_xxx()是查找到满足条件的第一个标签。driver.find_elements_by_xxx()是查找到满足条件的所有标签。
#找不到,就会返回异常,找到满足条件的就返回标签。
#1. 通过标签id查看单个/多个标签。driver.find_element_by_id("idVal")或driver.find_elements_by_id("idVal")
inputTag=driver.find_element_by_id("kw")
inputTag.send_keys("搜索python") #通过send_keys自动操作向百度页面输入框输入'搜索python'
#2. 通过标签name查看单个/多个标签。driver.find_element_by_name("nameVal")或driver.find_elements_by_name("nameVal")
inputTag=driver.find_element_by_name("wd")
inputTag.send_keys("搜索python")
#3. 通过标签class_name查看单个/多个标签。driver.find_element_by_class_name("classnameVal")或driver.find_elements_by_class_name("classnameVal")
inputTag=driver.find_element_by_class_name("line-controls")
inputTag.send_keys("搜索python")
#4. 通过标签xpath语法查看单个/多个标签。driver.find_element_by_xpath("")或driver.find_elements_by_xpath("")
inputTag=driver.find_element_by_xpath("//input[@id='kw']")
inputTag.send_keys("搜索python")
#5. 通过标签css选择器(包括通用/元素/类/id/群组/后代/子代/伪类选择器)查看单个/多个标签。driver.find_element_by_css_selector("")或driver.find_elements_by_css_selector("")
inputTag=driver.find_element_by_css_selector("input.wd")
inputTag.send_keys("搜索python")
#6. 通过标签查看单个/多个标签。driver.find_element_by_tag_name("")或driver.find_element_by_tag_name("")
inputTag=driver.find_element_by_tag_name("input")
inputTag.send_keys("搜索python")
6.4 selenium行为链:即页面中操作多个步骤,如百度搜索时,先鼠标移到输入框,在输入内容,再鼠标移到搜索按钮,在点击搜索按钮。
#encoding:utf-8
#行为链:即页面中操作多个步骤,如登录时,输入账号密码,点击登录。
#案例:模拟selenium操作百度页面浏览器,输入'Python',并点击搜索按钮。
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
driver_path=r"D:\chromedriver.exe" #找到下载的chromedriver.exe保存的文件路径,记得\要转义。
driver=webdriver.Chrome(executable_path=driver_path)
driver.get("https://www.baidu.com/") #通过驱动程序,实现打开chrome浏览器的百度页面,没有返回
inputTag=driver.find_element_by_id("kw")
submitBtn=driver.find_element_by_id("su")
actions=ActionChains(driver) #实例化一个行为链浏览器驱动对象的队列
actions.move_to_element(inputTag) #将第一步存入在行为链的队列里:鼠标移入到input输入框中
actions.send_keys_to_element(inputTag,"搜索python") #将第二步存入在行为链的队列里:向input输入框中输入'搜索python'
actions.move_to_element(submitBtn) #将第三步存入在行为链的队列里:鼠标移入到搜索按钮上
actions.click(submitBtn)#将第四步存入在行为链的队列里:点击搜索按钮进行搜索
action.perform() #启动行为链队列,依次执行上面步骤。
#注意:可链式操作,如ActionChains(driver).move_to_element(inputTag).click(submitBtn).perform()
#其他selenium模拟操作,如:
#1. actions.click_and_hold(inputTag) :点击但是不松开鼠标
#2. actions.context_click(inputTag) :右键点击
#3. actions.double_click(inputTag) :双击
6.5 selenium对cookie信息的操作:
#encoding:utf-8
from selenium import webdriver
driver_path=r"D:\chromedriver.exe"
driver=webdriver.Chrome(executable_path=driver_path)
driver.get("https://python-selenium-zh.readthedocs.io/zh_CN/latest/7.2%20%E8%A1%8C%E4%B8%BA%E9%93%BE/")
for cookie in driver.get_cookies(): #driver.get_cookies()是获取当前打开页面的所有cookie信息。
print(cookie)
driver.get_cookie(key) #获取指定key的cookie信息
driver.delete_cookie(key) #删除指定key的cookie信息
driver.delete_all_cookies() #获取所有key的cookie信息
6.6 selenium的显示等待和隐示等待:
#encoding:utf-8
#显示等待和隐示等待
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait #引入显式等待的api
from selenium.webdriver.support import expected_conditions as EC #引入标签出现的api
from selenium.webdriver.common.by import BY
driver_path=r"D:\chromedriver.exe"
driver=webdriver.Chrome(executable_path=driver_path)
driver.get("https://python-selenium-zh.readthedocs.io/zh_CN/latest/7.2%20%E8%A1%8C%E4%B8%BA%E9%93%BE/")
#1. 隐示等待:即页面通过axios获得数据后才渲染某个标签,在渲染之前是获取该标签的是不存在会报错的,所有就让代码等待一定时间,比如10s,在10s后去查看是否有该标签,如果任然没有就报错
driver.implicitly_wait(10) #设置隐示等待10s
driver.find_element(BY.ID, 'wk')
#2. 显示等待最大10s:即页面通过axios获得数据后才渲染某个标签,在渲染之前是获取该标签的是不存在会报错的,所有就让代码等待一定时间,比如最大10s,在10s内不停去查看是否有该标签,
#如果任仍然没有,就继续等待,直到最大10s后,仍然没有,就报错,在10s如果出现该标签,就不需要等待,直接执行接下来的代码 。
el=WebDriverWait(driver,10).until(
EC.presence_of_element_located((BY.ID,'wk'))
)
6.7 selenium的打开新窗户以及页面之前的切换:
#encoding:utf-8
#selenium的页面切换
from selenium import webdriver
driver_path=r"D:\chromedriver.exe"
driver=webdriver.Chrome(executable_path=driver_path)
driver.get("https://www.baidu.com/")
driver.execute_script("window.open('https://www.douban.com/')") #打开浏览器新窗口,但是driver仍然指向https://www.baidu.com/。
print(driver.window_handles) #返回driver打开的页面组成操作句柄的元祖('https://www.baidu.com/','https://www.douban.com/')
driver.switch_to_window(driver.window_handles[1]) #让driver指向https://www.baidu.com/。
print(driver.current_url) #获取当前driver指向的网址
6.8 selenium的设置代理ip:
#encoding:utf-8
#代理ip
from selenium import webdriver
driver_path=r"D:\chromedriver.exe"
options=webdriver.ChromeOptions() #实例化一个代理
options.add_argument("--proxy-server=代理ip:代理port") #设置代理ip地址
driver=webdriver.Chrome(executable_path=driver_path,chrome_options=options)
driver.get("https://www.baidu.com/")
6.9 webElement元素:selenium定位元素,返回的都是webElementL类型的标签。inputTag.get_attribute('name')获取其标签的属性值。driver.screentshot(),截取driver指向的页面截图,只能driver用。
#encoding:utf-8
#基于selenium爬取数据案例:
from lxml import etree
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait #引入显式等待的api
from selenium.webdriver.support import expected_conditions as EC #引入标签出现的api
from selenium.webdriver.common.by import BY
import time
class LagouSpider(object):
driver_path=r"D:\chromedriver.exe" #类属性
def __init__(self):
self.driver=webdriver.Chrome(executable_path=LagouSpider.driver_path)
self.url="https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput="
def run(self):
self.driver.get(self.url)
while True:
source= self.driver.page_source
WebDriverWait(self.driver,10).until( #因为是ajax加载数据,所有存在页面等待(即页面元素在数据加载渲染到时才会显示,需要显示/隐示等待处理)
EC.presence_of_all_elements_located((BY.XPATH,'//div[@class]'))
)
self.parse_list_page(source)
try:
next_btn=self.driver.find_element_by_xpath("") #找到下一页的按钮
if 'xxx' in next_btn.get_attribute(""): #如果下一页按钮已经被禁止点击,就中断while true循环,否则就点击进入下一页
break
else:
next_btn.click()
except:
print(source)
time.sleep(1) #避免被反爬虫识别,可以延迟1秒,操作不那么频繁
def parse_list_page(self,source):
html=etree.HTML(source)
hrefs=html.xpath("//a[@class='position_link']/@href")
for href in hrefs:
print(href)
self.request_detail_page(href)
def request_detail_page(self,href):
self.driver.execute_script("window.open(%s)"%href) #打开新窗口
self.driver.switch_to.window(self.driver.window_handles[1]) #driver切换到新窗口
WebDriverWait(self.driver,10).until(
EC.presence_of_all_elements_located((BY.XPATH,'//div[@class]'))
)
source=self.driver.page_source
self.parse_detail_page(source)
self.driver.close() #关闭当前详情页
self.driver.switch_to.window(self.driver.window_handles[0]) #切换driver到原来页面
def parse_detail_page(self,source):
html=etree.HTML(source)
#这里是根据html获取职位,待遇.....详细信息
if __name__=="__main__":
spider=LagouSpider()
spider.run()
7. tesseract库:图形验证码识别。window系统下下载文件到本地纯英文文件夹中。并将tesseract.exe设置环境变量。还需要将训练的数据文件路径tessdata设置环境变量。读取图片时需要借助第三方库PIL,可通过pip list查看是否安装,没有安装通过pip(pip.exe) install PIL安装。
参考:https://blog.csdn.net/showgea/article/details/82656515。
import pytesseract
from PIL import Image
pytesseract.pytesseract.tesseract_cmd=r'D:\tesseract.exe' #tesseract.exe的路径
image=Image.open('a.png') #打开当前同级下a.png文件
text= pytesseract.image_to_string(image,lang='chi_sim') #中文识别图片的文字
print(text)