python爬虫知识----爬虫进阶----threading、lock、condition、queue、selenium+chromedriver、tesseract(四)

 

添加小程序,兑换各种视频教程/数据资源。

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)  

 

 

 

 

你可能感兴趣的:(python)