hello兄弟们,这里是无聊的网友。愉快的周末过去了,欢迎回到学习频道。书接上文,我们说到了再用selenium登录12306时遇到了滑块验证的问题。当前的网站几乎每家都会在登录模块添加一个认证,来规避各种爬虫,而我们则不断的去想办法绕过这些验证模块。目前一些简单的验证如图片拼接,汉字识别的都可以借助如图鉴,超级鹰提供的功能解决。而主流的流行应用的验证绕过则需要非常复杂的步骤。我们就有点自知之明,先能够做到简单的绕过就好。
就拿12306为例,目前还只是最简单的滑块验证,还好不是类似哔哩哔哩的极验类的滑块验证,所以我们趁着它简单先拿它开刀
我们可以看到在输入账号密码后,页面就会跳出滑块认证,依旧是先分析再动手
按照正常思路,先点击到滑块认证,然后鼠标点击滑块,拖动到最边上。我们首先右键检查滑块认真,看看验证模块有没有存在iframe中
检查完发现div并没有被包裹在iframe中,就存在页面源码中,我们就可以直接通过find_element定位到此处并进行点击动作
先放上上一期的代码
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
# 定义登录方法
def login(user, pwd):
login_choice = web.find_element(By.XPATH, '//*[@id="toolbar_Div"]/div[2]/div[2]/ul/li[1]')
# 点击账号密码登录方式
login_choice.click()
web.find_element(By.XPATH, '//*[@id="J-userName"]').send_keys(user) # 向账号框传入账号信息
web.find_element(By.XPATH, '//*[@id="J-password"]').send_keys(pwd) # 向密码框传入密码
# 定位到登录按钮并点击
web.find_element(By.XPATH, '//*[@id="J-login"]').click()
if __name__ == '__main__':
opt = Options()
opt.add_experimental_option('excludeSwitches', ['enable-automation']) # 去除浏览器顶部显示受自动化程序控制
opt.add_experimental_option('detach', True) # 规避程序运行完自动退出浏览器
web = Chrome(options=opt)
web.get('https://kyfw.12306.cn/otn/resources/login.html')
user = '' # 此处输入账号
pwd = '' # 此处输入密码
login(user, pwd)
按照设计思路,我们应当在登录方法处添加模拟点击滑块的代码
到了这一步,学完基础的同学就应该想到要借助ActionChains,ActionChains模拟鼠标操作的常用方法。使用click()方法可以进行鼠标的单击操作,此外还提供了有关双击、右击、悬停、鼠标拖动等功能的方法,这里不一一阐述了。
设计代码如下
span = web.find_element(By.XPATH, '//*[@id="nc_2_n1z"]') # 首先定位到滑块
action = ActionChains(web)
action.click_and_hold(span).move_by_offset(300, 0).perform() # click_and_hold代表点击并保持点击动作。move_by_offset(x, y),其中x代表水平移动距离,y代表垂直移动距离
可以看到整个滑块的水平长度为340而需要移动的小滑块为38.4的宽度。所以大致需要移动的距离为340-38.4。如果move_by_offset的x参数过大可以导致程序报错超出滑框宽度,在循环中若移动的参数过小可能到达不了最右侧或者超时认证。因此此处我直接一次性滑动300.
编写完运行检测效果
可以看到第一次的运行失败,报错信息为滑块验证element没有定位到。那么为什么会出现这样的错误呢。是因为我们的速度太快了
因为我们的点击请求过快,导致页面的滑块验证模块还没有跳转出来就直接进行点击请求了,解决的方法也很简单,可以直接使用time.sleep进行强制睡眠。但我们不建议这样使用,最好是采用显示等待的方法对其处理。
我们都知道显式等待也称为智能等待,针对指定元素定位指定等待时间,在指定时间范围内进行元素查找,找到元素则直接返回,如果在超时还没有找到元素,则抛出异常,显示等待是 selenium 当中比较灵活的一种等待方式,他的实现原理其实是通过 while 循环不停的尝试需要进行的操作。那么知道了原理后就直接上手用它。
在定位到滑块验证并点击前设置显示等待。需要下面两个包配合使用
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 此处还去掉了点击滑块验证的操作
# 设置显示等待直到滑块的span标签被定位到
WebDriverWait(web, 0.5, 0.05).until(EC.presence_of_element_located((By.ID, 'nc_1_n1z')))
span = web.find_element(By.ID, 'nc_1_n1z') # 首先定位到滑块
WebDriverWait()中的参数主要为等待时间和刷新时间,经过我的测试0.5为我的程序最少的需要的等待时间,再少程序就要报错,同样0.05为最少刷新时间
这个是和个人电脑与网速有关的,报错的同学可以尝试修改
编写后在再次测试
ok,最后运行成功。成功绕过12396的滑块认证。老规矩运行成功放源码
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 定义登录方法
def login(user, pwd):
login_choice = web.find_element(By.XPATH, '//*[@id="toolbar_Div"]/div[2]/div[2]/ul/li[1]')
# 点击账号密码登录方式
login_choice.click()
username = web.find_element(By.XPATH, '//*[@id="J-userName"]') # 向账号框传入账号信息
passwd = web.find_element(By.XPATH, '//*[@id="J-password"]') # 向密码框传入密码
username.click()
username.send_keys(user)
passwd.click()
passwd.send_keys(pwd)
# 定位到登录按钮并点击
web.find_element(By.XPATH, '//*[@id="J-login"]').click()
# 设置显示等待直到滑块的span标签被定位到
WebDriverWait(web, 0.5, 0.05).until(EC.presence_of_element_located((By.ID, 'nc_1_n1z')))
span = web.find_element(By.ID, 'nc_1_n1z')
action = ActionChains(web)
action.click_and_hold(span).move_by_offset(300, 0).perform() # click_and_hold代表点击并保持点击动作。move_by_offset(x, y),其中x代表水平移动距离,y代表垂直移动距离
if __name__ == '__main__':
opt = Options()
opt.add_experimental_option('excludeSwitches', ['enable-automation']) # 去除浏览器顶部显示受自动化程序控制
opt.add_experimental_option('detach', True) # 规避程序运行完自动退出浏览器
web = Chrome(options=opt)
web.get('https://kyfw.12306.cn/otn/resources/login.html')
# 解除浏览器特征识别selenium
script = 'Object.defineProperty(navigator,"webdriver", {get: () => false,});'
web.execute_script(script)
user = '' # 此处输入账号
pwd = '' # 此处输入密码
login(user, pwd)