Python爬虫第八课:让爬虫定时汇报

我们来学习两个新的功能来增强爬虫程序的实用性:

  • 首先是程序可以根据我们设定的时间自动爬取数据,即定时爬取;
  • 然后程序可以把爬取到的数据结果以邮件的形式自动发送到我们的邮箱,也就是自动发送。

来做一个案例:每日更新天气预报情况,并发送邮件提醒。由三个部分构成:爬取天气数据+发送邮件+定时

一、爬取天气数据

天气网站地址为:http://www.weather.com.cn/weather/101280101.shtml

import requests
from bs4 import BeautifulSoup

# 请求头
headers ={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'}

# url
url = 'http://www.weather.com.cn/weather/101280101.shtml'

# 获取数据
res_weather = requests.get(url,headers=headers)
print(res_weather.status_code)
print(res_weather.text)
 # 输出结果:
 status_code为200,说明是状态是正常的
 但是网页源代码中有乱码
  • 网页源代码中有乱码,怎么办?
  • 用response.encoding属性就好。

好滴,那我们在网页上点击"右键"——“查看网页源代码”,会弹出一个新的标签页,然后搜索charset,查看一下编码方式,是“utf-8”的编码。

import requests
from bs4 import BeautifulSoup

# 请求头
headers ={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'}

# url
url = 'http://www.weather.com.cn/weather/101280101.shtml'

# 获取数据
res_weather = requests.get(url,headers=headers)
# 发现爬取下来的代码中有乱码,使用encoding转换为utf-8
res_weather.encoding = 'utf-8'
print(res_weather.status_code)
print(res_weather.text)

此时,网页源代码显示正常了!

import requests
from bs4 import BeautifulSoup

# 请求头
headers ={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'}

# url
url = 'http://www.weather.com.cn/weather/101280101.shtml'

# 获取数据
res_weather = requests.get(url,headers=headers)
# 发现爬取下来的代码中有乱码,使用encoding转换为utf-8
res_weather.encoding = 'utf-8'
# 发现爬取下来的代码中有乱码,使用encoding转换为utf-8
res_weather.encoding = 'utf-8'
# 见字符串转换为BS对象
bs_weather = BeautifulSoup(res_weather.text,'html.parser')
# 获取第一条也就是第一天的天气
wea = bs_weather.find(class_='wea')
# 获取温度
tem = bs_weather.find(class_='tem')
# 分别打印
print(wea.text)
print(tem.text)

二、发送邮件

在这里插入图片描述
我们的代码逻辑也会按照上图来进行,并且在其中用到两个——smtplib和email。
以QQ邮箱为例,先来看如何连接到QQ邮箱服务器。

import smtplib

# smtplib是python的一个内置库,所以不需要用pip安装

# 把qq邮箱的服务器地址赋值到变量mailhost上,地址需要是字符串的格式。
mailhost = 'smtp.qq.com'
# 实例化一个smtplib模块里的SMTP类的对象,这样就可以SMTP对象的方法和属性了
qqmail = smtplib.SMTP()
# 连接服务器,第一个参数是服务器地址,第二个参数是SMTP端口号
qqmail.connect(mailhost,25)

接着,使用邮箱账号和密码登录邮箱

import smtplib

# smtplib是python的一个内置库,所以不需要用pip安装

# 把qq邮箱的服务器地址赋值到变量mailhost上,地址需要是字符串的格式。
mailhost = 'smtp.qq.com'
# 实例化一个smtplib模块里的SMTP类的对象,这样就可以SMTP对象的方法和属性了
qqmail = smtplib.SMTP()
# 连接服务器,第一个参数是服务器地址,第二个参数是SMTP端口号
qqmail.connect(mailhost,25)

# 邮箱账号
sender = input('请输入邮箱账号:')
# 邮箱密码,对于QQ邮箱来说,改密码不是邮箱的登录密码,而是开启SMTP服务时的授权码
pwd = input('请输入邮箱密码')
# 登录邮箱,第一个参数为邮箱账号,第二个参数为邮箱密码
qqmail.login(sender,pwd)

# 收件人邮箱
receiver = input('请输入收件人邮箱:')

接着,我们看第三步,填写主题和撰写正文,这里需要使用到email库

# smtplib是python的一个内置库,所以不需要用pip安装
import smtplib
# 引入Header和MIMEText模块
from email.mime.text import MIMEText
from email.header import Header

# 把qq邮箱的服务器地址赋值到变量mailhost上,地址需要是字符串的格式。
mailhost = 'smtp.qq.com'
# 实例化一个smtplib模块里的SMTP类的对象,这样就可以SMTP对象的方法和属性了
qqmail = smtplib.SMTP()
# 连接服务器,第一个参数是服务器地址,第二个参数是SMTP端口号
qqmail.connect(mailhost,25)

# 邮箱账号
sender = input('请输入邮箱账号:')
# 邮箱密码,对于QQ邮箱来说,改密码不是邮箱的登录密码,而是开启SMTP服务时的授权码
pwd = input('请输入邮箱密码')
# 登录邮箱,第一个参数为邮箱账号,第二个参数为邮箱密码
qqmail.login(sender,pwd)

# 收件人邮箱
receiver = input('请输入收件人邮箱:')
# 邮件正文
content = input('请输入邮件正文:')
# 实例化一个MIMEText邮件对象,该对象需要写进三个参数,分别是邮件正文,文本格式和编码
message = MIMEText(content,'plain','utf-8')
# 邮件主题
subject = input('请输入邮件主题:')
# 在等号的右边,是实例化了一个Header邮件头对象,该对象需要写入两个参数,分别是邮件主题和编码,然后赋值给等号左边的变量message['Subject']
message['subject'] = Header(subject,'utf-8')

这个MIMEText对象有三个参数,一个是邮件正文;另一个是文本格式,一般设置为plain纯文本格式(当然如果邮件内容中带有格式,例如表格,添加一些代码就可以了);最后一个是编码,设置为utf-8,因为utf-8是最流行的万国码。

message[‘Subject’] = Header(subject, ‘utf-8’)。

  • 等号右边是实例化了一个Header邮件头对象,该对象需要写入两个参数,分别是邮件主题和编码。
  • 等号左边的message[‘Subject’]的变量是一个a[‘b’]的代码形式,它长得特别像字典根据键取值的表达,但是这里的message是一个MIMEText类的对象,并不是一个字典,那message[‘Subject’]是什么意思呢?

字典里面的元素是【键】和【值】一一对应,而类里面的【属性名】和【属性】也是一一对应的。我们可以根据字典里的【键】取到对应的【值】,同样的,也可以根据类里面的【属性名】取到【属性】。

在这里插入图片描述

所以message[‘Subject’]就代表着根据MIMEText类里面的Subject的属性名取到该属性。
需要注意的是,不是每一个类都可以这样访问其属性的,之所以能这样访问是因为这个MIMEText的类实现了这个功能。
接下来,发送邮件和退出邮箱。

# smtplib是python的一个内置库,所以不需要用pip安装
import smtplib
# 引入Header和MIMEText模块
from email.mime.text import MIMEText
from email.header import Header

# 把qq邮箱的服务器地址赋值到变量mailhost上,地址需要是字符串的格式。
mailhost = 'smtp.qq.com'
# 实例化一个smtplib模块里的SMTP类的对象,这样就可以SMTP对象的方法和属性了
qqmail = smtplib.SMTP()
# 连接服务器,第一个参数是服务器地址,第二个参数是SMTP端口号
qqmail.connect(mailhost,25)

# 邮箱账号
sender = input('请输入邮箱账号:')
# 邮箱密码,对于QQ邮箱来说,改密码不是邮箱的登录密码,而是开启SMTP服务时的授权码
pwd = input('请输入邮箱密码')
# 登录邮箱,第一个参数为邮箱账号,第二个参数为邮箱密码
qqmail.login(sender,pwd)

# 收件人邮箱
receiver = input('请输入收件人邮箱:')
# 邮件正文
content = input('请输入邮件正文:')
# 实例化一个MIMEText邮件对象,该对象需要写进三个参数,分别是邮件正文,文本格式和编码
message = MIMEText(content,'plain','utf-8')
# 邮件主题
subject = input('请输入邮件主题:')
# 在等号的右边,是实例化了一个Header邮件头对象,该对象需要写入两个参数,分别是邮件主题和编码,然后赋值给等号左边的变量message['Subject']
message['subject'] = Header(subject,'utf-8')

# 发送邮件,调用了sendmail()方法,写入三个参数,分别是发件人,收件人,和字符串格式的正文
qqmail.sendmail(sender,receiver,message.as_string())

# 退出邮箱
qqmail.quit()

调用sendmail()发送邮件,括号里面有三个参数,第1个是发件人的邮箱地址,第2个是收件人的邮箱地址,第3个是正文,但必须是字符串格式,所以用as_string()函数转换了一下。

但是我们希望发送成功后能显示“邮件发送成功”,失败的时候能提示我们“邮件发送失败”,可以使用try语句来实现。

# smtplib是python的一个内置库,所以不需要用pip安装
import smtplib
# 引入Header和MIMEText模块
from email.mime.text import MIMEText
from email.header import Header

# 把qq邮箱的服务器地址赋值到变量mailhost上,地址需要是字符串的格式。
mailhost = 'smtp.qq.com'
# 实例化一个smtplib模块里的SMTP类的对象,这样就可以SMTP对象的方法和属性了
qqmail = smtplib.SMTP()
# 连接服务器,第一个参数是服务器地址,第二个参数是SMTP端口号
qqmail.connect(mailhost,25)

# 邮箱账号
sender = input('请输入邮箱账号:')
# 邮箱密码,对于QQ邮箱来说,改密码不是邮箱的登录密码,而是开启SMTP服务时的授权码
pwd = input('请输入邮箱密码')
# 登录邮箱,第一个参数为邮箱账号,第二个参数为邮箱密码
qqmail.login(sender,pwd)

# 收件人邮箱
receiver = input('请输入收件人邮箱:')
# 邮件正文
content = input('请输入邮件正文:')
# 实例化一个MIMEText邮件对象,该对象需要写进三个参数,分别是邮件正文,文本格式和编码
message = MIMEText(content,'plain','utf-8')
# 邮件主题
subject = input('请输入邮件主题:')
# 在等号的右边,是实例化了一个Header邮件头对象,该对象需要写入两个参数,分别是邮件主题和编码,然后赋值给等号左边的变量message['Subject']
message['subject'] = Header(subject,'utf-8')

try:
    # 发送邮件,调用了sendmail()方法,写入三个参数,分别是发件人,收件人,和字符串格式的正文
    qqmail.sendmail(sender,receiver,message.as_string())
    print('邮件发送成功!')
    
except:
    print('邮件发送失败!')
    
# 退出邮箱
qqmail.quit()

以上的代码中,smtplib库主要负责的是横向的连接服务器、登录、发送和退出;而email库主要负责的是邮件主题和正文。

三、定时

定时功能,我们使用第三方库——schedule。

  • Windows系统:pip install schedule
  • Mac系统:pip3 install schedule
# 引入time和schedule
import schedule
import time



# 定义一个叫job的函数,函数的功能是打印'I'm working...'
def job():
    print('working is processing……')


# 部署情况
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(2)
  • 第1行和第2行,是引入schedule和time。
  • 第5行和第6行,是定义了一个叫job()的函数,调用这个函数时,函数会打印I’m working…。
  • 第9行-13行都是相关的时间设置,你可以根据自己的需要来确定。
  • 第15-17行是一个while循环,是去检查上面的任务部署情况,如果任务已经准备就绪,就去启动执行。其中,第15行的time.sleep(1)是让程序按秒来检查,如果检查太快,会浪费计算机的资源。

四、代码组装

至此,我们已经搞定了三段代码,现在将他们组装起来。

import requests
import schedule
import smtplib
import time
from email.mime.text import MIMEText
from email.header import Header
from bs4 import BeautifulSoup


# 将爬取天气数据封装为get_weather()函数
def get_weather():
    # 请求头
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'}

    # url
    url = 'http://www.weather.com.cn/weather/101280101.shtml'

    # 获取数据
    res_weather = requests.get(url, headers=headers)
    # 发现爬取下来的代码中有乱码,使用encoding转换为utf-8
    res_weather.encoding = 'utf-8'
    # 发现爬取下来的代码中有乱码,使用encoding转换为utf-8
    res_weather.encoding = 'utf-8'
    # 见字符串转换为BS对象
    bs_weather = BeautifulSoup(res_weather.text, 'html.parser')
    # 获取第一条也就是第一天的天气
    wea_data = bs_weather.find(class_='wea').text
    # 获取温度
    tem_data = bs_weather.find(class_='tem').text
    return wea_data, tem_data


# 将邮件发送程序封装为函数send_mail()
def send_mail(wea, tem, sender, pwd, receiver):
    # 把qq邮箱的服务器地址赋值到变量mailhost上,地址需要是字符串的格式。
    mailhost = 'smtp.qq.com'
    # 实例化一个smtplib模块里的SMTP类的对象,这样就可以SMTP对象的方法和属性了
    qqmail = smtplib.SMTP()
    # 连接服务器,第一个参数是服务器地址,第二个参数是SMTP端口号
    qqmail.connect(mailhost, 25)

    # 登录邮箱,第一个参数为邮箱账号,第二个参数为邮箱密码
    qqmail.login(sender, pwd)

    # 邮件正文
    content = '亲爱的David,\n' + '今天的天气是:' + tem + wea

    # 实例化一个MIMEText邮件对象,该对象需要写进三个参数,分别是邮件正文,文本格式和编码
    message = MIMEText(content, 'plain', 'utf-8')
    # 邮件主题
    subject = '今日天气预报'
    # 在等号的右边,是实例化了一个Header邮件头对象,该对象需要写入两个参数,分别是邮件主题和编码,然后赋值给等号左边的变量message['Subject']
    message['subject'] = Header(subject, 'utf-8')

    try:
        # 发送邮件,调用了sendmail()方法,写入三个参数,分别是发件人,收件人,和字符串格式的正文
        qqmail.sendmail(sender, receiver, message.as_string())
        return True

    except:
        return False
    # 退出邮箱
    qqmail.quit()


# 将天气爬取与发送邮件封装为函数
def job():
    print('开始一次发送任务…')
    wea, tem = get_weather()
    IsSucces = send_mail(wea, tem, sender, pwd, receiver)

    while IsSucces is False:
        print('邮件发送失败,正在尝试重新发送!')

    print('任务完成!')


# 设置定时功能,根据需要修改此次的循环与定时时间
schedule.every(1).minutes.do(job)

while True:
    schedule.run_pending()
    time.sleep(1)

四、练习

  1. 我们在第4课中爬取了下厨房里的“豆瓣新片榜”,在这个练习中,我们让程序每秒自动爬取数据,然后发送到我们的指定邮箱。总共爬取两次。
    地址:https://movie.douban.com/chart
    额外注意:smtp授权码需要按课程里的步骤去获取
import requests
import smtplib
import time
import schedule
from email.mime.text import MIMEText
from email.header import Header
from bs4 import BeautifulSoup

sender = input('请输入邮箱账号:')
pwd = input('请输入密码:')
receiver = input('请输入收件人邮箱:')
index = 0


def movie_bot():
    headers = {'User-Agent':
                   'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'}

    url = 'https://movie.douban.com/chart'
    # 获取数据
    response = requests.get(url, headers=headers)
    # 转为bs对象
    bs_movie = BeautifulSoup(response.text, 'html.parser')
    # 查找到电影信息的列表
    list_movie = bs_movie.find_all('div', class_='pl2')
    all_movie = []
    for movie in list_movie:
        tag_a = movie.find('a')
        url = tag_a['href']
        title = tag_a.text.replace(' ', '').replace('\n', '')
        tag_p = movie.find('p', class_='pl')
        info = tag_p.text.replace(' ', '').replace('\n', '')
        rating = movie.find(class_="star clearfix")
        rate = rating.text.replace(' ', '').replace('\n', '')
        all_movie.append(title + url + info + rate)

    return all_movie


def send_mail(movie_list):
    global sender,pwd,receiver
    # QQ邮箱服务器地址
    mailhost = 'smtp.qq.com'
    # 建立对象
    qqmail = smtplib.SMTP_SSL()
    # 连接服务器
    qqmail.connect(mailhost, 465)
    # 登录邮箱
    qqmail.login(sender, pwd)
    # 邮件正文
    content = '\n'.join(movie_list)
    message = MIMEText(content, 'plain', 'utf-8')
    # 邮件主题
    subject = '本周豆瓣新片榜'
    message['subject'] = Header(subject, 'utf-8')
    try:
        qqmail.sendmail(sender, receiver, message.as_string())
        print('邮件发送成功!')
    except:
        print('邮件发送失败!')

    qqmail.quit()


# 设置任务
def job():
    global index
    print('开始任务!')
    movie_list = movie_bot()
    send_mail(movie_list)
    print('任务完成!')
    index = index + 1


# 设置定时
schedule.every().second.do(job)

while index != 2:
    schedule.run_pending()
    time.sleep(1)
发布了35 篇原创文章 · 获赞 4 · 访问量 5292

猜你喜欢

转载自blog.csdn.net/fightingoyo/article/details/104607300