3ba242dc7c.jpg"/>
教程一:博客园
误区一:
下面整个button区域对应着整个登录框,可以直接使用id里的标签进行定位,而不是第1个span标签
button = self.wait.until(EC.presence_of_element_located((By.ID,"submitBtn")))误区二:注意关于图片的一些知识点
img1 = Image.open("E:/python27/pic/1.jpg")
img1.size
r, g, b = img1.split()
打开图片:Image.open(fp, mode='r')
保存图片:Image.save(fp, format=None, **params):
展示图片:Image.show(title=None, command=None):location = img.location
# location属性可以返回该图片对象(既这张图片)在浏览器中的位置,以字典的形式返回,{‘x’:30,‘y’:30} 这里我们图片的位置是(30,30)。坐标轴是以屏幕左上角为原点,x轴向右递增,y轴像下递增。(相对整个html的坐标)
size = img.size
# 通过size获取属性大小,size属性同样返回一个字典,{‘height’:30,‘width’:30 } 即图片对象的高度,宽度。
four_corner =(location['x'],
location['y'],
location['x']+size['width'],
location['y']+size['height'])
# 通过location和size获取上下左右,注意是小写的x和y
captcha = screenshot.crop((top, bottom, left, right))
# Image.crop() 从图像中提取出某个矩形大小的图像。它接收一个四元素的元组作为参数,各元素为(left, upper, right, lower),坐标系统的原点(0, 0)是左上角。# 因为上面截图,截取到的是整个页面,现在只把验证码图片取到
image.size属性是一个列表,下标0是横坐标(宽,图片的最右边),下标1是纵坐标
image.size[0]
image.size[1]完整代码
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from selenium import webdriver
# import PIL.Image as image
from PIL import Image
from io import BytesIO
import time
EMAIL =
PASSWORD =
BODER = 6
INIT_LEFT = 60
class CrackGeetest():
def __init__(self):
self.url = 'http://www.sf-express.com/cn/sc/dynamic_function/waybill'
self.browser = webdriver.Chrome('D:\\chromedriver.exe')
self.wait = WebDriverWait(self.browser, 100)
self.email = EMAIL
self.keyword = PASSWORD
self.BORDER = 6
def open(self):
"""
打开浏览器,并输入用户名和密码
:return:None
"""
self.browser.get(self.url)
email = self.wait.until(EC.presence_of_element_located((By.ID, 'email')))
password = self.wait.until(EC.presence_of_element_located((By.ID, 'password')))
email.send_keys(self.email)
password.send_keys(self.password)
def get_geetest_button(self):
"""
点击按钮,弹出没有缺口的图片
:return: 返回按钮对象
"""
button = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_radar_tip')))
return button
def __del__(self):
self.browser.close()
def get_position(self):
"""
获取验证码位置
:return:验证码位置元组
"""
img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img')))
time.sleep(2)
# location属性可以返回该图片对象(既这张图片)在浏览器中的位置,以字典的形式返回,{‘x’:30,‘y’:30} 这里我们图片的位置是(30,30)。坐标轴是以屏幕左上角为原点,x轴向右递增,y轴像下递增。(相对整个html的坐标)
location = img.location
# size属性同样返回一个字典,{‘height’:30,‘width’:30 } 即图片对象的高度,宽度。
size = img.size # 通过sizes属性获取大小
top,bottom,left,right = location['y'],location['y']+size['height'],location['x'],location['x']+size['width'] # 通过location和size获取上下左右
return (top,bottom,left,right)
def get_screenshot(self):
"""
获取网页截图
:return: 截图对象
"""
pic.png = self.browser.get_screenshot_as_png()
screenshot = Image.open(BytesIO(pic.png)) # 转换为字节对象
'''
img1 = Image.open("E:/python27/pic/1.jpg")
img1.size
r, g, b = img1.split()
打开图片:Image.open(fp, mode='r')
保存图片:Image.save(fp, format=None, **params):
展示图片:Image.show(title=None, command=None):
'''
def get_geetest_image(self, name='captcha.jpg'):
"""
获取验证码图片
:return: 图片对象
"""
top, bottom, left, right = self.get_position() # 定位到这个位置之后,再截图
print('验证码位置',top, bottom, left, right)
screenshot = self.get_screenshot() # 获取到截图
# Image.crop() 从图像中提取出某个矩形大小的图像。它接收一个四元素的元组作为参数,各元素为(left, upper, right, lower),坐标系统的原点(0, 0)是左上角。
captcha = screenshot.crop((top, bottom, left, right)) # 因为上面截图,截取到的是整个页面,现在只把验证码图片取到
captcha.save(name) # 把图片存储起来,name是传进来的变量
return captcha
def get_slider(self):
"""
获取滑块
:return: 滑块对象
"""
slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))
return slider
def is_pixel_equal(self, img1, img2, x, y):
"""
判断两个像素是否相同
:param image1: 图片1
:param image2: 图片2
:param x: 位置x
:param y: 位置y
:return: 像素是否相同
"""
# 取两个图片的像素点 (img.load()[x, y]获取某一点的像素值)
pixel1 = img1.load()[x, y] # 上面的 i 和 j
pixel2 = img2.load()[x, y]
threshold = 60 # 控制误差
if (abs(pixel1[0] - pixel2[0] < threshold) and abs(pixel1[1] - pixel2[1] < threshold) # 0 1 2 是RGB的三种颜色
and abs(pixel1[2] - pixel2[2] < threshold)):
return True
else: # 有一个超过阈值,则认为2个像素点不一致
return False
def get_gap(self, img1, img2):
"""
获取缺口偏移量
:param img1: 不带缺口图片
:param img2: 带缺口图片
:return:
"""
left = 60 # 从图片的60处开始往右,这样就可以把被拖动滑块跳过去,因为被拖动滑块处像素也不一样
# 相当于从60开始,一列列做对比
for i in range(left, img1.size[0]): # i 相当于横坐标 size属性是一个列表,下标0是横坐标(宽,图片的最右边),下标1是纵坐标
for j in range(img1.size[1]): # j 相当于纵坐标 从0开始
if not self.is_pixel_equal(img1, img2, i, j):
left = i # 如果相同位置的像素不一致,则替换为i
return left
return left
def get_track(self, distance):
"""
根据偏移量获取移动轨迹
:param distance: 偏移量
:return: 移动轨迹
"""
# 移动轨迹
track = []
# 当前位移
current = 0
# 减速阈值
mid = distance * 4 / 5
# 计算间隔
t = 0.2
# 初速度
v = 0
while current < distance: # 所以 track是不会大于总长度的
if current < mid:
# 加速度为正2
a = 2
else:
# 加速度为负3
a = -3
# 初速度v0
v0 = v
# 移动距离x = v0t + 1/2 * a * t^2,现做了加速运动
move = v0 * t + 1 / 2 * a * t * t
# 当前速度v = v0 + at 速度已经达到v,该速度作为下次的初速度
v = v0 + a * t
# 当前位移
current += move
# 加入轨迹
track.append(round(move)) # track 就是最终鼠标在 X 轴移动的轨迹
return track
def move_to_gap(self, slider, track):
"""
拖动滑块到缺口处
:param slider: 滑块
:param track: 轨迹
:return:
"""
ActionChains(self.browser).click_and_hold(slider).perform() # 利用动作链,获取slider,perform是
for x in track:
ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform() # xoffset横坐标,yoffset纵坐标。使得鼠标向前推进
time.sleep(0.5) # 推动到合适位置之后,暂停一会
ActionChains(self.browser).release().perform() # 抬起鼠标左键
def login(self):
"""
登陆
:return: None
"""
submit = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'login-btn')))
submit.click() # 登陆按钮
time.sleep(10)
print('登陆成功')
def crack(self):
# 打开浏览器,输入用户名和密码
self.open()
# 点击按钮,弹出没有缺口的图片
button = self.get_geetest_button()
button.click()
time.sleep(0.5)
# 获取验证码图片
image1 = self.get_geetest_image('captcha1.png')
#点按呼出滑块
slider = self.get_slider()
slider.click() # 一般的验证码,只有点完slide按钮之后,才会出现缺口
# 获取带缺口的验证码图片
image2 = self.get_geetest_image('captcha2.png')
# 获取缺口位置
gap = self.get_gap(image1, image2)
print('缺口位置', gap)
# 减去缺口的位移(因为,滑块移动img1到img2的时候,允许有误差范围。在范围内,即使有几个像素差,也是可以接受的
gap -= BODER
# 获取移动轨迹
track = self.get_track(gap)
print('滑动轨迹',track)
# 拖动滑块到缺口处。模拟人的行为习惯(先匀加速拖动后匀减速拖动),把需要拖动的总距离分成一段一段小的轨迹
self.move_to_gap(slider, track)
success = self.wait.until(
EC.text_to_be_present_in_element((By.CLASS_NAME,'geetest_sucess_radar_tip_constant'),'验证成功') # 证明极验成功
)
print(success)
# 失败后重试
if not success:
self.crack()
else:
self.login()
if __name__ == '__main__':
print('开始验证')
crack = CrackGeetest()
crack.crack()
print('验证成功')还有可以识别极验验证码的api可以用
https://www.kancloud.cn/abcabc123/yiyunapi/1879968