1. 程式人生 > >《python自然語言處理》筆記---chap3加工原料文字

《python自然語言處理》筆記---chap3加工原料文字

chap3中關於,NLP中的關鍵概念,包括分詞和詞幹提取。字串、檔案、正則表示式、去除HTML標籤

以下所有程式,預設匯入包

import nltk,re,pprint     #即,nltk包,正則表示式re包,輸出pprint包

3.1 從網路和硬碟訪問文字
電子書

#coding:utf-8
import nltk

from urllib import urlopen
url = "http://www.gutenberg.org/files/2554/2554.txt"
raw=urlopen(url).read()

print type(raw) #文字的型別
print len(raw)  #文字長度
print raw[:75]  #文字前75個字元,不要直接打印出raw,太長了

#使用代理訪問:
#proxies={'http':'http://www.someproxy.com:3128'}
#raw=urlopen(url,proxies=proxies).read()

分詞:將字串分解為詞和標點符號;經過分詞,產生一個詞彙和標點符號的連結串列

tokens=nltk.word_tokenize(raw)
print type(tokens)
print len(tokens)
print tokens[:10]
#從連結串列建立一個NLTK文字,對其進行操作
text=nltk.Text(tokens)
print type(text)
print text[:10]     #text似乎同tokens沒什麼區別?
#print text.collocations()

古騰堡專案的每個文字:包含一個首部,涵蓋了文字的名稱、作者、掃描和校對文字的人的名字、許可證等資訊。手工檢查檔案以發現標記內容開始和結尾的獨特的字
符串。

print raw.find("PART I")
print raw.rfind("End of Project Gutenberg's Crime")   #逆向查詢
#重新複製,將從"PART I"到"End of Project Gutenberg's Crime"部分截下來,賦給raw
raw=raw[raw.find("PART I"):raw.rfind("End of Project Gutenberg's Crime")]

處理的HTML

HTML全部內容包括:meta元標籤、影象標籤、map標籤、JavaScript、表單和表格。
提取文字:clean_html()將HTML字串作為引數,返回原始文字,然後對原始文字進行分詞,活得熟悉的文字結構

#coding:utf-8
import nltk

from urllib import urlopen
url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"
html = urlopen(url).read()
print html[:60]
#'<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN'
#html=html[:60]
raw = nltk.clean_html(html)
tokens = nltk.word_tokenize(raw)
tokens=tokens[96:399]
text=nltk.Text(tokens)
print text.concodance('gene')
'''
使用clean_html()函數出錯:
raise NotImplementedError ("To remove HTML markup, use BeautifulSoup's get_text() function")
NotImplementedError: To remove HTML markup, use BeautifulSoup's get_text() function

根據官方網站:介紹http://www.nltk.org/_modules/nltk/util.html
def clean_html(html):
    raise NotImplementedError ("To remove HTML markup, use BeautifulSoup's get_text() function")
[docs]def clean_url(url):
    raise NotImplementedError ("To remove HTML markup, use BeautifulSoup's get_text() function")
網站:http://stackoverflow.com/questions/10524387/beautifulsoup-get-text-does-not-strip-all-tags-and-javascript介紹:
以後的版本,似乎不支援clean_html()和clean_url()這兩個函式
Support for clean_html and clean_url will be dropped for future versions of nltk. Please use BeautifulSoup for now...it's very unfortunate.
'''

通過嘗試,找到內容索引的開始和結尾,並選擇你感興趣的識別符號,初始化一個文字。
更多更復雜的有關處理HTML 的內容,可以使用http://www.crummy.com/software/BeautifulSoup/上的Beautiful Soup 軟體包。
處理搜尋引擎的結果

搜尋引擎的主要優勢是規模


讀取本地檔案

open()函式:
f=open(r'D:\test.txt') #注意格式,檔案路徑前面用個r,或者對檔案路徑裡面的符號進行轉義
raw=f.read()

#按行讀出
for line in f:
 print line.strip()#去掉換行符
#nltk語料庫中的檔案,使用nltk.data.find()函式
path = nltk.data.find('corpora/gutenberg/melville-moby_dick.txt')
raw = open(path, 'rU').read()

從 PDF、MS Word 及其他二進位制格式中提取文字

開啟PDF和MSWord,用第三方函式庫如pypdf和pywin32,

捕獲使用者輸入

輸入函式:raw_input("")
輸出函式:print

NLP 的流程

處理流程:開啟一個URL,讀裡面HTML 格式的內容,去除標記,並選擇字元的切
片,然後分詞,是否轉換為nltk.Text 物件是可選擇的。我們也可以將所有詞彙小寫並提取詞彙表。

 一個物件的型別決定了它可以執行哪些操作,如可以追加元素到一個連結串列,但是不能追加元素到一個字串

可以用加號,連線字串與字串,但是不能連線字串與連結串列

3.2 字串:最底層的文字處理

字串的基本操作

 1.字串中包含單引號,需要用"\"轉義
2.可用單引號,雙引號,三重引號來指定字串,其中的區別,見部落格
3.字串跨好幾行,a:使用反斜槓"\",直譯器就知道第一行的表示式不完整;b:使用括號,將兩個字串括起來,中間換行即可,不用加逗號
4.對字串操作,“+”加法:連線字串;“*”乘法:多倍連線字串;不能使用減法和除法

>>> a='first'\
   'second'		#使用反斜槓跨行
>>> a
'firstsecond'

'very' + 'very' + 'very'
'very' * 3

輸出字串

print '逗號隔開','能夠連著一行輸出去'“,”告訴python不要再行尾輸出換行符

訪問單個字元

1.從0開始,長度為1的字串,用索引符號[]呼叫,
2.超出索引範圍,出錯
3.字串的負數索引,-1為最後一個字元的索引,-2,-3,...對應著過去,
4.計數單個字元。將所有字元小寫,忽略掉大小寫,並過濾掉非字母字元

import nltk
from nltk.corpus import gutenberg
raw=gutenberg.raw('melville-moby_dick.txt')
fdist=nltk.FreqDist(ch.lower() for ch in raw if ch.isalpha())
print fdist.keys()	#出現頻率最高排在最先的順序顯示出英文字母
print fdist.values()	#fdist如同key-value一般,呼叫keys和values方法,能夠顯示對應的字元情況
fdist.plot()		#視覺化輸出
'''執行結果:
[u'e', u't', u'a', u'o', u'n', u'i', u's', u'h', u'r', u'l', u'd', u'u', u'm', u'c', u'w', u'f', u'g', u'p', u'b', u'y', u'v', 

u'k', u'q', u'j', u'x', u'z']
[117092, 87996, 77916, 69326, 65617, 65434, 64231, 62896, 52134, 42793, 38219, 26697, 23277, 22507, 22222, 20833, 20820, 

17255, 16877, 16872, 8598, 8059, 1556, 1082, 1030, 632]
圖略'''

訪問子字串

1.使用切片,開始於第一個索引,結束於最後一個索引的前一個。注意,最後索引的前一個
2.負數索引切片,-1為最後一個,-2,-3...推算過去
3.省略:第一個值,即從字串開頭開始;第二個值,切到字元結尾結束;
4.in操作符:測試一個字串是否包含一個特定的子字串
5.find()函式操作:子字串在字串內的位置;從開頭到找到的第一個位置.(若是第二個怎麼算?)
6.rfind()函式,從末尾開始查詢,同findd().只是開始位置相反而已。

monty='Monty Python'
monty[6:10]
monty[-12:-7]
phrase = 'And now for something completely different'
if 'thing' in phrase:
    print '''find "thing"'''

更多的字串操作

 help(str)可以找到所有的有關函式

方法 功能
s.find(t) 字串s 中包含t 的第一個索引(沒找到返回-1)
s.rfind(t) 字串s 中包含t 的最後一個索引(沒找到返回-1)
s.index(t) 與s.find(t)功能類似,但沒找到時引起ValueError
s.rindex(t) 與s.rfind(t)功能類似,但沒找到時引起ValueError
s.join(text) 連線字串s 與text 中的詞彙
s.split(t) 在所有找到t 的位置將s 分割成連結串列(預設為空白符)
s.splitlines() 將s 按行分割成字串連結串列
s.lower() 將字串s 小寫
s.upper() 將字串s 大寫
s.titlecase() 將字串s 首字母大寫
s.strip() 返回一個沒有首尾空白字元的s 的拷貝
s.replace(t, u) 用u 替換s 中的t

連結串列與字串的差異

1.字串和連結串列之間不能連線
2.我們使用一個for 迴圈來處理讀入檔案(對應的檔案內容對應一個字串),所有我們可以挑選出的只是單個的字元——我們不選擇粒度;連結串列中的元素可以很大也可以很小,它們可能是段落、句子、短語、單詞、字元。連結串列的優勢在於我們可以靈活的決定它包含的元素,相應的後續的處理也變得靈活
3.我們在一段NLP 程式碼中可能做的第一件事情就是將一個字串分詞放入一個字元;當我們要將結果寫入到一個檔案或終端,我們通常會將它們格式化為一個字串
4.字串是不可改變的:一旦你建立了一個字串,就不能改變它。連結串列是可變的,內容可以隨時修改

 3.3 使用 Unicode 進行文書處理

什麼是 Unicode?

編碼點:每個字元分配一個編號;python中編碼點寫作\uXXXX 的形式,其中XXXX 是四位十六進位制形式數。
位元組流:
解碼:將文字翻譯成Unicode——翻譯成Unicode
編碼:將Unicode 轉化為其它編碼的過程
Unicode的角度看字元:,字元是可以實現一個或多個字形的抽象的實體。只有字形可以出現在螢幕上或被列印在紙上。一個字型是一個字元到字形對映。

Unicode 的解碼和編碼

從檔案中提取已編碼文字

nltk.data.find()函式:定位檔案

import nltk
path = nltk.data.find('corpora/unicode_samples/polish-lat2.txt')

codecs模組:提供了將編碼資料讀入為Unicode 字串和將Unicode 字串以編碼形式寫出的函式。
codecs.open()函式:encoding 引數來指定被讀取或寫入的檔案的編碼。
unicode_escape編碼:Python的一個虛擬的編碼;把所有非ASCII 字元轉換成它們的\uXXXX 形式。

path = nltk.data.find('corpora/unicode_samples/polish-lat2.txt')
f=codecs.open(path,encoding='latin2')
#print f
#似乎調用出錯,還是說沒有將f讀出來以Unicode返回

#f2=codecs.open(path,'w',encoding='utf-8')
#print f2
#檔案物件f 讀出的文字將以Unicode 返回
for line in f.readlines():
    line=line.strip()
    print line.encode('unicode_escape')

Unicode 字串常量:在字串常量前面加一個u,
ord()函式:查詢一個字元的整數序列。如ord('a')

>>> a=u'\u0062'		#對其進行轉義
>>> a
u'b'
>>> print a
b

print 語句:假設Unicode 字元的預設編碼是ASCII 碼。
repr()函式:轉化的字串,輸出utf-8轉義序列(以\xXX的形式)

nacute = u'\u0144'
nacute_utf = nacute.encode('utf8')
print nacute
print repr(nacute_utf)

unicodedata模組:檢查Unicode 字元的屬性。

在 Python中使用本地編碼

pass

3.4  使用正則表示式檢測片語搭配

使用基本的元字元

美元符號$:用來匹配單詞的末尾;
乘方符號^:用來匹配單詞的開始;
符號“?”:表示前面的一個字元可選;
萬用字元“.”:匹配任何單個字元。

«^e-?mail $»將匹配email 和e-mail
例1:查詢以ed結尾的詞彙,《ed$》
例2:假設我們有一個8 個字母組成的詞的字謎室,j 是其第三個字母,t 是其第六個字母。
例3:計數一個文字中出現email 或e-mail的次數,

import re,nltk
wordlist = [w for w in nltk.corpus.words.words('en') if w.islower()]

print [w for w in wordlist if re.search('ed$',w)]
print [w for w in wordlist if re.search('^..j..t..$',w)]
print sum(1 for w in text if re.search('^e-? mail$',w))
#用IDLE執行有點慢,直接用命令視窗的話,更快。。。

範圍與閉包

手機輸入法聯想提示:例如,hole 和golf 都是通過輸入序列4653。

T9:9 個鍵上的文字

閉包:+、*
“+”:前面的專案的一個或多個例項
“*”:前面的專案的零個或多個例項
“^”:出現在方括號內的第一個字元位置

查詢非母音字母組成的詞彙:«^[^aeiouAEIOU]+$»
例1:按鍵4653,產生哪些相同的序列單詞?

[w for w in wordlist if re.search('^[ghi][mno][jlk][def]$',w)]	
#以g或者h或者i開頭,以d或者e或者f結尾的,並且第二個字元是m,n,o中的一個,第三個字元是j,l,k中的一個

例2:“+”符號的使用

chat_words = sorted(set(w for w in nltk.corpus.nps_chat.words()))
print [w for w in chat_words if re.search('^m+i+n+e+$', w)]	#1個或者多個m,i,n,e,並且以m開頭,e結尾
print [w for w in chat_words if re.search('^[ha]+$', w)]	#以ha開頭,並且有1一個或者多個ha,
'''
[u'miiiiiiiiiiiiinnnnnnnnnnneeeeeeeeee', u'miiiiiinnnnnnnnnneeeeeeee', u'mine', u'mmmmmmmmiiiiiiiiinnnnnnnnneeeeeeee']
[u'a', u'aaaaaaaaaaaaaaaaa', u'aaahhhh', u'ah', u'ahah', u'ahahah', u'ahh', u'ahhahahaha', u'ahhh', u'ahhhh', u'ahhhhhh', 

u'ahhhhhhhhhhhhhh', u'h', u'ha', u'haaa', u'hah', u'haha', u'hahaaa', u'hahah', u'hahaha', u'hahahaa', u'hahahah', u'hahahaha', 

u'hahahahaaa', u'hahahahahaha', u'hahahahahahaha', u'hahahahahahahahahahahahahahahaha', u'hahahhahah', u'hahhahahaha']
'''
wsj = sorted(set(nltk.corpus.treebank.words()))
print [w for w in wsj if re.search('^[0-9]+\.[0-9]+$',w)]	#任何帶小數點的符號數
print [w for w in wsj if re.search('^[A-Z]+\$$',w)]		#以$結尾,前面有1個或者多個大寫字母
print [w for w in wsj if re.search('^[0-9]{4}$',w)]		#XXXX年
print [w for w in wsj if re.search('^[0-9]+-[a-z]{3,5}$',w)]	#['10-day', '10-lap', '10-year', '100-share', '12-point', '12-year', ...]
print [w for w in wsj if re.search('^[a-z]{5,}-[a-z]{2,3}-[a-z]{,6}$',w)]
#['black-and-white', 'bread-and-butter', 'father-in-law', 'machine-gun-toting','savings-and-loan']
print [w for w in wsj if re.search('(ed|ing)$',w)]		#以ed或者ing結尾的單詞或者符號

“\.”:匹配一個句號。
大括號表達:如{3,5},表示前面的專案重複指定次數。
管道字元:從其左邊的內容和右邊的內容中選擇一個。
圓括號:表示一個操作符的範圍,它們可以與管道(或叫析取)符號一起使用,如:«w(i|e|ai|oo)t»,匹配wit、wet、wait 和woot。

表:正則表示式基本元字元,其中包括萬用字元,範圍和閉包

操作符

行為

·

萬用字元,匹配所有字元

^abc

匹配以abc 開始的字串

abc$

匹配以abc 結尾的字串

[abc]

匹配字元集合中的一個

[A-Z0-9]

匹配字元一個範圍

ed|ing|s

匹配指定的一個字串(析取)

*

前面的專案零個或多個,如a*, [a-z]* (也叫Kleene 閉包)

+

前面的專案1 個或多個,如a+, [a-z]+

?

前面的專案零個或1 個(即:可選)如:a?, [a-z]?

{n}

重複n 次,n 為非負整數

{n,}

至少重複n 次

{,n}

重複不多於n 次

{m,n}

至少重複m 次不多於n 次

a(b|c)+

括號表示操作符的範圍

原始字串:字首"r";例如:原始字串r'\band\b'包含兩個“\b”符號會被re 庫解釋為匹配詞的邊界而不是解釋為退格字元。

3.5 正則表示式的有益應用
提取字元塊