Python 提供了強大的工具和函式庫,方便我們處理網路和本地端的文字資料。從檔案讀取、網路爬蟲到 HTML 解析,都能夠有效率地完成。利用 NLTK 可以進一步進行分詞、去除標記等文字預處理工作,為後續的自然語言處理任務奠定基礎。同時,文章也提供處理 RSS 饋流以及 PDF、MSWord 等二進位格式檔案的實用技巧,讓開發者能更彈性地應對不同資料來源的挑戰。對於常見的檔案處理問題,例如檔案路徑錯誤、編碼問題等,也提供對應的解決方案,讓讀者能更順利地進行文字資料的處理與分析。

存取原始文字的技術與應用

隨著網路的普及,網路已成為最重要且豐富的文字來源。雖然我們可以利用現有的語料函式庫進行研究,但大多數情況下,我們仍需要學習如何存取自己的文字來源。本章將著重於回答以下幾個關鍵問題:

  1. 如何編寫程式以存取本地檔案和網路上的文字,從而取得無限的語言材料?
  2. 如何將檔案分解為單個詞彙和標點符號,以便進行與前幾章中對文字語料函式庫相同的分析?
  3. 如何編寫程式以產生格式化的輸出並將其儲存到檔案中?

存取本地檔案與網路文字

在處理原始文字之前,我們首先需要了解如何存取本地檔案和網路上的文字。Python 提供了多種方法來實作這一點。

存取本地檔案

Python 的內建函式 open() 可以用來開啟本地檔案。例如:

with open('example.txt', 'r', encoding='utf-8') as file:
    text = file.read()

內容解密:

此段程式碼開啟了一個名為 example.txt 的檔案,並以讀取模式 ('r') 和 UTF-8 編碼方式讀取其內容。with 陳述式確保檔案在使用後正確關閉。

存取網路文字

要存取網路上的文字,我們可以使用 requests 函式庫。例如:

import requests

response = requests.get('http://example.com')
text = response.text

內容解密:

此段程式碼使用 requests.get() 方法向指定的 URL 傳送 GET 請求,並將伺服器的回應儲存在 response 物件中。然後,我們可以透過 response.text 屬性存取文字內容。

文字預處理

在存取文字之後,我們通常需要對其進行預處理,包括分詞(tokenization)和去除標記(markup)等步驟。

分詞(Tokenization)

分詞是將文字分解為單個詞彙或標點符號的過程。NLTK 提供了多種分詞工具,例如:

import nltk
from nltk.tokenize import word_tokenize

text = "This is an example sentence."
tokens = word_tokenize(text)
print(tokens)

內容解密:

此段程式碼使用 NLTK 的 word_tokenize() 函式將輸入的文字分解為單個詞彙和標點符號,並將結果儲存在 tokens 列表中。

格式化輸出與檔案儲存

最後,我們需要了解如何產生格式化的輸出並將其儲存到檔案中。Python 提供了多種方法來實作這一點,包括使用 print() 函式和 open() 函式。例如:

with open('output.txt', 'w', encoding='utf-8') as file:
    file.write('This is an example output.\n')

內容解密:

此段程式碼開啟了一個名為 output.txt 的檔案,並以寫入模式 ('w') 和 UTF-8 編碼方式寫入指定的文字內容。

練習與討論

  1. 試著使用不同的分詞工具對相同的文字進行分詞,並比較結果。
  2. 編寫一個程式,從指定的 URL 下載文字,並將其儲存到本地檔案中。
  3. 試著對下載的文字進行預處理,包括分詞和去除標記等步驟。

隨著 NLP 技術的不斷發展,我們可以預見未來會有更多高效、準確的文字處理工具出現。同時,如何更好地應用這些技術於實際場景,將是我們需要持續探索的方向。

章節練習解答與解析

練習 17:找出文字中出現頻率最高的 50 個非停用詞

import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from collections import Counter

def find_top_words(text, n=50):
    stop_words = set(stopwords.words('english'))
    tokens = word_tokenize(text.lower())
    tokens = [token for token in tokens if token.isalpha() and token not in stop_words]
    return Counter(tokens).most_common(n)

# 使用範例
text = "Your text here."
top_words = find_top_words(text)
print(top_words)

內容解密:

此函式首先將輸入的文字轉換為小寫並進行分詞,然後過濾掉非字母字元和停用詞。最後,使用 Counter 物件統計每個詞彙的出現頻率,並傳回出現頻率最高的 n 個詞彙。

練習 23:驗證 Zipf 定律

import nltk
from nltk.tokenize import word_tokenize
from collections import Counter
import matplotlib.pyplot as plt

def plot_zipf(text):
    tokens = word_tokenize(text.lower())
    tokens = [token for token in tokens if token.isalpha()]
    freqs = Counter(tokens)
    freqs = sorted(freqs.values(), reverse=True)
    ranks = list(range(1, len(freqs) + 1))
    plt.loglog(ranks, freqs)
    plt.xlabel('Rank')
    plt.ylabel('Frequency')
    plt.title('Zipf\'s Law')
    plt.show()

# 使用範例
text = "Your large text here."
plot_zipf(text)

內容解密:

此函式首先對輸入的文字進行分詞和頻率統計,然後將頻率按照降序排序。接著,繪製頻率與排名之間的雙對數圖,以驗證 Zipf 定律。

從網路和磁碟存取文字

電子書籍

NLTK語料函式庫收錄了Project Gutenberg的部分文字。然而,你可能對分析其他Project Gutenberg的文字感興趣。你可以瀏覽http://www.gutenberg.org/catalog/上的25,000本免費線上書籍目錄,並獲得ASCII文字檔的URL。雖然Project Gutenberg中90%的文字是英文,但它包含了超過50種其他語言的材料,包括加泰羅尼亞語、中文、荷蘭語、芬蘭語、法語、德語、義大利語、葡萄牙語和西班牙語(每種語言都有超過100個文字)。

存取特定電子書的範例

以《罪與罰》的英文翻譯(編號2554)為例,我們可以按照以下方式存取它:

>>> from urllib.request import urlopen
>>> url = "http://www.gutenberg.org/files/2554/2554-0.txt"
>>> raw = urlopen(url).read().decode('utf-8')
>>> type(raw)
<class 'str'>
>>> len(raw)
1176831
>>> raw[:75]
'The Project Gutenberg EBook of Crime and Punishment, by Fyodor Dostoevsky\r\n'

內容解密:

上述程式碼展示瞭如何從Project Gutenberg下載《罪與罰》的文字。首先,我們使用urlopen函式開啟指定的URL並讀取內容。讀取的內容儲存在raw變數中,它是一個包含1,176,831個字元的字串。我們可以看到這個字串包含了書的原始內容,包括不必要的細節,如空白字元、換行符和空行。

處理下載的文字

下載的文字需要經過**分詞(tokenization)**處理,將字串分解成單詞和標點符號。

>>> tokens = nltk.word_tokenize(raw)
>>> type(tokens)
<class 'list'>
>>> len(tokens)
255809
>>> tokens[:10]
['The', 'Project', 'Gutenberg', 'EBook', 'of', 'Crime', 'and', 'Punishment', ',', 'by']

內容解密:

這段程式碼使用NLTK函式庫的word_tokenize函式對raw字串進行分詞處理,生成一個包含單詞和標點符號的列表。這個列表儲存在tokens變數中,總計有255,809個元素。

建立NLTK文字物件

tokens列表進行進一步處理,建立NLTK的Text物件,可以進行各種語言處理操作。

>>> text = nltk.Text(tokens)
>>> type(text)
<class 'nltk.text.Text'>
>>> text[1020:1060]
['CHAPTER', 'I', 'On', 'an', 'exceptionally', 'hot', 'evening', 'early', 'in',
'July', 'a', 'young', 'man', 'came', 'out', 'of', 'the', 'garret', 'in',
'which', 'he', 'lodged', 'in', 'S', '.', 'Place', 'and', 'walked', 'slowly',
',', 'as', 'though', 'in', 'hesitation', ',', 'towards', 'K', '.', 'bridge', '.']
>>> text.collocations()
Katerina Ivanovna; Pulcheria Alexandrovna; Avdotya Romanovna; Pyotr
Petrovitch; Project Gutenberg; Marfa Petrovna; Rodion Romanovitch;
Sofya Semyonovna; Nikodim Fomitch; did not; Hay Market; Andrey
Semyonovitch; old woman; Literary Archive; Dmitri Prokofitch; great
deal; United States; Praskovya Pavlovna; Porfiry Petrovitch; ear rings

內容解密:

建立Text物件後,我們可以對文字進行各種分析,如切片操作和搭配詞分析。搭配詞分析結果顯示了一些常見的片語,如人名、地名等。

清理不必要的內容

下載的文字通常包含不必要的頁首、頁尾和其他資訊,需要手動檢查並清理。

>>> raw.find("PART I")
5303
>>> raw.rfind("End of Project Gutenberg's Crime")
1157681
>>> raw = raw[5303:1157681]
>>> raw.find("PART I")
0

內容解密:

這段程式碼使用findrfind方法找到文字中實際內容的起始和結束位置,並對raw進行切片操作,保留需要的部分。這樣可以去除不必要的頁首和頁尾資訊。

處理HTML內容

許多網頁內容是以HTML格式存在的,可以使用Python直接讀取和處理。

使用NLTK清理HTML內容

>>> url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"
>>> html = urlopen(url).read().decode('utf-8')
>>> raw = nltk.clean_html(html)
>>> tokens = nltk.word_tokenize(raw)
>>> tokens = tokens[96:399]
>>> text = nltk.Text(tokens)
>>> text.concordance('gene')
they say too few people now carry the gene for blondes to last beyond the next tw
t blonde hair is caused by a recessive gene . In order for a child to have blonde
to have blonde hair , it must have the gene on both sides of the family in the gra
there is a disadvantage of having that gene or by chance . They don ' t disappear
ondes would disappear is if having the gene was a disadvantage and I do not think

內容解密:

這段程式碼首先讀取一個BBC的新聞網頁,然後使用NLTK的clean_html函式清理HTML標籤,得到純文字內容。接著對文字進行分詞處理,並建立NLTK的Text物件,最後進行關鍵字的上下文分析。

更複雜的HTML處理

對於更複雜的HTML處理,可以使用Beautiful Soup套件,網址為http://www.crummy.com/software/BeautifulSoup/。

處理搜尋引擎結果

網頁可以視為一個巨大的未註解語料函式庫。搜尋引擎提供了一個高效的方式來搜尋相關的語言範例。使用搜尋引擎的主要優勢是其龐大的資料量,能夠找到更多的語言模式例項。此外,搜尋引擎非常容易使用,因此是快速驗證理論是否合理的便捷工具。具體範例如表3-1所示。

搜尋引擎的使用優勢:

  1. 資料量龐大,能夠找到更多的語言模式例項。
  2. 使用非常方便,是快速驗證理論的便捷工具。

從網路與本機存取文字資料

網路文字資料的存取挑戰

現代自然語言處理(NLP)工作經常需要從網路上取得大量文字資料。然而,使用搜尋引擎取得片語統計資料存在諸多限制。

使用搜尋引擎的限制

  • 搜尋模式受限:無法像在本機語料函式庫那樣使用程式搜尋複雜模式
  • 結果不一致:不同時間或地區的搜尋結果可能大不相同
  • 重複內容影響:重複釋出的內容可能誇大搜尋結果
  • 結果標記變化:搜尋引擎傳回結果的標記可能不可預測地變化
# 搜尋引擎結果範例(模擬資料)
collocations = {
    "absolutely": {"adore": 289000, "love": 905000, "like": 16200, "prefer": 644},
    "definitely": {"adore": 1460, "love": 51000, "like": 158000, "prefer": 62600}
}
ratio = {
    "absolutely": {"adore": 198, "love": 18, "like": 0.1, "prefer": 0.01},
    "definitely": {"adore": 1, "love": 1, "like": 10, "prefer": 97}
}

分析搜尋結果

透過分析「absolutely」和「definitely」與不同動詞的搭配比率,我們可以觀察到有趣的語言現象。例如:「absolutely adore」的比例遠高於「definitely adore」。

處理RSS饋流資料

使用Universal Feed Parser函式庫

透過第三方Python函式庫Universal Feed Parser,我們可以輕鬆存取部落格內容。

import feedparser

# 解析Language Log的RSS饋流
llog = feedparser.parse("http://languagelog.ldc.upenn.edu/nll/?feed=atom")
print(llog['feed']['title'])  # 輸出:Language Log
print(len(llog.entries))      # 輸出:15

# 處理單篇文章
post = llog.entries[2]
content = post.content[0].value
clean_content = nltk.word_tokenize(nltk.html_clean(content))
print(clean_content[:10])     # 輸出前10個詞元

內容解讀:

  1. 使用feedparser.parse()解析RSS饋流
  2. llog['feed']['title']取得饋流標題
  3. len(llog.entries)取得文章數量
  4. 使用nltk.html_clean()清理HTML標籤
  5. 使用nltk.word_tokenize()進行詞元切分

讀取本機檔案

基本檔案讀取操作

Python內建的open()函式用於開啟本機檔案。

# 開啟檔案
f = open('document.txt', 'rU')
raw = f.read()
print(raw)  # 輸出檔案內容

# 按行讀取檔案
for line in f:
    print(line.strip())  # 輸出每一行內容

常見問題與解決方案

  1. 檔案未找到錯誤:檢查當前工作目錄或使用絕對路徑

    import os
    print(os.listdir('.'))  # 列出當前目錄下的檔案
    
  2. 換行符號問題:使用'rU'模式開啟檔案以相容不同作業系統的換行符號

從二進位格式中擷取文字

處理PDF和MSWord檔案

對於PDF和MSWord等二進位格式,需要使用專門的函式庫如pypdfpywin32

實用轉換方法

  1. 使用應用程式開啟檔案並另存為純文字格式
  2. 將檔案的URL輸入Google搜尋框,通常可以找到HTML版本

捕捉使用者輸入

使用raw_input()函式

可以捕捉使用者在程式執行時的輸入內容。

s = raw_input("請輸入一些文字:")
print("您輸入了", len(nltk.word_tokenize(s)), "個詞元。")

內容解讀:

  1. 使用raw_input()提示使用者輸入文字
  2. 使用nltk.word_tokenize()對輸入文字進行詞元切分
  3. 輸出詞元數量