Всем привет.
В статье приведу немного скриптов на Python для решения вопросов с которыми иногда пересекается инженер ПТО строительной организации. Склёпал сам по мотивам информации из инета. Профи будет скучно), уровень "без диплома программиста".
На рабочем ноуте используется: Win10, Python 3.11, Office2019. Програмлю в PyScripter (нравится мне он))
Пример 1. Надо архивировать (не в смысле запаковать)
Проводятся испытания отдельных участков трубопровода, штук 50-60. На каждое заводится папка. Так как ни заказчик, ни технадзор, ни еще кто-нибудь не могут определиться, как лучше оформить это дело, то возникает ситуация когда куча документов имеет 2-3-4 варианта оформления. Значит надо скрипт, который текущий вариант закинет в архивную папку. (Ну да, "мой комьютер" или "тотал коммандер" никак с этим не справляются)):
##------------ЗАДАЧА---------------------------
## Создать папку Архив, в ней создать подпапку
## Переместить все файлы ворд/Эксель в подпапку архива
import glob, os, shutil
source_dir = os.getcwd() #Path where your files are at the moment
dst = source_dir+'/Архив/Вар2' #Path you want to move your files to
if not os.path.isdir(dst): #создаем архив
os.makedirs(dst)
#либо выбираем типы файлов - типа оффис
format_move_files = ['*.xls*', '*.doc*']
#либо все.
##format_move_files = ['*.*']
for fmf in format_move_files:
files = glob.iglob(os.path.join(source_dir, fmf))
for file in files:
if os.path.isfile(file):
shutil.move(file, dst)
Пример 2. Создаем папки
Собралось как-то много файлов в одной папке. И тут супостаты опять пристали со своими хотелками - каждый файл в свою папку. Нууу, лааадно. Лучше день потерять....потом за минуту само сделается. Идея такова, тем же тоталом получаю список файлов, его сохраняю в файле "папки.txt". Вот теперь его скрипт должен открыть и наштамповать папок:
import os
codirovk = 'utf-8'
#------------вар2 работает------------------
with open('папки.txt', "r", encoding=codirovk) as file1:
# итерация по строкам
for line in file1:
print(line.strip())
if not os.path.isdir(line.strip()): ##делаем папки если их нет
os.makedirs(line.strip())
#------------вар1 работает------------------
##file1 = open('папки.txt', "r")
##
### считываем все строки
##lines = file1.readlines()
##
### итерация по строкам
##for line in lines:
#### print(line.strip())
## if not os.path.isdir(line.strip()): делаем папки если их нет
## os.makedirs(line.strip())
##
### закрываем файл
##file1.close
Пример 3. Достаем файлы
И вот присылают как-то папку со сметами, а они каждый файл в отдельной папке, да еще вперемешку с ведомостями всякими, да еще названия файлов пляшут, ну и их 392шт. Шо делать, шо делать - расчехляем питон. Надо достать все файлы смет с попутным переименованием их в одну папку:
# задача - достать из множества папок все файлы в одну папку (где скрипт)
# допзадача - переименовать добавляя имена подпапок
import os, shutil
import glob
import re
dst = os.getcwd()
print(dst)
# допзадача переименовать добавляя имена подпапок
for name in sorted(glob.glob(dst+'/**/*локал*.xls', recursive=True)):
# имеем D:\1 На почту\КС-6\Глава 1\01-02-01Р-01\Л0326174_Локальная смета.xls
# вырезаем эту часть D:\1 На почту\КС-6\ и заменяем косые черты на тире
newname = name[len(dst)+1:]
newname = re.sub(r'\\', '-', newname)
os.rename(name, newname)
# пройтись по всем папкам каталога запуска скрипта и переместить
# файлы *локал*.xls в папку скрипта
for name in sorted(glob.glob(dst+'/**/*локал*.xls', recursive=True)):
try:
shutil.move(name, dst)
except:
pass
Пример 4. Из ворда в пдф)
Есть пару десятков файлов ворда в формате docx, надо из них pdf. Ну конечно, сча я буду сидеть каждый ручками открывать и делать сохранить как. Хотя есть еще и пдф-принтеры.....Так, ладно, питон погнали:
##*******************работает только docx
#pip install docx2pdf
from docx2pdf import convert
import os
convert(os.getcwd()) # конвертация текущего каталога
Пример 5 с подпримером 6. Работаем с Excel. Замена текста во множестве файлов.
Мы строили, строили и наконец - построили. Но....да твою ж дивизию: ну почему когда 85% исполнительной документации уже сделано и подписано (а это примерно 1200-1500 актов скрытых работ, сделанных в эксель) вдруг какой-то заразе приходит в голову поменять подписанта из-за игр генподрядчик-субподрядчик-субсубподрядчик. И, шо, эти люди думают, радостно потирая ручки, шо я буду мучиться. Ну держитесь - где там мой удав, пора придушить воооон того....А ладно, смотрим шо есть в интернете для питона. А там много чего есть, те что приглянулись вот:
openpyxl - работает только с xlsx Для его использования возникла подзадача сконвертировать из xls в xlsx. Смотрим вопрос в инете. Находим pyexcel, xls2xlsx - но рушат форматирование. Находим pywin32. Делаем:
# Задача: конвертация эксель из xls в xlsx
# Все остальные библиотеки работают вкось
# Эксель должен быть установлен на компе!!!
# pip bloks
## python -m pip install --upgrade pywin32
# import bloks
import os
import win32com.client as win32
# config bloks
path =os.getcwd()
format_files = ('.xls')
pred_prefiks_file_name = ''
# relise bloks
for root, dirs, files in os.walk(path):
for file in files:
if(file.endswith(format_files, 0, len(file))):
## print(os.path.join(file))## - этот вариант выводит только имя файла
print(file)
## print(os.path.join(root, file))## - этот вариант выводит полный путь и имя файла
# -------делаем через COM объект -----------------------------
excel = win32.gencache.EnsureDispatch('Excel.Application')
wb = excel.Workbooks.Open(os.path.join(root, file))
wb.SaveAs(pred_prefiks_file_name+os.path.join(root, file)+'x', FileFormat = 51) #FileFormat = 51 is for .xlsx extension
wb.Close() #FileFormat = 56 is for .xls extension
excel.Application.Quit()
input("Работа завершена. Тисни ентер.")
Далее обрабатываем openpyxl, но он тоже нарушает форматирование. Ищем дальше и находим:
aspose-cells -добавляет лист 'Evaluation Warning' и может еще какое ограничение содержит, так как он платный.
Практически он работает как надо. Делаю через него скрипт, а лишний лист удаляю с помощью pywin32.
## худо бедно работает как надо мне, но компонент платный
##pip install aspose-cells
##pip install aspose-cells-python
##python -m pip install --upgrade pywin32
import os
from aspose.cells import Workbook, ReplaceOptions
import win32com.client as win32
path =os.getcwd()
format_files = ('.xlsx')##'.pdf','.doc', '.docx', '.dwg', '.py' ## - закомментировать если нужны все файлы
find_str = 'Руководитель проекта ООО "Капуста" '
removent_str = 'Заместитель генерального директора по объектам ООО "Рога и копыта"'
# ВНИМАНИЕ - замена осуществляет по полным данным в ячейке. Т.е. меняется ячейка на ячейку
pred_prefiks_file_name = ''
for root, dirs, files in os.walk(path):
for file in files:
if(file.endswith(format_files, 0, len(file))):## - закомментировать если нужны все файлы
print(file)
# Load Excel file
workbook = Workbook(file) ##Workbook("Workbook.xlsx")
# Create replace options
replace = ReplaceOptions()
# Set case sensitivity and text matching options - раздел выдает ошибку
## replace.setCaseSensitive(False)
## replace.setMatchEntireCellContents(False)
# Replace text
workbook.replace(find_str, removent_str, replace)
# Save as Excel XLSX file
workbook.save(pred_prefiks_file_name+file);
## этот блок удаляет последний добавляемый лист 'Evaluation Warning' - по факту просто последний
excel = win32.gencache.EnsureDispatch('Excel.Application')
wb = excel.Workbooks.Open(os.path.join(root, pred_prefiks_file_name+file))
ws = wb.ActiveSheet
try:
excel.DisplayAlerts=False
wb.Worksheets(wb.Sheets.Count).Delete()
finally:
pass
wb.Save()
wb.Close()
excel.Application.Quit()
input("Работа завершена. Тисни ентер.")
Да шо ж я упускаю что-то. Как то все топорно. Стоп, эту работу лучше экселя никто не сделает, а это значит вот же решение - pywin32. И конвертировать ничего не надо было. Ищем материалы в инете, просматриваем, пробуем раз 50 с попутным улучшением, готово:
##python -m pip install --upgrade pywin32
import os
import win32com.client as win32
import re
path = os.getcwd()
format_files = ('.xlsx', '.xls')##'.pdf','.doc', '.docx', '.dwg', '.py' ## - закомментировать если что-то не надо
# ищем строку
find_str = 'Сидоров Ю.М.'
# строка для замены найденной строки
removent_str = 'Сидоров Ю.А.'
# делаем 4 замены по порядку, а можно было и через список сделать
#Сидоров Ю.М. Сидоров Ю.А.
#Руководитель проекта ООО "Капуста" Заместитель генерального директора по объектам ООО "Рога и копыта"
#Сидоров Ю.М. Приказ №15 29.03.2023 Сидоров Ю.А. Приказ № П100 от 31.09.2022
#Иванов А.А. Сидоров Ю.А.
#количество строк в которых надо делать поиск
row_find = 125
#количество столбцов в которых надо делать поиск
col_find = 40
#если надо спереди имени файла добавить префикс
pred_prefiks_file_name = ''
excel = win32.gencache.EnsureDispatch('Excel.Application')
for root, dirs, files in os.walk(path):
for file in files:
if ((file.endswith(format_files, 0, len(file))) and (not file.endswith('~$', 0, 2))):
# not file.endswith('~$', 0, 2) - защита от временных файлов эксель которые имена начинаются на ~$
print('файл: ',file)
#print(os.path.join(file))## - этот вариант выводит только имя файла
#print(os.path.join(root, file))## - этот вариант выводит полный путь и имя файла
## excel = win32.gencache.EnsureDispatch('Excel.Application')
wb = excel.Workbooks.Open(os.path.join(root, file))
print('листов: ', wb.Worksheets.Count)
for sh_i in range(1, wb.Worksheets.Count+1): #перебираем листы книги
ws = wb.Worksheets(sh_i) # идем на лист и активируем его
ws.Activate()
print('лист: ', sh_i)
fp = 0
cii2 = 0
for row_i in range(1, row_find): #ws.Columns.Count - это вообще все строки
for col_i in range(1, col_find): #ws.Rows.Count - это вообще все колонки
if ws.Cells(row_i,col_i).Value:
cii2 = cii2 + 1
#вариант с re - заменяет внутри
data_from_cell = str(ws.Cells(row_i,col_i).Value) #получаем строку из данных
result = re.findall(find_str, data_from_cell)
if len(result) > 0:
print('в ячейке: ',row_i,',',col_i)
print('данные в ячейке: '+data_from_cell)
fp = 1
data_from_cell2 = re.sub(find_str, removent_str, data_from_cell, count=0)
print('измененные данные: '+data_from_cell2)
ws.Cells(row_i,col_i).Value = data_from_cell2
print('замена выполнена')
#вариант через данные эксель - совпадение по ячейке полностью - плохой вариант
## if ws.Cells(row_i,col_i).Value == find_str:
## print('найдено на листе: ', sh_i)
## print('в ячейке: ',row_i,', ',col_i)
## fp = 1
## ws.Cells(109,1).Value = removent_str
## print('замена выполнена')
if fp == 0:
print('не найдено на листе: ', sh_i)
print('пройдено ячеек: ', cii2)
wb.Save(os.path.join(root, pred_prefiks_file_name+file))
wb.Close()
## excel.Application.Quit()
excel.Application.Quit()
print('')
print('----------------------------------------------')
input("Работа завершена. Тисни ентер.")
В общем как поиск/замену сделать через pywin32 не нашел. Перебираю заданную область по ячейково. Сначала всю ячейку заменял - но это отдельный текст внутри ячейки не заменишь, а только целиком. Потом через модуль re уже стало работать, как я и хотел. Работает медленнее, чем aspose, но мне как-то спокойнее)).
P.S.: Так и живём в ПТО :-)
Комментарии (16)
EShin
16.09.2023 16:14+1Тоже приходилось заниматься документацией для стройки. Делал для себя генератор актов. Был шаблон акта с плейсхолдерами, файл xls со списком выполняемых работ, датами, материалами. Скрипт пробегался по строках таблички и генерировал кучу актов.
FruTb
16.09.2023 16:14+1На винде есть павершелл. Вроде стандартные bash команды оно уже тоже умеет. Это про первые скрипты.
А про поиск и замену - стоить помнить что xlsx/docx это тупо zip архивы с пачкой xml и бинарных картинок внутри.
danjahjah
16.09.2023 16:14+3Баш и павершелл это, конечно, круто, но, имхо, питон проще, удобнее и универсальнее для подобных задачек. Особенно для человека чья проф деятельность не связана напрямую с программированием.
NAI
16.09.2023 16:14+1Ага, есть тут у меня REST-клиент написанный на PS и вроде бы все хорошо в 90% случаев, НО раз в 10-20 загрузок шелл виснет и пока не нажмёшь Enter ничего не происходит -> сессия отваливается -> загрузка обрывается -> теряется ~400р.
Ну и на линукс это добро перевозить сомнительное удовольствие. В общем для MVP ок, для чего-то серьезного все равно надо переписывать на python
andrewrubanov
16.09.2023 16:14Тоже работаю в ПТО. Автор конечно молодец, но для тех, кто не работал с PyScripter, мог бы накидать вкратце руководство что куда там нажимать.
По поводу автоматизации и шаблонизации актов лучше использовать "слияние" в word+excel. В скрипты не каждый сможет, а стандартные кнопочки в ворде понятны любой домохозяйке.
Robastik
16.09.2023 16:14Нативное слияние не очень заходит домохозяйкам. Есть варианты более востребованные.
andrewrubanov
16.09.2023 16:14Посмотрел описание, имеет место быть. Но для своей работы в ПТО я не увидел какого-либо преимущества перед стандартным слиянием. Зачем использовать стороннюю надстройку, когда встроенный функционал справляется с этой же задачей.
Robastik
16.09.2023 16:14Даже не знаю чем не устраивает встроенный функционал, но народ активно ищет что-то.
Kraleks Автор
16.09.2023 16:14+2Ну PyScripter интуитивно понятен, схож с остальными IDE. Я пробовал и пайчарм, и VSC, и другие. Пайчарм конечно монстр, но уж через чур, не пришло еще время для него). VSC тож понравился и одно время 2-мя пользовался. Но вот мне, как практически новичку, PyScripter полностью устроил. Наворотами я не пользуюсь в виде точек останова, трассеров, да даже отслеживания переменных. Потому моё руководство будет "никаким". Там просто можно попробовать, не понравилось - удалил и забыл).
Таким слиянием не пользовался. А ворд терпеть не могу - постоянно форматирование уплывает по неведомым законам. Да и в экселе сумму столбца узнать, что-то посчитать мгновенно можно, а ворд бестолковый. Я его б вообще запретил. Ну вот вы скажете, а как же письма писать - дык я вам отвечу: письма в экселе просто замечательные выходят. Под рукой практически все есть в непечатаемой зоне: все объекты, все договора, все адресаты и обращения к ним, все исполнители и даже шапки всех филиалов если надо. Болванка письма делается мгновенно, без необходимости как в ворде искать предыдущее письмо для заготовки. Одно только плохо - все остальные делопроизводители начинают биться в истерике и вешаться.
andrewrubanov
16.09.2023 16:14Эко вы сударь извращаяетесь! Форматирование уплывает у вас.... Я конечно встречал таких упоротых, кто готов руками землю копать, но для этого уже придумана лопата, и она называется ворд. Делопроизводители не просто так в истерике бьются, потому что таких, кто в экселе составляет текстовые документы, надо лишать премий с дальнейшим увольнением в случае непонимания.
coregabe
16.09.2023 16:14Добрый чел спасибо, я так парился с PDF да и с Excel ещё я делал с Google Cheat? всё собственно сделал но так вымотался
vserge
16.09.2023 16:14Большое спасибо за статью.
Хотел бы уточнить почему для управления всем техническим документооборотом не используете стандартные решения типа ASCON pilot-ice или CSoft CADLIB, да есть еще целый список других решений?
Kraleks Автор
16.09.2023 16:14Ну тут так скажу - работа не постоянная в плане места и одного объекта. Начали объект, построили, сдали, поехали на другой. К тому же не все время работаешь, ездишь на перевахтовки, а то и на другие объекты. Дальше - не все ПТО-шники могут/знают/умеют или хотят научится. Как стандарта по ведению документооборота в организации не принято. Я вот до сих пор борюсь с правильным наименованием файлов, шо б на него глянул и понимал, что внутри, но...не могу победить. Когда работу передаю всем легко, так как у меня все структурировано и по полочкам разложено. А как мне подхватывать чью-то работу - это ужас. По большому счету все как в фильме "Ночной продавец"))). Потому и не используем(.
omaxx
Используйте pathlib, и ваши примеры станут гораздо компактнее