Python文本整理案例分析:《全唐詩》文本整理

在整理《全唐詩》的文本之前,我們首先需要完成以下兩個步驟:

  • 確定需求

  • 瞭解文本

在完成以上步驟後,我們開始實際着手整理文本,在整理的過程中大體上也包含兩個流程:

  • 文本解析
  • 結果輸出

全唐詩文本語料在“全唐詩.txt”文件中,請參考語料閱讀以下內容。

確定需求

我們計劃將《全唐詩》中的每一首詩的各種信息分別提取出來,並轉存爲csv的形式。根據對文本的初步瞭解,我們發現我們需要提取的信息(即絕大部分詩文都包含的共性信息)包括:

  • 詩文的所屬的卷編號(後簡稱卷編號)
  • 詩文的在當前卷中的序號(後簡稱詩編號)
  • 詩文的標題
  • 詩文的作者
  • 詩文的內容

雖然有的詩並沒有作者(例如卷899_19),但是在整體結構設計的時候不用考慮它們。

瞭解文本

在瞭解文本的過程中,主要圍繞需要提取的信息的形式;通過了解文本,我們基本上得到可以解析大部分文本內容的規律性方法。

卷25_7【雜曲歌辭·俠客行】

李白

趙客縵胡纓,吳鉤霜雪明。銀鞍照白馬,颯沓如流星。
十步殺一人,千里不留行。事了拂衣去,深藏身與名。
閒過信陵飲,脫劍膝前橫。將炙啖朱亥,持觴勸侯嬴。
三杯吐然諾,五嶽倒爲輕。眼花耳熱後,意氣素霓生。
救趙揮金槌,邯鄲先震驚。千秋二壯士,烜赫大梁城。
縱死俠骨香,不慚世上英。誰能書閣下,白首太玄經。


   卷106_7 【送金城公主適西蕃應制】鄭愔 

下嫁戎庭遠,和親漢禮優。笳聲出虜塞,簫曲背秦樓。 
貴主悲黃鶴,徵人怨紫騮。皇情眷億兆,割念俯懷柔。主

首先,通過了解我們發現每首詩的形式,都類似以上的形式。以一個標題行開始,標題行中基本上都包括卷編號和詩編號(卷[0-9]+_[0-9]+)和標題(【[^】]+】)兩部分,有的時候也會包含作者名。因此,我們可以以標題行爲標誌,一旦發現標題行,就認爲一首詩的內容已經結束,下一首詩的內容即將開始(即完成一首詩的整理)。

接着,通過仔細觀察,我們發現標題行、詩文中偶爾會有多餘的空格(包括半角或全角)出現,在詩文末尾還會有校注者知古齋主的標註。因此,對每一行我們需要進行初步的清洗,包括移除空格、換行符和校注者的標註((?<=[),。])[知古齋主]$)。

至此,我們已經可以提取出卷編號、詩編號、標題、內容這四部分,但是作者部分仍然存在一些問題。經過進一步的觀察,我們發現當作者位於標題行時,作者名是除了卷編號、詩編號、標題以外的唯一的中文內容;當作者名存在於標題行之後的某一行中時,該行爲不包含任何標點符號的一行。

此外,我們發現,卷的標題(“第一百六十三卷”、“卷一百六十四”等)、版權信息等內容也是非詩文的無效內容,我們也需要通過正則表達式等方法過濾掉它們。

文本解析

在對文本初步瞭解的基礎上,我們可以實現對文本的解析。整體邏輯結構如下:按行遍歷文本語料;若該行爲標題行,則解析當前詩作信息;若該行非標題行,則將當前行的內容存入當前詩作的內容中。

在遍歷的過程中,我們需要對各行的文本語料進行清洗,並過濾空行、無效行等。

在解析過程中,我們可以將解析的結果存儲到list中,list中的每個元素爲一首詩,每個元素包括卷編號、詩編號、標題、作者、內容五個屬性。

讀取文本語料

首先,按行讀取txt格式的《全唐詩》文本語料。

with open("全唐詩.txt", encoding="UTF-8") as file:
    lines = file.readlines()
print("總行數:", len(lines))

清洗文本語料

對每一行都進行的文本清洗:

line = line.replace("\n", "").replace(" ", "").replace(" ", "")
line = re.sub("卷[一二三四五六七八九十百]+", "", line)
line = re.sub("第[一二三四五六七八九十百]+卷", "", line)

僅對文本內容行進行的文本清洗:

line = line.replace("¤", "。")  # 將錯誤句號替換爲標準句號
line = re.sub("(?<=[),。])[知古齋主]$", "", line)  # 剔除校注者名稱

過濾無效行(先文本清洗再過濾無效行,一遍將“第一百六十三卷”等過濾掉):

if "知古齋主精校" in line or "版權所有" in line or "[email protected]" in line:
    continue

文本語料解析

在標題行中提取卷編號(Python 3.8+):

if book_regex := re.search("(?<=卷)[0-9]+(?=_)", line):
    book_num = int(book_regex.group())  # 讀取卷編號

在標題行中提取詩編號(Python 3.8+):

if poem_regex := re.search("(?<=_)[0-9]+", line):
    poem_num = int(poem_regex.group())  # 讀取詩編號

在標題行中提取標題(Python 3.8+):

if title_regex := re.search("(?<=【)[^】]+(?=】)", line):
    title = title_regex.group()  # 讀取標題

在標題行中嘗試提取作者(Python 3.8+):

line = re.sub("卷[0-9]+_[0-9]+", "", line)
line = re.sub("【[^】]+】", "", line)
if author_regex := re.search("[\u4e00-\u9fa5]+", line):
    author = author_regex.group()  # 如果作者名位於標題行,則爲清除其他所有內容後剩餘的中文

在非標題行中嘗試提取作者:

if not re.search("[,。?!]", line):
	author_regex = re.search("[\u4e00-\u9fa5]+", line)
    author = author_regex.group()

非標題行中的內容在清洗之後直接添加到詩文的內容中即可。

結果輸出

在文本解析完成後,我們可以將存儲在list中的臨時數據存儲到文件中。

with open("全唐詩(清洗後).txt", "w+", encoding="UTF-8") as file:
    for poem_item in poem_list:
        file.write(",".join([str(poem_item["卷編號"]), str(poem_item["詩編號"]), poem_item["標題"], poem_item["作者"],
                             poem_item["內容"]]) + "\n")

完整源代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章