YOLO813

快捷处理word文档中的成语释义需求

​    需求:多达数十份docx文档,每一份docx文档里面都有很多问题(多达100+),每个问题存在4个选项,需要搜寻对应的成语或者词语的释义,然后按照规范一个个列在问题的下方,这些都是重复的工作,考虑能否通过python程序来解决。

    常规操作,我们还是先放结果(由于隐私问题,我只截取了部分word文档内容,文档内容都是同一种格式),先放一张待处理的文档截图,我需要将其词语的释义都写在后面:


↓↓程序处理后↓↓

    其中第二张图片中蓝色框内容即为程序处理之后的内容,可以看到程序运行完成之后,我们的成语释义也都按照指定的规则排列,且加上了标红的三步走,再也不担心手会复制粘贴(懒)的抽筋啦。

 

    我们处理word文档,需要用到python-docx库,使用pip下载安装即可。另外我们需要处理成语的释义,当然得用到新华词典啦,由于时间关系,我采取的是两位github主已经整理好的词库内容,文末会放两位github的链接。

pip install china-idiom  #安装工具pip,不多解释

    采用的是sfyc23已经写好的python工具版,我们可以进入其github查看相关介绍,使用如下命令安装工具,要求python版本大于3.5,并且要求安装好了依赖库(pandas>=0.23.4)。

    安装成功之后,我们可以进行工具调试,首先进入命令行工具,输入python,开始导入工具。

import china_idiom as idiom  # 导入工具
# 查询成语第 2 个字是『心』的成语,最大查询数量为 2, 返回详细内容。
idiom.search_idiom(word='心', position=2, count=2, is_detail=True) 
>>> 
[{'word': '热心快肠',
  'pinyin': 'rè xīn kuài cháng',
  'abbreviation': 'rxkc',
  'explanation': '形容热情直爽。',
  'derivation': '柯岩《奇异的书简·东方的明珠三》也许因为是她热心快肠,群众有事爱找她拿主意,帮个忙。”',
  'example': '无'},
 {'word': '人心大快',
  'pinyin': 'rén xīn dà kuài',
  'abbreviation': 'rxdk',
  'explanation': '快痛快。指坏人坏事受到惩罚或打击,使大家非常痛快。',
  'derivation': '明·沈德符《万历野获编·立枷》东山受恩反噬,其罪盖浮于诸龙光。当时人心大快,佐以此得缙绅闻声,然亦不云立枷。”',
  'example': '无'}]

    可以看到,我们调用工具的search_idiom方法,返回两条符合我们搜索的结果,当然,这个工具不仅仅只能查询词语释义,还可以判断一个词是不是成语(存在局限,仅限于作者收集的成语csv文件内的判断),成语接龙,自动接龙模式(输入一个汉字或者成语,程序自动输出一组成语接龙的结果)等等,具体可自行前往github翻阅。

    我们有了这个工具之后,目标就很明确了,问题变成了将word文档之中的ABCD四个选项内容读取出来,然后分别调用search_idiom方法,取出列表当中json格式的explanation,当然,由于作者整理的只是成语文档,而且并非所有成语都具备,所以我还准备了三个本地的json词语库,用以备用(pwxcoo整理,分别是成语、词语、字),当第一个库查询不到内容时,分别在本地文件中轮询查找。

    

    在本地准备的三个词语、成语库,为github主pwxcoo整理,下载的过程还有点曲折(因为某些原因,大陆无法下载),所以整个程序的逻辑变成了:

    读取word文档->获取准确的字符串内容->调用china_idiom库查询返回内容->如有结果,则将结果写入新的word文档当中;如果无结果,则调用本地库查询;再无内容则返回空结果。

---代码基础解析---
我们可以利用如下方法读取docx文档每一行的内容(未包含图片):

from docx import Document
docx_name = 'input.docx'  #原始文档名称
doc = Document(docx_name) # read
#读取每一段的内容
for para in doc.paragraphs:
    print(para.text)

我们可以利用如下方法创建一个新docx文档(具体可参阅docx库的官方文档):

from docx.oxml.ns import qn # 设置字体颜色

document = Document() # 创建空文档 write
style = document.styles['Normal'] # 设置一个空白样式
style.font.name = u'黑体'
style.element.rPr.rFonts.set(qn('w:eastAsia'), u'黑体')
# 添加标题,设置级别level,0为Title,1或省略为Heading 1,0<=level<=9
document.add_heading('标题', 0)
# 添加段落,参数为text=''和style=None
p = document.add_paragraph('空白段落 ')
# 添加run对象,参数为text=None和style=None,
# run对象有bold(加粗)和italic(斜体)这两个属性
p.add_run('加粗').bold = True
p.add_run('倾斜.').italic = True
# 添加段落
document.add_heading('标题, 等级一', level=1)

    当然,也可以封装一些段落、页眉之类的方法,缩减代码量,基本解题逻辑就是如此。

 

    参考

https://github.com/sfyc23/China-idiom

https://github.com/pwxcoo/chinese-xinhua

python docx库的相关文档:https://python-docx.readthedocs.io/en/latest/index.html

 

    完整代码

# -*- coding: utf8 -*-

import china_idiom as idiom
import datetime
from docx import Document
from docx.shared import Inches
from docx.oxml.ns import qn
from docx.shared import RGBColor,Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH  #设置对象居中、对齐等。
from docx.enum.text import WD_UNDERLINE
from docx.enum.text import WD_LINE_SPACING
from docx.shared import Length # 间距长度
from docx.shared import Inches, Pt
import json
from translate import Translator

#要处理的文档
docx_name = 'input.docx'
output_docx_name = "output.docx"

class ReadWord():
  def __init__(self):
    #词典库↓
    self.file_ci = './ci.json'
    self.file_idiom = './idiom.json'
    self.file_word = './word.json'
    #词典库↑
    self.doc = Document(docx_name) # read
    self.document = Document() # 创建空文档 write
    style = self.document.styles['Normal'] # 设置一个空白样式
    style.font.name = u'黑体'
    style.element.rPr.rFonts.set(qn('w:eastAsia'), u'黑体')

  def run(self):
    self.headerEyebrow()
    self.readXlsx()
    self.document.save(output_docx_name)

  def getCurrentTime(self):
    m_today = datetime.datetime.now().strftime('%Y-%m-%d')
    m_today_list = m_today.split("-")
    currentDate = m_today_list[0] + "年" +m_today_list[1] + "月" +m_today_list[2]+ "号"
    translator= Translator(from_lang="chinese",to_lang="english")
    translation = translator.translate(currentDate)
    return translation
  # 成语、词语方法的封装
  def getLocalJson(self, ci_name):
    ci_name = ci_name.strip()
    try:
      with open(self.file_idiom, 'r', encoding="utf-8") as loadF:
        load_dict = json.load(loadF)
        for i in range(len(load_dict)):
          # print(load_dict[i]['word'])
          if load_dict[i]['word'] == ci_name:
            print(ci_name)
            print(load_dict[i]['explanation'])
            return load_dict[i]['explanation']
          # else:
          #   return False  因为可能第一个就不等于,就直接return false了
      with open(self.file_ci, 'r', encoding="utf-8") as loadF:
        load_dict = json.load(loadF)
        for i in range(len(load_dict)):
          if load_dict[i]['ci'] == ci_name:
            print(load_dict[i]['explanation'])
            return load_dict[i]['explanation']
      with open(self.file_word, 'r', encoding="utf-8") as loadF:
        load_dict = json.load(loadF)
        for i in range(len(load_dict)):
          if load_dict[i]['word'] == ci_name:
            print(load_dict[i]['explanation'])
            return load_dict[i]['explanation']
    except:
      return None

  # docx的封装
  def headerEyebrow(self):
    section=self.document.sections[0]
    header=section.header
    head_title=header.paragraphs[0]
    head_title.text= self.getCurrentTime()
    head_title.alignment = WD_ALIGN_PARAGRAPH.RIGHT

  def readXlsx(self):
    for para in self.doc.paragraphs:
      each_line = para.text.strip()
      if each_line.startswith("A"):
        self.addFormatPara(para.text)
        list_line_A = []
        split_line = each_line.replace("A.","").strip().split("B.")
        for i in range(len(split_line)):
          each_choice_field = split_line[i].split("\t")
          for j in range(len(each_choice_field)):
            if each_choice_field[j] != '': # ['举足轻重', '时代感', '', ' 不可或缺', '表现力']
              list_line_A.append(each_choice_field[j].strip())
        print(f"list_line_A:{list_line_A}")
      elif each_line.startswith("C"):
        self.addFormatPara(para.text)
        list_line_C = []
        split_line = each_line.replace("C.","").strip().split("D.")
        for i in range(len(split_line)):
          each_choice_field = split_line[i].split("\t")
          for j in range(len(each_choice_field)):
            if each_choice_field[j] != '':
              list_line_C.append(each_choice_field[j].strip()) 
        print(f"list_line_C:{list_line_C}")
        #一二三
        self.addFormatPara("【解析】第一步:", m_bold= True, m_underline=WD_UNDERLINE.SINGLE, m_space=5, m_rgb=RGBColor(220,20,60))
        self.addFormatPara("第二步:", m_bold= True, m_underline=WD_UNDERLINE.SINGLE, m_space=5, m_rgb=RGBColor(220,20,60))
        self.addFormatPara("第三步:拓展讲课过程中没有涉及到的成语和实词:", m_bold= True, m_underline=WD_UNDERLINE.SINGLE, m_space=5, m_rgb=RGBColor(220,20,60))
        # 写入成语
        for i in range(len(list_line_A)):
          results = idiom.search_idiom(word=list_line_A[i], position=0, count=1, is_detail=True)
          if results == []: #有可能没有结果
            # 调用本地json库解释
            result_from_local_json = self.getLocalJson(list_line_A[i])
            if result_from_local_json:
              print(f"{list_line_A[i]}: {result_from_local_json}")
              self.addFormatPara(f"{list_line_A[i]}: {result_from_local_json}", m_space=1, m_style='ListBullet')
            else:
              empty_idiom = list_line_A[i]
              print(f"empty idiom: {empty_idiom}")
              self.addFormatPara(f"{empty_idiom}:",  m_space=1)
          else:
            m_explanation = results[0]['explanation']
            print(f"{list_line_A[i]}: {m_explanation}")
            self.addFormatPara(f"{list_line_A[i]}: {m_explanation}",  m_space=1, m_style='ListBullet')
        #C选项
        for i in range(len(list_line_C)):
          results = idiom.search_idiom(word=list_line_C[i], position=0, count=1, is_detail=True)
          if results == []: #有可能没有结果
            # 调用本地json库解释
            result_from_local_json = self.getLocalJson(list_line_C[i])
            if result_from_local_json:
              print(f"{list_line_C[i]}: {result_from_local_json}")
              self.addFormatPara(f"{list_line_C[i]}: {result_from_local_json}",  m_space=1, m_style='ListBullet')
            else:
              empty_idiom = list_line_C[i]
              print(f"empty idiom: {empty_idiom}")
              self.addFormatPara(f"{empty_idiom}:",  m_space=1)
          else:
            m_explanation = results[0]['explanation']
            print(f"{list_line_C[i]}: {m_explanation}")
            self.addFormatPara(f"{list_line_C[i]}: {m_explanation}",  m_space=1, m_style='ListBullet')
      else:
        self.addFormatPara(para.text,  m_space=1)

  #初始化添加段落的方法
  def addFormatPara(self, contents, m_bold=False, m_underline=WD_UNDERLINE.NONE, m_space=0, m_rgb=RGBColor(0,0,0), m_style=None):
    p = self.document.add_paragraph()
    p.style = m_style
    p.paragraph_format.space_after = Pt(m_space) #行距
    m_format = p.add_run(contents)
    m_format.bold = m_bold
    m_format.underline = m_underline
    m_format.font.color.rgb = m_rgb

if __name__ == "__main__":
  start = datetime.datetime.now()
  rw = ReadWord()
  rw.run()
  end = datetime.datetime.now()
  print(f"\n运行时间:{end - start}")