接上文:
【超详细】使用python3做一个爬虫,监控网站信息(一)
上文我们已经利用python3的urllib模块和BeautifulSoup模块实现了这几个页面的抓取和分析提取信息。接下来要做的就是对抓取的信息进行循环比较,能体现出更新提示。上文中我们也说了,这个程序分为:信息提取模块(从网页中提取有用信息)、对比模块、还有邮件模块。这么多模块如果要直接写在程序里,一旦需要循环调用,那代码量可以翻好几倍。这里我们开始使用python的函数。
函数可以看做一个黑箱子,有输入和输出。输入东西(传递参数),函数处理完毕之后输出(return),在需要的时候,直接调用函数处理即可,无需再写代码。比如上一篇文章里,最后实现的功能,就是输入url_list,输出抓取到的多个页面的需要的信息。
一、把上一篇文章的代码封装成函数
我们先把上一篇文章的代码封装成两个函数,第一个函数输入url,return出当前url需要提取的信息:
#定义一个名为get_webInfo的函数,传入参数url def get_webInfo(url): req = urllib.request.Request(url) rsp = urllib.request.urlopen(req) html = rsp.read().decode('utf8','ignore') html = BeautifulSoup(html,'html.parser') for link in html.find_all('a',limit=3): info_link = link.get('href') info_text = link.get_text(strip=True) #函数执行完后,return(输出)执行的结果 return info_text+'n'+url[:-50]+info_link+'n'
另一个函数输入url_list,调用get_webInfo函数,return出所有url需要提取的信息:
def parseWeb(url_list): 初始化result为一个列表 result = [] for url in url_list: #每循环一次,就调用get_webInfo,传入参数url,解析出结果,存入变量webInfo webInfo = get_webInfo(url) print(webInfo) #每循环一次,就将解析结果放入result列表中 result.append(webInfo) 函数执行结束后,return(输出) result return result
二、写一个循环对比函数
在上一步中,我们封装了2个函数,get_webInfo()和parseWeb,其中parseWeb()中调用了函数get_webInfo(),相当于执行parseWeb()并传入参数url_list,就可以实现多个页面第一条信息的获取并解析。那么接下来我们可以开始对解析出来的结果进行循环对比了。再看一遍程序流程图:
从图中可以看到,程序开始运行的时候,会抓取一次页面。这里我们运用定义一个全局字典tmp,保存抓取的结果,并跟最新抓取的结果进行对比,写最外面的大循环,判断是否为第一次执行。
先定义(初始化)一个字典tmp作为存储中间内容,值为None (空),再定义一个函数check(),用于循环对比,写最外层的大循环:
#定义一个字典,包含一个空元素history tmp = {'history':None} #定义一个比对函数check() def check(): #判断字典内的元素history不为空 if (tmp['history']): #把临时值给tmp字典内的history元素 用于循环比较 hisroty = tmp['history'] now = parseWeb(url_list) ...比较过程... #比较完成后把值覆盖传递 tmp [history] = now else: #如果tmp里的history元素为空则判定第一次运行 tmp['history'] = parseWeb(url_list)
这样就完成了一个基本的逻辑,可以对上一次对比和本次获取到的内容进行循环对比了。接下来我们要做的是,在tmp不为空(非第一次运行)的情况下,判断信息是否更新,当然,使用if判断就行了。
首先为了排除因为网络原因导致的数据错误(获取不到数据、数据获取缺失等情况),需要对前后次获取的列表的长度(列表内字符个数)进行判断,比如本次抓取10个页面的第一条信息,那么列表长度固定为10。以下为比较过程:
#大前提,history的列表长度等于now的列表长度 if len(history) == len(now): #定义一个空的变量 result result = '' #使用zip函数,对history和now函数按顺序进行对比 for a,b in zip(history,now): if a == b: print('未发现更新!') else: print('发现更新') #等价于result=result+b 发现更新就在变量result内添加内容,不会覆盖 result+=b ...发送邮件... #注意空格,上面for循环执行后才会执行下面的if判断 #为防止误判,两次获取内容都为空也满足len(history)==len(now),对result进行非空判断 if result != '': #输出结果 print('更新内容如下:'+result) else: print('数据错误!')
三、使用smtp发送邮件
pyhon3自带email和smtplib模块,可以使用smtp发送邮件。由于云服务器默认屏蔽25端口,这里就使用465端口来发送加密邮件。
这里我们定义一个发送邮件的函数 send_mail(),传入参数邮件标题(title)、邮件正文(article)、邮件接收人(receiver)。由于只需要发送文本内容,所以邮件模块比较简单:
#导入需要使用的模块 import smtplib from email.header import Header from email.mime.text import MIMEText #定义发送邮件的函数,传入title,article,receiver参数 def send_mail(title,article,receiver): #定义邮件地址、账号密码等变量 host = 'smtp.mxhichina.com' user = 'admin@xiaoweigod.com' passwd = 'xxxxxxxxxx' sender = user coding = 'utf8' #定义message变量,写邮件内容、邮件头 message = MIMEText (article,'plain',coding) message ['From'] = Header(sender,coding) message ['To'] = Header(receiver,coding) message ['subject'] = Header(title,coding) #错误处理 try: #定义mail_client变量,开启SSL邮件,传入邮件服务器地址和端口 mail_client = smtplib.SMTP_SSL(host,465) #连接smtp服务器 mail_client.connect(host) #登录smtp服务器 mail_client.login(user,passwd) #发送邮件 mail_client.sendmail(sender,receiver,message.as_string()) #发送完成关闭连接 mail_client.close() print('邮件已成功发送给:'+receiver) except: #如果过程中抛出异常则提示用户 print('发送失败!')
我们弄好了发送邮件的函数,可以单独把邮件内容拎出来测试下,在函数前面加几个用户输入动作:
title = input(str('输入邮件标题:')) article = input(str('输入邮件内容:')) receiver = input(str('输入接收人:'))
在函数下方加入:
send_mail(title,article,receiver)
这几个变量会自动传入send_mail()函数中。 保存为send_mail.py,运行一下:
成功接收到邮件:
四、整合成一个完整的程序并优化
最后我们把整个程序整合一下,拼成一个完整的爬虫程序。(只写架构和优化部分)
#coding=utf-8 #author=xiaoweigod import urllib.request from bs4 import BeautifulSoup import smtplib from email.header import Header from email.mime.text import MIMEText import time url_list=[' tmp = {'history':None} receiver = input(str('请输入邮件接收人:')) def get_webInfo(url) #加入UA,防止被网站封 head = {} head['User-Agent']='Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19' req = urllib.request.Request(url,header=head) ...... return info_text+'n'+url[:-50]+info_link+'n' def parseWeb(url_list): ...... return result def check(): ...... print('发现更新') #美化输出的格式 result+='-----------------------------------------n' result+=b result+='-----------------------------------------n' ...... print('更新内容如下:+result') #将result传入article变量,作为邮件正文 send_mail('你关注的网站有更新',result,receiver) #无限循环 while True: check() print('n休息30秒继续运行!') time.sleep(30) print('继续工作...')
运行效果如下: