Собираем данные с чеков гипермаркетов на Python

Время чтения текста – 7 минут

Update: к сожалению, информация в данном посте устарела. Рекомендуем изучить наш новый пост.

Недавно, покупая в очередной раз продукты в гипермаркете, вспомнил, что согласно ФЗ-54 любой оператор торговли, который пробивает кассовый чек, обязан отправлять данные чека в налоговую.

Чек из гипермаркета «Лента», QR-код, который нас интересует, обведен

Что это значит для нас, аналитиков данных? Что мы можем лучше узнать себя, свои потребности и получить интересные данные о собственных покупках.

Попробуем в рамках серии постов собрать небольшой прототип приложения, которое позволит строить динамику своих покупок. Итак, начнем с того, что в каждом чеке есть QR-code, если его распознать, то мы получим следующую строку:

t=20190320T2303&s=5803.00&fn=9251440300007971&i=141637&fp=4087570038&n=1

В данной строке содержатся:

t — timestamp, время, когда вы осуществили покупку
s — сумма чека
fn — кодовый номер fss, потребуется далее в запросе к API
i — номер чека, он нам потребуется далее в запросе к API
fp — параметр fiscalsign, потребуется далее в запросе к API

В рамках решения первого шага нашей задачи мы будем парсить данные чека и собирать их в pandas dataframe, используя модули Python.

Мы воспользуемся API, который отдает данные по чеку с сайта налоговой.

В начале получим аутентификационные данные:

import requests
your_phone = '+7XXXYYYZZZZ' #нужно указать ваш телефон, на него придет СМС с паролем
r = requests.post('https://proverkacheka.nalog.ru:9999/v1/mobile/users/signup', json = {"email":"email@email.com","name":"USERNAME","phone":your_phone})

В результате выполнения POST-запроса мы получим пароль в виде SMS на указанный мобильный телефон. Далее, мы будем использовать его в переменной pwd

Теперь распарсим нашу строку со значениями из QR-кода:

import re
qr_string='t=20190320T2303&s=5803.00&fn=9251440300007971&i=141637&fp=4087570038&n=1'
t=re.findall(r't=(\w+)', qr_string)[0]
s=re.findall(r's=(\w+)', qr_string)[0]
fn=re.findall(r'fn=(\w+)', qr_string)[0]
i=re.findall(r'i=(\w+)', qr_string)[0]
fp=re.findall(r'fp=(\w+)', qr_string)[0]

Будем использовать полученные переменные для извлечения данных.
В посте на Хабре довольно подробно изучены статусы ошибок при формировании запроса к API, не буду повторять эту информацию.

В начале необходимо проверить, что по данному чеку есть данные, формируем GET-запрос.

headers = {'Device-Id':'', 'Device-OS':''}
payload = {'fiscalSign': fp, 'date': t,'sum':s}
check_request=requests.get('https://proverkacheka.nalog.ru:9999/v1/ofds/*/inns/*/fss/'+fn+'/operations/1/tickets/'+i,params=payload, headers=headers,auth=(your_phone, pwd))
print(check_request.status_code)

В запросе необходимо указать headers, хотя бы пустые. В моем случае GET-запрос возвращает ошибку 406, из чего я понимаю, что такой чек находится (почему GET-запрос возвращает 406 для меня загадка, буду рад подсказкам в комментариях). Если не указать сумму или дату, то GET-запрос вернет ошибку 400 — bad request.

Переходим к самому интересному, получаем данные чека:

request_info=requests.get('https://proverkacheka.nalog.ru:9999/v1/inns/*/kkts/*/fss/'+fn+'/tickets/'+i+'?fiscalSign='+fp+'&sendToEmail=no',headers=headers,auth=(your_phone, pwd))
print(request_info.status_code)
products=request_info.json()

Должны получить код 200 (успешное выполнение GET-запроса), а в переменной products — все, что относится к нашему чеку.

Чтобы работать с этими данными воспользуемся pandas и преобразуем все в dataframe.

import pandas as pd
from datetime import datetime
my_products=pd.DataFrame(products['document']['receipt']['items'])
my_products['price']=my_products['price']/100
my_products['sum']=my_products['sum']/100
datetime_check = datetime.strptime(t, '%Y%m%dT%H%M') #((https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior отформатируем дату))
my_products['date']=datetime_check
my_products.set_index('date',inplace=True)

Теперь мы имеем рабочий pandas.dataframe с чеками, визуально это выглядит так:

«Шапка» чековых данных

Можно построить гистограмму покупок или посмотреть на все в виде «ящика с усами»:

import matplotlib.pyplot as plt
%matplotlib inline
my_products['sum'].plot(kind='hist', bins=20)
plt.show()
my_products['sum'].plot(kind='box')
plt.show()

В завершение элементарно получим описательные статистики в текстовом виде командой .describe():

my_products.describe()

Данные удобно записать в .csv-файл, чтобы в следующий раз дополнить статистику:

with open('hyper_receipts.csv', 'a') as f:
             my_products.to_csv(f, header=True)
Поделиться
Отправить
Запинить
9 комментариев
Виктор 2019

Работает ли данный способ на данный момент? Получается ли загружать чеки описанным способом? У меня работает только проверка существования чека, но при попытке загрузить чек запрос возвращает 202 без сообщений и любого содержимого.

Николай Валиотти 2019

Да, способ работает, только что проверил.

А что отдает данная команда у вас: print(check_request.status_code)?

Виктор 2019

Вчера таки смог получить чеки. Видимо были какие то то трудности на стороне ФНС.

«А что отдает данная команда у вас: print(check_request.status_code)?»
Запрос существования чека возвращает 204, использую C# реализацию автора статьи на хабре, на которую вы ссылаетесь.

Николай Валиотти 2019

Хорошо, что все получилось!

Mihail Costa 2019

Оба принта выдают 406, что не так?

Николай Валиотти 2019

Вроде бы все так, почему выдает 406-ую ошибку мне так и не удалось разобраться (об этом пишу в тексте).

Пробовали этот код далее?

request_info=requests.get('https://proverkacheka.nalog.ru:9999/v1/inns/*/kkts/*/fss/'+fn+'/tickets/'+i+'?fiscalSign='+fp+'&sendToEmail=no',headers=headers,auth=(your_phone, pwd))
print(request_info.status_code)
products=request_info.json()
Maslennikov Valentin 2020

406 возвращает потому что сумма в вашем примере должна быть передана как 580300, то есть копейки в одну строку с рублями

Николай Валиотти 2020

О, спасибо большое за комментарий!

etovityusha_hs 2020

При проверке наличия возвращает 204 (если добавить копейки в строку с рублями), но при запросе данных с чека возвращает 406 и непонятно что менять

1 2020

там обновилось апи сегодня

POST https://irkkt-mobile.nalog.ru:8888/v2/ticket HTTP/1.1
Host: irkkt-mobile.nalog.ru:8888
Accept: */*
Device-OS: iOS
Device-Id: B2462521-6CF4-4E1A-8AB2-477EF42CDB66
If-None-Match: W/«39-v6LLCdIxNMpvWBhxOORTQGEZ2m8»
clientVersion: 2.8.2
Accept-Language: ru-RU;q=1
Accept-Encoding: gzip, deflate, br
sessionId: 5f1c54aed67b86d2dc6543ef:47044a6e-0956-41ef-912f-1c8a4f904451
User-Agent: billchecker/2.8.2 (iPhone; iOS 13.5.1; Scale/3.00)
Content-Length: 81
Connection: keep-alive
Content-Type: application/json

{«qr»:«t=20200810T131800&s=346.77&fn=92804123123048&i=90192&fp=28121231230891&n=1»}

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 57
ETag: W/«39-yMpfNJI5YltqpYIDvwSbW3LSdTQ»
Date: Wed, 12 Aug 2020 04:43:34 GMT
Connection: keep-alive

{«kind»:«kkt»,«id»:«5f3373761231231230383f5»,«status»:0}

GET https://irkkt-mobile.nalog.ru:8888/v2/tickets/5f337376d38b52fdf00383f5 HTTP/1.1
Host: irkkt-mobile.nalog.ru:8888
sessionId: 5f1c54aed67b86d2dc6543ef:47044a6e-0956-41ef-912f-1c8a4f904451
Device-OS: iOS
clientVersion: 2.8.2
Device-Id: B2462521-6CF4-4E1A-8AB2-477EF42CDB66
Accept: */*
User-Agent: billchecker/2.8.2 (iPhone; iOS 13.5.1; Scale/3.00)
Accept-Language: ru-RU;q=1
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 1744
ETag: W/«6d0-PNrWgcN+N3tbhG4U9VKmouk52v0»
Date: Wed, 12 Aug 2020 04:43:35 GMT
Connection: keep-alive

данные чека

Николай Валиотти 2020

Спасибо большое за комментарий. Проверю работоспособность и обновлю статью!

update: в итоге этой информации не хватило, чтобы собрать данные, но написали отдельный объект для получения нужных данных: http://leftjoin.ru/all/nalog-ru-client/

Антон 2020

sessionId только не понятно откуда взять

Андрей Петров 2020

Приветствую, коллеги. Аналогично столкнулись с изменением api налоговой. Сидели на нем. В поиске альтернативы обнаружили такой сервис: проверка чека http://proverkacheka.com/ Есть свое api (предоставляют по обращению), распознает QR-коды. На ура распознали и вернули все запрашиваемые чеки. Как вариант — рекомендую.

Nikolay Valiotti 2020

Коллеги, к сожалению, данный метод устарел, информация в комментариях ведет на внешние ресурсы.
Новый метод, позволяющий получать данные, описан в новой заметке: http://leftjoin.ru/all/nalog-ru-client/

Популярное