用python给说说点赞的人群发新年祝福邮件
不久之前突发奇想想要用python给大家群发新年祝福,于是就有了下面的这段过程。
一. 大致思路
- 取得连接:首先我们要获得qq空间首页的连接,这里要解决的一个问题就是qq空间的权限问题。
- 网页爬虫:然后我们要获取给说说点赞的列表,我们都知道qq邮箱的格式是【qq号 + @qq.com】,所以说列表的内容必须是qq号。
- 发送邮件:在获取列表后,我们要做的就是给列表中的每一位好友发送邮件,当然这个邮件不应该是简单的一句话,他应该是富文本的。
二. “取得连接”的一些问题
很显然如果要获得连接就要向Web服务器发送HTTP请求,在这里我使用的是urllib3的模块(虽然现在网上教程中使用的较多的是urllib模块)。用它来获取我们页面的静态HTML文件,然后我们就可以使用爬虫工具来分析了。urllib3的用法也十分简单,创建PoolManage实例,用于管理连接池,然后通过request方法发送GET请求,最后用使用返回数据的data属性即可。整个过程只有三步,让我们先来看看会出什么问题。
from urllib3 import *
disable_warnings()
http = PoolManager()
url = 'https://user.qzone.qq.com/1234567' #这里1234567替换为你自己的qq号
response = http.request("GET", url,)
with open("qq_html.txt", 'w') as file:
file.write(response.data.decode('utf-8'))
这里我把获得的HTML文档放在一个叫做qq_html.txt的文本文档中了,下面我们看看这个文档中究竟有什么东西:
然后我们用开发者工具再看看实际我们的HTML页面 (这里我用的是Edge)
好吧,其实是完全不同的,其本质原因是因为我们没有设置cookie,导致服务器无法识别用户。细心的读者可能就会发现,我们获得的HTML文件的内容其实是qq空间登陆页面的内容。如果没有设置cookie的话设置一下就好了,大家可以从Devtool中将cookie全部复制下来,不过这个方法比较麻烦,下面我们来看看一个更好的方法。
三. 模拟用户登陆
我们平时在使用qq空间的时候,一般是不需要用到cookie的知识的,只要用鼠标随便点点就好了,那么我们不妨使用能模拟用户点击的模块来取得我们页面的连接。这里我是用的是著名的selenium模块,selenium其实是一套完整的web应用程序测试系统。使用selenium时我们还需要了解webdriver的知识:webdriver是一个开源工具,用于许多浏览器上自动测试webapps。Webdriver是selenium和browser通信的桥梁,selenium实质上是相关协议的底层封装,同时提供对外部webdriver的上层调用。
在写代码时,chromedriver由于不可抗的未知因素无法下载,所以我选择了firefox,这个浏览器所对应的webdriver叫做geckodriver,注意将geckodriver放在python的PATH下(放到系统环境变量中的PATH也不行),不然会出现以下错误:
下面我们就可以用这个模块来模拟用户的各种行为了,由于在这边selenium官网的连接总是超时,所以我对它的了解也不深,想要进一步了解的请自行去官网查看。下面的代码可以帮助你登陆到你自己的qq空间。
# Initialize
browser = webdriver.Firefox()
browser.get('https://user.qzone.qq.com')
username = "1234567" # 这里填你自己的qq号
password = "abcdefg" # 这里填你自己的密码
# Login
browser.switch_to.frame('login_frame')
log = browser.find_element_by_id("switcher_plogin")
log.click()
time.sleep(0.5)
uname = browser.find_element_by_id('u')
uname.send_keys(username)
ps = browser.find_element_by_id('p')
ps.send_keys(password)
btn = browser.find_element_by_id('login_button')
time.sleep(0.5)
btn.click()
time.sleep(0.5)
这段代码简单地来说就是利用DOM取得和操作相应的元素(表单和按钮),然后模拟事件(就是按按钮的那个事件)登入。可以看到selenium不仅解决(其实是避开)了网页的连接问题,顺便还把网络爬虫的问题给解决了。有一点要注意的是在iframe中的内容是无法爬取的,必须使用switch_to_frame方法,或者使用xpath,后者在chromium上取得比较方便,大家可以自己寻找教程。
四. 获得第一条说说中点赞好友的qq号列表
既然已经登陆了,接下来要做的就是网页爬虫了,了解到selenium也有爬虫的功能,我们将继续使用这个模块,首先检查我们点赞好友的元素:
这里我们比较感兴趣的是这个iframe和列表子元素中的属性href,如果我们抓到所有子元素的href属性,就可以获得点赞好友的qq空间地址,最后用正则表达式处理一下就可以获得qq号列表了。
这里有用到一个小技巧:那就是用find_element系列方法获得的是第一元素,这个跟很多爬虫模块都很像,比如beautifulsoup。有了这个技巧以后我们就可以很简单地获取第一条说说的信息了。
def get_qq(element):
"""
Return qq number in element's attribute.
"""
return re.search('\d+', element.get_attribute('href')).group()
# Get the so-called like list
qq_list = []
browser.get("https://user.qzone.qq.com/123456/main") # 还是要换成自己的qq号
browser.switch_to.frame('QM_Feeds_Iframe')
# Get first list
li = browser.find_element_by_class_name('user-list')
user_list = li.find_elements_by_tag_name('a')
for user in user_list:
qq_list.append(get_qq(user))
五. 群发邮件
下面的事情就比较简单了,就是利用SMTP发送邮件,这个邮件的内容最好不要只是一句简单的新年祝福, 应该是富文本的,比如说应该带一张图片,这里所到的知识是所谓的“电子邮件风格”的代码(说白了还是HTML的内容),由于SMTP的内容是在是纷繁复杂,这里不过多展开,下面贴出代码:
def mail(to):
"""
:param to: The parameter 'to' is each one's qq mail's address.
:return: The function has no return. Sending email is its work.
"""
msg = MIMEMultipart("related")
msg['From'] = sender # 发送者
msg['To'] = to # 接收方
msg['Subject'] = "Happy New Year!" #邮件主题
msgAlternative = MIMEMultipart("alternative") # 富文本模式
# 下面的就是邮件的内容,采用电子邮件风格的代码
mail_content = """
<p>HappyNewYear!</p>
<br/>
<img src='cid:image1'></img>
"""
msgAlternative.attach(MIMEText(mail_content, 'html', 'utf-8'))
msg.attach(msgAlternative)
# 取得图片的位置,并获取它
with open(r'C:\Users\24312\Desktop\NewYear.jpg', 'rb') as file:
img = MIMEImage(file.read())
img.add_header('Content-ID', '<image1>')
msg.attach(img)
server = smtplib.SMTP_SSL("smtp.qq.com", 465)
server.login(sender, t_password)
server.sendmail(sender, [to, ], msg.as_string())
server.quit()
# Send mail to everyone
sender = '[email protected]' # 自己的邮箱
# It was the third party password
t_password = 'deep♂dark♂fantasy' # 第三方软件的登陆码,下面会讲
for qq in qq_list:
mail(qq + '@qq.com')
这里有个东西叫做第三方软件的登陆码,不同人,不同的协议是不一样的,下面我简单地介绍一下如何获取这个登陆码。
首先进入邮箱——点击设置——进入账号——下翻直到找到上述页面——开启POP3/SMTP服务。这里选择SMTP服务是因为我们所用的就是SMTP协议,接下来按它的指示操作就可以获得第三方应用的登陆码了。注意这个登陆码不同于邮箱的密码,使用邮箱的密码是无法成功的。
六. 完善
-
由于是新年的邮件,所以一般这种邮件都会在凌晨发,考虑到能够完全自动化,我们可以在程序的一开始就设置一个while循环(不断地进行判断),再使用time模块中的time.localtime获取本地时间;判断是否进入新年,来决定程序是否执行 。为了防止邮件的重复发送,在程序最后要加一个break。
-
邮件发送完成的通告:整个过程耗时比较长,尤其是点赞人数比较多的时候,所以最好能够再程序结束时提醒我们以下,我这里使用了一个print,再发送完毕后会显示All done!
七. 成果展示以及源码地址
成果:
源码地址 :https://github.com/Greg7shen/HappyNewYear-E-mail
在我提示可能会有bug(我最害怕的还是垃圾邮件的发送,而不是什么语法错误),还有这么多人支持我,真的很开心,最后祝大家新年快乐,万事如意。