中文分词之类的东西

受朋友之托,写一个小脚本,断断续续做了两天,写一下两天的收获。

起因

有个朋友说专业文档很枯燥难懂,需要一个能把全文的关键词找出来并排序的东西,找不到现成的,问我能不能做一个。我前些天也听车神说有关分词的东西,用这个机会认识一下中文分词也不错。本来还想弄下 PDF 2 TXT的,不过没找到合适的工具,先弄这个吧。

要实现把全文的关键词找出来并排序,就需要识别文中的词而不是字,有了词才能进行排序。中文和其他语言不同,没有明确的词分界,不像英语有空格作为词边界。分词算法什么的我实在弄不出来了,所以用别人的吧。上百度和 Github 找工具,最后确定用结巴分词,因为结巴分词有 JavaScript 版,分词还是很 OK 的。

开干

分词统计并排序

之前是用 JavaScript 版的结巴分词写的,不过在分词统计的时候没想起来 JavaScript 有什么能排序的模块,就换 Python 版的结巴分词写了。

因为那个朋友的资料是繁体的,为了兼容繁体,我引入了繁体字典,不过加载好像有问题,我直接把繁体字典当用户字典加载了。

说这么多干嘛,上源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# index.py
#encoding=utf-8
'''
/*@version: 0.1
* @author: Bubao
* @contact: asd565586630@gmail.com
* @time: 2017-07-13 00:22:00
*/
'''
import jieba
import jieba.posseg as pseg
from collections import Counter
# 用户字典 可自行添加专业名词 防止被 jieba 分错了
jieba.load_userdict('dict/user.dict')
# jieba.load_userdict('dict/dict.txt.big')
# 简体&繁体字典 这个不用修改,所以我让他当用户字典加载了
#jieba.set_dictionary('dict/dict.txt.big')
# './源文件。txt'为想打开的文件
file_object = open('./源文件。txt','r')
## 定义一个 list
L =list()
## 获取带词性的词对象(没学过 py 不知道得到的是什么)
words = pseg.cut(file_object.read())
i=0
## 遍历这个词对象
for word, flag in words:
## 如果词性 (flag) 或者词 (word) 属于这个范围就直接跳过 自己填写
if flag=="x" or flag=="p" or flag=="uj" or word=="年"or word=="月"or word=="日" :
continue
## 打印计数 为了区分程序是不是挂了
i=i+1
print(i)
##把词追加到 list 中
L.append(word)
## 计算出现次数
getObj = Counter(L)
## 写到。/getObj.json 文件里
f = open('./getObj.json', 'w')
f.write(str(getObj).replace("Counter(","").replace(")","").replace("'",'"'))
f.close()
file_object.close()
## end

查询关键词频

弄完分词,朋友又说能不能加一个关键词查找,可以到想要的词出现的频率。我毕竟 Python 是边学边写的,还是用 node 写这个功能把。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* Search.js*/
/*@version: 0.1
* @author: Bubao
* @contact: asd565586630@gmail.com
* @time: 2017-07-13 00:22:00
*/
var fs = require('fs');
/**
* [keyWord description]
* @type {Array}
* 把想搜索的词用 “"关键词",” 这种方式写在 “[ ]” 中间,
* 记得关键词引号后面有英文逗号
* 引号也是引文的
*/
var keyWord = [
"本行",
"人民币",
];

var json = JSON.parse(fs.readFileSync('./getObj.json'));
var Arr = []
for (var i = 0; i < keyWord.length; i++) {
let get = keyWord[i] + ':' + json[keyWord[i]]
console.log(get)
Arr.push(get)
}
//你要的关键词在这里
fs.writeFileSync('keyWord.txt', Arr.join('\n'), 'utf8')

自动化脚本

因为用了两种语言,运行要分段,我这么懒的人,这么会等第一段运行完再运行第二段呢,一言不合写sh

1
2
3
4
5
6
7
8
9
10
11
12
# main.sh
#/*@version: 0.1
# * @author: Bubao
# * @contact: asd565586630@gmail.com
# * @time: 2017-07-13 00:22:00
# */
python index.py
echo "----------------关键词-------------------"
node Search.js
echo "----------------------------------------
排序在 getObj.json 文件
关键词搜索在 keyWord.txt 文件"

怎么用

  1. 把要转换的文件放在在当前文件夹,并改名为源文件。txt,运行sudo easy_install jieba
  2. 终端下运行 python index.py,等待完成,等待完成期间可以在Search.js文件里把想要搜索的关键字填在数组里,保存好。
  3. 完成第二步,接着执行node Search.js,等待完成。
  4. 关键字搜索结果在keyWord.txt文件里
  5. 或者写个脚本自动化,sh main.sh

额外收获

毕竟是自己摸索写的东西,收获还是不错的。为了以后想用的时候能找得到,便也记录下来。

分词工具

jieba:这个工具挺好用的,这个是 python 版的,还有很多种版本

codecs:一个 python 库,用来转码的

Counter:一个 python 库工具,用来做统计的

一些杂脚本

gbk2utf8

网上下载些文本文件,很多都是在 win 上写的,win 上一般默认保存为gbk。这样在 Linux 上打开都是乱码,所以上网找了段代码自己改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#encoding=utf-8
'''
/*@version: 0.1
* @author: Bubao
* @contact: asd565586630@gmail.com
* @time: 2017-07-13 00:22:00
*/
'''
import codecs
# gbk 转 utf8 脚本
def ReadFile(filePath,encoding="gb18030"):
with codecs.open(filePath,"r",encoding) as f:
return f.read()
def WriteFile(filePath,u,encoding="utf-8"):
with codecs.open(filePath,"w",encoding) as f:
f.write(u)
def UTF8_2_GBK(src,dst):
content = ReadFile(src,encoding="gb18030")
WriteFile(dst,content,encoding="utf-8")

UTF8_2_GBK("./in.txt","./out.txt")

nodejiba

之前是用 node 版的结巴写的分词,后来因为 py 比较容易写统计,就换 py 写分词了。留下这段代码以后需要再看看咯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*@version: 0.1
* @author: Bubao
* @contact: asd565586630@gmail.com
* @time: 2017-07-13 00:22:00
*/
var nodejieba = require("nodejieba")
var fs = require("fs")
var data = fs.readFileSync("./4.txt", "utf-8")
nodejieba.load({
userDict: './dict.utf8',
})
var result = nodejieba.extract(data, 100);
var a = {
"fen": []
}
let j = 0
for (let i = 0; i < result.length; i++) {
if (result[i].tag !== "uj" && result[i].tag !== "zg" && result[i].tag !== "x") {
a.fen[j] = {
"word": "1",
"tag": "2"
};
a.fen[j].word = result[i].word;
a.fen[j].tag = result[i].tag;
fs.writeFileSync("./5.txt", JSON.stringify(a), 'utf8')
j++
}
}
console.log(a);

scel2txt

因为弄分词的原因,想找一下专业名词,就跑到搜狗输入法的词库下载词包,发现这个格式我解码不了,上 github 找了别人的脚本(其实我并不知道这段代码是谁写的)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#!/usr/bin/python  
# -*- coding: utf-8 -*-

import struct
import sys
import binascii
import pdb
#搜狗的 scel 词库就是保存的文本的 unicode 编码,每两个字节一个字符(中文汉字或者英文字母)
#找出其每部分的偏移位置即可
#主要两部分
#1. 全局拼音表,貌似是所有的拼音组合,字典序
# 格式为 (index,len,pinyin) 的列表
# index: 两个字节的整数 代表这个拼音的索引
# len: 两个字节的整数 拼音的字节长度
# pinyin: 当前的拼音,每个字符两个字节,总长 len
#
#2. 汉语词组表
# 格式为 (same,py_table_len,py_table,{word_len,word,ext_len,ext}) 的一个列表
# same: 两个字节 整数 同音词数量
# py_table_len: 两个字节 整数
# py_table: 整数列表,每个整数两个字节,每个整数代表一个拼音的索引
#
# word_len: 两个字节 整数 代表中文词组字节数长度
# word: 中文词组,每个中文汉字两个字节,总长度 word_len
# ext_len: 两个字节 整数 代表扩展信息的长度,好像都是 10
# ext: 扩展信息 前两个字节是一个整数(不知道是不是词频) 后八个字节全是 0
#
# {word_len,word,ext_len,ext} 一共重复 same 次 同音词 相同拼音表

#拼音表偏移,
startPy = 0x1540;

#汉语词组表偏移
startChinese = 0x2628;

#全局拼音表

GPy_Table ={}

#解析结果
#元组(词频,拼音,中文词组)的列表
GTable = []

def byte2str(data):
'''''将原始字节码转为字符串'''
i = 0;
length = len(data)
ret = u''
while i < length:
x = data[i] + data[i+1]
t = unichr(struct.unpack('H',x)[0])
if t == u'\r':
ret += u'\n'
elif t != u' ':
ret += t
i += 2
return ret
#获取拼音表
def getPyTable(data):

if data[0:4] != "\x9D\x01\x00\x00":
return None
data = data[4:]
pos = 0
length = len(data)
while pos < length:
index = struct.unpack('H',data[pos]+data[pos+1])[0]
#print index,
pos += 2
l = struct.unpack('H',data[pos]+data[pos+1])[0]
#print l,
pos += 2
py = byte2str(data[pos:pos+l])
#print py
GPy_Table[index]=py
pos += l

#获取一个词组的拼音
def getWordPy(data):
pos = 0
length = len(data)
ret = u''
while pos < length:

index = struct.unpack('H',data[pos]+data[pos+1])[0]
ret += GPy_Table[index]
pos += 2
return ret

#获取一个词组
def getWord(data):
pos = 0
length = len(data)
ret = u''
while pos < length:

index = struct.unpack('H',data[pos]+data[pos+1])[0]
ret += GPy_Table[index]
pos += 2
return ret

#读取中文表
def getChinese(data):
#import pdb
#pdb.set_trace()

pos = 0
length = len(data)
while pos < length:
#同音词数量
same = struct.unpack('H',data[pos]+data[pos+1])[0]
#print '[same]:',same,

#拼音索引表长度
pos += 2
py_table_len = struct.unpack('H',data[pos]+data[pos+1])[0]
#拼音索引表
pos += 2
py = getWordPy(data[pos: pos+py_table_len])

#中文词组
pos += py_table_len
for i in xrange(same):
#中文词组长度
c_len = struct.unpack('H',data[pos]+data[pos+1])[0]
#中文词组
pos += 2
word = byte2str(data[pos: pos + c_len])
#扩展数据长度
pos += c_len
ext_len = struct.unpack('H',data[pos]+data[pos+1])[0]
#词频
pos += 2
count = struct.unpack('H',data[pos]+data[pos+1])[0]

#保存
GTable.append((count,py,word))

#到下个词的偏移位置
pos += ext_len

def deal(file_name):
print '-'*60
f = open(file_name,'rb')
data = f.read()
f.close()

if data[0:12] !="\x40\x15\x00\x00\x44\x43\x53\x01\x01\x00\x00\x00":
print "确认你选择的是搜狗 (.scel) 词库?"
sys.exit(0)
#pdb.set_trace()

print "词库名:" ,byte2str(data[0x130:0x338])#.encode('GB18030')
print "词库类型:" ,byte2str(data[0x338:0x540])#.encode('GB18030')
print "描述信息:" ,byte2str(data[0x540:0xd40])#.encode('GB18030')
print "词库示例:",byte2str(data[0xd40:startPy])#.encode('GB18030')

getPyTable(data[startPy:startChinese])
getChinese(data[startChinese:])

if __name__ == '__main__':

#将要转换的词库添加在这里就可以了
o = ['1.scel',]
for f in o:
deal(f)

#保存结果
f = open('sougou.txt','w')
for count,py,word in GTable:
#GTable 保存着结果,是一个列表,每个元素是一个元组(词频,拼音,中文词组),有需要的话可以保存成自己需要个格式
#我没排序,所以结果是按照上面输入文件的顺序
f.write( unicode('{%(count)s}' %{'count':count}+py+' '+ word).encode('utf8') )#最终保存文件的编码 GB18030,可以自给改
f.write('\n')
f.close()

emoji-scel2txt

这是 Ruby 写的,颜文字导出

scel2mmseg

这是 python 写的,到处成 mmseg 格式,据说这个可以用来做中文分词,以后再看看吧。

scel2pyim
一个将搜狗输入法 scel 细胞词库转换为 emacs chinese-pyim 文本词库的小工具。因为我最近在学 emacs,就收藏了。

scel2txt

还是个 scel2txt 小工具。


中文分词之类的东西
https://bubao.github.io/posts/5058e8e9.html
作者
一念
发布于
2017年7月13日
许可协议