反爬机制壹——js控制的登录逻辑
没有表单form,根本不可能点击确认提交
- 北邮信息门户登录开发者控制台,没有From Data,只有Payload
- 但是Payload只有一行信息 - service: http://my.bupt.edu.cn/system/resource/code/auth/clogin.jsp?owner=1664271694
- 转到 http://my.bupt.edu.cn/system/resource/code/auth/clogin.jsp 进行登录,甚至连payload都没有了
- 只用urllib库和request库,我们什么都做不到
# from urllib.request import urlopen import requests
url ="http://my.bupt.edu.cn/"
header ={"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"}
para ={"service":"http://my.bupt.edu.cn/"}
resp = requests.get(url, params=para)
string = resp.text
print(string)
resp.close()
解决思路
- 用selenium
- 先get登录页面
options = FirefoxOptions()
options.add_argument('user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0"')
web = Firefox(options=options)# web = Chrome() 用Chrome也可以,但对应的options也要换成ChromeOptions
web.get("http://my.bupt.edu.cn/")
- 尝试使用find_element(By.XPATH, “//*”)去寻找对应元素
<ahref="javascript:;"i18n="login.type.password"class="">密码登录</a>
login_pwd = web.find_element(By.XPATH,"//*[@id="content-title"]/a[2]")
- 但是会出现无法定位
反爬机制贰——以文档形式嵌套的HTML网页
为什么找不到目标超链接元素?
- 我们先看网页代码,怎么会有两个html标签?
<!DOCTYPEhtml><html>
......
<body><iframeid="loginIframe"src="/authserver/cas/login-normal.html">
#document
<!DOCTYPEhtml><htmllang="en">
....
</html></iframe></body></html>
- 究竟怎么绘世呢?
- 如果我们尝试打印所有元素的class,我么会发现嵌套在iframe里面的html的标签的类根本不出现在find_elements中
l = web.find_elements(By.XPATH,"//*")for ele in l:print(ele.get_attribute('class'))
- 再细看,这里的src是啥
<iframeid="loginIframe"src="/authserver/cas/login-normal.html">
- 去访问一下 https://auth.bupt.edu.cn/authserver/cas/login-normal.html 吧
- 这原来是是登录网页的静态页面,登录的按钮、超链接等元素都封存在该静态页面里了
解决思路
- 由此,我们发现找不到元素的根本原因在于—— - 网站通过html的JavaScript访问内嵌html文件,取出了静态页面- 再把所需要展示的元素通过别的手段取出,填充到静态页面中- 在DevTools可视化界面中,我们会默认文件里的内容也是网页的一部分- 但实际上无论是直接在DevTools上直接拷贝,还是selenium中访问,都不会认为这个文件里面的内容是网页
- 实际上就是这种范式
<html><head>...</head><body><frameid=xxx,name=***><html><span>xx</span><p>......</p><div>x8888</div></html></frame></body></html>
此处参考:
python selenium获取html网页中嵌套的 frame/iframe中的html内容方法
- 做法: 先转到所在iframe中,再去搜索
iframe = web.find_element(By.XPATH,"//*[@id='loginIframe']")
web.switch_to.frame(iframe)
- 现在再找,也不会报错了
login_pwd = web.find_element(By.XPATH,"//a[@i18n='login.type.password']")
反爬机制叁——href为空的超链接元素
- selenium报错,不能点击
selenium.common.exceptions.ElementNotInteractableException: Message: Element <a href="javascript:;"> could not be scrolled into view
- 为啥呢?
解决思路
- 由于超链接为空(javascript:; ),所以这里大概是用js代码监听点击事件来控制页面显隐
- 这几个超链接所在div有id,因此,我们可以先从此入手
<divclass="content-title auto"id="content-title"><ahref="javascript:;"class="active"i18n="login.type.qrcode">扫码登录</a><ahref="javascript:;"i18n="login.type.password">密码登录</a><ahref="javascript:;"i18n="login.type.sms">短信登录</a></div>
- 在DevTools的network中搜索content-title,发现有且仅有一个与之相关的函数
//登录方式切换$('#content-title a').on('click',function(){var index =$(this).index();var length =$('#content-title a').length;
localStorage.loginType = index
for(var x =0; x < length; x++){if(x === index){$('#content-con div.content-con-box').eq(index).show()$('#content-title a').eq(index).addClass('active')}else{$('#content-con div.content-con-box').eq(x).hide()$('#content-title a').eq(x).removeClass('active')}if(index ===0)initQR();}})
从show, hide, active来看,很显然,就是用这个来控制页面显隐的
- 结合前人的经验来看,我们需要以js代码的形式来点击这个超链接
login_pwd = web.find_element(By.XPATH,"//a[@i18n='login.type.password']")
web.execute_script("arguments[0].click();", login_pwd)
此处参考:
Selenium模拟用户点击爬取javascript void(0)的超链接
- 看起来似乎已经结束了?
反爬机制肆——加载时间
仍然没有反应?
- 难道一切的努力都已经白费? - 非也
解决思路
- 只是切换完iframe之后还需要一段时间加载资源等,需要用time.sleep()来等等 😉
我知道你很急,但你先别急
iframe = web.find_element(By.XPATH,"//*[@id='loginIframe']")
web.switch_to.frame(iframe)
time.sleep(2)
login_pwd = web.find_element(By.XPATH,"//a[@i18n='login.type.password']")
web.execute_script("arguments[0].click();", login_pwd)
- 这样子,就大功告成了
- 网络状况良好的条件下,最低等待时间(time.sleep(n))大约在0.15s左右
反爬,终究是有极限的!
尾声——登录实现
time.sleep(0.2)
username = web.find_element(By.XPATH,'//*[@id="username"]')
password = web.find_element(By.XPATH,'//*[@id="password"]')
submit_btn = web.find_element(By.XPATH,'//*[@value="账号登录"]')
username.send_keys(usr)
password.send_keys(pwd)
submit_btn.click()
如今,类似的反爬机制已经大量应用于各个网站的登录界面,看似轻巧的设计却需要诸多时间去反制,站在巨人的肩膀上,我也花费了不少的时间才能走到这一步
不过有一说一,这很令人开心 😃
版权归原作者 小助手牧濑红莉栖 所有, 如有侵权,请联系我们删除。