Using Django with GAE Python 后台抓取多个网站的页面全文


Posted in Python onFebruary 17, 2016

一直想做个能帮我过滤出优质文章和博客的平台 给它取了个名 叫Moven。。 把实现它的过程分成了三个阶段:
1. Downloader: 对于指定的url的下载 并把获得的内容传递给Analyser--这是最简单的开始
2. Analyser: 对于接受到的内容,用Regular Expression 或是 XPath 或是 BeautifulSoup/lxml 进行过滤和简化--这部分也不是太难
3. Smart Crawler: 去抓取优质文章的链接--这部分是最难的:

Crawler的话可以在Scrapy Framework的基础上快速的搭建
但是判断一个链接下的文章是不是优质 需要一个很复杂的算法

最近就先从Downloader 和 Analyser 开始: 最近搭了一个l2z story 并且还有一个 Z Life 和 Z Life@Sina 还有一个她的博客 做为一个对Downloader 和 Analyser的练习 我就写了这个东西来监听以上四个站点 并且把它们的内容都同步到这个站上:

http://l2zstory.appspot.com

Using Django with GAE Python 后台抓取多个网站的页面全文

App 的特色
这个站上除了最上面的黑色导航条 和 最右边的About This Site 部分外, 其他的内容都是从另外的站点上自动获得
原则上, 可以添加任何博客或者网站地址到这个东西。。。当然因为这个是L2Z Story..所以只收录了四个站点在里面
特点是: 只要站点的主人不停止更新, 这个东西就会一直存在下去---这就是懒人的力量

值得一提的是, Content 菜单是在客户端用JavaScript 自动生成的--这样就节约了服务器上的资源消耗

Using Django with GAE Python 后台抓取多个网站的页面全文

这里用的是html全页面抓取 所以对那些feed没有全文输出的站点来说, 这个app 可以去把它要隐藏的文字抓来
在加载的时候会花很多时间因为程序会自动到一个没有全文输出的页面上抓取所有的文章列表,作者信息,更新时间,以及文章全文。。所以打开的时候请耐心。。。下一步会加入数据存储部分,这样就会快了。。

技术准备
 
前端:

1. CSS 在信奉简单之上的原则上 twitter的bootstrap.css满足了我大多数的要求 个人超喜欢它的 Grid System
2. Javascript上, 当然选用了jQuery 自从我开始在我的第一个小项目上用了jQuery 后 我就爱上了它   那个动态的目录系统就是用jQuery快速生成的
   为了配合bootstrap.css, bootstrap-dropdown.js 也用到了

服务器:

这个app有两个版本:
     一个跑在我的Apache上, 但是因为我的网络是ADSL, 所以ip一直会变基本上只是我在我的所谓的局域网内自测用的。。这个版本是纯Django的
     另一个跑在Google App Engine上 地址是 http://l2zstory.appspot.com 在把Django 配置到GAE的时候我花了很多功夫才把框架搭起来

详情请见: Using Django with Google App Engine GAE: l2Z Story Setup-Step 1           http://blog.sina.com.cn/s/blog_6266e57b01011mjk.html

后台:

主要语言是Python--不解释, 自从认识Python后就没有离开它

主要用到的module是

1. BeautifulSoup.py 用于html 的解析--不解释
2. feedparser.py 用于对feed xml的解析--网上有很多人说GAE不支持feedparser..这里你们得到答案了。。可以。。这里我也是花了很久才弄明白到底是怎么回事。。总之简单讲就是: 可以用!但是feedparser.py这个文件必须放到跟app.yaml同一个目录中 不然会出现网上众人说的不可以import feedparser的情况

数据库:
Google Datastore: 在下一步中, 这个程序会每隔30分钟醒来 逐一查看各个站点有没有更新并抓取更新后的文章并存入Google 的Datastore中

App 的配置

遵循Google的规则, 配置文件app.yaml 如下:
这里主要是定义了一些static directory--css 和 javascript的所在地

application: l2zstory 

version: 1

runtime: python

api_version: 1


handlers:

   

- url: /images

  static_dir: l2zstory/templates/template2/images

- url: /css

  static_dir: l2zstory/templates/template2/css

- url: /js

  static_dir: l2zstory/templates/template2/js  

- url: /js

  static_dir: l2zstory/templates/template2/js

- url: /.*

  script: main.py

URL的配置

这里采用的是Django 里的正则表达式

from django.conf.urls.defaults import *
# Uncomment the next two lines to enable the admin:

# from django.contrib import admin

# admin.autodiscover()


urlpatterns = patterns('',

    # Example:

    # (r'^l2zstory/', include('l2zstory.foo.urls')),


    # Uncomment the admin/doc line below and add 'django.contrib.admindocs'

    # to INSTALLED_APPS to enable admin documentation:

    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),


    # Uncomment the next line to enable the admin:

    # (r'^admin/(.*)', admin.site.root),

    (r'^$','l2zstory.stories.views.L2ZStory'),

    (r'^YukiLife/','l2zstory.stories.views.YukiLife'),

     (r'^ZLife_Sina/','l2zstory.stories.views.ZLife_Sina'),

     (r'^ZLife/','l2zstory.stories.views.ZLife')

)

Views的细节

对Django比较熟悉的人应该会从url的配置中看到view的名字了 我只把L2ZStory的这个view贴出来因为其他的在view里的架构至少是差不多的

#from BeautifulSoup import BeautifulSoup 

from PyUtils import getAboutPage

from PyUtils import getPostInfos
def L2ZStory(request):

    url="feed://l2zstory.wordpress.com/feed/"

    about_url="http://l2zstory.wordpress.com/about/"

    blog_type="wordpress"

    htmlpages={}

    aboutContent=getAboutPage(about_url,blog_type)

    if aboutContent=="Not Found":

        aboutContent="We use this to tell those past stories..."

    htmlpages['about']={}

    htmlpages['about']['content']=aboutContent

    htmlpages['about']['title']="About This Story"

    htmlpages['about']['url']=about_url

    PostInfos=getPostInfos(url,blog_type,order_desc=True)

    return render_to_response('l2zstory.html',

{'PostInfos':PostInfos,

'htmlpages':htmlpages

})

这里主要是构建一个dictionary of dictionary  htmlpages 和一个list of dictionary PostInfos
htmlpages 主要是存贮站点的 About, Contact US 之类的页面
PostInfos 会存贮所有文章的 内容, 作者, 发布时间 之类的

这里面最重要的是PyUtils。。这是这个app的核心

PyUtils的细节

我把一些我认为比较重要的细节加深了 并加了评论

import feedparser 

import urllib2

import re

from BeautifulSoup import BeautifulSoup

header={

'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:8.0.1) Gecko/20100101 Firefox/8.0.1',

}

#用来欺骗网站的后台。。象新浪这类的网站对我们这类的app十分不友好。。。希望它们可以多象被墙掉的wordpress学一学。。

timeoutMsg="""

The Robot cannot connect to the desired page due to either of these reasons:

1. Great Fire Wall

2. The Blog Site has block connections made by Robots.

"""
def getPageContent(url,blog_type): 

    try:

        req=urllib2.Request(url,None,header)

        response=urllib2.urlopen(req)

        html=response.read()

        html=BeautifulSoup(html).prettify()

        soup=BeautifulSoup(html)

        Content=""

        if blog_type=="wordpress":

            try:

                for Sharesection in soup.findAll('div',{'class':'sharedaddy sd-like-enabled sd-sharing-enabled'}):

                    Sharesection.extract()

                for item in soup.findAll('div',{'class':'post-content'}):

                    Content+=unicode(item)

            except:

                Content="No Post Content Found"

        elif blog_type=="sina":

            try:

                for item in soup.findAll('div',{'class':'articalContent  '}):

                    Content+=unicode(item)

            except:

                Content="No Post Content Found"


        #对于不同的网站类型 应用不同的过滤器


    except:

        Content=timeoutMsg

    return removeStyle(Content)


def removeStyle(Content):

    #add this to remove all the img tag : (<img +(\w=\".*\")>)|(</img>)|(src=\".*\")|

    patn=re.compile(r"(align=\".*\")|(id=\".*\")|(class=\"*\")|(style=\".*\")|(</font>)|(<font.*\">)|(<embed +(\w*=\".*\")>)|(</embed>)")

    replacepatn=""


    Content=re.sub(patn,replacepatn,Content)

    #运用正则表达式把抓取的内容中那些格式通通去掉 这样得到的文字比较纯粹

    return Content

   

def getPostInfos(url,blog_type,order_desc=False):

    feeds=feedparser.parse(url)

    PostInfos=[]

    if order_desc:

        items=feeds.entries[::-1]

    else:

        items=feeds.entries

    Cnt=0

    for  item in items:

        PostInfo={}

        PostInfo['title']=item.title

        PostInfo['author']=item.author

        PostInfo['date']=item.date

        PostInfo['link']=item.link

       

        if blog_type=="wordpress":

            Cnt+=1

            if Cnt<=8:

                PostInfo['description']=getPageContent(item.link,blog_type)

            else:

                PostInfo['description']=removeStyle(item.description)

        elif blog_type=="sina":

            PostInfo['description']=removeStyle(item.description)

           

       

        PostInfos.append(PostInfo)

       

    return PostInfos

template 的概览

在简单之上的原则的鼓舞下, 所有的站点都统一使用一个template 这个template 只接受两个变量--前文中提到的htmlpages 和 PostInfos
重要的片断是:

<div class="page-header">

                              <a href="{{htmlpages.about.url}}" name="{{htmlpages.about.title}}"><h3>{{htmlpages.about.title}}</h3></a>

                             

                         </div>

                         <p>

                              {{htmlpages.about.content}}

                         </p>

                         {%for item in PostInfos%}

                         <div class="page-header">

                              <a href="{{item.link}}" name="{{item.title}}"><h3>{{item.title}}</h3></a>

                             

                         </div>

                         <p><i>author: {{item.author}}    date: {{item.date}}</i></p>

                         <p>{{item.description}}</p>

                         {%endfor%}

                    </div>

总结

一句话, 我爱死Python了
两句话, 我爱死Python了,我爱死Django了
三句话, 我爱死Python了,我爱死Django了,我爱死jQuery了。。。

Python 相关文章推荐
python显示天气预报
Mar 02 Python
python 七种邮件内容发送方法实例
Apr 22 Python
解决python 输出是省略号的问题
Apr 19 Python
python实现图片识别汽车功能
Nov 30 Python
11个Python3字典内置方法大全与示例汇总
May 13 Python
Django 过滤器汇总及自定义过滤器使用详解
Jul 19 Python
python 三元运算符使用解析
Sep 16 Python
python实现大量图片重命名
Mar 23 Python
Python中如何添加自定义模块
Jun 09 Python
Python编写memcached启动脚本代码实例
Aug 14 Python
Opencv+Python识别PCB板图片的步骤
Jan 07 Python
Python可视化学习之seaborn绘制矩阵图详解
Feb 24 Python
python实现RSA加密(解密)算法
Feb 17 #Python
使用python实现rsa算法代码
Feb 17 #Python
Python的GUI框架PySide的安装配置教程
Feb 16 #Python
Python实现快速排序和插入排序算法及自定义排序的示例
Feb 16 #Python
python实现红包裂变算法
Feb 16 #Python
轻松实现python搭建微信公众平台
Feb 16 #Python
十条建议帮你提高Python编程效率
Feb 16 #Python
You might like
DOTA2【瓜皮时刻】Vol.91 RTZ山史最惨“矿难”
2021/03/05 DOTA
PHP制作图形验证码代码分享
2014/10/23 PHP
Zend Framework框架路由机制代码分析
2016/03/22 PHP
PHPExcel实现表格导出功能示例【带有多个工作sheet】
2018/06/13 PHP
js 绑定带参数的事件以及手动触发事件
2010/04/27 Javascript
jquery如何把参数列严格转换成数组实现思路
2013/04/01 Javascript
js获取某元素的class里面的css属性值代码
2014/01/16 Javascript
node+express+ejs制作简单页面上手指南
2014/11/26 Javascript
jquery实现拖拽调整Div大小
2015/01/30 Javascript
jQuery实现的精美平滑二级下拉菜单效果代码
2016/03/28 Javascript
Servlet实现文件上传,可多文件上传示例
2016/12/05 Javascript
jquery拖动改变div大小
2017/07/04 jQuery
详解ES6中的代理模式——Proxy
2018/01/08 Javascript
echarts设置图例颜色和地图底色的方法实例
2018/08/01 Javascript
微信小程序日历弹窗选择器代码实例
2019/05/09 Javascript
layer弹出框确定前验证:弹出消息框的方法(弹出两个layer)
2019/09/21 Javascript
使用Vue调取接口,并渲染数据的示例代码
2019/10/28 Javascript
用JS实现一个简单的打砖块游戏
2019/12/11 Javascript
node爬取新型冠状病毒的疫情实时动态
2020/02/06 Javascript
[03:17]2014DOTA2 国际邀请赛中国区预选赛 四强专访
2014/05/23 DOTA
[01:08:09]DOTA2上海特级锦标赛主赛事日 - 1 胜者组第一轮#1Liquid VS Alliance第二局
2016/03/02 DOTA
pygame学习笔记(5):游戏精灵
2015/04/15 Python
利用python如何处理nc数据详解
2018/05/23 Python
HTML5之web workers_动力节点Java学院整理
2017/07/17 HTML / CSS
html5的canvas元素使用方法介绍(画矩形、画折线、圆形)
2014/04/14 HTML / CSS
Web前端页面跳转并取到值
2017/04/24 HTML / CSS
英国最大的运动营养公司之一:LA Muscle
2018/07/02 全球购物
GetYourGuide台湾:预订旅游活动、景点和旅游项目
2019/06/10 全球购物
Python的两道面试题
2013/06/29 面试题
中专生自我鉴定
2013/12/17 职场文书
综合实践教学反思
2014/01/31 职场文书
三好学生演讲稿范文
2014/04/26 职场文书
大学班级文化建设方案
2014/05/06 职场文书
一年级班主任工作总结2014
2014/11/08 职场文书
永中文档在线转换预览基于nginx配置部署方案
2022/06/10 Servers
苹果macOS 13开发者预览版Beta 8发布 正式版10月发布
2022/09/23 数码科技