selenium+python实现自动登陆QQ邮箱并发送邮件功能


Posted in Python onDecember 13, 2019

本期做一个selenium详细实例,会把我在元素定位中遇到的一些阻塞和经验分享给大家。
(浏览器为Chrome)

(如果只需要最终的完整代码,请直接跳转到文章最后)

浏览器打开QQ邮箱登录网址

QQ邮箱登录地址为:https://mail.qq.com/

from selenium import webdriver
 import time
 zhengyi = webdriver.Chrome()
 zhengyi.get('https://mail.qq.com/')

这一步没有遇到问题,至于为什么导入时间库,后面会说。

元素定位,输入QQ账号和QQ密码

手动进入QQ邮箱登录网页,按下F12打开开发者工具,点击查看元素,选择元素:

selenium+python实现自动登陆QQ邮箱并发送邮件功能

可以看到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'

selenium+python实现自动登陆QQ邮箱并发送邮件功能

在定位元素之前,先输入如下代码:

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()

这样即可定位到写信按钮,进入到了发邮件的步骤

元素定位,邮件发送

selenium+python实现自动登陆QQ邮箱并发送邮件功能

邮件内容编辑有四个部分,收件人、主题、正文,以及最后点击发送按钮

通过之前踩的坑,到了这一步,我对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”

selenium+python实现自动登陆QQ邮箱并发送邮件功能

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"]'))

selenium+python实现自动登陆QQ邮箱并发送邮件功能

4.邮件正文需要先调用一个点击事件激活,才能启动send事件。如果没有先点击再编写,那么send的内容会放在主题后面的文本框中。(也不知道为啥会有这样的设定~)

所以综上所述,这一步的代码为:

# 切换到mainFrame
zhengyi.switch_to.frame('mainFrame')
time.sleep(1)
# 定位收件人,并输入
zhengyi.find_element_by_xpath("//*[@id='toAreaCtrl']/div[2]/input").send_keys("XXXXXXXXX@qq.com")
# 定位主题,并输入
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()
zhengyi.get('https://mail.qq.com/')
# 定位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.com")
# 定位主题,并输入
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嵌套

selenium+python实现自动登陆QQ邮箱并发送邮件功能

2、Elements,选中想定位的元素,右键?COPY?Copy XPath,可以直接复制元素的xpath
3、希望您能给我分享一点tips

总结

以上所述是小编给大家介绍的selenium+python实现自动登陆QQ邮箱并发送邮件功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
python生成随机mac地址的方法
Mar 16 Python
python中pylint使用方法(pylint代码检查)
Apr 06 Python
python使用PIL给图片添加文字生成海报示例
Aug 17 Python
python编程使用协程并发的优缺点
Sep 20 Python
centos6.5安装python3.7.1之后无法使用pip的解决方案
Feb 14 Python
深入浅析python 协程与go协程的区别
May 09 Python
详解pandas删除缺失数据(pd.dropna()方法)
Jun 25 Python
pybind11在Windows下的使用教程
Jul 04 Python
tensorflow 报错unitialized value的解决方法
Feb 06 Python
Python进程的通信Queue、Pipe实例分析
Mar 30 Python
一个非常简单好用的Python图形界面库(PysimpleGUI)
Dec 28 Python
windows系统Tensorflow2.x简单安装记录(图文)
Jan 18 Python
python中设置超时跳过,超时退出的方式
Dec 13 #Python
python opencv实现gif图片分解的示例代码
Dec 13 #Python
python多进程并发demo实例解析
Dec 13 #Python
使用Matplotlib 绘制精美的数学图形例子
Dec 13 #Python
python plotly画柱状图代码实例
Dec 13 #Python
Pytorch实现的手写数字mnist识别功能完整示例
Dec 13 #Python
使用matplotlib绘制图例标签中带有公式的图
Dec 13 #Python
You might like
php XMLWriter类的简单示例代码(RSS输出)
2011/09/30 PHP
PHP编写RESTful接口
2016/02/23 PHP
WordPress中设置Post Type自定义文章类型的实例教程
2016/05/10 PHP
PHP读取并输出XML文件数据的简单实现方法
2017/12/22 PHP
jQuery中setTimeout的几种使用方法小结
2013/04/07 Javascript
jQuery中animate动画第二次点击事件没反应
2015/05/07 Javascript
javascript轻量级库createjs使用Easel实现拖拽效果
2016/02/19 Javascript
Bootstrap每天必学之标签页(Tab)插件
2020/08/09 Javascript
JavaScript中0和""比较引发的问题
2016/05/26 Javascript
Javascript实现图片不间断滚动的代码
2016/06/22 Javascript
轮播图组件js代码
2016/08/08 Javascript
一步一步封装自己的HtmlHelper组件BootstrapHelper(三)
2016/09/14 Javascript
基于JS对象创建常用方式及原理分析
2017/06/28 Javascript
EL表达式截取字符串的函数说明
2017/09/22 Javascript
JS跳转手机站url的若干注意事项
2017/10/18 Javascript
浅谈vue中慎用style的scoped属性
2017/11/28 Javascript
利用jsonp解决js读取本地json跨域的问题
2018/12/11 Javascript
js中比较两个对象是否相同的方法示例
2019/09/02 Javascript
layui 表格操作列按钮动态显示的实现方法
2019/09/06 Javascript
详解python 发送邮件实例代码
2016/12/22 Python
基于Python的关键字监控及告警
2017/07/06 Python
PyQt5每天必学之关闭窗口
2018/04/19 Python
pytorch载入预训练模型后,实现训练指定层
2020/01/06 Python
基于Tensorflow使用CPU而不用GPU问题的解决
2020/02/07 Python
CSS3中Transition动画属性用法详解
2016/07/04 HTML / CSS
马来西亚领先的在线礼品店:Giftr
2018/08/23 全球购物
建筑工程实习自我鉴定
2013/09/19 职场文书
《猴子种树》教学反思
2014/02/14 职场文书
大学军训感言400字
2014/03/11 职场文书
2014年教师党员公开承诺书
2014/05/28 职场文书
留学生求职信
2014/06/03 职场文书
移交协议书
2014/08/19 职场文书
银行贷款委托书范本
2014/10/11 职场文书
公司庆典欢迎词
2015/01/26 职场文书
民事诉讼答辩状范文
2015/05/21 职场文书
电工实训心得体会
2016/01/14 职场文书