python爬虫从入门到放弃之十一:定时发送邮件
我们希望为一般的爬虫程序新增两个实用性比较强的功能:
第一是定时功能,即程序可以根据我们设定的时间自动爬取数据;第二是通知功能,即程序可以把爬取到的数据结果以邮件的形式自动发送到我们的邮箱。
这两个功能可以让爬虫程序定时向我们汇报。
——试想一下:
如果你是一位股票(或比特币)的持有者,你希望及时爬取股票(或比特币)每日的价格数据,方便你能及时卖出或买入,那每天都去启动一遍爬虫程序是极其不高效的。
而此时,如果你的爬虫程序有定时和发送邮件功能,能自动爬取每天的数据,并且只有当价格达到某个你设置的价位时,才通知你可以有所行动了,平时都不打扰你,是不是很爽?
不止如此,如果你有特别想看的演唱会,但一开售就卖完了,有定时和发送邮件功能的爬虫程序同样可以辛勤地帮你刷票,当刷到有余票时,马上通知你去购票,多好。(买火车票也是一样的道理噢)
这两个功能不仅能帮你获取这种实时变化的数据,还可以帮你获取周期性的数据。
比如,你所在的公司每周都会把周报发到官网上,而你所在的部门是由你去负责下载周报,并整理相关信息,再传递给部门成员。那如果有定时和通知功能的程序,每周你就可以静待程序把更新的周报信息爬下来,并自动发送到你邮箱。
-
定时功能
调用schedule库可以实现定时功能
schedule官方文档上的代码如下:
- schedule功能总体展示
import schedule
import time
#引入schedule和time
def job():
print("I'm working...")
#定义一个叫job的函数,函数的功能是打印'I'm working...'
schedule.every(10).minutes.do(job) #部署每10分钟执行一次job()函数的任务
schedule.every().hour.do(job) #部署每×小时执行一次job()函数的任务
schedule.every().day.at("10:30").do(job) #部署在每天的10:30执行job()函数的任务
schedule.every().monday.do(job) #部署每个星期一执行job()函数的任务
schedule.every().wednesday.at("13:15").do(job)#部署每周三的13:15执行函数的任务
while True:
schedule.run_pending()
time.sleep(1)
#13-15都是检查部署的情况,如果任务准备就绪,就开始执行任务。
第1行和第2行,是引入schedule
和time
。
第5行和第6行,是定义了一个叫job()
的函数,调用这个函数时,函数会打印I'm working...
。
第9行-13行都是相关的时间设置,你可以根据自己的需要来确定。
第15-17行是一个while
循环,是去检查上面的任务部署情况,如果任务已经准备就绪,就去启动执行。其中,第15行的time.sleep(1)
是让程序按秒来检查,如果检查太快,会浪费计算机的资源。
下面,展示一下schedule的作用,我们看下面这段代码:是每两秒就运行job()函数。
import schedule
import time
#引入schedule和time模块
def job():
print("I'm working...")
#定义一个叫job的函数,函数的功能是打印'I'm working...'
schedule.every(2).seconds.do(job) #每2s执行一次job()函数
while True:
schedule.run_pending()
time.sleep(1)
运行过程如下:
-
邮件
Python内置模块:smtplib是用来发送邮件用的,email是用来构建邮件内容的。
基本语法如下:
import smtplib
from email.mime.text import MIMEText #MIMEText:内容形式为纯文本、HTML 页面
from email.header import Header
# email 模块
msg = MIMEText('这里写邮件内容','plain','utf-8') # 内容、格式(plain 为纯文本)、编码
msg['From'] = Header('发件人')
msg['To'] = Header('收件人')
msg['Subject'] = Header('主题','utf-8')
# smtplib 模块
server = smtplib.SMTP_SSL('邮箱服务器') # 实例化 SMTP 这个类,下面调用类的方法
server.connect('邮箱服务器','服务器端口') # 连接服务器
server.login('发件人', '授权码') # 登录服务器
server.sendmail('发件人','收件人', msg.as_string()) #发送邮件
server.quit()
# as_string()是将发送的信息 msg 变为字符串类型。
两大邮箱 SMTP 服务器及端口如下:
QQ 邮箱
服务器地址:smtp.qq.com SSL 协议端口:465 或 587 非 SSL 协议端口:25
网易邮箱
服务器地址:smtp.163.com SSL 协议端口:465 或 994 非 SSL 协议端口:25
以上代码,使用SSL协议,此外,发件邮箱需要开启SMTP服务,使用授权码
如果不使用SSL协议,则将server = smtplib.SMTP_SSL('邮箱服务器')
写成server = smtplib.SMTP('邮箱服务器')
下面是QQ邮箱开启SMTP服务和提取授权码,进入邮箱—设置—账户
以QQ邮箱为例:
- 如果出现编码错误UnicodeDecodeError,则写为:
server.connect('smtp.qq.com', 465,'utf-8')
- 若出现此错误:smtplib.SMTPException: No suitable authentication method found,使用QQ外的其他邮箱可能会出现此错误,解决方案是:在登录(login)之前调用starttls()方法。
群发的方法:
即变更收件人
msg['To'] = Header('收件人')
server.sendmail('发件人','收件人', msg.as_string())
将上面两行代码替换如下代码:
receiver = ['12345678@qq.com','23456789@qq.com','34567890@qq.com']
msg['to'] = Header(",".join(receiver))
server.sendmail('发件人',receiver, msg.as_string())
这是因为Header接受的第一个参数的数据类型必须要是字符串或者字节,列表不能解码,所以使用join()函数
更直观的做法是,将收件人内容写在csv文件上,再通过代码读取
代码实现:
import csv
import os
def get_receiver():
receiver = []
if os.path.exists('receiver.csv'):
f = open('receiver.csv', 'r')
reader = csv.reader(f)
for row in reader:
receiver.append(row[1])
else:
while True:
a=input('请输入收件人邮箱:')
receiver.append(a)
b=input('是否继续输入,n退出,任意键继续:')
if b == 'n':
break
return receiver
print(get_receiver())
判断是否存在一个收件人文件receiver.csv,不存在则手动输入
-
爬取天气
爬取广州最近7天天气,链接如下:
http://www.weather.com.cn/weather/101280101.shtml
代码实现:
import requests
from bs4 import BeautifulSoup
url = 'http://www.weather.com.cn/weather/101280101.shtml'
headers = {'User-Agent': 'Mozilla/5.0'}
r = requests.get(url,headers = headers)
r.encoding = r.apparent_encoding
soup = BeautifulSoup(r.text,'lxml')
weathers = soup.find('ul',class_ = 't clearfix').find_all('li')
for weather in weathers:
date = weather.h1.text
wea = weather.find('p',class_ = 'wea').text
tem = weather.find('p',class_ = 'tem').text.replace('\n','')
print(date,wea,tem)
-
定时发送天气预报
接下来,把代码拼接起来就可以了
范例代码:
from email.mime.text import MIMEText
from email.header import Header
from bs4 import BeautifulSoup
import requests
import smtplib
import schedule
import time
import csv
import os
def get_weather():
url = 'http://www.weather.com.cn/weather/101280101.shtml'
headers = {'User-Agent': 'Mozilla/5.0'}
r = requests.get(url,headers = headers)
r.encoding = r.apparent_encoding
soup = BeautifulSoup(r.text,'lxml')
f = open('weather.txt','a',encoding = 'utf-8')
f.write('温馨提醒:广州最近7天的天气如下:\n')
weathers = soup.find('ul',class_ = 't clearfix').find_all('li')
for weather in weathers:
date = weather.h1.text
wea = weather.find('p',class_ = 'wea').text
tem = weather.find('p',class_ = 'tem').text.replace('\n','')
f.write(date+' '+wea+' '+tem+'\n')
f.close()
def read_weather():
get_weather()
f = open('weather.txt','r',encoding = 'utf-8')
reader = f.read()
f.close()
os.path.exists(r'weather.txt')
os.remove(r'weather.txt')
return reader
def get_receiver():
receiver = []
if os.path.exists('receiver.csv'):
f = open('receiver.csv', 'r')
reader = csv.reader(f)
for row in reader:
receiver.append(row[1])
else:
pass
return receiver
def send_mail():
text = read_weather()
receiver = get_receiver()
msg = MIMEText(text,'plain','utf-8')
msg['From'] = Header('123456789@qq.com')
msg['To'] = Header(",".join(receiver))
msg['Subject'] = Header('天气预报','utf-8')
qqmail = smtplib.SMTP_SSL('smtp.qq.com')
qqmail.connect('smtp.qq.com','465')
qqmail.login('123456789@qq.com', '授权码')
try:
qqmail.sendmail('123456789@qq.com',receiver, msg.as_string())
print ('邮件发送成功')
except:
print ('邮件发送失败')
qqmail.quit()
if __name__ == "__main__":
schedule.every().day.at("10:30").do(send_mail)
while True:
schedule.run_pending()
time.sleep(1)
>>>
阅读更多文章请点击以下链接:
python爬虫从入门到放弃之一:认识爬虫
python爬虫从入门到放弃之二:HTML基础
python爬虫从入门到放弃之三:爬虫的基本流程
python爬虫从入门到放弃之四:Requests库基础
python爬虫从入门到放弃之五:Requests库高级用法
python爬虫从入门到放弃之六:BeautifulSoup库
python爬虫从入门到放弃之七:正则表达式
python爬虫从入门到放弃之八:Xpath
python爬虫从入门到放弃之九:Json解析
python爬虫从入门到放弃之十:selenium库
python爬虫从入门到放弃之十一:定时发送邮件
python爬虫从入门到放弃之十二:多协程
python爬虫从入门到放弃之十三:Scrapy概念和流程
python爬虫从入门到放弃之十四:Scrapy入门使用