selenium+python实现⾃动登陆QQ邮箱并发送邮件功能
本期做⼀个selenium详细实例,会把我在元素定位中遇到的⼀些阻塞和经验分享给⼤家。
(浏览器为Chrome)
(如果只需要最终的完整代码,请直接跳转到⽂章最后)
浏览器打开QQ邮箱登录⽹址
from selenium import webdriver
import time
zhengyi = webdriver.Chrome()
<('mail.qq/')
这⼀步没有遇到问题,⾄于为什么导⼊时间库,后⾯会说。
元素定位,输⼊QQ账号和QQ密码
⼿动进⼊QQ邮箱登录⽹页,按下F12打开开发者⼯具,点击查看元素,选择元素:
可以看到QQ账号输⼊框的id、name等属性,都是可以直接拿来定位的好选择。
我这⾥选择最通⽤的xpath⽅法来定位,id=‘u',并⽤send事件输⼊QQ账号
(也可以⽤zhengyi.find_element_by_id(‘u')来定位,代码更清晰,不过我个⼈习惯采⽤通⽤的xpath)
zhengyi.find_element_by_xpath('//*[@id="u"]').send_keys("XXXXXXXXX")
同理,QQ密码元素属性id=‘p',同样定位⽅法
zhengyi.find_element_by_xpath('//*[@id="p"]').send_keys("XXXXXXXX")
再同理,登录按钮元素属性id=‘login_button',这⾥不需要发送信息,所以选择click点击事件
zhengyi.find_element_by_xpath('//*[@id="login_button"]').click()
好的,原则上到此为⽌,运⾏pycharm应该是能够输⼊账号和密码,并登陆成功。
but事情没有那么简单,运⾏后发现,selenium报错,⽆法到id为u的元素。
进过仔细观察,发现是frame嵌套页⾯在作怪。
我们可以这样理解,每⼀个⽹页都是⼀个⽗类的frame,从我们访问这个⽹址开始,就已经进⼊了这个⽗类frame嵌套。顾名思义,有⽗即有⼦。frame(⽗)⾥嵌套了iframe(⼦),如果我们要定位的元素在iframe⾥,那么我们需要先切换⾄iframe。
iframe也是有⾃⼰的元素属性的,selenium也提供了switch⽅法供我们使⽤
重新F12往上iframe信息,可以看到被iframe嵌套了,id和name都是‘login_frame'
在定位元素之前,先输⼊如下代码:
zhengyi.switch_to.frame("login_frame")
这样就切换到iframe了,再继续之前的元素定位,即可成功。
这⼀步的完整代码为:
# 定位login_frame
zhengyi.switch_to.frame("login_frame")
zhengyi.find_element_by_xpath('//*[@id="switcher_plogin"]').click()
# 定位账号、密码,并输⼊
zhengyi.find_element_by_xpath('//*[@id="u"]').send_keys("839811794")
zhengyi.find_element_by_xpath('//*[@id="p"]').send_keys("199306zy")
# 定位登录按钮
zhengyi.find_element_by_xpath('//*[@id="login_button"]').click()
这个时候⽹页已经可以成功QQ邮箱。
元素定位,写信界⾯
继续,邮箱登录成功之后,来到写信界⾯,按照常规操作,我们需要先点击左上⾓写信按钮,展开具体写信界⾯
同样的⽅法,F12操作起来,查看写信按钮,元素定位为id=‘composebtn',发送点击事件
# 定位写信按钮
zhengyi.find_element_by_xpath('//*[@id="composebtn"]').click()
这个时候调试程序,⽹页成功打开结果为selenium定位不到id为composebtn的元素。
这个时候就暴露了在上⼀个环节中出现的问题,之前的代码将嵌套切换到了iframe的login_frame中,⽽此时的写信元素,不在iframe中,所以在定位之前,需要先离开这个嵌套,返回到主⽂档中
zhengyi.switch_to.default_content()
这样嵌套就切换到主⽂档了,再次运⾏程序,发现还是报错
通过反复查资料,终于发现,写信是在QQ登录后才会出现的。如果我们登陆之后的瞬间就去定位写信按钮,这个时候受⽹速、PC的客观影响,会定位不到元素。
我们只需要加⼀个sleep⼀秒,即可完美解决。(这个时候就体现了导⼊时间库的作⽤了~)
#离开login_frame
zhengyi.switch_to.default_content()
#等待⼀秒
time.sleep(1)
# 定位写信按钮
zhengyi.find_element_by_xpath('//*[@id="composebtn"]').click()
这样即可定位到写信按钮,进⼊到了发邮件的步骤
元素定位,邮件发送
邮件内容编辑有四个部分,收件⼈、主题、正⽂,以及最后点击发送按钮
通过之前踩的坑,到了这⼀步,我对iframe嵌套变得格外⼩⼼,准备定位的每个元素都去观察是否被iframe嵌套。
果不其然,“收件⼈”、“主题”和“发送”被主⽂档下的mainFrame嵌套了,⽽“正⽂”⼜被mainFrame的⼦frame嵌套了。
so,这⼀步的逻辑为:
1、先切换到mainFrame,
2、分别定位收件⼈和主题,调⽤发送事件
3、继续切换到⼦frame
4、定位正⽂,调⽤发送事件
5、从⼦frame,返回到它的⽗frame,也即是mainFrame中
6、定位发送按钮,调⽤点击事件
这⼀步中,也有很多意向不到的坑:
1、定位收件⼈的时候,发现定位到的元素,还有⼦div,经过模拟,发现只有第⼆个⼦div才是真正能够定位到收件⼈的元素,于是先定位id=‘toAreaCtrl',然后选择第⼆个div中的input作为定位。
具体xpath定位内容为:“//*[@id=‘toAreaCtrl']/div[2]/input”
2.同样是定位收件⼈遇到的问题,必须在切换到mainFrame后、定位收件⼈之前,加⼀个延迟执⾏,不然⼀定会⽆法定位到收件⼈元素。原因不明,所以我建议如果以后遇到元素定位不到,可以尝试加⼀个time.sleep。
3.定位正⽂时,从mainFrame切换到iframe,发现iframe的id和name是动态的⼀串数字,但是switch_to.frame只⽀持固定id或者name。所以想了别的法⼦,先⽤iframe的class进⾏xpath定位,然后把传给switch_to.frame来切换。具体为:
#切换到iframe
zhengyi.switch_to.frame(zhengyi.find_element_by_xpath('//*[@class="qmEditorIfrmEditArea"]'))
4.邮件正⽂需要先调⽤⼀个点击事件激活,才能启动send事件。如果没有先点击再编写,那么send的内容会放在主题后⾯的⽂本框中。(也不知道为啥会有这样的设定~)
所以综上所述,这⼀步的代码为:
# 切换到mainFrame
zhengyi.switch_to.frame('mainFrame')
time.sleep(1)
qq怎么发邮件
# 定位收件⼈,并输⼊
zhengyi.find_element_by_xpath("//*[@id='toAreaCtrl']/div[2]/input").send_keys("XXXXXXXXX@qq")
# 定位主题,并输⼊
zhengyi.find_element_by_xpath('//*[@id="subject"]').send_keys("来⾃zhengyi的邮件")
# 定位邮件正⽂,先进⼊到iframe
zhengyi.switch_to.frame(zhengyi.find_element_by_xpath('//*[@class="qmEditorIfrmEditArea"]'))
# 必须先点击正⽂,再send_keys
zhengyi.find_element_by_xpath('/html/body').click()
zhengyi.find_element_by_xpath('/html/body').send_keys("Hello World","\nZhengyi")
# 返回到mainframe
zhengyi.switch_to.parent_frame()
# 定位发送按钮
zhengyi.find_element_by_xpath('//*[@name="sendbtn"]').click()
(就这么短短数⼗⾏,耗死了不少脑细胞)
元素定位总结
1、frame很重要,⼀定要看清楚是否被嵌套,以及注意切换
2、元素的id或者name如果是动态的,请放弃
3、⽤xpath定位真⾹
4、如果元素有⼦节点,使⽤相对路径继续定位
5、实在排查不出为什么定位失败,尝试⼀下⽤time.sleep()
最终程序代码
from selenium import webdriver
#导⼊时间模块
import time
# 注意⼤写Chrome的C
zhengyi = webdriver.Chrome()
<('mail.qq/')
# 定位login_frame
zhengyi.switch_to.frame("login_frame")
zhengyi.find_element_by_xpath('//*[@id="switcher_plogin"]').click()
# 定位账号、密码,并输⼊
zhengyi.find_element_by_xpath('//*[@id="u"]').send_keys("839811794")
zhengyi.find_element_by_xpath('//*[@id="p"]').send_keys("199306zy")
# 定位登录按钮
zhengyi.find_element_by_xpath('//*[@id="login_button"]').click()
# 离开login_frame
# zhengyi.switch_to.parent_frame()
zhengyi.switch_to.default_content()
# 等待⼀秒
time.sleep(1)
# 定位写信按钮
zhengyi.find_element_by_xpath('//*[@id="composebtn"]').click()
# 切换到mainFrame
zhengyi.switch_to.frame('mainFrame')
time.sleep(1)
# 定位收件⼈,并输⼊
zhengyi.find_element_by_xpath("//*[@id='toAreaCtrl']/div[2]/input").send_keys("839811794@qq")
# 定位主题,并输⼊
zhengyi.find_element_by_xpath('//*[@id="subject"]').send_keys("来⾃zhengyi发来的邮件")
# 定位邮件正⽂,先进⼊到iframe
zhengyi.switch_to.frame(zhengyi.find_element_by_xpath('//*[@class="qmEditorIfrmEditArea"]'))
# 必须先点击正⽂,再send_keys
zhengyi.find_element_by_xpath('/html/body').click()
zhengyi.find_element_by_xpath('/html/body').send_keys("Hello World","\nZhengyi")
# 返回到mainframe
zhengyi.switch_to.parent_frame()
# 定位发送按钮
zhengyi.find_element_by_xpath('//*[@name="sendbtn"]').click()
time.sleep(5)
#关闭浏览器
zhengyi.quit()
tips
分享⼏点在开发者⼯具⾥,⽐较⽅便的⼩窍门:
1、在开发者⼯具⾥,选中元素,点击Console,可以很直观的看到元素是否被iframe嵌套