欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-25 13:01:11
...

在整理《全唐诗》的文本之前,我们首先需要完成以下两个步骤:

  • 确定需求

  • 了解文本

在完成以上步骤后,我们开始实际着手整理文本,在整理的过程中大体上也包含两个流程:

  • 文本解析
  • 结果输出

全唐诗文本语料在“全唐诗.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")

完整源代码