《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 正則表示式的有益應用
提取字元塊