⽤python解析word⽂件(⼆):table
太长了,我决定还是拆开三篇写。
(⼆)表格篇(table)(本篇)
选你所需即可。下⾯开始正⽂。
上⼀篇我们讲了⽤python-docx解析docx⽂件中的段落,也就是paragraph,不过细⼼的同学可能发现了,只有⾃然段是可以⽤paragraph处理的,如果word中有表格,根本读都读不到。这是正常的,因为表格在docx中是另⼀个类。
⼀个word⽂档中⼤概有这么⼏种类型的内容:paragraph(段落),table(表格),character(我也不知道该怎么叫,字符?)。我现在要解析的word⽂档中,基本都是段落和表格,所以character的具体内容我也没有特别关注。本⽂主要来讲⼀下如何从word中解析出表格,并在html中展⽰出来。
⾸先,很简单,使⽤
docx.tables
可以获得⽂档中的全部表格。跟excel中类似,word⽂档的表格也是分⾏(row)和列(column)的,读的⽅法是,对每⼀个table,先读出全部的rows,再对每⼀个row读出全部的column,这⾥的每⾏中的⼀列叫做⼀个单元格(cell),cell能做到的就跟⼀个paragraph类似了。如果⽤不着那么⿇烦地获得表格的样式,就直接⽤
<
获取单元格内容就好了。那么,⼀个⼆重循环,就获取到了table中的全部⽂字内容。
但是这是不够的。我的⽬的是要在html上展⽰出来。所以,需要在这⼀堆内容上添加html标签。具体的做法,我们来举个栗⼦吧。
不对,拿错了。应该是这样:
这是⼀个word中的table。按照上⾯的⽅法,我们可以写代码如下:
for t in docx.tables:
# todo
但其实对于word中的table,并没有这么简单。有的时候,明明这⼀⾏只有⼀列,但是却读出多个值。那么,对于相邻的相同内容,就需要做去重处理。当然,这⾥的“去重”也不是list(set())这么简单的,因为⼀⾏中的所有列应当有顺序。所以,我们采⽤添加元素的⽅式:
_table_list = []
for i, row in ws):  # 读每⾏
row_content = []
for cell lls:  # 读⼀⾏中的所有单元格
c =
if c not in row_content:
row_content.append(c)
# print(row_content)
_table_list.append(row_content)
当要添加的元素跟⾏尾相同时不添加。结果是,上⾯的tables处理完后,是这样的⼀堆列表(上⾯代码中print的位置打印出的结果):
['我们来插⼊⼀个表格']
['这是⼀级标题1', '这是⼆级标题1.1', '这是三级标题1.1.1', '总结']
['这是⼀级标题1', '这是⼆级标题1.1', '这是三级标题1.1.2', '总结']
['这是⼀级标题1', '这是⼆级标题1.1', '这是三级标题1.1.3', '总结']
['这是⼀级标题1', '这是⼆级标题1.2', '这是三级标题1.2.1', '总结']
['这是⼀级标题1', '这是⼆级标题1.3', '这是三级标题1.3.1', '总结']
['这是⼀级标题1', '这是⼆级标题1.3', '这是三级标题1.3.2', '总结']
['别忙,还有内容']
['内容', '另⼀段内容']
不过在去重之后,是没法直接⽤的……表格是个⽅格,从整体上来说就是⼀个矩阵,只是有些位置合并了单元格⽽已,我们现在的⼆维数组各列可不是对齐的。所以接下来,需要进⾏填充处理。填充的⽅式并没有⼀腚之龟⼀定之规,这是因为我们并不知道表格的具体规则如何。好在我要填的表有⼏本规则:最多4列,如果某⼀⾏只有⼀个元素,就扩充为4个,如果有两个元素,就扩充为前两个元素⼀致,后两个⼀致,即[0,1]的列表变成[0,0,1,1]这种形式。没有⼀⾏三个元素的时候。我⽤了⼀个简单的函数对每⾏进⾏了处理,这样每⼀⾏都变成了4列,整个⼆维数组也变成了⼀个矩阵形式。
我的⼿动填充代码是这样的:
def _fill_blank(table):
cols = max([len(i) for i in table])
new_table = []
for i, row in enumerate(table):
new_row = []
[d([i] * int(cols / len(row))) for i in row]
print(new_row)
new_table.append(new_row)
return new_table
⽣成的结果是:
['我们来插⼊⼀个表格', '我们来插⼊⼀个表格', '我们来插⼊⼀个表格', '我们来插⼊⼀个表格']
['这是⼀级标题1', '这是⼆级标题1.1', '这是三级标题1.1.1', '总结']
['这是⼀级标题1', '这是⼆级标题1.1', '这是三级标题1.1.2', '总结']
['这是⼀级标题1', '这是⼆级标题1.1', '这是三级标题1.1.3', '总结']
['这是⼀级标题1', '这是⼆级标题1.2', '这是三级标题1.2.1', '总结']
['这是⼀级标题1', '这是⼆级标题1.3', '这是三级标题1.3.1', '总结']
['这是⼀级标题1', '这是⼆级标题1.3', '这是三级标题1.3.2', '总结']
['别忙,还有内容', '别忙,还有内容', '别忙,还有内容', '别忙,还有内容']
['内容', '内容', '另⼀段内容', '另⼀段内容']
像我这样有规律的表格可以这样做,如果表格毫⽆规律,就只能听天由命了。
那么,为什么要先去重,再扩充?真的不是吃饱了撑的吗?
原因是,我在我项⽬中要处理的表,⼀⾏⾥⾯最多有4列,第⼀⾏本来只有1列,结果我读出来了6列。⾄于为什么会这样,我也不清楚,只能说,⽤户对于word的⽤法是五花⼋门的,只要能做出来想要的样⼦,就完全没有规矩可⾔。⿁知道他们是不是把⼀个4个单元格拆成了6个,⼜合并成了1个。如果我直接使⽤6列的结果,是没办法做成html样式的table的。
做扩展的⽬的,主要是为了合并单元格。在html标签中,⽤rowspan和colspan来表⽰跨⾏和跨列。举个列⼦,如果⼀⾏是跨4列的,这⼀⾏
应该是
<table border="1" align="center">
<tr align="center"><td colspan="4">Row One</td></tr>
<tr align="center"><td>Row Two</td><td>Row Two</td><td>Row Two</td><td>Row Two</td></tr>
</table>
形成这样⼀个表格:
如果是跨⾏的,这⼀列应该是
<table border="1" align="center">
<tr><td rowspan="3">Left</td><td>Right</td></tr>
<tr><td>Right</td></tr>
<tr><td>Right</td></tr>
</table>
形成这样⼀个表格:
所以,我们的下⼀步⼯作就是数数。数清楚在⼀⾏中有多少个相同的列,合并到⼀起,整个矩阵中有多少个相同的⾏,合并到⼀起。所谓的合并,就是数量加到上⼀⾏/列上去,⽽本⾏为空。下图是算法的⽰意:
合并⾏:
合并列:
在这样⼀个⼆维数组中,每⼀个元素是⼀个三元组,第⼀个元素是表格中的⽂本内容,第⼆个元素是rowspan的数量,第三个元素是colspan的数量。如果本⾏/列跟上⼀个有内容的⾏/列内容相同,就把内容加到那⼀⾏/列中,本⾏/列为["", 0, 0];如果不同,则保留。代码是酱婶的:
def _table_matrix():
if not table:
return""
# 处理同⼀⾏的各列
temp_matrix = []
for row in table:
if not row:
continue
col_last = [row[0], 1, 1]
line = [col_last]
for i, j in enumerate(row):
if i == 0:
continue
if j == col_last[0]:
col_last[2] += 1
line.append(["", 0, 0])
else:
col_last = [j, 1, 1]
line.append(col_last)
temp_matrix.append(line)
# 处理不同⾏
matrix = [temp_matrix[0]]
last_row = []
for i, row in enumerate(temp_matrix):
if i == 0:
d(row)
python怎么读文件夹下的文件夹continue
new_row = []
for p, r in enumerate(row):
if p >= len(last_row):
break
last_pos = last_row[p]
if r[0] == last_pos[0] and last_pos[0] != "":
last_row[p][1] += 1
new_row.append(["", 0, 0])
else:
last_row[p] = row[p]
new_row.append(r)
matrix.append(new_row)
return matrix
逻辑上会有⼀点点难读,在什么情况下数量加1,在哪⾥加1,需要⽐较细致地算,否则⼀定会乱。
最后这个数组出来之后,就可以转化html了。这个很简单,套上tr和td标签即可。代码如下:
def table2html(t):
table = _fill_blank(t)
matrix = _table_matrix(table)
html = ""
for row in matrix:
tr = "<tr>"
for col in row:
if col[1] == 0 and col[2] == 0:
continue
td = ["<td"]
if col[1] > 1:
td.append(" rowspan=\"%s\"" % col[1])
if col[2] > 1:
td.append(" colspan=\"%s\"" % col[2])
td.append(">%s</td>" % col[0])
tr += "".join(td)
tr += "</tr>"
html += tr
return html
我没有套table标签,因为这个可以在页⾯上调⼀调样式。不必完全拘泥于word中的样⼦,也没办法完全按word来——如果⼀板⼀眼地按照word的⽅式设置,出来的html上的table肯定是错乱的,原因嘛,还是在于⽤户的使⽤习惯。
最后,要是在jinja模板中使⽤的话,记得把字符串传到页⾯上的时候加上safe过滤器。
{{ table|safe }}
出来的表格⼤概是这样的:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。