之前皮爷的文章里面,讲述了怎样编写爬虫,怎样把爬虫通过Scrapyd部署到远端的服务器上,也讲了怎样通过SpringBoot来实现每天自动执行爬虫。
那么今天我们就来讲一个可以“保证生产环节”的操作,预警系统。
为啥要有预警
为啥要有预警系统?是因为你的爬虫的功能是爬取别人网页上面的信息。一旦别人的网页结果做了修改,或者域名发生变化。在爬虫每天自动执行的时代,就会对你的数据造成确实。因为别人的修改导致了你的爬虫失效。这个时候就要预警系统进来参与。
预警系统有两个作用:
- 当爬虫爬取信息,如果某些信息需要及时通知工作人员,这可时候可以使用预警来通知相关人员。
- 当爬虫爬取数据产生异常的时候,需要及时通知相关人员。
本文背景
这篇文章里面的项目和代码,均出自皮爷之前写的文章里面。比如之前的Scrapy爬虫,还有Spring Boot为基础写的的 PeekpaHub 项目。
下文会详细讲解两种情况。
第一种情况
当爬虫爬到你需要的数据的时候,要及时通知你。
这部分代码主要写在了爬虫里面。以下通过一个例子来给大家讲解一下。
背景知识:
皮爷曾经写爬虫爬取过日本气象局的地震网站:http://www.jma.go.jp/jp/quake/quake_local_index.html
网页里面是一张表格,里面列出了地震的情况。一般来说,一旦有地震发生,这个网站的更新速度是很快的。所以我写的爬虫通过每两分钟爬取这个网站,在一定延迟的基础上,获取最新的地震信息。
当每次爬虫爬取信息的时候,如果发现爬取的新数据里面,マグニチュード 或者 最大震度 超过一定数字的时候,这些数据就应该发送到我的邮箱里面。
做法很简单,这个操作步骤只需要在Scrapy的 pipelines.py
文件中的 process_item(self, item, spider)
函数中完成就可以。
因为 process_item
里面的调用时刻就是在爬虫爬取完信息之后,存入数据库之前的这个时刻。所以当数据传到这个方法的时候,我们可以在里面做判断处理,如果条件符合,就发送邮件。
代码部分截取如下:
def process_item(self, item, spider): try: if self.jpearth2.find_one({"jp_id": item['jp_id']}) is None: if self.needToSendHigh(item): self.sendEmail(item, True) if self.needToSendLow(item): self.sendEmail(item, False) self.jpearth2.insert(dict(item)) else: logging.info("items: " + item['jp_id'] + " has in jpearth2.") except Exception as e: logging.error("PIPLINE EXCEPTION: " + str(e))复制代码
其中,sendEmial()
方法就是Python 发送email。
# pipeline.py def sendEmail(self, item, heighOrLow): subject = "INFO! INFO! 地震报告: " + item['jp_location'] + " -- 震级:" + item['jp_level'].strip()[1:] + " -- " + item[ 'jp_title'].strip() bodyhtml = '<html><body>' + \ '<h1>日本实时地震报告: ' + \ '地点:<a href="' + item['jp_url'] + '">' + item['jp_location'] + '</a>' + \ '震级:<a href="' + item['jp_url'] + '">' + item['jp_level'] + '</a>' + \ '</h1>' + \ '<h3>位置:<img src="' + item['jp_location_image_url'] + '"/></h3>' + \ '<h3>时间:<a href="' + item['jp_url'] + '">' + item['jp_title'] + '</a></h3>' + \ '<h3>强度:<a href="' + item['jp_url'] + '">' + item['jp_max_level'] + '</a></h3>' + \ '<p> 点击上面的任意链接即可跳转到『日本气象厅』网站查看详情 </p>' + \ '</body></html>' self.emailClient.sendEmail(self.toHighSendEmailLst, subject, bodyhtml) # emailClient.py def sendEmail(self, toLst, subject, body): ''' 发送邮件 :param toLst: 收件人的邮箱列表 :param subject: 邮件标题 :param body: 邮件内容 :return: ''' logging.info("sendEmail") message = MIMEText(body, 'html', 'utf-8') # 邮件内容,格式,编码 message['From'] = self.sender # 发件人 message['To'] = ",".join(toLst) # 收件人列表 message['Subject'] = subject # 邮件标题 try: smtpSSLClient = smtplib.SMTP_SSL(self.smtp_host, self.smtp_port) # 实例化一个SMTP_SSL对象 loginRes = smtpSSLClient.login(self.smtp_user, self.smtp_pwd) # 登录smtp服务器 # logging.info(f"登录结果:loginRes = {loginRes}") # loginRes = (235, b'Authentication successful') if loginRes and loginRes[0] == 235: logging.info(f"登录成功,code = {loginRes[0]}") smtpSSLClient.sendmail(self.sender, toLst, message.as_string()) logging.info(f"mail has been send successfully. message:{message.as_string()}") else: logging.info(f"登陆失败,code = {loginRes[0]}") except Exception as e: logging.info(f"发送失败,Exception: e={e}")复制代码
最后实际效果就是,如果一旦发生地震比较严重,我的邮箱里面就会收到一封服务器自动发送的邮件:
这部分的东西很简单,皮爷之前写过一个很详细的文章:
『【Python实战】Scrapy的高阶骚操作,带邮件功能的“种子吞噬器2.0”版本,更高更快更强!』 (https://www.jianshu.com/p/c360d12d8ddf)
接下来我们主要讲一下Spring Boot里面如何自动发邮件。
第二种情况
因为PeekpaHub是作为一个信息集合类网站展现的.
https://www.peekpa.tech/复制代码
所以,判断爬虫是否出错(比如目标网站域名变化导致爬取失败),可以写一个定时循环任务,来每天定时从数据库里面看是否有数据。如果发生异常,则向管理员发送警告邮件。
@Scheduled(cron = "0 0 9,12,15,18 * * ? ") public void dailyDataCheck() { boolean hasData = databaseDao.checkHasData(TimeUtils.getInstance().getCurDayTime(), "fid7"); emailComponent.sendAlertMail("hasData: " + hasData); }复制代码
上面的 Schedule 里面的周期是用的 cron 写的,意思是每天的9点,12点,15点和18点会自动调用函数。关于 Cron 的写法,大家可以去下面这个网站,里面可以根据大家自己的需求,自动生成相对应的局域,非常的方便。
在线Cron表达式生成器http://cron.qqe2.com/复制代码
在 Spring Boot 里面实现发送邮件,其实也很简单,只需要用 JavaMailSender
就可以。
@Componentpublic class EmailComponent { @Value("${mail.fromMail.sender}") private String sender; @Value("${mail.fromMail.receiver}") private String receiver; @Autowired private JavaMailSender javaMailSender; public void sendAlertMail(String textMessage) { SimpleMailMessage message = new SimpleMailMessage(); String curDayAndTime = TimeUtils.getInstance().getCurDayAndTime(); message.setFrom(sender); message.setTo(receiver); message.setSubject("DATA CHECK:: " + curDayAndTime + " PeekpaHub 数据测试结果"); message.setText(textMessage + "\nTime: " + curDayAndTime + "\nhttp://peekpa.tech/\n数据测试"); try { javaMailSender.send(message); } catch (Exception e) { print(e); } }}复制代码
这里面需要在 application.properties
里面配置一些信息。
mail.fromMail.sender=发送邮件地址@126.commail.fromMail.receiver=接受邮件地址@126.comspring.mail.host=smtp.126.com //可以用qq,163,这里用的是126的spring.mail.username=发送邮件地址@126.comspring.mail.password=密码spring.mail.default-encoding=UTF-8spring.mail.properties.mail.smtp.auth=truespring.mail.properties.mail.smtp.starttls.enable=truespring.mail.properties.mail.smtp.starttls.required=true// 在阿里云上使用SMTP_SSL端口则是465spring.mail.properties.mail.smtp.socketFactory.port=465 spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory复制代码
就这样,我们把代码部署到阿里云服务器上,然后每天都会准点调用函数,来检测数据库是否正常:
CLOSE
今天就先说到这里吧,其实有了这两个功能,我感觉程序的可用性会提高很多。你可以在手机上再安装一个接受警告邮件的客户端,打开App的推送,这样如果一旦服务器有什么问题,你就可以在第一时间在手机上接到通知。这个功能其实还可以扩展到其他应用上面,其实是个挺不错的idea。
因为文章都是涉及到服务器的,所以福利就要写在最前面:
皮爷这里就有上千元的阿里云和腾讯云的优惠券给你使用(每一款优惠只要点击优惠链接,进入即可领取):阿里云部分:
【阿里云新人1888元云产品通用代金券】:
promotion.aliyun.com/ntms/yunpar…【阿里云爆款云主机,2折优惠券】:
promotion.aliyun.com/ntms/act/qw…【阿里云企业级服务器2折优惠券】:
promotion.aliyun.com/ntms/act/en…腾讯云:
【新客户无门槛领取总价值高达2775元代金券,每种代金券限量500张,先到先得】:
cloud.tencent.com/redirect.ph…
如果你对文章中所写的东西有什么疑问或者不明白的地方,可以关注公众号『皮爷撸码』,加入到『皮克啪交流群』里来和这里面的大神一起讨论。