| 2001 年 7 月 01 日 本文介紹了 Txt2Html,由 David 創(chuàng )建的公共域工作項目,用于說(shuō)明 Python 的編程技巧。Txt2Html 是“基于 Web 的過(guò)濾代理”-- 一種替用戶(hù)讀取基于 Web 的文檔,然后將修改過(guò)的頁(yè)面顯示到用戶(hù)瀏覽器的程序。為了使這成為可能,Txt2Html 以 CGI 程序的形式運行,查詢(xún)外部 Web 資源的信息,并利用規則表達式。David 將為您逐步解釋、說(shuō)明和演示這些多種用途的子任務(wù)。 在撰寫(xiě)本 developerWorks 系列文章的過(guò)程中,我曾遇到過(guò)以最佳格式進(jìn)行撰寫(xiě)的問(wèn)題。文字處理程序格式都是專(zhuān)用的,在格式之間轉換總不能盡如人意,也很麻煩(而且每種格式都會(huì )各自將文檔綁定到不同的專(zhuān)用工具,這種情況又與開(kāi)放源碼的精神相違背)。HTML 還算中立 -- 也許您現在閱讀的文章正是這種格式 -- 但它也添加了標記,而這些標記很容易引起誤輸入(或者使人束縛于 HTML 增強型編輯器)。DocBook 是一種有趣的 XML 格式,它可以轉換成許多目標格式,并且它擁有技術(shù)文章(或書(shū)籍)的正確語(yǔ)義;但就像 HTML 一樣,在撰寫(xiě)過(guò)程中會(huì )擔心許多標記。LaTeX 特別適合復雜的印刷格式;但它也有許多標記,而這些文章并不需要復雜的印刷格式。 為了在寫(xiě)作時(shí)能真正省心 -- 特別是要具有平臺和工具的中立性 -- 無(wú)格式 ASCII 正是最好的選擇。但是,因特網(wǎng)(特別是 Usenet)建議在完全無(wú)格式文本的基礎上,開(kāi)發(fā)一種“智能 ASCII”文檔的非正式標準(請參閱 參考資料 )?!爸悄?ASCII”只添加了一點(diǎn)額外的語(yǔ)義內容和上下文,而且它們在文本顯示中看起來(lái)是那么“自然”。電子郵件、新聞組郵件、FAQ、項目自述文件 (README) 和其它電子文檔通常包括一些印刷/語(yǔ)義元素,如強調字前后的星號、標題下的下劃線(xiàn)、描述文本關(guān)系的豎直和水平空格、有選擇性的全大寫(xiě)和其它一些信息。Project Gutenberg(請參閱 參考資料 )是一種驚人的成果,它將許多想法加入其自身格式構思中,并認為“智能 ASCII”是長(cháng)時(shí)間保存和分發(fā)好書(shū)的最佳選擇。即使這些文章不會(huì )像文學(xué)名著(zhù)那樣經(jīng)久不衰,仍決定將它們寫(xiě)成“智能 ASCII”格式,并用方便的 Python 腳本將它們自動(dòng)轉換成其它格式。 Txt2Html 最初是一個(gè)簡(jiǎn)單的文件轉換器,從其名稱(chēng)上就可以看出。但因特網(wǎng)建議在工具上添加幾個(gè)明顯的增強功能。因為有許多讀者想以“HTML 化”格式查看的文檔都在 http: 或 ftp: 鏈接的后面,所以工具應該真正直接處理這樣的遠程文檔(而不需要下載/轉換/查看循環(huán)周期)。因為轉換的目標最終是 HTML,通常我們要做的就是在 Web 瀏覽器中查看轉換后的目標文檔。 將這些放到一起后,Txt2Html 就成了“基于 Web 的過(guò)濾代理”。這個(gè)詞很奇特,也許恰好能“完全表達其含義”。它們體現了以下想法:程序代表您閱讀 Web 頁(yè)面(或其它資源),以某種方式處理內容,然后以某種比原始頁(yè)面 更好 的形式(至少能滿(mǎn)足某些特殊目的)向您顯示該頁(yè)面。這種工具的一個(gè)很好例子就是 Babelfish 翻譯服務(wù)(請參閱 參考資料 )。在通過(guò) Babelfish 運行了 URL 之后,您看到的 Web 頁(yè)面與原始頁(yè)面非常相似,但它顯示了您能夠讀懂的文字,而不是您不理解的語(yǔ)言。在某種程度上,所有顯示搜索結果頁(yè)面摘要的搜索引擎所做的都是相同的事情。但那些搜索引擎(按設計)在目標頁(yè)面的格式以及外觀(guān)方面有更多的自由度,同時(shí)會(huì )去掉許多內容。當然,Txt2Html 并不如 Babelfish 那樣功能強大;但概念上,它們很大程度上完成相同的事情。請參閱 參考資料以獲取更多的例子,其中一些還很幽默。 Txt2Html 最大的優(yōu)點(diǎn)是使用許多編程技巧,而這些技巧對于不同面向 Web 使用 Python 是共通的。本文將介紹那些技巧,并會(huì )說(shuō)明編碼技巧和一些 Python 模塊的作用域。請注意:Txt2Html 中的實(shí)際模塊叫做 dmTxt2Html,以避免與其他人編寫(xiě)的模塊名稱(chēng)發(fā)生沖突。
Python 標準發(fā)行版中的 cgi 模塊對于任何用 Python 開(kāi)發(fā)“公共網(wǎng)關(guān)接口”應用程序的人來(lái)說(shuō)都是意外驚喜??梢圆皇褂盟鼇?lái)創(chuàng )建 CGI,但您不會(huì )這樣做。 最通常的情況下,您會(huì )通過(guò) HTML 表單與 CGI 應用程序進(jìn)行交互。要填寫(xiě)調用 CGI 的表單以執行使用規范的操作。例如,Txt2Html 文檔使用這個(gè)例子來(lái)調用 HTML 表單(Txt2Html 自身生成的表單比較復雜,并且可能會(huì )改變,但示例將會(huì )很好地工作,甚至是在您自己的 Web 頁(yè)面中): 調用 ‘Txt2Html‘ 的 HTML 表單
可以在 HTML 表單中包括許多輸入字段,而字段可以是許多不同類(lèi)型中的一種(文本、復選框、單選列表、單選按鈕等)。任何講述 HTML 的好書(shū)可以幫助初學(xué)者創(chuàng )建定制 HTML 表單。此處最需要牢記的是每個(gè)字段都有名稱(chēng)屬性,而以后會(huì )在 CGI 腳本中使用該名稱(chēng)來(lái)引用該字段。另一個(gè)需要了解的細節是表單可以使用以下兩種方法中的一種:"get" 和 "post"。它們的基本差異是 "get" 在 URL 中包括了查詢(xún)信息,并且這個(gè)方法使用戶(hù)易于保存特定查詢(xún)以便日后重復使用。另一方面,如果您不希望用戶(hù)保存查詢(xún),請使用 "post" 方法。 以上表單調用的 Python 腳本將導入 cgi 以使整理其調用表單變得更容易。這個(gè)模塊所做的一件事就是隱藏 CGI 腳本中 "get" 和 "post" 方法之間差異的任何細節。在進(jìn)行調用之前,這并不是 CGI 創(chuàng )建程序需要操心的細節。該 CGI 模塊的主要作用是處理類(lèi)似于字典樣式的調用 HTML 表單中的所有字段。您所得到的并不是 真正 的 Python 字典,但它們的使用方式極其相似: 使用 Python [cgi] 模塊
在以上幾行中,要注意幾處細節。我們所使用的一個(gè)技巧是設置 sys.stderr = sys.stdout。如果我們的腳本遇到了一個(gè)未捕獲的錯誤,通過(guò)這個(gè)操作,追溯就顯示回到客戶(hù)機瀏覽器。這可以節省許多調試 CGI 應用程序的時(shí)間。但您也許不想讓用戶(hù)看到這些(或者如果它們可能向您報告問(wèn)題詳細信息,您也許會(huì )向用戶(hù)顯示這些內容)。接著(zhù),我們將 HTML 表單值讀取到類(lèi)似于字典的 至此,HTML 表單中的所有東西都成了純 Python 變量,而且我們可以在其它任何 Python 程序中處理它們。
就像大多數 Python 模塊一樣,urllib 以直觀(guān)且簡(jiǎn)單的方法處理許多復雜事物。urllib 中的 urlopen() 函數可以處理任何遠程資源 -- 無(wú)論是 http:、ftp: 還是 gopher: --就將它當作是本地文件。如果使用 urlopen() 抓取了一個(gè)遠程(偽)文件對象,可以將它當作本地(只讀)文件的文件對象來(lái)進(jìn)行處理: 使用 Python [urllib] 模塊
我曾遇到過(guò)一個(gè)小問(wèn)題,由于生成資源的平臺和您的平臺使用不同的行結束約定,生成的文本中可能會(huì )發(fā)生某些奇怪的事(這似乎是 urllib 中的錯誤)。這個(gè)問(wèn)題的解決方法是在以上代碼中執行小小的 .readlines() 循環(huán)。不管該資源原來(lái)是什么樣子,這個(gè)操作會(huì )給您一個(gè)字符串,它都有所使用平臺的正確行結束約定(估計可能合理)。
由于本文篇幅限制,這里只討論一部分規則表達式。 參考資料中列出了許多關(guān)于該主題的參考書(shū)籍。 re 模塊在 Txt2Html 中的使用很廣泛,用于標識源文本中的各種文本模式。我們查看一個(gè)比較復雜的例子: 使用 Python [re] 模塊
URLify() 是一個(gè)小巧函數,它的功能正如其名稱(chēng)所示。如果在“智能 ASCII”文件中遇到了類(lèi)似于 URL 的字符串,它將轉換成 HTML 輸出中同一個(gè) URL 真正的熱鏈接。讓我們看看 re.sub() 的作用。首先,從最主要的角度來(lái)看,函數的目的是“查找與第一個(gè)模式中的值匹配的字符串,然后通過(guò)將第三個(gè)變量作為要操作的字符串,以第二個(gè)模式替換匹配結果”。非常好,從這些角度來(lái)看,它與 string.replace() 沒(méi)有什么區別。 第一個(gè)模式有幾個(gè)元素。首先,請注意括號:最高層次由兩對括號組成:(\S) 前復雜的字符串。括號對與可能組成部分替換模式的“子表達式”匹配。第二個(gè)子表達式 (\s) 只表示“查找與任何空格字符匹配的字符串,讓我們回過(guò)來(lái)看什么與之匹配”。因此,讓我們研究一下第一個(gè)子表達式。 Python 規則表達式有它自己的一些技巧。其中一個(gè)技巧是子表達式開(kāi)始處的 ?: 運算符。這表示“查找子模式的匹配字符串,但不包括反向引用中的匹配結果”。那么,讓我們檢查一下這個(gè)子表達式:
首先,請注意這個(gè)子表達式本身由兩個(gè)子表達式組成,兩者之間的字符并不屬于任何一方。但是,每個(gè)子表達式都由 在規則表達式中,方括號只表示“查找與方括號中任何字符的匹配字符”。但是,如果第一個(gè)字符是插入記號 (^),含義則相反,它表示“查找任何與后面的字符 不匹配的字符”。因此,我們正在查找 不 是空格、CR、LF、"<" 或 ")" 的字符(還請注意,可以通過(guò)在字符前面加 "\",避開(kāi)對于規則表達式有特殊意義的字符)。結尾處的加號表示“查找最后字符串的一個(gè)或多個(gè)匹配”(星號表示“零個(gè)或多個(gè)”,問(wèn)號表示“零個(gè)或一個(gè)”)。 這個(gè)規則表達式有許多內容有待理解,但如果多看幾遍,您就會(huì )明白這就是 URL 的格式。 接著(zhù)就是替換部分。這就更簡(jiǎn)單了??瓷先ヮ?lèi)似
|
聯(lián)系客服