爬虫-python -综合练习-51job信息-滑块验证-selenium

利用selenium爬51job职位信息-破解滑块验证

  • 1.爬51job职位信息
    • 0.头文件
    • 1.初始化
    • 2.页面登陆
    • 3.滑块验证
    • 4.获取网站的职位信息
  • 2.爬51job页面-利用selenium-破解滑块验证-完整代码

1.爬51job职位信息

0.头文件

需要用到以下文件

from selenium import webdriver
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
import time
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from PIL import Image
import csv

1.初始化

防止打印一些无用或者显示错误的日志。
wait是为后面wait.until做准备。

# 初始化
# 防止打印一些无用的日志
option = webdriver.ChromeOptions()
option.add_experimental_option("excludeSwitches", ['enable-automation', 'enable-logging'])
web = Chrome(options = option)
# 设置等待超时
wait = WebDriverWait(web, 20)

2.页面登陆

这个没什么好说的,打开页面以及输入账号密码点确定。

# 登录
def login():
    web.get(url)
    web.maximize_window() #窗口最大化
    time.sleep(2)
    #登录
    web.find_element(By.ID,'loginname').send_keys('账户')
    web.find_element(By.ID,'password').send_keys('密码')
    web.find_element(By.ID,'isread_em').click()
    web.find_element(By.ID,'login_btn_withPwd').click()
    time.sleep(2)

3.滑块验证

重点的来了,滑块验证需要分成两部分,
第一部分为将验证滑块的图片的,带缺口图片和完整背景图片保存。
第二部分比对这两张图片,并计算缺口位置距图片左边界的距离,
第三部分为模拟人工滑动滑块。
1.保存带缺口图片和完整背景图片
这里保存了四张图片,其中51job_slice是没有用的,51job图片是为后面处理可以图片截取部分。
其中1.25系数是将图片与网页上原图做对比后得出的系数。
设置可见与不可见,对应网站更新后的代码内,验证图片的路径,可以通过浏览器工具获得,类似这样验证码这里是不变的,所以可以直接复制。

#对某元素截图   
def save_pic(obj,name):
    try:
        pic_url=web.save_screenshot('.\\51job.png')
        print("%s:截图成功!" % pic_url)
        
        #获取元素位置信息
        left = obj.location['x']*1.25  #自己通过原图与实际图片对比得出的系数
        top = obj.location['y']*1.25
        right = left + obj.size['width']*1.25
        bottom = top + obj.size['height']*1.25
        
        print('图:'+name)
        print('Left %s' % left)
        print('Top %s' % top)
        print('Right %s' % right)
        print('Bottom %s' % bottom)
        print('')
         
        im = Image.open('.\\51job.png')
        im = im.crop((left, top, right, bottom))    #元素裁剪
        file_name='51job_'+name+'.png'
        im.save(file_name)    #元素截图
    except BaseException as msg:
        print("%s:截图失败!" % msg)
#设置元素可见    
def show_element(element):
    web.execute_script("arguments[0].style=arguments[1]",element,"display: block;")
#设置元素不可见
def hide_element(element):
    web.execute_script("arguments[0].style=arguments[1]",element,"display: none;")
def cut():
    c_background=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_bg.geetest_absolute')))
    c_slice=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_slice.geetest_absolute')))
    c_full_bg=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute')))
    hide_element(c_slice)
    save_pic(c_background,'back') #隐藏滑块
    show_element(c_slice)
    save_pic(c_slice,'slice') #所有的
    show_element(c_full_bg)
    save_pic(c_full_bg,'full') #隐藏所有的

2.计算移动距离
爬虫-python -综合练习-51job信息-滑块验证-selenium_第1张图片
爬虫-python -综合练习-51job信息-滑块验证-selenium_第2张图片
将图一中1移动到到2 相当于比对缺口图与背景图的从左向右第一个不同的点的位置。

# 判断像素是否相同
def is_pixel_equal(bg_image, fullbg_image, x, y):
    """
    :param bg_image: (Image)缺口图片
    :param fullbg_image: (Image)完整图片
    :param x: (Int)位置x
    :param y: (Int)位置y
    :return: (Boolean)像素是否相同
    """
    # 获取缺口图片的像素点(按照RGB格式)
    bg_pixel = bg_image.load()[x, y]
    # 获取完整图片的像素点(按照RGB格式)
    fullbg_pixel = fullbg_image.load()[x, y]
    # 设置一个判定值,像素值之差超过判定值则认为该像素不相同
    threshold = 20
    # 判断像素的各个颜色之差,abs()用于取绝对值
    if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs(bg_pixel[2] - fullbg_pixel[2] < threshold)):
        # 如果差值在判断值之内,返回是相同像素
        return True
    else:
        # 如果差值在判断值之外,返回不是相同像素
        return False
# 计算滑块移动距离
def get_distance(bg_image, fullbg_image):
    '''
    :param bg_image: (Image)缺口图片
    :param fullbg_image: (Image)完整图片
    :return: (Int)缺口离滑块的距离
    '''
    # 滑块的初始位置
    distance = 60
    # 遍历像素点横坐标
    for i in range(distance, fullbg_image.size[0]):
        # 遍历像素点纵坐标
        for j in range(fullbg_image.size[1]):
            # 如果不是相同像素
            if not is_pixel_equal(fullbg_image, bg_image, i, j):
                # 返回此时横轴坐标就是滑块需要移动的距离
                return i

3.模拟现实移动滑块
这里的滑块会验证是否为机器操作,如果被发现了,即使移动位置正确,也会出现‘怪物吃了拼图 请重试’。
其实这里很简单,主要看滑块移动时候,是否在中间断开,其实可以一下子拉到某个位置,然后稍微调整。

#破解滑块验证
def slide():
    distance=get_distance(Image.open('.\\51job_back.png'),Image.open('.\\51job_full.png'))/1.25   #要将原图与实际图对比的系数除掉
    try:
        slider=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'div.geetest_slider_button')))  #找到滑块
        if slider:
            print("====有滑块验证=====")
            action_chains = webdriver.ActionChains(web)
            # 点击,准备拖拽
            action_chains.click_and_hold(slider)
            action_chains.pause(0.2)
            action_chains.move_by_offset(distance-10,0)
            action_chains.pause(0.6)
            action_chains.move_by_offset(10,0)   #添加修正过程
            action_chains.pause(0.6)
            action_chains.release()
            action_chains.perform()   # 释放滑块
            time.sleep(5)
        else:
            print("===没有滑块验证===")
    except Exception as e:
        print("==="+str(e))

4.获取网站的职位信息

获取分为两步,第一步定位到某个页page_nums=1,第二步将网页内的有用数据保存。
将page_nums变大,同时点击下一页。

#取的页面职位信息
def get_data(page_nums):
    lists = web.find_element(By.CLASS_NAME,'j_joblist').find_elements(By.CLASS_NAME,'e')
    with open('data_job.csv',mode='a',encoding='utf-8',newline='') as f:
        csv_w = csv.writer(f)
        for list in lists:  
            job_name = list.find_element(By.CLASS_NAME,'jname').text
            com_name = list.find_element(By.CLASS_NAME,'cname').text
            sal_val = list.find_element(By.CLASS_NAME,'sal').text
            din = list.find_element(By.CLASS_NAME,'d').text 
            csv_w.writerow([job_name,com_name,sal_val,din])
    print(page_nums,'over!!')
#搜索框输入python开始搜索前max_page页面
def search(input_str,max_page):
    web.find_element(By.ID,'kwdselectid').send_keys(input_str)
    web.find_element(By.XPATH,'/html/body/div[3]/div/div[1]/div/button').click()
    time.sleep(0.5)
    page_nums = 1
    while page_nums<=max_page:
        get_data(page_nums)
        page_nums+=1
        web.find_element(By.CLASS_NAME,'p_in').click()
        time.sleep(1)

2.爬51job页面-利用selenium-破解滑块验证-完整代码

from selenium import webdriver
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
import time
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from PIL import Image
import csv
# 初始化
# 防止打印一些无用的日志
option = webdriver.ChromeOptions()
option.add_experimental_option("excludeSwitches", ['enable-automation', 'enable-logging'])
web = Chrome(options = option)
# 设置等待超时
wait = WebDriverWait(web, 20)

# 登录
def login():
    web.get(url)
    web.maximize_window() #窗口最大化
    time.sleep(2)
    #登录
    web.find_element(By.ID,'loginname').send_keys('账号')
    web.find_element(By.ID,'password').send_keys('密码')
    web.find_element(By.ID,'isread_em').click()
    web.find_element(By.ID,'login_btn_withPwd').click()
    time.sleep(2)
#对某元素截图   
def save_pic(obj,name):
    try:
        pic_url=web.save_screenshot('.\\51job.png')
        print("%s:截图成功!" % pic_url)
        
        #获取元素位置信息
        left = obj.location['x']*1.25  #自己通过原图与实际图片对比得出的系数
        top = obj.location['y']*1.25
        right = left + obj.size['width']*1.25
        bottom = top + obj.size['height']*1.25
        
        print('图:'+name)
        print('Left %s' % left)
        print('Top %s' % top)
        print('Right %s' % right)
        print('Bottom %s' % bottom)
        print('')
         
        im = Image.open('.\\51job.png')
        im = im.crop((left, top, right, bottom))    #元素裁剪
        file_name='51job_'+name+'.png'
        im.save(file_name)    #元素截图
    except BaseException as msg:
        print("%s:截图失败!" % msg)
#设置元素可见    
def show_element(element):
    web.execute_script("arguments[0].style=arguments[1]",element,"display: block;")
#设置元素不可见
def hide_element(element):
    web.execute_script("arguments[0].style=arguments[1]",element,"display: none;")
def cut():
    c_background=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_bg.geetest_absolute')))
    c_slice=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_slice.geetest_absolute')))
    c_full_bg=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute')))
    hide_element(c_slice)
    save_pic(c_background,'back') #隐藏滑块
    show_element(c_slice)
    save_pic(c_slice,'slice') #所有的
    show_element(c_full_bg)
    save_pic(c_full_bg,'full') #隐藏所有的
# 判断像素是否相同
def is_pixel_equal(bg_image, fullbg_image, x, y):
    """
    :param bg_image: (Image)缺口图片
    :param fullbg_image: (Image)完整图片
    :param x: (Int)位置x
    :param y: (Int)位置y
    :return: (Boolean)像素是否相同
    """
    # 获取缺口图片的像素点(按照RGB格式)
    bg_pixel = bg_image.load()[x, y]
    # 获取完整图片的像素点(按照RGB格式)
    fullbg_pixel = fullbg_image.load()[x, y]
    # 设置一个判定值,像素值之差超过判定值则认为该像素不相同
    threshold = 20
    # 判断像素的各个颜色之差,abs()用于取绝对值
    if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs(bg_pixel[2] - fullbg_pixel[2] < threshold)):
        # 如果差值在判断值之内,返回是相同像素
        return True
    else:
        # 如果差值在判断值之外,返回不是相同像素
        return False
# 计算滑块移动距离
def get_distance(bg_image, fullbg_image):
    '''
    :param bg_image: (Image)缺口图片
    :param fullbg_image: (Image)完整图片
    :return: (Int)缺口离滑块的距离
    '''
    # 滑块的初始位置
    distance = 60
    # 遍历像素点横坐标
    for i in range(distance, fullbg_image.size[0]):
        # 遍历像素点纵坐标
        for j in range(fullbg_image.size[1]):
            # 如果不是相同像素
            if not is_pixel_equal(fullbg_image, bg_image, i, j):
                # 返回此时横轴坐标就是滑块需要移动的距离
                return i
#破解滑块验证
def slide():
    distance=get_distance(Image.open('.\\51job_back.png'),Image.open('.\\51job_full.png'))/1.25   #要将原图与实际图对比的系数除掉
    try:
        slider=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'div.geetest_slider_button')))  #找到滑块
        if slider:
            print("====有滑块验证=====")
            action_chains = webdriver.ActionChains(web)
            # 点击,准备拖拽
            action_chains.click_and_hold(slider)
            action_chains.pause(0.2)
            action_chains.move_by_offset(distance-10,0)
            action_chains.pause(0.6)
            action_chains.move_by_offset(10,0)   #添加修正过程
            action_chains.pause(0.6)
            action_chains.release()
            action_chains.perform()   # 释放滑块
            time.sleep(5)
        else:
            print("===没有滑块验证===")
    except Exception as e:
        print("==="+str(e))
#取的页面职位信息
def get_data(page_nums):
    lists = web.find_element(By.CLASS_NAME,'j_joblist').find_elements(By.CLASS_NAME,'e')
    with open('data_job.csv',mode='a',encoding='utf-8',newline='') as f:
        csv_w = csv.writer(f)
        for list in lists:  
            job_name = list.find_element(By.CLASS_NAME,'jname').text
            com_name = list.find_element(By.CLASS_NAME,'cname').text
            sal_val = list.find_element(By.CLASS_NAME,'sal').text
            din = list.find_element(By.CLASS_NAME,'d').text 
            csv_w.writerow([job_name,com_name,sal_val,din])
    print(page_nums,'over!!')
#搜索框输入python开始搜索前max_page页面
def search(input_str,max_page):
    web.find_element(By.ID,'kwdselectid').send_keys(input_str)
    web.find_element(By.XPATH,'/html/body/div[3]/div/div[1]/div/button').click()
    time.sleep(0.5)
    page_nums = 1
    while page_nums<=max_page:
        get_data(page_nums)
        page_nums+=1
        web.find_element(By.CLASS_NAME,'p_in').click()
        time.sleep(1)

if __name__=='__main__':
    url = 'https://login.51job.com/login.php?loginway=0&isjump=0&lang=c&from_domain=i&url=http%3A%2F%2Fwww.51job.com%2F'
    login()
    cut()
    slide()
    search('python',10)
    web.close()


你可能感兴趣的:(python(爬虫)-学习笔记,python,爬虫,selenium)