记录第一次使用Selenium完成阿里云盘签到的过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import timefrom selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.action_chains import ActionChainsfrom selenium.webdriver.support.wait import WebDriverWaitoption = webdriver.ChromeOptions() option.add_experimental_option('excludeSwitches' , ['enable-automation' ]) option.add_argument('--disable-blink-features=AutomationControlled' ) driver = webdriver.Chrome(options=option) driver.get("https://www.aliyundrive.com/sign/in" ) iframe=driver.find_element(By.CSS_SELECTOR, "iframe" ) driver.switch_to.frame(iframe) iframe=driver.find_element(By.CSS_SELECTOR, "iframe" ) driver.switch_to.frame(iframe) login=driver.find_elements(By.CSS_SELECTOR, "div.login-blocks.block0>.sms-login-link" )[-1 ] login.click() user=driver.find_element(By.CSS_SELECTOR, "input#fm-login-id" ) passwd=driver.find_element(By.CSS_SELECTOR, "input#fm-login-password" ) user.send_keys("" ) passwd.send_keys("" ) time.sleep(2 ) driver.find_element(By.CSS_SELECTOR, "button.fm-button.fm-submit.password-login[tabindex='3']" ).click() windows=driver.window_handles driver.switch_to.window(windows[-1 ]) driver.find_element(By.CSS_SELECTOR, "div.sign-btn---rZSA" ).click() driver.switch_to.frame(driver.find_element(By.CSS_SELECTOR, "iframe[src='https://pages.aliyundrive.com/mobile-page/web/dailycheckpc.html']" )) time.sleep(1 ) driver.find_element(By.CSS_SELECTOR, "img[src='//gw.alicdn.com/imgextra/i4/O1CN01aVlk0J1KIALJhQfCV_!!6000000001140-2-tps-72-72.png']" ).click() driver.switch_to.default_content() time.sleep(1 ) driver.find_element(By.CSS_SELECTOR, "span[data-icon-type='PDSMoreCircle']>svg" ).click() time.sleep(1 ) (driver.find_elements(By.CSS_SELECTOR, "div.outer-menu--ihDUR" ))[-1 ].click() time.sleep(1 ) driver.find_element(By.CSS_SELECTOR, "button.ant-btn.ant-btn-primary.ant-btn-dangerous" ).click() time.sleep(1 ) driver.quit()
首先在阿里云盘登录界面切换至账号密码登录,此时面临的第一个问题是滑块
滑块
在输入账号密码后会弹出滑块,让用户从左推到右。这算是比较简单的一种反爬虫手段了,然而我半天都没弄好
首先是尝试动作链ActionChains
1 2 3 4 5 6 7 8 9 action = ActionChains(driver) action.drag_and_drop(dragger, item1).perform() # 1.移动dragger到目标1 sleep(2) action.click_and_hold(dragger).release(item2).perform() # 2.效果与上句相同,也能起到移动效果 sleep(2) action.click_and_hold(dragger).move_to_element(item3).release().perform() # 3.效果与上两句相同,也能起到移动的效果 sleep(2) # action.drag_and_drop_by_offset(dragger, 400, 150).perform() # 4.移动到指定坐标 action.click_and_hold(dragger).move_by_offset(400, 150).release().perform() # 5.与上一句相同,移动到指定坐标
Selenium之动作链(ActionChains) - liangxb - 博客园 (cnblogs.com)
大致就是上面几种方法,还有些改进方法,如调整拖拽速度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 from selenium.webdriver import ActionChainsfrom selenium import webdriverimport timedriver=webdriver.Chrome() driver.get("https://www.qichacha.com/user_login" ) time.sleep(1 ) driver.find_element_by_xpath('//*[@id="normalLogin"]' ).click() time.sleep(1 ) huakuai=driver.find_element_by_xpath('//*[@id="nc_1_n1z"]' ) def get_track (distance ): track=[] current=0 mid=distance*4 /5 t=0.2 v=1 while current<distance: if current<mid: a=4 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 move_to_gap (slider,tracks ): ActionChains(driver).click_and_hold(slider).perform() for x in tracks: ActionChains(driver).move_by_offset(xoffset=x,yoffset=0 ).perform() time.sleep(0.5 ) ActionChains(driver).release().perform() if __name__ == '__main__' : move_to_gap(huakuai,get_track(340 ))
Python+Selenium 拖动滑块 (一) - 简书 (jianshu.com)
另外的参考文章:selenium之滑块操作_selenium拖动滑块_王大傻0928的博客-CSDN博客
使用selenium模拟登录解决滑块验证问题_锅炉房刘大爷的博客-CSDN博客
python- selenium 淘宝爬虫之滑块验证(滑动速度放慢版)_move_by_offset速度控制_小泽泽泽ya的博客-CSDN博客
selenium 滑动解锁(drag_and_drop_by_offset)_weixin_34067049的博客-CSDN博客
Selenium鼠标方法drag_and_drop_by_offset、move_by_offset无响应_我乱来的a的博客-CSDN博客
然而实际上都没用,因为阿里云盘设置了一些反爬虫手段
隐去特征
那么应该如何解决呢?
参考selenium 反爬虫之跳过滑块验证 - 知乎 (zhihu.com) , 在原有代码前加上:
1 2 3 4 option = webdriver.ChromeOptions() option.add_experimental_option('excludeSwitches' , ['enable-automation' ]) option.add_argument('--disable-blink-features=AutomationControlled' ) driver = webdriver.Chrome(options=option)
以及参考修改Chromedriver特征字符串_chromedriver 特征_MichaelYZ111的博客-CSDN博客 ,在Kali中用hexedit修改chromedriver.exe
将$cdc_asdjflasutopfhvcZLmcfl_改为任意字符
关于Options(),如登录时关闭密码保存提示框:
1 2 3 4 prefs = {} prefs['credentials_enable_service' ] = False prefs['profile.password_manager_enabled' ] = False options.add_experimental_option('prefs' , prefs)
其他用途参考:
selenium之options模块_selenium options__xiao_gu的博客-CSDN博客
就惊奇地发现不再需要滑块验证了!
Notes
之后便是水到渠成,有几个值得注意的点:
切换window, frame之后要加time.sleep(1)
,否则其后的find_element
会找不到
有时候CSS_SELECTOR
写的不好(不准确是一方面,有时候有些啥啥啥-id还会变,就不能用这个作为定位器)会导致定位到其他元素上,因此务必确保每一步都写对。
更方便的办法是F12右键copy selector,但仍有动态变化的可能
详见下文
做了好多次后发现突然又要滑块验证了,猜测是ip被察觉到异常访问了,于是在命令行中
1 2 ipconfig/release ipconfig/renew
获得新地址即可
等待
将time.sleep(1)
替换为隐式等待driver.implicity_wait(30)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import timefrom selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.action_chains import ActionChainsfrom selenium.webdriver.support.wait import WebDriverWaitoption = webdriver.ChromeOptions() option.add_experimental_option('excludeSwitches' , ['enable-automation' ]) option.add_argument('--disable-blink-features=AutomationControlled' ) prefs = {} prefs['credentials_enable_service' ] = False prefs['profile.password_manager_enabled' ] = False option.add_experimental_option('prefs' , prefs) driver = webdriver.Chrome(options=option) driver.get("https://www.aliyundrive.com/sign/in" ) driver.implicitly_wait(30 ) iframe=driver.find_element(By.CSS_SELECTOR, "iframe" ) driver.switch_to.frame(iframe) iframe=driver.find_element(By.CSS_SELECTOR, "iframe" ) driver.switch_to.frame(iframe) login=driver.find_elements(By.CSS_SELECTOR, "div.login-blocks.block0>.sms-login-link" )[-1 ] login.click() user=driver.find_element(By.CSS_SELECTOR, "input#fm-login-id" ) passwd=driver.find_element(By.CSS_SELECTOR, "input#fm-login-password" ) user.send_keys("18381605149" ) passwd.send_keys("xuharry2022" ) driver.find_element(By.CSS_SELECTOR, "button.fm-button.fm-submit.password-login[tabindex='3']" ).click() windows=driver.window_handles driver.switch_to.window(windows[-1 ]) driver.find_element(By.CSS_SELECTOR, "#layout > div.sider--3QQ75 > div > div > div > div.simplebar-wrapper > div.simplebar-mask > div > div > div > div > div.sider-top--2wTJr > ul > div > div > div.sign-btn---rZSA" ).click() driver.switch_to.frame(driver.find_element(By.CSS_SELECTOR, "iframe[src='https://pages.aliyundrive.com/mobile-page/web/dailycheckpc.html']" )) driver.find_element(By.CSS_SELECTOR, "#root > div > div.rax-view-v2.DailyCheckPc--main--1cbm8da > div.rax-view-v2.DailyCheckPc--header--2CXJvuw > div > img" ).click() driver.switch_to.default_content() driver.find_element(By.CSS_SELECTOR, "#layout > div.sider--3QQ75 > div > div > div > div.simplebar-wrapper > div.simplebar-mask > div > div > div > div > div.sider-bottom--2ltRW > div.bottom-wrapper--19rog > div.ant-dropdown-trigger.more-wrapper--2dMhX > span > svg" ).click() (driver.find_elements(By.CSS_SELECTOR, "body > div:nth-child(12) > div > div > ul > li:nth-child(13) > div > div" ))[-1 ].click() driver.find_element(By.CSS_SELECTOR, "body > div:nth-child(14) > div > div.ant-modal-wrap > div > div.ant-modal-content > div > div > div.ant-modal-confirm-btns > button.ant-btn.ant-btn-primary.ant-btn-dangerous" ).click() time.sleep(3 ) driver.quit()
关于显式和隐式的区别,
隐式等待的好处是不用像固定等待方法一样死等时间N秒,可以在一定程度上提升测试用例的执行效率。不过这种方法也存在一定的弊端,那就是程序会一直等待整个页面加载完成,也就是说浏览器窗口标签栏中不再出现转动的小圆圈,才会继续执行下一步。
显示等待会每个一段时间(该时间一般都很短,默认为0.5秒,也可以自定义),执行自定义的程序判断条件,如果判断条件成立,就执行下一步,否则继续等待,直到超过设定的最长等待时间,然后抛出TimeOutEcpection的异常信息。
selenium之WebDriverWait类(等待机制)_虚坏叔叔的博客-CSDN博客
implicitly_wait()隐式等待和explicit_wait()显示等待_implicitly_wait用法_油菜花啊的博客-CSDN博客
正则表达式匹配selector
在“退出登录”那步,复制selector得到:body > div:nth-child(12) > div > div > ul > li:nth-child(13) > div > div
CSS中的 :nth-child()
是一个伪类选择器,用于匹配其父元素的第 n 个子元素。而 div:nth-child()
则是指父元素的所有子元素中为 div
标签的第 n 个元素。
在给定的代码中,div:nth-child(12)
表示父元素中所有 div
标签的第 12 个元素,而 li:nth-child(13)
表示父元素中所有 li
标签的第 13 个元素。最终,div:nth-child(12) > div > div > ul > li:nth-child(13) > div > div
表示一个元素,它是具有如上所述子元素层次结构的 div
元素的子元素,即父元素的第 12 个 div
标签,其子元素是一个 div
标签,该标签的子元素是一个 ul
标签,ul
标签的子元素中第 13 个 li
标签,该 li
标签的子元素是一个 div
标签,而该 div
标签又具有一个子元素为 div
标签。
但这时可能会变成body > div:nth-child(13) > div > div > ul > li:nth-child(12) > div > div
这种情况甚至不好用正则表达式匹配
重新测试数次,都没再遇到上述情况,遂作罢
那么就不再对本次代码做正则匹配了。
如果要做,比如…>div.arch_main_开头的类:
首先定位到父元素,用findall(r'arch_main_(.*)',element.get_attribute('innerHTML')
获得其中以arch_main_开头的字符串
对get_attribute的测试:print(driver.find_element(By.CSS_SELECTOR, "div.sider-bottom--2ltRW > div.bottom-wrapper--19rog".get_attribute("innerHTML"))
可获得设置所在的div内容
之后可以遍历字符串,去匹配类
相关参考:
正则表达式
结合Selenium和正则表达式提高爬虫效率 - TVHead - 博客园 (cnblogs.com)
selenium get_attribute的几种用法_gaimechen的博客-CSDN博客
selenium利用正则表达式定位元素_selenium 正则表达式_杨二狗2333的博客-CSDN博客
cookie登录
参考selenium cookie 登录 - 风,又奈何 - 博客园 (cnblogs.com) ,先手动登录并保存cookie至本地,之后即可自动登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from selenium import webdriverimport timeimport jsondriver = webdriver.Chrome() url='https://www.csdn.net' driver.get(url) time.sleep(20 ) with open ('cookies.txt' ,'w' ) as cookief: cookief.write(json.dumps(driver.get_cookies())) driver.close()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from selenium import webdriverimport timeimport jsondriver = webdriver.Chrome() url='https://www.csdn.net' driver.get(url) driver.maximize_window() driver.delete_all_cookies() with open ('cookies.txt' ,'r' ) as cookief: cookieslist = json.load(cookief) for cookie in cookieslist: driver.add_cookie(cookie) driver.refresh() time.sleep(30 ) driver.quit()
问题是cookie可能会失效,对此作者给出两种办法:
将expiry类型变为int / 删除该字段
另一篇文章用的方法类似,只是改为yaml: Python Selenium Cookie 绕过验证码实现登录 - Blue·Sky - 博客园 (cnblogs.com)
这个也类似,获取sessionid和token,但没有实现token的更新:Python3+Selenium获取session和token供Requests使用教程 - adolfmc - 博客园 (cnblogs.com)
类似文章:python+selenium 通过添加cookies或token解决网页上验证码登录问题_selenium token_aiee的博客-CSDN博客
【Selenium 小知识】获取 token 和 cookies_selenium获取token_Warolitbos的博客-CSDN博客
略微不同的一篇博客,用了fiddler抓包获取token,之后直接带着token前往登录后的界面:
11、python+selenium绕过验证码登录 - YLG001 - 博客园 (cnblogs.com)
翻了好久仍没找到想要的,即如何像CSDN上阿里云盘三月自动签到Python脚本,可本地、青龙、云函数自动执行_残荷亭的博客-CSDN博客 做的,通过refresh_token获得新的access_token以及refresh_token,上面那些大都是通过手动方式获得token/cookie/sessionid,总结下就是cookie用get_cookie()
,token/sessionid用driver.ececute_script('return sessionStorage.getItem("token")')
这种。
待研究
借助refresh_token获取access_token