Как лучше забрать значение формулы в Excel таблице через pandas?

Ссылка скопирована
1 ответ

По вводным: у меня есть функция, которая забирает данные из ексель таблицы и вставляет их в.docx документ, все данные четко забираются, кроме тех в которых есть ВПР формула, а именно значения section и dimensions

По вводным: section - =ВПР(RC[-1];R2C21:R16C22;2;1)
По вводным: dimensions - =ВПР(RC[-1];R18C21:R26C22;2;1)

Код:

def safe_get_number(value): if pd.isna(value) or value == "": return None try: num = float(str(value).replace(',', '.')) return int(num) if num.is_integer() else num except (ValueError, TypeError): return None async def process_matching_row(row, shu_number, index, message, bot): try: name_of_the_unit = row.iloc[2] if len(row) > 2 else "Не указано" serial_number = row.iloc[3] if len(row) > 3 else "Не указано" ip = int(row.iloc[18]) if len(row) > 17 else "Не указано" weight = safe_get_number(row.iloc[15]) if len(row) > 15 else None weight_display = f"{weight} кг" if weight is not None else "Не указано" A = row.iloc[4] if len(row) > 4 else "Не указано" section = row.iloc[6] if len(row) > 6 else "Не указано" # тут формула ВПР (выдает nan) voltage = int(row.iloc[8] if len(row) > 8 else "Не указано") rated_A = row.iloc[4] if len(row) > 4 else "Не указано" dimensions = row.iloc[14] if len(row) > 6 else "Не указано". #тут формула ВПР (выдает nan) sn = row.iloc[16] if len(row) > 16 else "Не указано" logger.info(row) try: doc = Document(passport_config.get_target_file()) replacements = { "Заводской номер:": f"Заводской номер: {str(serial_number)}", "Проект №": f"Проект № ШУ {shu_number}", "Шкаф №": f"Шкаф {str(name_of_the_unit)}", "Дата:": f"Дата: {datetime.now().strftime('%d.%m.%Y')}", "IP": f"IP{ip}", "A": f"{A}A", "`": f"{section}мм2", "~": f"{dimensions}мм", "± 10 %": f"{voltage}± 10 %", "}": f"{weight}кг", "{": f"{sn}" } def replace_text_with_style(paragraph, old_text, new_text): if old_text in paragraph.text: for run in paragraph.runs: if old_text in run.text: run.text = run.text.replace(old_text, new_text) for paragraph in doc.paragraphs: for old_text, new_text in replacements.items(): replace_text_with_style(paragraph, old_text, new_text) for table in doc.tables: for row in table.rows: for cell in row.cells: for paragraph in cell.paragraphs: for old_text, new_text in replacements.items(): replace_text_with_style(paragraph, old_text, new_text) output_filename = f"ШУ_{shu_number}_{serial_number}.docx" desktop_path = SHUConfig.get_directory_path() pattern = re.compile(r".*ШУ\s*" + re.escape(str(shu_number)) + r"\D*$", re.IGNORECASE) matching_folders = [] for folder_name in os.listdir(desktop_path): if pattern.search(folder_name): matching_folders.append(folder_name) save_path = "." if matching_folders: matching_folders.sort(reverse=True) folder_path = os.path.join(desktop_path, matching_folders[0]) pasporta_path = os.path.join(folder_path, "Паспорта") try: os.makedirs(pasporta_path, exist_ok=True) save_path = pasporta_path except Exception as e: save_path = folder_path output_path = os.path.join(save_path, output_filename) doc.save(output_path) logger.info(Fore.GREEN + f"✓ Документ сохранен: {output_path}" + Style.RESET_ALL) if matching_folders: pdf_file = None elektrika_path = os.path.join(folder_path, "электрика") if os.path.exists(elektrika_path): pdf_files = [f for f in os.listdir(elektrika_path) if f.lower().endswith('.pdf') and f.startswith('1')] if pdf_files: pdf_files.sort() pdf_file = os.path.join(elektrika_path, pdf_files[0]) if not pdf_file: pdf_files = [f for f in os.listdir(folder_path) if f.lower().endswith('.pdf') and f.startswith('1')] if pdf_files: pdf_files.sort() pdf_file = os.path.join(folder_path, pdf_files[0]) if pdf_file: if await process_pdf_attachment(pdf_file, pasporta_path, message, bot, shu_number): else: await bot.send_message(message.from_user.id, "Не найден PDF файл электрики!") except Exception as e: await bot.send_message(ADMIN_ID, f"× Ошибка в замене шаблона: {str(e)}") logger.error(Fore.RED + f"× Ошибка: {str(e)}" + Style.RESET_ALL) except Exception as e: await bot.send_message(ADMIN_ID, f"Ошибка при обработке строки {index + 1}: {e}") logger.error(Fore.RED + f"Ошибка при обработке строки {index + 1}: {e}" + Style.RESET_ALL) async def find_in_register(file_path, shu_number, message, bot): try: df = pd.read_excel(file_path) pattern = fr'(?:^|\b)ШУ\s*{re.escape(shu_number)}(?:\b|$)' mask = df.apply(lambda col: col.astype(str).str.contains(pattern, regex=True)).any(axis=1) matching_rows = df[mask] if not matching_rows.empty: logger.info(Fore.YELLOW + f"\nВсего найдено {len(matching_rows)} совпадений в реестре для ШУ{shu_number}:" + Style.RESET_ALL) for i, (_, row) in enumerate(matching_rows.iterrows()): await process_matching_row(row, shu_number, i, message, bot) else: await bot.send_message(message.from_user.id, f"\nВ реестре не найдено записей для ШУ{shu_number}") except Exception as e: await bot.send_message(message.from_user.id, f"Ошибка при поиске в реестре: {e}")
Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Лучший ответ
1
Дмитрий К. Ответ

Pandas сам по себе не вычисляет формулы Excel. Когда вы читаете файл через pandas/openpyxl, вы получаете либо текст формулы, либо последнее сохранённое Excel значение. Если файл был создан программно и ни разу не пересчитывался в Excel/LibreOffice, сохранённого значения у формулы может просто не быть.

Для получения именно результата ВПР есть три рабочих варианта.

Первый вариант - читать файл через openpyxl с data_only=True. Это вернёт кэшированное значение формулы, если оно есть:

from openpyxl import load_workbook
 
wb = load_workbook('file.xlsx', data_only=True)
ws = wb['Лист1']
 
section = ws['B2'].value
dimensions = ws['C2'].value

from openpyxl import load_workbook wb = load_workbook('file.xlsx', data_only=True) ws = wb['Лист1'] section = ws['B2'].value dimensions = ws['C2'].value

Если возвращается None, значит Excel не сохранил рассчитанный результат. Тогда второй вариант - перед обработкой пересчитать файл в Excel или LibreOffice. Например, на сервере часто используют LibreOffice headless:

soffice --headless --convert-to xlsx --outdir ./recalc ./input.xlsx

soffice --headless --convert-to xlsx --outdir ./recalc ./input.xlsx

Третий вариант - не полагаться на Excel-формулу, а повторить логику ВПР в Python. Это часто надёжнее, если вы генерируете документы автоматически:

main = pandas.read_excel('file.xlsx', sheet_name='main')
dict_table = pandas.read_excel('file.xlsx', sheet_name='dict')
 
main = main.merge(
    dict_table[['key', 'section', 'dimensions']],
    on='key',
    how='left'
)

main = pandas.read_excel('file.xlsx', sheet_name='main') dict_table = pandas.read_excel('file.xlsx', sheet_name='dict') main = main.merge( dict_table[['key', 'section', 'dimensions']], on='key', how='left' )

Если документ дальше уходит в .docx, я бы выбрал третий путь: забрать исходные таблицы, сделать merge в pandas и подставлять уже готовые значения. Тогда результат не зависит от того, открыт ли файл в Excel, пересчитались ли формулы и сохранил ли Excel кэш формулы. Формулы в исходном файле можно оставить для людей, но в автоматизации лучше считать данные на стороне Python.

Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно