Есть ли способ разобрать электронные таблицы Excel на xml (электронная таблица содержит абзацы и таблицы) в Python? - PullRequest
0 голосов
/ 04 августа 2020

Спасибо, что поделились своим временем, и вот проблема, с которой я столкнулся ниже

[Сводка] Я пытаюсь передать информацию xml в отчет в виде таблицы Excel. Вот снимок электронной таблицы и желаемый результат ниже.

Таблица Excel

Желаемый результат такой же.

<p>1. Title for Sheet1</p>
<p></p>
<p>Paragraph for Sheet1</p>
<p></p>
<p>Paragraph2 for Sheet1</p>
<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>

[Проблема] Я искал способы синтаксического анализа excel до xml, но не могу понять, как разделить значения абзацев и значения таблиц в электронной таблице. Я думаю о logi c, чтобы распознавать ячейки как абзацы, «если в столбце B нет значения», а если еще, распознавать их как таблицы. Вероятно, на каждом листе будет несколько абзацев и таблиц.

[Что мне нужно]

  1. Лучший способ разобрать электронные таблицы как таковые на xml
  2. Как сохранить информацию о форматах «выравнивание», «ширина» и «высота» (я хочу содержать их в xml).
  3. Если есть возможность, получить информацию и по объединенной ячейке. Я думаю, что в некоторых случаях отчет в виде электронной таблицы будет содержать объединенные ячейки.

Я ищу ответы на Python, а также VBA, если нужно. Я очень ценю вашу помощь и надеюсь, что вы хорошо проведете день.

Ответы [ 2 ]

0 голосов
/ 04 августа 2020

Электронная таблица очень сложна, поэтому, если вам нужна дополнительная помощь, вы можете предоставить более подробную информацию.

Вам нужно:

  • Лучший способ для анализа электронных таблиц как таковых, чтобы XML
    • Каково ваше состояние?
      • Excel означает Microsoft-Office, Google-Spreadsheet или?
      • какую версию Excel вы можете принять?
      • Что делать, если Excel содержит сценарий? (VBA ...)
      • Что делать, если ячейка содержит формулу?
      • Что делать, если блокировка (или защита)?
      • Нужно ли вам напишите что-нибудь в Excel. (вероятно, нет)
      • Тебя волнует производительность или нет? (COM)
      • Вы хотите обработать конкретный текст (например, _x0020_, ...) ...

Я имею в виду, если вы хотите поговорить о лучшем , пожалуйста, поставьте это условие. В противном случае это относится к личному виду.

Я даю вам ссылку , чтобы вы могли увидеть, какая библиотека может быть вам полезна

Я все же приведу вам пример (я предполагаю, что случай максимально простой), как показано ниже,

тестовые данные

test_data.xlsx

enter image description here

you can get it from this ссылка

код

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

demo

<?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 намеренно. Это означает, что они находятся в одном ряду.

0 голосов
/ 04 августа 2020

Для этого нет хороших вариантов, но есть 4 варианта:

вариант 1. Из Excel сохраните как XML. Затем вам нужно будет указать сопоставление с нужной структурой XML.

вариант 2. Файл xlsx на самом деле является заархивированным файлом XML, поэтому вы можете сохранить файл xlsx (например, сохранить в C: \ темп \ a.xlsx). Затем измените расширение файла на «.zip». Если вы открываете zip-файл, там находится каталог файлов. Содержимое первого рабочего листа находится в: C: \ temp \ a.zip \ xl \ worksheets \ sheet1. xml

Используйте XSL для преобразования XML в любую структуру, которую вы хотите. XML содержит значения, размеры и форматирование стиля (это электронная таблица).

Есть и другие файлы метаданных в заархивированном каталоге.

вариант 3. Из excel, Сохранить как .htm. Этот файл html на самом деле является сценарием, поэтому вы, вероятно, захотите открыть файл html в браузере, а затем снова сохранить файл html, чтобы получить нужную таблицу.

вариант 4. Сохраните как csv, затем напишите сценарий для l oop через каждую строку csv и оберните каждую строку вокруг каждого значения ячейки. Это, наверное, самый простой вариант.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...