Using Django with GAE Python 後台抓取多個網站的頁面全文

來源:互聯網
上載者:User
一直想做個能幫我過濾出優質文章和部落格的平台 給它取了個名 叫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

App 的特色
這個站上除了最上面的黑色導航條 和 最右邊的About This Site 部分外, 其他的內容都是從另外的網站上自動獲得
原則上, 可以添加任何部落格或者網站地址到這個東西。。。當然因為這個是L2Z Story..所以只收錄了四個網站在裡面
特點是: 只要網站的主人不停止更新, 這個東西就會一直存在下去---這就是懶人的力量


值得一提的是, Content 菜單是在用戶端用JavaScript 自動產生的--這樣就節約了伺服器上的資源消耗

這裡用的是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 裡的Regex

複製代碼 代碼如下:


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 : ()|()|(src=\".*\")|
patn=re.compile(r"(align=\".*\")|(id=\".*\")|(class=\"*\")|(style=\".*\")|()|()|()|()")
replacepatn=""


Content=re.sub(patn,replacepatn,Content)
#運用Regex把抓取的內容中那些格式通通去掉 這樣得到的文字比較純粹
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
重要的片斷是:
複製代碼 代碼如下:



{{htmlpages.about.title}}





{{htmlpages.about.content}}


{%for item in PostInfos%}

{{item.title}}




author: {{item.author}} date: {{item.date}}


{{item.description}}


{%endfor%}

總結

一句話, 我愛死Python了
兩句話, 我愛死Python了,我愛死Django了
三句話, 我愛死Python了,我愛死Django了,我愛死jQuery了。。。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.