Парсинг платёжного поручения 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 шт):

Автор решения: Dmitry

Вопрос эффективности спорный, пока нет количественных ограничений по каким либо параметрам. Поэтому ответить на вопрос: "Как можно реализовать гораздо эффективнее?", без четких рамок эффективности не получится.

Я представлю свой вариант, не претендующий на "самый эффективный", но я так вижу.

Работать буду, только со словарями. Их будет 2

  1. Прочитаный из файла, который не содержит двух сущностей КонецДокумента, КонецФайла.
    {
    "Документ": "Платежное поручение",
    "СекцияДокумент": "Платежное поручение",
    "Номер": "2",
    "Дата": "16.05.2022",
    ......
    }
    
  2. Словарь сопоставления, некий шаблон, который содержит сущности ключ=ключНадоЗаменить, значение=ключЗамена. Не буду писать все сущности, только для примера, остальное сами
    {
    "ПолучательСчет": "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",
}
→ Ссылка
Автор решения: eri
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
→ Ссылка
Автор решения: wironxxx

Написал на основе данных вопроса свой парсер с выгрузкой в xslx и построение графика со средними оборотами за месяц, подробно оснастил комментариями: https://github.com/Woldemarus/1cExchangeParserToXLSWithGraph

→ Ссылка