打算用python分析一次啊考研真题单词的词频,并加上翻译等内容,方便背诵
手头有近20年的考研英语一二真题word文件几十个,需要对每个文件的内容进行读取,并提取属于文章和题目的部分,即去掉介绍部分
使用docx包来读取word,因为只支持docx后缀,所以原有文件另存为docx形式
导入库,并设立要去掉的标点符号以及停用词,其中停用词通过nltk库from nltk.corpus import stopwords 导入
from docx import Document
import nltk
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
import re
from nltk.stem import PorterStemmer
from nltk.stem.lancaster import LancasterStemmer
from nltk.stem.snowball import SnowballStemmer
from nltk.stem import WordNetLemmatizer
interpunctuations = [',', '.', ':', ';', '?', '(', ')', '[', ']', '&', '!', '*', '@', '#', '$', '%', '_', '-', '—', '"', '“', '”', "'"]
stops = set(stopwords.words("english"))
通过docx的Document方法可以读取word文件,将要读取的文件组成一个列表
docxs1 = [Document(str(year)+'-1.docx') for year in range(1998, 2022)]
下面输入文件名列表,输出分好词的列表
def get_words(docxs): # 输入文件名列表,输出单词列表(去标点数字符号,题目要求标题)
words = [] # 储存单词
for doc in docxs: # 遍历每一个文件
for para in doc.paragraphs: # 遍历文件中的每一个段落
if len(para.runs)>=1 and len(para.text)>=1: # 确实是有效段落
if para.runs[0].font.bold or para.runs[0].font.italic: # 如果是粗体或者斜体,不处理
continue
# s = re.sub(r'[.*?]', ' ', para.text)
s = re.sub(r'[[A-Z]+]', ' ', para.text) # 去掉特殊符号
s = re.sub(r'[0-9]+', ' ', s)
s = re.sub(r'②|③|①|④|⑤|⑥|⑦|⑧|⑨|⑩|⑪|⑫|⑬|⑭|⑮|⑯|⑰|_|—|-|\.', ' ', s)
s = re.sub(r'\[0-9a-z]', ' ', s)
# print(s)
s_words = word_tokenize(s) # 分词
# print(s_words)
cutwords = [word for word in s_words if word not in interpunctuations]
cutwords = [word.lower() for word in cutwords if word not in stops] # 去除停用词与标点
if cutwords:
if cutwords[0] == 'read' or cutwords[0] == 'translate': # 有的翻译和阅读题目介绍没有粗体和斜体
continue
words+=cutwords
return words
docx 中的 runs 一个run对象是相同样式文本的延续(只要文本的格式没有改变,那么就是一个run,一旦改变了就是列外一个run了) 所以我们使用para.runs[0].font.bold or para.runs[0].font.italic
来判断这一段的开头是否为粗体或者斜体
因为在文件中,题目介绍是粗体或斜体,这部分不参与统计:
使用re库 正则表达式re.sub来替换特殊符号等为空格,后续分词可直接分开 re.sub使用参考https://blog.csdn.net/jackandsnow/article/details/103885422
其中[[A-Z]+]为中括号里面带字母的,[0-9]+为连续或非连续数字,②|③|①|④|⑤|⑥|⑦|⑧|⑨|⑩|⑪|⑫|⑬|⑭|⑮|⑯|⑰|_|—|-|.为特殊符号,因为.
表示所有除换行符以外的符号,所以在前面加\转义,表示为.本身
分词 使用nltk中的word_tokenize进行分词,去除停用词与标点符号,最后将所有文件和段落分词列表叠加,输出words
为了提高词性还原的准确度,采用nltk的WordNetLemmatizer方法,WordNetLemmatizer可通过单词,词性(可选)两个参数提取词干,为了提高准确率,首先提取每一个单词的词性
>>> from nltk.stem import WordNetLemmatizer
>>> import nltk
>>> lem = WordNetLemmatizer()
>>> nltk.pos_tag(['better'])
[('better', 'RBR')]
>>> lem.lemmatize('better', 'RBR')
Traceback (most recent call last):
File "" , line 1, in <module>
File "D:\anaconda\lib\site-packages\nltk\stem\wordnet.py", line 38, in lemmatize
lemmas = wordnet._morphy(word, pos)
File "D:\anaconda\lib\site-packages\nltk\corpus\reader\wordnet.py", line 1906, in _morphy
exceptions = self._exception_map[pos]
KeyError: 'RBR'
>>> lem.lemmatize('better', 'a')
'good'
>>> lem.lemmatize('better', wordnet.ADV)
'well'
>>> lem.lemmatize('better', wordnet.ADJ)
'good'
如上可看出,pos_tag可获得单词的词性,但词性直接用作lemmatize的参数会发生错误,所以需要转换一下(应该是NLTK的小缺憾),可以用的参数为wordnet里面的词性
转换函数:
from nltk.corpus import wordnet
def get_wordnet_pos(tag): # 词性转化翻译
if tag.startswith('J'):
return wordnet.ADJ
elif tag.startswith('V'):
return wordnet.VERB
elif tag.startswith('N'):
return wordnet.NOUN
elif tag.startswith('R'):
return wordnet.ADV
else:
return ''
获得词干:输入单词列表,输出词干列表
def get_stems(words): # 获得词干
lem = WordNetLemmatizer() # 词形还原
words_tag = nltk.pos_tag(words)
words_final = []
for couple in words_tag:
if couple[0][0]<'a' or couple[0][0]>'z' or len(couple[0])<=1: # 去除非单词及空格
continue
if get_wordnet_pos(couple[1]):
words_final.append(lem.lemmatize(couple[0], get_wordnet_pos(couple[1])))
else:
words_final.append(lem.lemmatize(couple[0]))
return(words_final)
import collections
coun = dict(collections.Counter(words_final).most_common())
使用collections库的counter,返回每个单词及其个数,most_common(n)为返回由大到小前n个,默认为从大到小返回全部
这里使用有道智云的文本翻译API,参照文档中的python3写法
文本翻译API文档
使用参考
https://zhuanlan.zhihu.com/p/59527880
import uuid
import requests
import hashlib
import time
import json
# APPID 与 秘钥
appid = '****'
secretKey = '********'
myurl = 'https://openapi.youdao.com/api'
def encrypt(signStr):
hash_algorithm = hashlib.sha256()
hash_algorithm.update(signStr.encode('utf-8'))
return hash_algorithm.hexdigest()
def translate(q):
# 将key设置为字典,填写参照文档
data = {}
data['from'] = 'en'
data['to'] = 'zh-CHS'
data['signType'] = 'v3'
curtime = str(int(time.time()))
data['curtime'] = curtime
salt = str(uuid.uuid1())
signStr = appid + q + salt + curtime + secretKey
sign = encrypt(signStr)
sign = encrypt(signStr)
data['appKey'] = appid
data['q'] = q
data['salt'] = salt
data['sign'] = sign
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
# 使用requests的post获取
response = requests.post(myurl, data=data, headers=headers)
# print(response.content)
# 对response的内容解码
result_all = response.content.decode("utf-8")
# 转换为json形式
result = json.loads(result_all)
if 'basic' in result:
return result['basic']['explains']
else:
return '释义错误'
最后的result为一个字典,里面有包括源词、语言、翻译等
其中字典的解释在basic中的explains中,故进行提取,若没有,说明单词出现错误
def translate_alls(coun):
ans = {}
for k,v in coun.items():
trans = translate(k)
ans[k] = [v, trans]
return ans
translate_alls函数:输入统计的单词,对于每一个单词,进行翻译,并把单词、翻译、词频放入同一个字典中
将上面的函数进行操作
words_1 = get_words(docxs1) # 由文件获得分词列表
words_final_1 = get_stems(words_1) # 获得词干列表
coun_1 = dict(collections.Counter(words_final_1).most_common()) # 进行统计
ans_1 = translate_alls(coun_1) # 调用API获得翻译+词频字典
这里使用openpyxl进行Excel的读写
from openpyxl import Workbook, load_workbook
wb = load_workbook('words_times.xlsx') # 加载文件
ws_2 = wb.create_sheet('考研英语一1') # 建立新sheet
for k,v in ans_1.items(): # 对于字典中每一项,写入一行
ws_2.append([k, v[0], str(v[1])])
wb.save('words_times.xlsx') # 保存
单词结果自取:
链接:https://pan.baidu.com/s/1Zdr8yDZ607ZuGMxjJQg28A
提取码:s985
结果分为英一、英二、英一+英二