在整理《全唐詩》的文本之前,我們首先需要完成以下兩個步驟:
-
確定需求
-
瞭解文本
在完成以上步驟後,我們開始實際着手整理文本,在整理的過程中大體上也包含兩個流程:
- 文本解析
- 結果輸出
全唐詩文本語料在“全唐詩.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")