需要用到以下文件
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
防止打印一些无用或者显示错误的日志。
wait是为后面wait.until做准备。
# 初始化
# 防止打印一些无用的日志
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)
重点的来了,滑块验证需要分成两部分,
第一部分为将验证滑块的图片的,带缺口图片和完整背景图片保存。
第二部分比对这两张图片,并计算缺口位置距图片左边界的距离,
第三部分为模拟人工滑动滑块。
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.计算移动距离
将图一中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))
获取分为两步,第一步定位到某个页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)
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()