需求:多达数十份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}")