使用selenium模拟登录解决滑块验证问题

本次主要是使用selenium模拟登录网页端的TX新闻,本来最开始是模拟请求的,但是某一天突然发现,部分账号需要经过滑块验证才能正常登录,如果还是模拟请求,需要的参数太多了,找的心累。不过好在TX的滑块验证是他们自己开发的,没有极验那么复杂,当然相反的,想要模拟就得自己去一点点探索了,毕竟对极验滑块的破解,网上已经可以找到现成的代码来用了。下面说一下模拟的实现过程和我遇见的问题。

1.登录入口
我是通过来当做登录入口的

部分代码实现:

1

2

3

driver = webdriver.Chrome()

  

driver.get(url)

2.点击“账号密码登录”
selenium可以实现对网页元素的定位,我这里是通过id属性来定位“帐号密码登录”按钮的。这里需要注意的是,有时候可能会因为网络不好等问题导致加载登录入口页会很慢,所以在点击“帐号密码登录”按钮前,需要做一个判断:判断代表“帐号密码登录”的HTML元素是否已经加载完成。

“账号密码登录”按钮的id属性截图:

部分代码实现

element = WebDriverWait(driver, 5, 0.5).until(

  

EC.presence_of_element_located((By.ID, "switcher_plogin")))

 
# from selenium.webdriver.common.by import By element.click()

3.输入账号、密码并点击登录

这一步比较简单,直接上代码:

driver.find_element_by_id('u').send_keys('123456') # 输入用户名

  

driver.find_element_by_id('p').send_keys('ccccc') # 输入密码

  

driver.find_element_by_id('login_button').click() # 点击登录

4.滑块验证过程

1)简要说明

因为主要目的就是为了模拟滑块验证,所以在输入用户名和密码的时候直接选择输入“123456”和“ccccc”,这样就必然会跳到滑块验证的页面:

接下来的问题就是如何模拟滑动的过程。这里首先要说一下,经过多次测试发现,TX的滑块验证每次需要拖动的距离是有一定范围的,“缺口”部分的位置基本上都在靠右侧的一面,不像极验的滑块验证,“缺口”部分可能出现在任意的位置,这样在实现“滑动”过程前,就必须判断每次滑动的距离是多少,具体可以看看学习一下大神们都是如何实现极验滑块验证的。所以,对于TX的滑块验证,只要设置一个大概的距离“模拟滑动”即可,失败的时候可以通过增减移动距离进行重试,后面会进一步说明。

2)为什么找不到“蓝色滑块”

前面已经点击了“登录”并跳转到“安全验证”的页面,接着就是去模拟“拖动”截图中的“蓝色滑块”,所以首先要告诉driver,代表“蓝色滑块”的html元素是什么。代表“蓝色滑块”的html元素截图:

使用selenium模拟登录解决滑块验证问题_第1张图片

通过上面的截图可以知道,id值为"tcaptcha_drag_button"的div标签代表的就是“蓝色滑块”,所以最开始我是直接尝试去拖动它,但是这时候发现报错了,部分截图如下:

使用selenium模拟登录解决滑块验证问题_第2张图片

报错的原因很明显,在当前得到的所有html元素中,找不到id值为"tcaptcha_drag_button"的div标签。这是为什么?

3)切换frame

为什么出现上面的问题?通过查找相关的资料才知道,在跳转到“安全验证”的页面的时候,“进入”了一个新的frame,可以理解为,在“登录页面”嵌套了一个“验证页面”,而当前的driver加载的html元素全部都是“登录页面”的,想要找到并拖动“蓝色滑块”,就要先切换到“验证页面”,这里通过driver.switch_to方法实现:

iframe = driver.find_element_by_xpath('//iframe') # 找到“嵌套”的iframe

  

driver.switch_to.frame(iframe)     # 切换到iframe

4)模拟拖动

切换到iframe之后,就可以通过driver.find_element_by_id('tcaptcha_drag_button')找到“蓝色滑块”并拖动它了。拖动操作会用到selenium.webdriver的ActionChains类,部分代码如下:

button = driver.find_element_by_id('tcaptcha_drag_button')    # 找到“蓝色滑块”

  

action = ActionChains(driver)            # 实例化一个action对象

  

action.click_and_hold(button).perform()  # perform()用来执行ActionChains中存储的行为

  

action.reset_actions()

  

action.move_by_offset(180, 0).perform()  # 移动滑块

5)构造移动轨迹

为了使拖动过程模拟的更“真实”,可以构造一个滑动轨迹,我这里也是参考了别人的代码看这里,简单实现了一下,实际上TX新闻的滑块验证对这方面好像要求不是很严格:

def get_track(distance):

  

track = []

  

current = 0

  

mid = distance * 3 / 4

  

t = 0.2

  

v = 0

  

while current < distance:

  

if current < mid:

  

a = 2

  

else:

  

a = -3

  

v0 = v

  

v = v0 + a * t

  

move = v0 * t + 1 / 2 * a * t * t

  

current += move

  

track.append(round(move))

  

return track

6)如何确定已经“验证成功”了

接下来的问题就是,我如何告诉程序,已经“验证成功”了呢?经过测试发现,当拖动滑块完成拼图“验证成功”后,网页又从“安全验证”的页面又跳回了“登录页面”,滑动前截图:

使用selenium模拟登录解决滑块验证问题_第3张图片

滑动验证成功的截图:

使用selenium模拟登录解决滑块验证问题_第4张图片

 成功后跳转回“登录”页面:

使用selenium模拟登录解决滑块验证问题_第5张图片

通过上面的截图我们可以知道,在“验证通过”之前,在“安全验证”页面我们一直可以看到“拖动下方滑块完成拼图”的文字提示,也就是说,如果验证没有通过,那么在当前的所有html元素中,我们是可以找到文本为“拖动下方滑块完成拼图”的标签的: 

使用selenium模拟登录解决滑块验证问题_第6张图片

通过截图可以知道,该标签的class为"tcaptcha-title",通过driver.find_element_by_class_name('tcaptcha-title').text来判断验证是否成功。

7)重试

前面说了,我们可以通过提前设置一个“可能的”值当初始距离来移动滑块,如果移动的距离“过长”,就减小该值当做下次移动的距离,所以可以加一个while循环。以上过程实现的完整代码如下:

# encoding=utf8

  

  

  

from time import sleep

  

from selenium import webdriver

  

from selenium.webdriver import ActionChains

  

from selenium.webdriver.common.by import By

  

from selenium.webdriver.support import expected_conditions as EC

  

from selenium.webdriver.support.wait import WebDriverWait

  

  

  

url = 'https://xui.ptlogin2.qq.com/cgi-bin/xlogin?&low_login=0&appid=636014201&target=self&border_radius=1&maskOpacity=40&s_url=http%3A//www.qq.com/qq2012/loginSuccess.htm'

  

  

  

def get_track(distance):

  

track = []

  

current = 0

  

mid = distance * 3 / 4

  

t = 0.2

  

v = 0

  

while current < distance:

  

if current < mid:

  

a = 2

  

else:

  

a = -3

  

v0 = v

  

v = v0 + a * t

  

move = v0 * t + 1 / 2 * a * t * t

  

current += move

  

track.append(round(move))

  

return track

  

  

  

def main():

  

driver = webdriver.Chrome()

  

driver.set_window_position(900, 10)

  

driver.get(url)

  

# 检测id为"switcher_plogin"的元素是否加在DOM树中,如果出现了才能正常向下执行

  

element = WebDriverWait(driver, 5, 0.5).until(

  

EC.presence_of_element_located((By.ID, "switcher_plogin"))

  

)

  

element.click()

  

  

  

sleep(1)

  

# 输入用户名和密码

  

driver.find_element_by_id('u').clear()

  

driver.find_element_by_id('u').send_keys('123456')

  

driver.find_element_by_id('p').clear()

  

driver.find_element_by_id('p').send_keys('ccccc')

  

sleep(1)

  

# 点击登录

  

driver.find_element_by_id('login_button').click()

  

  

  

sleep(5)

  

  

  

# 切换iframe

  

try:

  

iframe = driver.find_element_by_xpath('//iframe')

  

except Exception as e:

  

print 'get iframe failed: ', e

  

sleep(2) # 等待资源加载

  

driver.switch_to.frame(iframe)

  

  

  

# 等待图片加载出来

  

WebDriverWait(driver, 5, 0.5).until(

  

EC.presence_of_element_located((By.ID, "tcaptcha_drag_button"))

  

)

  

try:

  

button = driver.find_element_by_id('tcaptcha_drag_button')

  

except Exception as e:

  

print 'get button failed: ', e

  

  

  

sleep(1)

  

# 开始拖动 perform()用来执行ActionChains中存储的行为

  

flag = 0

  

distance = 195

  

offset = 5

  

times = 0

  

while 1:

  

action = ActionChains(driver)

  

action.click_and_hold(button).perform()

  

action.reset_actions() # 清除之前的action

  

print distance

  

track = get_track(distance)

  

for i in track:

  

action.move_by_offset(xoffset=i, yoffset=0).perform()

  

action.reset_actions()

  

sleep(0.5)

  

action.release().perform()

  

sleep(5)

  

  

  

# 判断某元素是否被加载到DOM树里,并不代表该元素一定可见

  

try:

  

alert = driver.find_element_by_class_name('tcaptcha-title').text

  

except Exception as e:

  

print 'get alert error: %s' % e

  

alert = ''

  

if alert:

  

print u'滑块位移需要调整: %s' % alert

  

distance -= offset

  

times += 1

  

sleep(5)

  

else:

  

print '滑块验证通过'

  

flag = 1

  

driver.switch_to.parent_frame()    # 验证成功后跳回最外层页面

  

break

  

  

  

sleep(2)

  

driver.quit()

  

print "finish~~"

  

return flag

  

  

  

if __name__ == '__main__':

  

main()

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走!

使用selenium模拟登录解决滑块验证问题_第7张图片

使用selenium模拟登录解决滑块验证问题_第8张图片

你可能感兴趣的:(技术分享,软件测试,selenium,测试工具)