电子表格非常复杂,所以如果您需要更多帮助,可以提供更多详细信息。
你需要:
-
将电子表格解析为 XML 的最佳方式
- 您的病情如何?
- Excel 是指 Microsoft-Office、Google-Spreadsheet 还是?
- 你能接受 Excel 的哪个version?
- 如果 Excel 包含脚本,你会怎么做? (VBA ...)
- 如果单元格中包含公式怎么办?
- 如果锁定(或受保护)怎么办?
- 您是否需要将某些内容写回 Excel。 (可能不会)
- 您是否关心性能? (COM)
- 您是否要处理特定文本(例如,_x0020_,...)
...
我的意思是,如果您要谈论最好的,请附上一些条件。否则属于个人观点。
我给你一个link,这样你就可以看到哪个库对你有帮助
我仍然给你一个例子(我假设情况尽可能简单)如下,
测试数据
test_data.xlsx
你可以从这个link得到它
代码
from openpyxl import load_workbook
from xml.dom import minidom
from xml.etree.ElementTree import tostring
from xml.etree.ElementTree import Element, ElementTree
from openpyxl.worksheet.merge import MergedCellRange
from openpyxl.worksheet.dimensions import SheetFormatProperties
from openpyxl.worksheet.worksheet import Worksheet
from openpyxl.workbook.workbook import Workbook
from openpyxl.cell.cell import Cell
from typing import Tuple, List, Union
def prettify(elem: Element):
"""
https://pymotw.com/2/xml/etree/ElementTree/create.html
"""
rough_string = tostring(elem, 'utf-8')
re_parsed = minidom.parseString(rough_string)
return re_parsed.toprettyxml(indent=' ') # 4 space
TABLE_START_ROW = 10
def main():
wb: Workbook = load_workbook('test_data.xlsx')
root = Element('root')
table = Element('table')
tbody = Element('tbody')
for sheet in [wb[sheet_name] for sheet_name in wb.sheetnames]:
# print(sheet.title)
sheet: Worksheet
sheet_prop: SheetFormatProperties = sheet.sheet_format
default_width = sheet_prop.baseColWidth
default_height = sheet_prop.defaultRowHeight
merge_cell_list: MergedCellRange = sheet.merged_cells.ranges
for row in sheet.rows:
if not any([cell.value for cell in row]):
root.append(Element('p')) # empty line
continue
cur_row = row[0].row
if cur_row < TABLE_START_ROW:
# `NOT TABLE` DATA
data_list = []
for cell in row:
cell: Cell
if cell.value:
merge_info: List[Union[None, MergedCellRange]] = [
merge_cell for merge_cell in filter(lambda merge_cell: cell.coordinate in merge_cell,
merge_cell_list)
]
is_merge = len(merge_info) > 0
if is_merge:
# It should recalculate the width and height.
merge_range: MergedCellRange = merge_info[0]
size: dict = merge_range.size
num_col = size['columns']
num_row = size['rows']
width = 0
for idx in range(num_col):
width += sheet.column_dimensions[
chr(ord(cell.column_letter)+idx) # A B ...
].width
height = 0
for idx in range(num_row):
new_height = sheet.row_dimensions[cell.row+idx].height
height += new_height if new_height else default_height
else:
width = sheet.column_dimensions[cell.column_letter].width
height = sheet.row_dimensions[cell.row].height
height = default_height if height is None else height
merge_info: Union[Tuple[bool, str], List[None]] = \
(is_merge, merge_info[0].coord) if merge_info else [None]
align: Tuple[str, str] = (cell.alignment.horizontal, cell.alignment.vertical)
data_list.append((str(cell.value), align, (height, width), merge_info))
p_list = [] # for the `div` tag
for value, (text_align, vertical_align), (h, w), merge_info in data_list:
is_merge, *coord = merge_info
p = Element('p')
p.text = value
if text_align:
p.attrib['text_align'] = text_align
# show w and h only on align cells
p.attrib['width'] = str(w)
p.attrib['height'] = str(h)
if vertical_align:
p.attrib['vertical_align'] = vertical_align
if is_merge:
p.attrib['merge_coord'] = coord[0]
p_list.append(p)
if len(data_list) > 1:
div = Element('div')
for tag_p in p_list:
div.append(tag_p)
root.append(div)
else:
root.append(p_list[0])
elif cur_row == TABLE_START_ROW:
# TABLE HEADER
thead = Element('thead')
tr = Element('tr')
thead.append(tr)
for cell in row:
th = Element('th')
th.text = str(cell.value)
tr.append(th)
table.append(thead)
table.append(tbody)
root.append(table)
else:
# TABLE ROW
tr = Element('tr')
for cell in row:
td = Element('td')
td.text = f'{cell.value:0,.0f}' if isinstance(cell.value, int) else str(cell.value)
tr.append(td)
tbody.append(tr)
if not 'output mode is compressed':
tree = ElementTree(root)
tree.write('result_compressed.xml') # ugly (The output format compressed that is not easy for human reading.)
return
with open('result.xml', 'w', encoding='utf-8') as f:
f.write(prettify(root))
if __name__ == '__main__':
main()
在 Excel 2013 (v15.0) 上测试 | Python 3.7.3 | openpyxl 3.0.4
演示
<?xml version="1.0" ?>
<root>
<p>1.Title for Sheet1</p>
<p/>
<div>
<p>Paragraph for Sheet1</p>
<p>p1.sub.msg</p>
</div>
<p/>
<p height="47.25" merge_coord="A5:B6" text_align="center" width="53.0">Paragraph2 for Sheet1 (merge)</p>
<p/>
<p/>
<p height="36.75" text_align="right" vertical_align="center" width="33.0">align_right_vertical_center</p>
<p/>
<table>
<thead>
<tr>
<th>Content</th>
<th>2019</th>
<th>2020</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td>2,500</td>
<td>3,500</td>
</tr>
<tr>
<td>B</td>
<td>4,500</td>
<td>6,000</td>
</tr>
</tbody>
</table>
</root>
我特意添加了div。这意味着它们在同一行。