美文网首页
Python边学边用 - 学校新闻爬取并通过邮件发送

Python边学边用 - 学校新闻爬取并通过邮件发送

作者: kakaan | 来源:发表于2016-10-22 21:35 被阅读684次

    人生苦短,我用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
    

    Ref:windows下面安装Python和pip终极教程

    抓取网页

    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
    同理书写获取解析其他网站的函数,最后得到的所有解析结果大概是这样的

    这里用到了BeautifulSoupdatetimeurllibparse
    说一下时间转换吧,来自网络,详细自行Google

    Ref:python BeautifulSoup 抓取网页内指定内容
    用python的BeautifulSoup分析html

    排序

    排序就很简单啦,直接用的listsort
    newsAllList.sort(key=lambda x: x[0],reverse=True)
    这里有一个知识点是lambda表达式x:x[0],就是x是list中的每一个元素,排序按照x[0]来进行比较,自己猜测体会的,不一定说的很完备

    Ref:python list排序的两种方法及实例讲解

    组织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发送邮件

    好啦,到这里就结束了

    以上

    相关文章

      网友评论

          本文标题:Python边学边用 - 学校新闻爬取并通过邮件发送

          本文链接:https://www.haomeiwen.com/subject/bxmhuttx.html