Парсинг платёжного поручения 1С
коллеги.
Мне нужно распарсить платёжное поручение, выгруженное из 1С, которое имеет следующий вид:
Документ=Платежное поручение
СекцияДокумент=Платежное поручение
Номер=1
Дата=16.05.2022
Сумма=888.88
ПлательщикСчет=03111111111111111111
Плательщик=ИНН 9777777777 ООО "Рога&Копыта"
ПлательщикИНН=9777777777
Плательщик1=ООО "Рога&Копыта"
ПлательщикРасчСчет=03111111111111111111
ПлательщикБанк1=УФК ПО Г. МОСКВЕ
ПлательщикБанк2=г. Москва
ПлательщикБИК=004444444
ПлательщикКорсчет=40101111111111111111
ПолучательСчет=40222222222222222222
Получатель=ИНН 000000000000 Иванов Иван Иванович
ПолучательИНН=000000000000
Получатель1=Иванов Иван Иванович
ПолучательРасчСчет=40222222222222222222
ПолучательБанк1=ПАО СБЕРБАНК
ПолучательБанк2=г. Москва
ПолучательБИК=044525225
ПолучательКорсчет=30101111111111111111
КодНазПлатежа=1
ВидОплаты=01
ПлательщикКПП=772222222
Очередность=3
КонецДокумента
СекцияДокумент=Платежное поручение
Номер=2
Дата=16.05.2022
Сумма=7777.77
ПлательщикСчет=03111111111111111111
Плательщик=ИНН 9777777777 ООО "Рога&Копыта"
ПлательщикИНН=9777777777
Плательщик1=ООО "Рога&Копыта"
ПлательщикРасчСчет=03111111111111111111
ПлательщикБанк1=УФК ПО Г. МОСКВЕ
ПлательщикБанк2=г. Москва
ПлательщикБИК=004444444
ПлательщикКорсчет=40101111111111111111
ПолучательСчет=40333333333333333333
Получатель=ИНН 000000000000 Петров Петр Петрович
ПолучательИНН=000000000000
Получатель1=Петров Петр Петрович
ПолучательРасчСчет=40333333333333333333
ПолучательБанк1=ПАО СБЕРБАНК
ПолучательБанк2=г. Москва
ПолучательБИК=044525225
ПолучательКорсчет=30102222222222222222
КодНазПлатежа=1
ВидОплаты=01
ПлательщикКПП=772222222
Очередность=3
КонецДокумента
КонецФайла
Реализовал парсинг так:
def collect_pay_info(file) -> list:
result = []
with open(file, 'r', encoding='Windows-1251') as f:
tmp = f.readline().strip()
while 'КонецФайла' not in tmp:
data = {'docNum': '',
'docDate': datetime.now().strftime('%Y-%m-%d'),
'paySum': '',
'sumNds': '0.00',
'recip_Name': '',
'recip_CheckAcc': '',
'recip_BIK': '',
'recip_BankName': '',
'recip_CorrAcc': ''}
while 'КонецДокумента' not in tmp:
if 'ПолучательСчет=' in tmp:
data['recip_CheckAcc'] = tmp.strip().split('=')[1]
if 'Получатель1=' in tmp:
data['recip_Name'] = tmp.strip().split('=')[1]
if 'ПолучательРасчСчет=' in tmp:
data['recip_CheckAcc'] = tmp.strip().split('=')[1]
if 'ПолучательБанк' in tmp:
data['recip_BankName'] = tmp.strip().split('=')[1]
if 'ПолучательБИК=' in tmp:
data['recip_BIK'] = tmp.strip().split('=')[1]
if 'ПолучательКорсчет=' in tmp:
data['recip_CorrAcc'] = tmp.strip().split('=')[1]
if 'Сумма=' in tmp:
data['paySum'] = tmp.strip().split('=')[1]
tmp = f.readline().strip()
result.append(data)
tmp = f.readline().strip()
return result
Понимаю, что можно реализовать гораздо эффективнее, но не хватает опыта. Подскажите, пожалуйста, как можно улучшить мой код. Заранее благодарен.
Большое спасибо всем за помощь! Результат:
from datetime import datetime
def collect_pay_info(file):
translators = {
'Дата': 'docDate',
'ПолучательСчет': 'recip_CheckAcc',
'Получатель1': 'recip_Name',
'ПолучательРасчСчет': 'recip_CheckAcc',
'ПолучательБанк1': 'recip_BankName',
'ПолучательБИК': 'recip_BIK',
'ПолучательКорсчет': 'recip_CorrAcc',
'Сумма': 'paySum',
}
formaters = {
'Дата': datetime.now().strftime('%Y-%m-%d'),
}
docpack = []
curdoc = {}
text = open(file, 'r', encoding='Windows-1251').read()
for line in text.splitlines()[1:]:
if line.startswith('КонецДокумента'):
docpack.append(curdoc)
curdoc = {}
continue
if line.startswith('КонецФайла'):
break
key, value = line.split('=', maxsplit=1)
value = formaters.get(key, value)
key = translators.get(key, None)
if key:
curdoc[key] = value
return docpack
Ответы (3 шт):
Вопрос эффективности спорный, пока нет количественных ограничений по каким либо параметрам. Поэтому ответить на вопрос: "Как можно реализовать гораздо эффективнее?", без четких рамок эффективности не получится.
Я представлю свой вариант, не претендующий на "самый эффективный", но я так вижу.
Работать буду, только со словарями. Их будет 2
- Прочитаный из файла, который не содержит двух сущностей
КонецДокумента,КонецФайла.{ "Документ": "Платежное поручение", "СекцияДокумент": "Платежное поручение", "Номер": "2", "Дата": "16.05.2022", ...... } - Словарь сопоставления, некий шаблон, который содержит сущности
ключ=ключНадоЗаменить,значение=ключЗамена. Не буду писать все сущности, только для примера, остальное сами{ "ПолучательСчет": "recip_CheckAcc", "Получатель1": "recip_Name", "ПолучательРасчСчет": "recip_CheckAcc", "ПолучательБанк1": "recip_BankName", ...... }
Ну, чтож, приступим
# словарь шаблон
pattern_dict = {
"ПолучательСчет": "recip_CheckAcc",
"Получатель1": "recip_Name",
"ПолучательРасчСчет": "recip_CheckAcc",
"ПолучательБанк1": "recip_BankName",
}
# читаем из файла и создаем словарь
with open("t.txt", encoding="utf8") as file:
doc_list = file.readlines()
result = []
doc_dict = {}
for item in doc_list:
if "=" in item:
key, value = item.split("=")
doc_dict[key] = value.strip()
elif "КонецДокумента":
result.append(doc_dict)
# а это код замены ключей
for item_dict in result:
for item in list(item_dict):
if item in pattern_dict:
item_dict[pattern_dict[item]] = item_dict.pop(item)
На выходе будем иметь что-то такое
{
.....
"recip_CheckAcc": "40333333333333333333",
"recip_Name": "Петров Петр Петрович",
"recip_BankName": "ПАО СБЕРБАНК",
.....
}
В чем резон такого подхода? Вы можете четко определять замену вне вашего кода. У вас есть словарь с заменами и все, что нужно это, например,добавить в него сущность, которая вам нужна
{
"ПолучательСчет": "recip_CheckAcc",
"Получатель1": "recip_Name",
"Получатель2": "recip_Name",
"Получатель": "recip_Name",
"ПолучательРасчСчет": "recip_CheckAcc",
"ПолучательБанк1": "recip_BankName1",
"ПолучательБанк": "recip_BankName",
}
text = open(file, 'r', encoding='Windows-1251').read()
Переводы ключей можно лучше положить в отдельный словарь чтоб не делать простыню из if, тоже самое с форматированием некоторых полей (если оно нужно):
translators = {
"Номер":"Number"
}
import datetime
import decimal
formaters = {
"Дата": lambda value: datetime.datetime.strptime(value,"%d.%m.%Y").date(),
"Сумма": lambda value: decimal.Decimal(value)
}
Чтоб не вкладывать циклы друг в друга, беру переменную скидываю её в результат по метке конца документа
docpack = [ ]
curdoc = {}
for l in text.splitlines():
print(repr(l))
if l.startswith("КонецДокумента"):
docpack.append(curdoc)
curdoc = {}
continue
if l.startswith("КонецФайла"):
break
key, value = l.split("=", maxsplit=1)
value = formaters.get(key, str)(value)
key = translators.get(key, key)
curdoc[key] = value
import pprint
pprint.pprint(docpack)
Если ненужные ключи нужно исключить, то:
key = translators.get(key, None)
if key: curdoc[key] = value
Написал на основе данных вопроса свой парсер с выгрузкой в xslx и построение графика со средними оборотами за месяц, подробно оснастил комментариями: https://github.com/Woldemarus/1cExchangeParserToXLSWithGraph