一、浏览器打开QQ邮箱登录网址
QQ邮箱登录地址为:https://mail.qq.com/
driver = webdriver.Chrome()
driver.get('https://mail.qq.com/')
二、元素定位,输入QQ账号和QQ密码
手动进入QQ邮箱登录网页,按下option+command+i打开开发者工具,点击查看元素,选择元素:
可以看到QQ账号输入框的id、name等属性,都是可以直接拿来定位的好选择。
我这里选择最通用的xpath方法来定位,id=‘u’,并用send事件输入QQ账号
(也可以用driver.find_element_by_id(‘u’)来定位,代码更清晰,不过我个人习惯采用通用的xpath)
driver.find_element_by_xpath('//*[@id="u"]').send_keys("XXXXXXXXX")
同理,QQ密码元素属性id=‘p’,同样定位方法
driver.find_element_by_xpath('//*[@id="p"]').send_keys("XXXXXXXXX")
再同理,登录按钮元素属性id=‘login_button’,这里不需要发送信息,所以选择click点击事件
driver.find_element_by_xpath('//*[@id="login_button"]').click()
好的,原则上到此为止,运行pycharm应该是能够输入账号和密码,并登陆成功。
但是事情没有那么简单,运行后发现,selenium报错,无法找到id为u的元素。
进过仔细观察,发现是frame嵌套页面在作怪。
我们可以这样理解,每一个网页都是一个父类的frame,从我们访问这个网址开始,就已经进入了这个父类frame嵌套。顾名思义,有父即有子。frame(父)里嵌套了iframe(子),如果我们要定位的元素在iframe里,那么我们需要先切换至iframe。
iframe也是有自己的元素属性的,selenium也提供了switch方法供我们使用
重新option+command+i往上找iframe信息,可以看到被iframe嵌套了,id和name都是‘login_frame’
在定位元素之前,先输入如下代码:
driver.switch_to.frame("login_frame")
这样就切换到iframe了,再继续之前的元素定位,即可成功。
这一步的完整代码为:
# 定位login_frame
driver.switch_to.frame("login_frame")
# 定位账号、密码,并输入
driver.find_element_by_xpath('//*[@id="u"]').send_keys("XXXXXXXXX")
driver.find_element_by_xpath('//*[@id="p"]').send_keys("XXXXXXXXX")
driver.find_element_by_xpath('//*[@id="login_button"]').click()
# 定位登录按钮
driver.find_element_by_xpath('//*[@id="login_button"]').click()
这个时候网页已经可以成功QQ邮箱。
三、元素定位,写信界面
继续,邮箱登录成功之后,来到写信界面,按照常规操作,我们需要先点击左上角写信按钮,展开具体写信界面
同样的方法,option+command+i操作起来,查看写信按钮,元素定位为id=‘composebtn’,发送点击事件
driver.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
driver.switch_to.frame(driver.find_element_by_xpath('//*[@class="qmEditorIfrmEditArea"]'))
4.邮件正文需要先调用一个点击事件激活,才能启动send事件。如果没有先点击再编写,那么send的内容会放在主题后面的文本框中。(也不知道为啥会有这样的设定~)
所以综上所述,这一步的代码为:
# 切换到mainFrame
driver.switch_to.frame('mainFrame')
sleep(1)
# 定位收件人,并输入
driver.find_element_by_xpath("//*[@id='toAreaCtrl']/div[2]/input").send_keys("XXXXXX@qq.com")
# 定位主题,并输入
driver.find_element_by_xpath('//*[@id="subject"]').send_keys("来自lucas的邮件")
# 定位邮件正文,先进入到iframe
driver.switch_to.frame(driver.find_element_by_xpath('//*[@class="qmEditorIfrmEditArea"]'))
# 必须先点击正文,再send_keys
driver.find_element_by_xpath('/html/body').click()
driver.find_element_by_xpath('/html/body').send_keys("Hello World", "\nlucas")
# 返回到mainframe
driver.switch_to.parent_frame()
# 定位发送按钮
driver.find_element_by_xpath('//*[@name="sendbtn"]').click()
最终程序代码:
# -*- coding: utf-8 -*-
"""
@author: lucas
@Function:
@file: sendEmail.py
@time: 2021/8/26 2:55 下午
"""
from telnetlib import EC
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get('https://mail.qq.com/')
sleep(2)
driver.maximize_window()
# 定位login_frame
driver.switch_to.frame('login_frame')
# driver.find_element_by_xpath('//*[@id="switcher_plogin"]').click()
# 定位账号、密码,并输入
sleep(2)
driver.find_element_by_xpath('//*[@id="u"]').send_keys("XXXXXXXXX")
sleep(2)
driver.find_element_by_xpath('//*[@id="p"]').send_keys("XXXXXXXXX")
sleep(2)
driver.find_element_by_xpath('//*[@id="login_button"]').click()
# 定位登录按钮
driver.find_element_by_xpath('//*[@id="login_button"]').click()
sleep(2)
driver.find_element_by_xpath('//*[@id="composebtn"]').click()
# 切换到mainFrame
driver.switch_to.frame('mainFrame')
sleep(1)
# 定位收件人,并输入
driver.find_element_by_xpath("//*[@id='toAreaCtrl']/div[2]/input").send_keys("XXXXXXXXX")
# 定位主题,并输入
driver.find_element_by_xpath('//*[@id="subject"]').send_keys(" from lucas")
# 定位邮件正文,先进入到iframe
driver.switch_to.frame(driver.find_element_by_xpath('//*[@class="qmEditorIfrmEditArea"]'))
# 必须先点击正文,再send_keys
driver.find_element_by_xpath('/html/body').click()
driver.find_element_by_xpath('/html/body').send_keys("Hello World", "\nlucas")
# 返回到mainframe
driver.switch_to.parent_frame()
# 定位发送按钮
driver.find_element_by_xpath('//*[@name="sendbtn"]').click()
sleep(5)
最后邮件发送成功:
四、元素定位总结
1、frame很重要,一定要看清楚是否被嵌套,以及注意切换
2、元素的id或者name如果是动态的,请放弃
3、用xpath定位真香
4、如果元素有子节点,使用相对路径继续定位
5、实在排查不出为什么定位失败,尝试一下用time.sleep()
五、小技巧:
分享几点在开发者工具里,比较方便的小窍门:
1、在开发者工具里,选中元素,点击Console,可以很直观的看到元素是否被iframe嵌套
2、邮件复制可以直接得到元素定位的xpath