15.1 屏幕抓取
屏幕抓取是程序下載網頁並且提取信息的過程。
簡單的屏幕抓取程序
from urllib import urlopen
import re
p = re.compile('<h3><a .*?><a .*? href="(.*?)">(.*?)</a>')
text = urlopen('http://python.org/community/jobs').read()
for url,name in p.findall(text):
print '%s (%s)' % (name,url)
上述這個已經可以用了,但是至少有3個缺點
1.正則表達式並不是完全可讀的。對於更復雜的HTML代碼和查詢來說,表達式會變得亂七八糟並且不可維護。
2.程序對CDATA部分和字符實體之類的HTML特性是無法處理的。如果碰到了這類特性,程序很有可能會失敗。
3.正則表達式被HTML源代碼約束,而不是取決於更抽象的結構。這就意味着網頁結構中很小的改變就會導致程序中斷。
15.1.1 Tidy和XHTML解析
1.Tidy是什麼
Tidy是用來修復不規範且隨意的HTML的工具。它能以相當智能的方法修復一般的錯誤,做那些你不願意做的事情。它也是可設置的,也可以打開或關閉各種修改選項。
Tidy不能修復HTML文件的所有問題,但是它會確保文件的格式是正確的,這樣一來解析的時候就輕鬆多了。
2.獲取Tidy庫
可以從網上下載
3.在Python中使用命令行Tidy
如果正在使用UNIX或Linux系統的話,就不信要安裝任何庫,因爲系統可能已經包括Tidy的命令行版本。
獲得二進制版本後,可以使用subprocess模塊運行Tidy程序。假設有個叫做messy.html的混亂的HTML文件,那麼下面的程序會對該文件運行Tidy,然後打印結果:
from subprocess import Popen,PIPE
text = open('messy.html').read()
tidy = Popen('tidy',stdin=PIPE,stdout=PIPE,stderr=PIPE)
tidy.stdin.write(text)
tidy.stdin.close()
print tidy.stdout.read()
4.但爲什麼用XHTML
XHTML和舊版本的HTML之間的最主要區別是XHTML對於顯式關閉所有元素要求更加嚴格。所以HTML中可能只用一個開始標籤(<p>標籤)結束一段然後開始下一段,而在XHTML中首先需要顯示地關閉當前段落。這種行爲讓XHTML更容易解析,因爲可以直接告訴程序什麼時候進入或者離開各種元素。XHTML的另外一個好處是它是XML的一種,所以可以對它使用XML的工具,例如Xpath。
解析這類從Tidy中獲得的表現良好的XHTML的方法是使用標準庫模塊HTMLParser。
5.使用HTMLParser
使用HTMLParser的意思是繼承它,並且對handle_starttage或handle_data等事件處理方法進行覆蓋。
如果要進行屏幕抓取,一般不需要實現所有的解析器回調,也可能不用創造整個文檔的抽象表示法來查找自己需要的內容。如果只需要記錄所需信息的最小部分,那麼就足夠了。
使用HTMLParser模塊的屏幕抓取程序
from urllib import urlopen
from HTMLParser import HTMLPaeer
class Scraper(HTMLParser):
in_h3 = False
in_link = False
def handle_starttag(self,tag,attrs):
attrs = dict(attrs)
if tag == 'h3':
self.in_h3 = True
if tag == 'a' and 'href' in attrs:
self.in_link = True
self.chunks = []
self.url = attrs['href']
def handle_data(self,data):
if self.in_link:
self.chunks.append(data)
def handle_endtag(self,tag):
if tag == 'h3':
self.in_h3 = False
if tag == 'a':
if self.in_h3 and self.in_link:
print '%s (%s)' % (''.join(self.chunks),self.url)
self.in_link = False
text = urlopen('http://python.org/community/jobs').read()
parser = Scraper()
parser.feed(text)
parser.close()
首先,沒有使用Tidy,因爲網頁中HTML已經足夠規範了。使用了一些布爾狀態變量以追蹤是否已經位於h3元素和鏈接內。在事件處理程序中檢查並且更新這些變量。handle_starttag的attrs參數是由(鍵,值)元組組成的列表,所以使用dict函數將它們轉化爲字典。
handle_data方法可能還得解釋一下。它使用了在處理HTML和XML這類結構化標記的基於事件的解析工作時非常常見的技術。我沒有假定只掉用handle_data就能獲得所有需要的文本,而是假定會通過多次調用函數獲得多個文本塊。這樣做的原因有幾個:忽略了緩衝、字符實體和標記等----只需確保獲得所有文本。然後在準備輸出結果時,只是將所有的文本聯結在一起。可以讓文本調用feed方法以運行這個解析器,然後再調用close方法。
15.1.2 Beautiful Soup
Beautiful Soup是個小模塊,用來解析和經常在網上看到的那些亂七八糟而且不規則的HTML。
下載和安裝beautiful Soup:下載BeautifulSoup.py文件,然後將它放置在python路徑中。如果需要的話,還能下載帶有安裝腳本和測試的tar檔案文件。
使用beautiful Soup的屏幕抓取程序
from urllib import urlopen
from BeautifulSoup import BeautifulSoup
text = urlopen('http://python.org/community/jobs').read()
soup = BeautifulSoup(text)
jobs = set()
for header in soup('h3'):
links = header('a','reference')
if not links:continue
link = links[0]
jobs.add('%s (%s)' % (link.string,link['href']))
print '\n'.join(sorted(jobs,key=lambda s: s.lower()))
用HTML文本實例化BeautifulSoup類,然後使用各種方法提取處理後的解析樹的各個部分。
15.2 使用CGI創建動態網頁
CGI(通用網關接口)。CGI是網絡服務器可以將查詢傳遞到專門的程序中並且在網頁上顯示結果的標準機制。它是創建萬維網應用程序而不用編寫特殊用途的應用服務器的簡單方法。
Python CGI程序設計的關鍵工具是cgi模塊。
15.2.1 第一步:準備網絡服務器
15.2.2 第二步:加入Pound Bang行
當把腳本放在正確位置後,需要在腳本的開始處增加pound bang行。
#!/usr/bin/env python
15.2.3 設置文件許可
設置權限
15.2.5 簡單的CGI腳本
簡單的CGI腳本
#!/usr/bin/env python
print 'Content-type:text/plain'
print 'hello,world'
text.plain 說明是普通文本,如果頁面是HTML,這一行就是text/html
15.2.6 使用cgitb調試
調用回溯的CGI腳本
#!/usr/bin/env python
import cgitb;cgitb.enable()
print 'Content-type: text/html'
print 1/0
print 'hello,world'
可以通過瀏覽器訪問下
15.2.7 使用cgi模塊
輸入時通過HTML表單提供給CGI腳本的鍵-值對,或稱字段。可以使用cgi模塊的FieldStorage類從CGI腳本中獲取這些字段。當創建FieldStorage實例時,它會從請求中獲取輸入變量,然後通過類字典接口將它們提供給程序。FieldStorage的值可以通過普通的鍵查找方式訪問,但是因爲一些技術原因,FieldStorage的元素並不是真正所要的值。比如知道請求中包括名爲name的值,就不應該像下面這麼做:
form = cgi.FieldStorage()
name = form['name']
而應該這樣做:
form = cgi.FieldStorage()
name = form['name'].value
獲取值得簡單方式就是用getvalue方法,它類似於字典的get方法,但它會返回項目的value特性的值。
form = cgi.FieldStorage()
name = form.getvalue('name','Unknown')
在上面的代碼,我提供了一個默認值unknown。如果不提供的話,就會將None作爲默認值使用。
利用FieldStorage獲取一個值得CGI腳本
#!/usr/bin/env python
import cgi
form = cgi.FieldStorage()
name = from.getvalue('name','world')
print 'Content-type:text/plain'
print 'Hello,%s!' % name
CGI腳本的輸入一般都是從已經提交的web表單中獲得,但是也可以直接使用參數調用CGI程序。
15.2.8 簡單的表單
從CGI腳本獲取信息的方法有兩種:GET方法和POST方法。
帶有HTML表單的問候腳本
#!/usr/bin/env python
import cgi
form = cgi.FieldStorage()
name = from.getvalue('name','world')
print ""Content-type:text/html
<html>
<head>
<title>Greeting Page</title>
</head>
<body>
<h1>hello,%s1</h1>
<form action='simple3.cgi'>
change name <input type='text' name='name'/>
<input type='submit'/>
</form>
</body>
</html>
""" % name
在腳本的開始處先獲取CGI參數name。
15.3 mod_python
mod_python是apache的擴展模塊。
15.3.1 安裝mod_python
$./configure --with-apxs=/usr/local/apache/bin/apxs
$make
$make install
配置apache
LoadModule python_module libexec/mod_python.so
在.htaccess文件添加
<Directory /path/to/your/directory>
(add the directory here)
</Directory>
15.3.2 CGI處理程序
CGI處理程序在使用CGI的時候會模擬程序運行的環境。所以可以用mod_python運行程序,但是還可以使用gi和gitb模塊把它當作CGI腳本來寫。
如果要使用CGI處理程序,要將下面的代碼放在CGI腳本所在目錄中的.htaccess文件內
SetHandler mod_python
PythonHandler mod_python.cgihandler
需要調試信息的話,增加代碼:
PythonDebug On
開發完之後,在去掉。
爲了運行CGI腳本,可能需要腳本以.py結尾---儘管訪問的時候還是用以.cgi結尾的URL,mod_python在查找滿足請求的文件時會將.cgi轉換爲.py
15.3.3 PSP
PSP文檔是HTML以及python代碼的混合,python代碼會包括在具有特殊用途的標籤中。任何HTML會被轉換爲輸出函數的調用。
只要把下面 的代碼放在.htaccess文件中即可設置PSP頁面:
AddHandler mod_python .psp
PythonHandler mod_python .psp
PSP標籤有兩類:一類用於語句,另外一類用於表達式。
帶有少量隨機數據的PSP例子
<%
from random import choice
adjectives = ['beautiful','cruel']
%>
<html>
<head>
<title>hello</title>
</head>
<body>
<p>hello, <%=choice(adjectives)%> world. my name is Mr. Gumby.</p>
</body>
</html>
<% - 像這樣 -%> 書寫註釋。
15.3.4 發佈
要使用發佈處理程序,需要下面代碼放在.htaccess文件中。
AddHandler mod_python .py
PythonHandler mod_python.publisher
這樣可以使用發佈處理程序把所有以.py當作python腳本運行。
下面的函數已經可以發佈處理程序了:
def index(req):
return "hello,world"
請求對象訪問受到請求中德信息,以及設置自定義HTTP首部等。
使用mod_python發佈處理程序進行驗證
from sha import sha
__auth_realm__ = "A simple test"
def __auth__(req,user,pswd):
return user == "Gumby" and sha(pswd).hexdigest() == \
'13hj3123012kllkjfl1'
def __access__(req,user):
return True
def index(req,name="world"):
return "<html>hello,%s!</html>" % name
15.4 網絡應用程序框架
15.5 web服務:正確分析
15.5.1 RSS
15.5.2 使用XML-RPC進行遠程過程調用。