在Python中使用OpenPyXL包写入数据后,如何保持样式格式不变? 您所在的位置:网站首页 excel怎么编辑文本格式不变 在Python中使用OpenPyXL包写入数据后,如何保持样式格式不变?

在Python中使用OpenPyXL包写入数据后,如何保持样式格式不变?

2024-07-13 11:17| 来源: 网络整理| 查看: 265

我正在使用openpyxl库包来读取和写入一些数据到现有的excel文件test.xlsx中。

在向其中写入一些数据之前,文件的内容如下所示:

单元格A1包含高棉Unicode字符,英语字符为粗体。

单元格A3使用的是Lemons1字体,并且英文字符为斜体。

我正在使用下面的脚本向此excel文件的单元格B2读取和写入数据" It is me":

1234567891011121314151617181920212223242526from openpyxl import load_workbook import os FILENAME1 = os.path.dirname(__file__)+'/test.xlsx' from flask import make_response from openpyxl.writer.excel import save_virtual_workbook from app import app @app.route('/testexel', methods=['GET']) def testexel():     with app.app_context():         try:             filename = 'test'             workbook = load_workbook(FILENAME1, keep_links=False)             sheet = workbook['Sheet1']             sheet['B2']='It is me'             response = make_response(save_virtual_workbook(workbook))             response.headers['Cache-Control'] = 'no-cache'             response.headers["Content-Disposition"] ="attachment; filename=%s.xlsx" % filename             response.headers["Content-type"] ="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8"             return response         except Exception as e:             raise

然后将结果excel文件的格式修改为以下格式,我从不希望它像这样:

在向其写入数据之前,格式样式与原始文件有很大不同:

单元格A1中所有数据均采用英文字符的粗体显示

单元格B3英文字符成为常规样式,并且将字体从前面的高棉字符更改为字体limons1。

我要完成的工作是将文件的现有内容保持为原来的格式(样式和字体),同时向其中写入其他数据。

请告知我的脚本有什么问题,如何在运行上述脚本后如何保持现有样式和字体不变?谢谢。

相关讨论 在openpyxl中使用富文本格式编辑工作簿的可能重复项 @CharlieClark为什么重复?如果provider链接似乎确实可以解决问题。 问题是重复的,答案尚未更改。 至少,@ CharlieClark的答案似乎无法解决我的问题。我搜索了一些类似的问题,但没有人提出通过代码实现这一愿望的答案。因此,我正在寻找一种解决方法,至少是不可能的原因,这是另一种选择,避免使用一句话回答" openpyxls在一个单元格中不支持多种格式",这是无奈的,谢谢:) 用openpyxl无法完成您想要的事情。那就是答案。 @CharlieClark如果不可能的话,您能给我一个替代解决方案吗,可以是另一个软件包吗?非常感谢您的解决方案,谢谢 @HouyNarun如果将新数据写入现有内容之后的单元格(例如,单元格B4),会发生什么情况? @YvonneAburrow实际上,我得到了一个已经格式化了单元格样式和不同字体的excel文件,只有一些我必须向其中写入数据的单元格,因此在将数据写入到我应该写入的单元格之后,另一个我不是的单元格应该碰他们也会受到影响,这是我的问题,谢谢 看来他们可能需要再给您另一个Excel文件。 :( @YvonneAburrow是的,我的任务是将数据写入给定的Excel文件,同时保持其他内容不变。 @YvonneAburrow我试图四处搜寻,但我发现其他一些人也像OpenPyXl一样面临同样的问题,但是我没有找到解决他们问题的答案。

Excel文件(扩展名为.xlsx)实际上是zip存档。 (您实际上可以使用7-zip或其他类似程序打开excel文件。)因此excel文件包含一堆xml文件,其中存储了数据。 openpyxl的作用是,在打开excel文件时从这些xml文件中读取数据,并在保存excel文件时使用xml文件创建zip存档。很难过,openpyxl读取一些xml文件,然后解析该数据,然后您可以使用openpyxl库中的功能来更改和添加数据,最后,当您保存工作簿时,openpyxl将创建xml文件,向其中写入数据并保存它们作为zip存档(这是Excel文件)。这些xml文件包含存储在excel文件中的所有数据,(一个xml文件包含来自excel文件的公式,其他将包含样式,其他将包含有关excel主题的数据,依此类推)。我们只关心excel文件中存储在两个xml文件中的字符串:

sharedStrings.xml

此文件包含excel文件中的所有字符串以及这些字符串的格式,下面是一个示例:

123456789101112131415161718192021222324252627282930313233343536373839404142                                                                                                                                                 < T >Hello                                                                                                                                     ?                                     ?                                                                                                                                                     < T >sike            

sheet1.xml

该文件包含您的字符串的位置(哪个单元格包含哪个字符串)。 (在此excel文件中,每个工作表都有一个文件,但就本示例而言,假设您的文件中只有一个工作表。)以下是一个示例:

123456789101112131415161718192021222324252627282930313233                                                                                                             0                                                                                                 1                                            

如果您使用openpyxl打开此excel,然后保存(不更改任何数据),则sharedStrings.xml将如下所示:

12345678             < T >Hello                   < T >  sike    

如您所见,您将丢失所有单元格(字符串)的原始格式,而将获得某种形式的单元格合并格式(因此,如果单元格中的某些字符为粗体而另一些则不是,那么在保存文件时,整个单元格将以粗体显示或整个单元格将是正常的)。现在人们已经要求开发人员实现这种富文本选项(link1,link2),但他们感到遗憾的是,实现这样的东西会很复杂。我同意这并不是一件容易的事,但是我们可以做些更简单的事情:打开excel文件时可以从sharedStrings.xml获取数据,而要保存excel文件时可以使用xml代码,但仅用于打开文件时存在的单元格。这可能不容易理解,所以让我们看下面的例子:

假设您有这样的excel文件:

对于此excel文件,sharedStrings.xml将是以下内容:

1234567891011121314151617181920212223242526                                                                                                                                                 < T >Hello                                                                                                                                     ?            

如果您运行以下python代码:

12345from openpyxl import load_workbook workbook = load_workbook(FILENAME1, keep_links=False) sheet = workbook.active sheet['A2'] = 'It is me' workbook.save('out.xlsx')

文件out.xlsx将如下所示:

对于out.xlsx文件,sharedStrings.xml将是这样的:

12345678             < T >Hello                   < T >It is me    

所以我们要做的是使用以下xml代码:

1234567891011121314151617181920212223                                                                                                     < T >Hello                                                                                             ?    

对于包含Hello ?和以下xml代码的旧单元格A1:

123     < T >It is me

用于包含It is me的新单元格A2。

因此,我们可以将以下xml部分组合起来以获取xml文件,如下所示:

12345678910111213141516171819202122232425262728                                                                                                                                                 < T >Hello                                                                                                                                     ?                         < T >It is me    

我写了一些函数来做到这一点。 (有很多代码,但是大多数都是从openpyxl复制的。如果您要更改openpyxl库,则可以用10或20行代码来完成,但这绝不是好主意,所以我宁愿复制整个函数需要先更改然后再更改一小部分。)

您可以将以下代码保存在单独的文件extendedopenpyxl.py中:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224from openpyxl import load_workbook as openpyxlload_workbook from openpyxl.reader.excel import _validate_archive, _find_workbook_part from openpyxl.reader.worksheet import _get_xml_iter from openpyxl.xml.functions import fromstring, iterparse, safe_iterator, tostring, Element, xmlfile, SubElement from openpyxl.xml.constants import ARC_CONTENT_TYPES, SHEET_MAIN_NS, SHARED_STRINGS, ARC_ROOT_RELS, ARC_APP, ARC_CORE, ARC_THEME, ARC_SHARED_STRINGS, ARC_STYLE, ARC_WORKBOOK, ARC_WORKBOOK_RELS from openpyxl.packaging.manifest import Manifest from openpyxl.packaging.relationship import get_dependents, get_rels_path from openpyxl.packaging.workbook import WorkbookParser from openpyxl.packaging.extended import ExtendedProperties from openpyxl.utils import coordinate_to_tuple from openpyxl.cell.text import Text from openpyxl.writer.excel import ExcelWriter as openpyxlExcelWriter from openpyxl.writer.workbook import write_root_rels, write_workbook_rels, write_workbook from openpyxl.writer.theme import write_theme from openpyxl.writer.etree_worksheet import get_rows_to_write from openpyxl.styles.stylesheet import write_stylesheet from zipfile import ZipFile, ZIP_DEFLATED from operator import itemgetter from io import BytesIO from xml.etree.ElementTree import tostring as xml_tostring from xml.etree.ElementTree import register_namespace from lxml.etree import fromstring as lxml_fromstring register_namespace('', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main') def get_value_cells(workbook):     value_cells = []     for idx, worksheet in enumerate(workbook.worksheets, 1):         all_rows = get_rows_to_write(worksheet)         for row_idx, row in all_rows:             row = sorted(row, key=itemgetter(0))             for col, cell in row:                 if cell._value is not None:                     if cell.data_type == 's':                         value_cells.append((worksheet.title,(cell.row, cell.col_idx)))     return value_cells def check_if_lxml(element):     if type(element).__module__ == 'xml.etree.ElementTree':         string = xml_tostring(element)         el = lxml_fromstring(string)         return el     return element def write_string_table(workbook):     string_table = workbook.shared_strings     workbook_data = workbook.new_interal_value_workbook_data     data_strings = workbook.new_interal_value_data_strings     value_cells = get_value_cells(workbook)     out = BytesIO()     i = 0     with xmlfile(out) as xf:         with xf.element("sst", xmlns=SHEET_MAIN_NS, uniqueCount="%d" % len(string_table)):                         for i, key in enumerate(string_table):                 sheetname, coordinates = value_cells[i]                 if coordinates in workbook_data[sheetname]:                     value = workbook_data[sheetname][coordinates]                     xml_el = data_strings[value]                     el = check_if_lxml(xml_el)                 else:                     el = Element('si')                     text = SubElement(el, 't')                     text.text = key                     if key.strip() != key:                         text.set(PRESERVE_SPACE, 'preserve')                 xf.write(el)     return  out.getvalue() class ExcelWriter(openpyxlExcelWriter):     def write_data(self):        """Write the various xml files into the zip archive."""         # cleanup all worksheets         archive = self._archive         archive.writestr(ARC_ROOT_RELS, write_root_rels(self.workbook))         props = ExtendedProperties()         archive.writestr(ARC_APP, tostring(props.to_tree()))         archive.writestr(ARC_CORE, tostring(self.workbook.properties.to_tree()))         if self.workbook.loaded_theme:             archive.writestr(ARC_THEME, self.workbook.loaded_theme)         else:             archive.writestr(ARC_THEME, write_theme())         self._write_worksheets()         self._write_chartsheets()         self._write_images()         self._write_charts()         string_table_out = write_string_table(self.workbook)         self._archive.writestr(ARC_SHARED_STRINGS, string_table_out)         self._write_external_links()         stylesheet = write_stylesheet(self.workbook)         archive.writestr(ARC_STYLE, tostring(stylesheet))         archive.writestr(ARC_WORKBOOK, write_workbook(self.workbook))         archive.writestr(ARC_WORKBOOK_RELS, write_workbook_rels(self.workbook))         self._merge_vba()         self.manifest._write(archive, self.workbook)         return     def save(self, filename):         self.write_data()         self._archive.close()         return def get_coordinates(cell, row_count, col_count):     coordinate = cell.get('r')     if coordinate:         row, column = coordinate_to_tuple(coordinate)     else:         row, column = row_count, col_count     return row, column def parse_cell(cell):     VALUE_TAG = '{%s}v' % SHEET_MAIN_NS     value = cell.find(VALUE_TAG)     if value is not None:         value = int(value.text)     return value def parse_row(row, row_count):     CELL_TAG = '{%s}c' % SHEET_MAIN_NS     if row.get('r'):         row_count = int(row.get('r'))     else:         row_count += 1     col_count = 0     data = dict()     for cell in safe_iterator(row, CELL_TAG):         col_count += 1         value = parse_cell(cell)         if value is not None:             coordinates = get_coordinates(cell, row_count, col_count)             data[coordinates] = value     return data def parse_sheet(xml_source):     dispatcher = ['{%s}mergeCells' % SHEET_MAIN_NS, '{%s}col' % SHEET_MAIN_NS, '{%s}row' % SHEET_MAIN_NS, '{%s}conditionalFormatting' % SHEET_MAIN_NS, '{%s}legacyDrawing' % SHEET_MAIN_NS, '{%s}sheetProtection' % SHEET_MAIN_NS, '{%s}extLst' % SHEET_MAIN_NS, '{%s}hyperlink' % SHEET_MAIN_NS, '{%s}tableParts' % SHEET_MAIN_NS]     row_count = 0     stream = _get_xml_iter(xml_source)     it = iterparse(stream, tag=dispatcher)     row_tag = '{%s}row' % SHEET_MAIN_NS     data = dict()     for _, element in it:         tag_name = element.tag         if tag_name == row_tag:             row_data = parse_row(element, row_count)             data.update(row_data)             element.clear()     return data def get_workbook_parser(archive):     src = archive.read(ARC_CONTENT_TYPES)     root = fromstring(src)     package = Manifest.from_tree(root)     wb_part = _find_workbook_part(package)     workbook_part_name = wb_part.PartName[1:]     parser = WorkbookParser(archive, workbook_part_name)     parser.parse()     return parser, package def get_data_strings(xml_source):     STRING_TAG = '{%s}si' % SHEET_MAIN_NS     strings = []     src = _get_xml_iter(xml_source)     for _, node in iterparse(src):         if node.tag == STRING_TAG:             strings.append(node)     return strings def load_workbook(filename, *args, **kwargs):     workbook = openpyxlload_workbook(filename, *args, **kwargs)     archive = _validate_archive(filename)     parser, package = get_workbook_parser(archive)     workbook_data = dict()     for sheet, rel in parser.find_sheets():         sheet_name = sheet.name         worksheet_path = rel.target         fh = archive.open(worksheet_path)         sheet_data = parse_sheet(fh)         workbook_data[sheet_name] = sheet_data     data_strings = []     ct = package.find(SHARED_STRINGS)     if ct is not None:         strings_path = ct.PartName[1:]         strings_source = archive.read(strings_path)         data_strings = get_data_strings(strings_source)     workbook.new_interal_value_workbook_data = workbook_data     workbook.new_interal_value_data_strings = data_strings     return workbook def save_workbook(workbook, filename,):     archive = ZipFile(filename, 'w', ZIP_DEFLATED, allowZip64=True)     writer = ExcelWriter(workbook, archive)     writer.save(filename)     return True def save_virtual_workbook(workbook,):     temp_buffer = BytesIO()     archive = ZipFile(temp_buffer, 'w', ZIP_DEFLATED, allowZip64=True)     writer = ExcelWriter(workbook, archive)     try:         writer.write_data()     finally:         archive.close()     virtual_workbook = temp_buffer.getvalue()     temp_buffer.close()     return virtual_workbook

现在,如果您运行以下代码:

123456from extendedopenpyxl import load_workbook, save_workbook workbook = load_workbook(FILENAME1, keep_links=False) sheet = workbook['Sheet'] sheet['A2'] = 'It is me' save_workbook(workbook, 'out.xlsx')

当我在上面示例中使用的excel文件上运行此代码时,得到以下结果:

如您所见,单元格A1中的文本按原样设置格式(Hello以粗体显示,而?不是)。

根据此问题的答案,您可以使用openpyxl在Excel中格式化单元格。

此处给出的答案仅将目标单元格更改为粗体,但也许您可以将字体更改回lemons1。

1234567from openpyxl.workbook import Workbook from openpyxl.styles import Font wb = Workbook() ws = wb.active ws['B3'] ="Hello" ws['B3'].font =  Font(name='lemons1', size=14) wb.save("FontDemo.xlsx")

但是,根据文档,您只能将样式应用于整个单元,而不能应用于单元的一部分。因此,您需要将高棉语字符放在一个单元格中,并将英文字符放在另一个单元格中。

相关讨论 除了您想推荐我可以尝试作为替代解决方案的任何东西,例如可以实现上述结果的另一个软件包,库? 非常感谢 抱歉,直到看到您的帖子,我才听说过这个包裹。 您可能想调查与Google Spreadsheets进行交互的API? @Yvonne您可以在这里提供帮助stackoverflow.com/questions/59888367/ 我看了一下,但是那里的一个答案似乎很有道理。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有