python 处理doc、docx以及xls

手上有一些.xls.doc.docx以及.wps后缀的格式文件需要解析,知道python-docxxlrd可以分别读取.doc.xls,但是.docpython却无法读取,通过上网查找到了textract。下面我对其可行性进行了测试:

textract

这是一个号称可以解析任何形式文件的库,实际上就是利用了特定的库来解析,下面是其可以解析的文件类型,以及其凭借的包:

使用方式:

pip install textract

1
2
import textract
text = textract.process("path/to/file.extension")

解析.doc文件时会用到antiword包,但是该工具包早已停更了,目前没有windows64位的,无法使用。

解析.docx:

1
2
3
4
5
6
7
8
9
10
11
import textract
import docx2txt

text = textract.process('2019020316053296822.docx')
text1 = docx2txt.process('2019020316053296822.docx')
print(text)
print(text1)

text: b'\xe8\xa1\x8c\xe6\x94\xbf\xe5\xa4\x84\xe7\xbd\x9a\xe4\xbf\xa1\xe6\x81\xaf\xe5\x85\xac\xe7\xa4\xba\xe8\xa1\xa8'

text1:行政处罚信息公示表

\x编码是将原来的字符串逐个字符进行处理,先将字符转化为ascii值,再将ascii码值转变成十六进制,然后再将0x中的0变成反斜杠\,最后将这些单个的字符拼接在一起。

下面是我写的简单的编码和解码方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def decode_x(text):
text_decode = ''
text = text.replace('\\', '0')
for i in range(len(text)//4):
hex_str = text[i*4:i*4+4]
text_decode += chr(int(hex_str, 16))
return text_decode


def encode_x(text):
text_encode = ''
for char in text:
hex_str = hex(ord(char))
text_encode += hex_str.replace('0', '\\')
return text_encode


print(encode_x("_changeItemCrossLayer"))
print(decode_x(r"\x5f\x63\x68\x61\x6e\x67\x65\x49\x74\x65\x6d\x43\x72\x6f\x73\x73\x4c\x61\x79\x65\x72"))

'\x5f\x63\x68\x61\x6e\x67\x65\x49\x74\x65\x6d\x43\x72\x6f\x73\x73\x4c\x61\x79\x65\x72'
_changeItemCrossLayer

然而用上面写的解码的方法来解析textract返回的那一串却并不可行

1
2
print(decode_x(r"\xe8\xa1\x8c\xe6\x94\xbf\xe5\xa4\x84\xe7\xbd\x9a\xe4\xbf\xa1\xe6\x81\xaf\xe5\x85\xac\xe7\xa4\xba\xe8\xa1\xa8"))
<<< 行政处罚信息公示表

这里算个问题,先记下。

后来得知,对于\x编码,python可以直接进行decode得到原来的值

1
2
3
4
b'\x5f\x63\x68\x61\x6e\x67\x65\x49\x74\x65\x6d\x43\x72\x6f\x73\x73\x4c\x61\x79\x65\x72'.decode()
<<< '_changeItemCrossLayer'
b'\xe8\xa1\x8c\xe6\x94\xbf\xe5\xa4\x84\xe7\xbd\x9a\xe4\xbf\xa1\xe6\x81\xaf\xe5\x85\xac\xe7\xa4\xba\xe8\xa1\xa8'.decode()
<<< '行政处罚信息公示表'

其解析.docx文件时直接利用docx2txt提取文件中的所有的文字,缺少可以按照一定格式提取的方法,不利用后续按字段提取(除非就是写正则匹配),到这里基本可以告别这个库了。

python-docx库可以根据word文档中的表格,按单元格来提取。

安装pip install python-docx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from docx import Document

document = Document(path)
# word中的表格解析
tables = document.tables
table = tables[0]
for i in range(len(table.rows)):
for j in range(len(table.columns)):
print(table.cell(i, j).text.replace('\n', ''))

# 获取word中的文字
text = ''
document = Document(BytesIO(file_content))
paragraphs = document.paragraphs
for paragraph in paragraphs:
text += paragraph.text.strip()

# 若想直接对文件的二进制流进行操作,可以用如下方法:
from io import BytesIO
document = Document(BytesIO(binary_content))

上述方法只能解析.docx文件,无法解析.doc文件,目前也没有其他的可以直接用的包,只能利用相关工具将.doc文件转为.docx文件,.wps后缀的文件也可以通过这种方式转为.docx文件。

安装:pip install pypiwin32,这个方法会安装pypiwin32,而这个模块就包含了win32com

1
2
3
4
5
from win32com import client as wc

w = wc.Dispatch('Word.Application')
doc = w.Documents.Open('2019090915495348616.doc')
doc.SaveAs("2019090915495348616.docx", 16)

解析xls:

1
2
3
4
5
6
7
import textract

text = textract.process('2019091211231620778.xls')
print(text.decode())

序号 企业名称 处罚决定书文号 违法行为类型 行政处罚内容 作出行政处罚决定机关名称 作出行政处罚决定日期 备注
1.0 智裕国际货运代理(上海)有限公司 沪银罚[2010]第014795号 违反票据管理规定 罚款1000.00元 中国人民银行上海分行 43714.0

尝试了一下,使用textract解析.xls文件也是返回全部的文字,虽然单元格之间有一定的空格或者换行符,但是还要手动去分隔,对于想直接提取所有文字的需求是最简单的,但是对于想格式化数据的需求,就无法满足需求了。

使用xlrd读取excel文件:

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
import xlrd

x1 = xlrd.open_workbook('2019091211231620778.xls')

# 若想直接对二进制流进行处理
x1 = xlrd.open_workbook(file_contents=binary_content)

names = x1.sheet_names() # 获取所有sheet名字,返回列表类型
number = x1.nsheets # 获取sheet数量,整型
obj = x1.sheets() # 获取所有sheet对象,返回列表类型
test = x1.sheet_by_name("test") # 通过sheet名字查找,若名字不存在将会报错
sheet_1 = x1.sheet_by_index(0) # 通过索引查找,若索引值超出范围将会报错

print(sheet_1.name) # 获取sheet名
print(sheet_1.nrows) # 获取总行数
print(sheet_1.ncols) # 获取总列数

print(sheet_1.row(0)) # 获取第一行所有内容,合并单元格,以列表显示,包括单元格值类型和内容
print(sheet_1.row_values(0)) # 仅获取单元格值
print(sheet_1.row_types(0)) # 仅获取单元格数据类型

[number:1.0, text:'智裕国际货运代理(上海)有限公司', text:'沪银罚[2010]第014795号', text:'违反票据管理规定', text:'罚款1000.00元', text:'中国人民银行上海分行', xldate:43714.0, empty:'']
[1.0, '智裕国际货运代理(上海)有限公司', '沪银罚[2010]第014795号', '违反票据管理规定', '罚款1000.00元', '中国人民银行上海分行', 43714.0, '']
array('B', [2, 1, 1, 1, 1, 1, 3, 0])

# 相应的也有按列获取的
print(sheet_1.col(0))
print(sheet_1.col_values(0))
print(sheet_1.col_types(0))

# 还可以将行(或者列)进行切片
print(sheet_1.row_slice(2, 0, 2)) # 获取单元格值类型和内容,同sheet_1.row()
print(sheet_1.row_values(0, 6, 8)) # 获取单元格值,取第1行,第6~10列(不含第10列)
print(sheet_1.col_values(0, 0, 5)) # 获取单元格值,取第1列,第0~5行(不含第5行)
print(sheet_1.row_types(1, 0, 2)) # 获取单元格数据类型

此外,还可以利用pandasnumpy,直接读取excel转换成二维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
import numpy as np


class HandleExcel(object):

@staticmethod
# read_excel会默认excel第一行为标题,将excel转换为二维数组返回(去掉第一行后的)
def read_excel(name, sheet_name=''):
if not sheet_name:
data_x = pd.read_excel(name)
else:
data_x = pd.read_excel(name, sheet_name=sheet_name)
train_data = np.array(data_x)
train_x_list = train_data.tolist()
return train_x_list

使用xlwtexcel:

1
2
3
4
5
6
import xlwt

workbook = xlwt.Workbook(encoding='utf-8')
sheet = workbook.add_sheet('sheet1')
sheet.write(row, col, value)
workbook.save('test.xls')

excel时间格式问题

使用xlrd读取excel时,时间格式会被转化为浮点数,可以使用以下方法还原为标准时间格式:

1
2
3
4
5
6
7
import xlrd

time_str = 43789.78
time_form = xlrd.xldate.xldate_as_datetime(time_str, 0).__format__('%Y-%m-%d')
print(time_form)

2019-11-20