Python边学边用 - 学校新闻爬取并通过邮件发送
人生苦短,我用Python。
Python真的是很好的语言,很好用,那么我们该如何入门呢?
我觉得不用特别的学习,只要你有C/C++的一些知识,学习Python将不是一件很困难的事情。
这样一门语言,这样一个很好地工具,应该是不需要太高的学习成本的,所以我《Python边学边用》这个系列的文章,我将边用边学,可能代码不是那么“漂亮”,代码不是那么“优雅”,但是肯定实现了功能。
慢慢学,慢慢做,我会把这个工具用的越来越好。
文中描述方法不一定是最好的方法,只是我才疏学浅,自己只知道这么做能做出来,所以我就这么做了,欢迎交流批评。
要做什么
学校里有很多网站,教务处,研究生院,就业班,学术信息网,学院官网,实验室官网,等等等。。。。
每天各个网站上都会发几条新闻,有些还是蛮重要的新闻,比如奖学金申请,但是呢,每天翻78个网站,从78个网站的10多个页面中看一下当天的新闻,真是一件麻烦的事情。
大概都是这个样子的
效果呢希望做成这个样子:
每天定时收到一封邮件,邮件内容从所有这些网站中提取的新闻中,找出最近3天的发布的新闻,排序后以一个列表的方式发送到我的邮箱,标题就是新闻内容的超链接。 最后再手机端查看邮件的效果
怎么做
基本思路是这样
【抓取网页 -> 提取筛选信息 -> 排序 -> 组织HTML -> 通过邮件发送】 + 定时运行
那么这里主要记录一下【】中的主要过程
搭建开发环境
这里使用Python3.5,官网下载直接安装就可以
IDE使用PyCharm,社区版免费。
基础语法学习资料推荐 Python基础教程,作为入门教程很合适。看完入门之后,官方文档是最好的教程
PIP安装教程(Windows)
Python的优点是有许许多多的好用的轮子,为了方便获取这些轮子,需要安装pip来方便的获取。类似Linux的apt-get
Step1、官网下载
点击打开链接https://pypi.python.org/pypi/pip#downloads
Step2、安装PIP
下载完成之后,解压到一个文件夹,用CMD控制台进入解压目录,输入:
python setup.py install
Step3、使用PIP安装轮子
安装好PIP之后,可以直接使用下面的语法来安装轮子xxxxx
pip install xxxxx
抓取网页
import urllib.request #库载入 response=urllib.request.urlopen('http://meeting.xidian.edu.cn/') #打开链接 page = response.read() # 读入网页
查阅文档,或得到如何改变UA的参数,形成如下代码:
url = reqUrl headers = { 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' } req = request.Request(url=url, headers=headers) response = request.urlopen(req) page = response.read()
获取到的网页HTML将存储在page变量中,可以直接使用
print(page)
查看获取到的HTML页面,大概是这样一些内容,标准的HTML4页面
Ref:使用 Python 轻松抓取网页
提取筛选信息
得到的结果设置一个HTML页面,HTML页面的解析这里使用BeautifulSoup4来完成
安装BeautifulSoup4
pip install BeautifulSoup4
官方文档是最好的学习资料
首先需要分析页面的HTML标签结构来确定怎么提取数据,这里我使用Chrome浏览器的开发者工具来分析。
打开页面,按F12打开代码检查工具
分析完了,使用BeautifulSoup
提取
1、首先分离所有li标签,使用select功能
pageSoup = BeautifulSoup(page, "html.parser") newsList = pageSoup.select('div[class="main-right-list"] > ul > li')
得到结果大概是这个样子
2、然后针对每个li标签,分离具体的url,标题和时间。
使用get_text
获得标题和时间,至于~||#|$|%|&||~
这个是什么鬼,指示我用来split的标志,我觉得这个标志就不会和文本重复了,这个方法肯定不是很好,求不吐槽。
oneNewsText = oneNews.get_text('~||#|$|%|&||~').split('~||#|$|%|&||~')
得到的结果大概这个样子
3、使用
find
标签功能,获取href类别中的url片段oneNewsUrl = oneNews.find('a')['href']
结果大概是这个样子
4、使用
urljoin
来合并url片段和主域名oneNewsUrl = parse.urljoin('http://gr.xidian.edu.cn/', oneNewsUrl)
结果大概是这个样子
5、将前面得到的时间利用datetime解析成标准时间格式
oneNewsText[1] = datetime.datetime.strptime(oneNewsText[1], '%Y-%m-%d')
得到的结果大概是这个样子
6、将获得的时间,标题,URL合并到一个list中就得到了最终的信息提取结果
7、将日期与当前日期判断,如果是最新两天的新闻,就放入大list中准备返回,如果不是就丢弃(这里可以先判断时间在进行其他的解析吧)
if (datetime.datetime.now().date() - oneNewsText[0].date() <=datetime.timedelta(days=NewsDataDelet)): lnewsalllist.append(oneNewsText)
然后就ok啦,所有代码时这样的
def gr_xidian(reqUrl,NewsDataDelet): lnewsalllist = [] url = reqUrl headers = { 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' } req = request.Request(url=url, headers=headers) response = request.urlopen(req) page = response.read() pageSoup = BeautifulSoup(page, "html.parser") newsList = pageSoup.select('div[class="main-right-list"] > ul > li') for oneNews in newsList: oneNewsText = oneNews.get_text('~||#|$|%|&||~').split('~||#|$|%|&||~') oneNewsUrl = oneNews.find('a')['href'] oneNewsUrl = parse.urljoin('http://gr.xidian.edu.cn/', oneNewsUrl) oneNewsText.append(oneNewsUrl) oneNewsText[1] = datetime.datetime.strptime(oneNewsText[1], '%Y-%m-%d') oneNewsText[0],oneNewsText[1] = oneNewsText[1],oneNewsText[0] del oneNewsText[2] if (datetime.datetime.now().date() - oneNewsText[0].date() <= datetime.timedelta(days=NewsDataDelet)): lnewsalllist.append(oneNewsText) return lnewsalllist
同理书写获取解析其他网站的函数,最后得到的所有解析结果大概是这样的
这里用到了BeautifulSoup
,datetime
和urllib
的parse
说一下时间转换吧,来自网络,详细自行Google
Ref:python BeautifulSoup 抓取网页内指定内容
用python的BeautifulSoup分析html
排序
排序就很简单啦,直接用的list
的sort
newsAllList.sort(key=lambda x: x[0],reverse=True)
这里有一个知识点是lambda表达式x:x[0]
,就是x是list中的每一个元素,排序按照x[0]来进行比较,自己猜测体会的,不一定说的很完备
组织HTML
这里呢,要说一说,遇到了一些坑。
根据我边学习边Google的结果,最先知道的方法是Python直接填写HTML,感觉太麻烦。
后来又找到了一个PYH,据说能比较方便的组织HTML,未研究。来自Google Code,传送门
后来找到了Web框架,感觉找到了救星
参考知乎回答,确定了使用最简单的Flask框架
关于虚拟环境,其实不需要,直接使用本机环境运行就可以
其他过程参考这篇教程
最后在windows的cmd中执行下面的语句开启Flask服务器
python run.py
在浏览器访问
http://localhost:5000
获得结果
虽然说这使用模板完成了HTML的解析,但是需要运行一个服务器,然后通过浏览器访问这个服务器,和我的要求不符合。
遂研究发现Flask使用render_template
来进行HTML渲染
page = render_template("index.html", title = 'Home', info = infomation, postList = posts)
那么render_template
方法能不能用来直接渲染HTML,尝试未果。放弃
模板从天而降
查询发现Flask使用的是Jinja2引擎,所以能不能直接调用这个引擎呢,尝试未果
又找到了Mako模板引擎
Mako是一个高性能的Python模板库,它的语法和API借鉴了很多其他的模板库,如Django、Jinja2等等。
需要一个HTML模板(其实什么模板都可以,使用HTML模板渲染出来的就是HTML)和一个脚本
渲染出来的结果,大概时这样
好,下面就这个干
渲染很简单,直接建立Mako的Template,然后使用render方法就可以进行渲染
from mako.template import Template def rederHtml(templateFile,postlist): t = Template(filename=templateFile) page = t.render( name = 'Jack', postlist = postlist ) return page
需要提前写好模板
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>XidianNews</title> </head> <body> <div class="newslist"> <table> <tbody> % for post in postlist: <tr bgcolor="${loop.cycle('#cce0ff', '#e6f0ff')}"> <td width="95" align="center" class="arctime"><font size = "3">${post['time']}</font></td> <td class="arctitle"><a target="_blank" href=${post['url']} title="Click to open in new tab"><font size = "3">${post['body']}</font></a></td> </tr> % endfor </tbody> </table> </div> </body> </html>
Ref:PYH
Web框架比较
Flask教程
Mako语法
通过邮件发送
发邮件使用smtplib完成,可以使用import traceback来跟踪详细的调试信息
`def SendMail(title,mailHtml,rcvList):
sender = '15829911730@sina.cn'
pwd = 'sina2011'
smtpHost = 'smtp.sina.com'
smtpPort = '25'
message = MIMEText(mailHtml, 'html')
message['Subject'] = title
message['From'] = sender
for receiver in rcvList:
message['To'] = receiver
try:
smtpObj = smtplib.SMTP_SSL(smtpHost)
#smtpObj.set_debuglevel(1)
smtpObj.login(sender, pwd)
smtpObj.sendmail(sender, receiver, message.as_string())
print("Success to send to",receiver)
except smtplib.SMTPException as e:
errorMsg = "Error: %s" % (e)
print(errorMsg)
# traceback.print_exc()
else:
smtpObj.quit()`
通过smtplib
的调试信息可以得到与服务器之间交互的信息
smtpObj.set_debuglevel(1)
得到大概这样子的反馈信息
Ref:Python smtplib发送邮件
python3 发邮件实例(包括:文本、html、图片、附件、SSL、群邮件)
Python3 SMTP发送邮件
好啦,到这里就结束了
以上