一、監(jiān)控的意義
爬蟲腳本是基于網(wǎng)站的結(jié)構(gòu)去寫的,但是隨著互聯(lián)網(wǎng)的發(fā)展环壤,很多公司的頁面結(jié)構(gòu)會發(fā)生變化,發(fā)生的變化就會導(dǎo)致我們寫的爬蟲失效钞诡,最主要的失效方式是xpath的失效镐捧。隨著我們寫的爬蟲越來越多,越就越來越難以發(fā)現(xiàn)這些失效的腳本臭增,導(dǎo)致數(shù)據(jù)更新失敗懂酱,不及時,或者不滿足抓取的質(zhì)量和數(shù)量要求誊抛。這就需要對爬蟲的腳本進行監(jiān)控列牺。
監(jiān)控的目的是讓數(shù)據(jù)及時的更新,腳本及時的發(fā)現(xiàn)bug以及解決bug拗窃。提高我們的工作效率瞎领。更好的保證線上數(shù)據(jù)產(chǎn)品的優(yōu)質(zhì)性。
我們需要介紹兩種常見的監(jiān)控方式:1随夸、郵件 2九默、釘釘
二、Python郵件監(jiān)控(SMTP)
1. SMTP簡介
SMTP(Simple Mail Transfer Protocol)即簡單郵件傳輸協(xié)議,它是一組用于由源地址到目的地址傳送郵件的規(guī)則宾毒,由它來控制信件的中轉(zhuǎn)方式驼修。
python的smtplib提供了一種很方便的途徑發(fā)送電子郵件。它對smtp協(xié)議進行了簡單的封裝。
Python創(chuàng)建 SMTP 對象語法如下:
import smtplib
smtpObj = smtplib.SMTP( [host [, port [, local_hostname]]] )
參數(shù)說明:
- host: SMTP 服務(wù)器主機乙各。 你可以指定主機的ip地址或者域名如: runoob.com墨礁,這個是可選參數(shù)。
- port: 如果你提供了 host 參數(shù), 你需要指定 SMTP 服務(wù)使用的端口號耳峦,一般情況下 SMTP 端口號為25恩静。
- local_hostname: 如果 SMTP 在你的本機上,你只需要指定服務(wù)器地址為 localhost 即可蹲坷。
Python SMTP 對象使用 sendmail 方法發(fā)送郵件驶乾,語法如下:
SMTP.sendmail(from_addr, to_addrs, msg[, mail_options, rcpt_options])
參數(shù)說明:
- from_addr: 郵件發(fā)送者地址。
- to_addrs: 字符串列表循签,郵件發(fā)送地址轻掩。
- msg: 發(fā)送消息
這里要注意一下第三個參數(shù),msg 是字符串懦底,表示郵件。我們知道郵件一般由標題罕扎,發(fā)信人聚唐,收件人,郵件內(nèi)容腔召,附件等構(gòu)成杆查,發(fā)送郵件的時候,要注意 msg 的格式臀蛛。這個格式就是 smtp 協(xié)議中定義的格式亲桦。
2. 郵件監(jiān)控五個步驟
#導(dǎo)入郵件包
import smtplib
# 1.創(chuàng)建郵件對象
smtp_obj = smtplib.SMTP()
# 2.連接服務(wù)器
smtp_obj.connect()
#3.登錄操作
smtp_obj.login()
#4.發(fā)郵件
smtp_obj.sendmail()
#5.退出操作
smtp_obj.quit()
3. 郵件監(jiān)控發(fā)送(代碼)
# 導(dǎo)入發(fā)送郵件包
import smtplib
from email.mime.text import MIMEText # 用來創(chuàng)建文本格式的郵件體內(nèi)容
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
class Send_Email:
def __init__(self, num):
self.num = num
self.smtp = self.get_conn()
# print(self.smtp)
self.send_email()
def get_conn(self):
# 創(chuàng)建郵件對象
smtp_obj = smtplib.SMTP()
# 連接服務(wù)器
smtp_obj.connect("smtp.163.com")
# 登錄郵箱
smtp_obj.login("13349949963@163.com", "zb376100870")
return smtp_obj
def send_email(self):
# 定義發(fā)送郵件的三要素
sender = "13349949963@163.com"
receiver = "376100870@qq.com"
# 獲取發(fā)送郵件的 郵件體
msg = self.get_msg(sender, receiver)
# 發(fā)送郵件
self.smtp.sendmail(from_addr=sender, to_addrs=receiver, msg=msg.as_string())
print("send success")
def get_msg(self, sender, receiver):
# 定義郵件主題
subject = "恭喜你 你已經(jīng)被阿里巴巴公司錄用 "
# 獲取郵件體中的 文本內(nèi)容(消息體)
msg = self.get_content()
# 生成郵件體的 三要素
msg["From"] = sender
msg["To"] = receiver
msg["Subject"] = subject
return msg
def get_content(self):
if self.num == 1:
content = "請xx準時報到"
# 將內(nèi)容寫到面板中 文本格式
msg = MIMEText(content, "plain", "utf-8")
return msg
elif self.num==2:
# 讀取文件
# with open("02.html", "r", encoding="utf-8") as f:
# content = f.read()
# print(type(content))
content = """
<html>
<h1>請xx準時報到<h1>
<a >baidu</a>
</html>"""
print(content)
msg = MIMEText(content, "html", "utf-8")
return msg
elif self.num==3:
#獲取含有內(nèi)嵌圖片資源的HTML格式的郵件體
msg=self.get_pic()
return msg
#獲取圖片信息
def get_pic(self):
#通過cid圖片文件關(guān)聯(lián)起來
content="<b>Some<i>HTML</i>text</b> and an image <br><img src='cid:image1'><br>goood"
#如果content 中內(nèi)嵌資源,必須定義related字段
#使用related定義內(nèi)嵌套資源的郵件體
msgRoot=MIMEMultipart("related")
#創(chuàng)建HTML格式的郵件體
msgText=MIMEMultipart(content,"html","utf-8")
#將msgText中的內(nèi)容附加到MIMEMultipart對象中
msgRoot.attach(msgText)
#讀取圖片文件內(nèi)容
with open("2.jpg","rb")as f:
result=f.read()
#使用圖片信息創(chuàng)建一個圖片對象
msgImage=MIMEImage(result)
#指定文件的Content-ID 為image1
msgImage.add_header("Content-ID","image1")
#將msgImage中的圖片內(nèi)容附加到MIMEMultipart對象的指定image1當中
msgRoot.attach(msgImage)
#返回攜帶有內(nèi)嵌套圖片資源的HTML格式郵件的MIMEMultipart對象
return msgRoot
def __del__(self):
# 關(guān)閉
self.smtp.quit()
if __name__ == '__main__':
# 1 # 發(fā)送的郵件體是字符串
2 # 發(fā)送一個html的郵件體
3 # 發(fā)送一個圖片的郵件體
num=int(input("請發(fā)送郵件所對應(yīng)的數(shù)字"))
Send_Email(num)
應(yīng)用場景:
鏈家租房 每天都要爬取一遍插入數(shù)據(jù)庫的時候 需要增加一個字段 更新插入時間
refresh_time
如果爬蟲 正常 這樣就可以保證 每天的refresh_time
都是最新的如何監(jiān)控那些 不正常更新的渠道(例如:鏈家租房),你可以寫sql語句查詢每個渠道的更新時間;浊仆。如果是最新的更新時間則說明是正常客峭;如果不是判斷多久沒有更新,說爬蟲數(shù)據(jù)異常抡柿。這時需要 給指定的負責人發(fā)郵件 讓他修改代碼 使得爬蟲正常運行舔琅。
郵件監(jiān)控和下面的釘釘監(jiān)控一樣,可以根據(jù)釘釘監(jiān)控來改寫
二洲劣、釘釘監(jiān)控
鏈家租房監(jiān)控腳本
import pymysql
import time
import requests
'''
kpi小姐姐的api接口:
https://oapi.dingtalk.com/robot/send?access_token=8daebe660297f090e6839b6a4454ff05382b59f3d515b3d7c14bc07f5fb642dd
'''
class Moniter:
def __init__(self):
self.conn_mysql()
self.get_data()
def conn_mysql(self):
# 創(chuàng)建數(shù)據(jù)庫的連接對象
# 字符集是utf8 不是utf-8
self.conn = pymysql.connect(host="127.0.0.1", user="root",password='123',
database="0218", charset="utf8")
# 創(chuàng)建操作數(shù)據(jù)庫的對象
self.cur = self.conn.cursor()
def get_data(self):
# 定義一天前的時間戳
day_ago = time.time() - 86400
# 定義查詢出總數(shù)和更新數(shù)量的sql語句
sql = """
select count(*) from lianjia
union
select count(*) from lianjia where refresh_time > {}
union
select phone from source_name where source = "lianjia"
""".format(day_ago)
# 執(zhí)行sql語句 并獲取sql語句查詢出來的 數(shù)據(jù)
self.cur.execute(sql)
self.conn.commit()
data = self.cur.fetchall()
# print(data, type(data))
# 提出數(shù)據(jù) 獲取總數(shù) 和更新數(shù)
total_count = int(data[0][0])
refresh_count = int(data[1][0])
phone = data[2][0]
# print(total_count, refresh_count, phone)
# 生成刷新數(shù)量的 百分比
per_no_refresh = refresh_count / total_count
# print(per_no_refresh)
#如果最新數(shù)據(jù)的占比小于80%备蚓,則說明需要提醒
if per_no_refresh < 0.8:
per_no_refresh = "%.2f%%" % (per_no_refresh * 100)
# print(per_no_refresh)
# 發(fā)送釘釘消息
self.dingding(phone, per_no_refresh)
def dingding(self, phone, per_no_refresh):
dd_api = "https://oapi.dingtalk.com/robot/send?access_token=" \
"8daebe660297f090e6839b6a4454ff05382b59f3d515b3d7c14bc07f5fb642dd"
# print(phone, per_no_refresh)
dd_json = {
"msgtype": "text",
"text": {
"content": "你的渠道有問題 請及時解決 更新比例為:{}".format(per_no_refresh)
},
"at": {
"atMobiles": [
phone,
"18679030315"
],
"isAtAll": False
}
}
requests.post(dd_api, json=dd_json)
print("send success")
def __del__(self):
self.cur.close()
self.conn.close()
if __name__ == '__main__':
Moniter()