Парсинг данных, это всегда увлекательная и полезная штука, ведь получив данные мы можем выполнить различные виды анализа или использовать полученные данные в нужных приложениях, будь то телеграмм-бот или иное приложение. В марте этого года на сайте фриланса fl.ru мне попалось задание, которое показалось интересным и, хоть у меня и нет там премиум-аккаунта, я решил попробовать выполнить это задание для себя, просто, чтобы попрактиковаться. Благо, параметры задания были доступны для всех. Конечно же, для сбора необходимых данных я решил использовать Python.
Сбор данных и анализ запросов
К сожалению, так как прошло уже довольно много времени, страница с заданием недоступна, а я просто не догадался сохранить документ с ним. Тем не менее, так как я задание это выполнял, суть его я помню. И, даже если оно несколько различается в деталях фильтров, суть от этого не меняется.
Итак, в задании был указан сайт
Таблица на данном сайте просто гигантская. Да и данных достаточно много. Вот, для примера, то, что влезло на страницу браузера.
После того, как были выставлены фильтры и нажата кнопка «Применить», отфильтровалось 129 751 записей. Что же, теперь настало время заглянуть в «Инструменты разработчика». В Google Chrome щелкаем правой кнопкой по странице и выбираем пункт «Просмотреть код».
Выбрав таблицу мы видим, что она отображается во вкладке «Элементы», но находиться в <iframe>, что не есть особо хорошо.
Что же, ладно. Уже неплохо. Давайте попробуем получить код страницы и посмотреть, что же прилетает нам в запросе. Для этого напишем небольшой код:
После его выполнения посмотрим на выведенные в терминал результаты. Что мы имеем? Да практически ничего.
Сама таблица, а вернее элемент <iframe> в запросе присутствует. Вот только данных нет никаких. Это печально. Нужно копать глубже. Можно было бы попробовать использовать selenium, но, мы еще не проверили все варианты, а значит, пока еще рано.
Идем во вкладку «Network» инструментов разработчика и фильтруем запросы по «Fetch/XHR». Для того, чтобы понять, что вообще и откуда прилетает, удалим все, что есть в фильтре и обновим страницу. Картинку показывать не буду, но скажу, запросов там просто тьма. Тем более, что при обновлении слетели фильтры, которые были выставлены по недрам. Удалим все, что прилетело в запросах при обновлении страницы. После чего выставим фильтры заново. После того, как мы выставили фильтры и нажали кнопку «Применить», видим, что выполнилось четыре запроса. Проверяем каждый из них, и видим, что по крайней мере два из них, имеют данные, которые нам необходимы, да еще и в формате JSON.
Единственное, что не совсем хорошо, это то, что в запросе присутствуют только первые 20 записей. Но, это все из-за того, что в таблице есть фильтр, выставив который мы можем выводить от 20 до 1000 записей.
Так как мы все же не будем просматривать записи вручную, фильтр можно выставить на максималку. Но сделаем мы это чуть позже.
Щелкнем по запросу правой кнопкой мыши и выберем пункт «Open in new tab», чтобы посмотреть, возможно ли получить данные обычным GET запросом.
Но увы, попытка получения данных не принесла результата. В ответе от сервера мы увидели ошибку. И в принципе это правильно, так как тип запроса не GET, а POST, то есть, необходимо передать некоторые параметры.
Значит нужно попробовать действовать иначе. Щелкаем правой кнопкой мыши по запросу, выбираем пункт «Copy -> Copy all us cURL(bash)».
Теперь, после того, как запрос скопирован, идем на сайт
В итоге мы получили довольно длинную «портянку» с кодом, основная часть кода, это параметры, которые передаются в запросе. Копируем то, что получилось, вставляем в IDE и пробуем выполнить.
Вот только допишем распечатку того, что получаем.
Однако при выполнении кода IDE ругается на SSL-сертификат. Печально, но, попробуем обойти данную проблему.
Добавляем в запрос параметр verify=False, чтобы код запроса выглядел примерно так:
и снова пробуем его выполнить. И да, теперь запрос выполняется, вот только нужных нам данных все равно в нем нет.
Копаем дальше. Так как запрос бьется на страницы, с определенным количеством записей, очистим запросы и попробуем перейти на одну из них. Посмотрим, какие данные там есть и есть ли они вообще.
И да, при переходе на вторую страницу мы получаем POST-запрос, ответом на который является JSON с данными. Попробуем его преобразовать в код и выполнить в IDE. Копируем cURL и идем на сайт для преобразования в код Python. Выполняем и… да, это то, что нужно. В ответе прилетели нужные данные в количестве 20 строк. Ну да ладно. С этим мы попробуем разобраться.
Давайте посмотрим в те данные, которые передаются в запросе. Здесь есть параметры «offset» и «limit». Первый, очевидно, означает смещение, то есть строку, с которой нужно выводить данные, а второй – количество записей на странице. Таким образом, поменяв offset на 0, а limit на 1000 мы получим первые 1000 строк. А вот для того, чтобы получить следующую порцию данных, offset нужно постепенно увеличивать на 1000.
Если мы заглянем в JSON, который прилетает в ответ на запрос, то мы увидим не только необходимые данные, но и ключ, в котором содержится полное количество записей по выставленным фильтрам.
Что же, теперь у нас есть все необходимые данные и параметры, чтобы начать сбор данных. Клиенту нужно было получить следующие данные: «Дата присвоения государственного регистрационного номера лицензии», «Целевое назначение лицензии», «Наименование участка недр…» и «Сведения о пользователе недр». Вот их и будем забирать из получаемого JSON.
Что потребуется?
Установим, если вы этого еще не сделали, библиотеку requests, с помощью которой будем выполнять запросы и получать данные. Пишем в терминале:
Собственно и все. Устанавливать ничего больше не нужно. Импортируем библиотеки, которые понадобятся в коде:
time будет необходим для небольшой задержки между выполнением запросов, json – для сохранения полученных данных, а datetime для конвертации даты в нужный формат.
Теперь нужно написать небольшую строку кода, которая подавит предупреждение от библиотеки requests о том, что мы выполняем запросы без верификации по SSL. Это не обязательно, но если можно, почему бы нет.
Заголовки, которые нужны для запросов, сделаем глобальными, чтобы к ним был доступ из любой функции. В итоге, на начальном этапе должно получиться следующее:
Получение общего количества записей по фильтрам
Двигаемся дальше. Так как у нас в JSON содержится количество записей при применении фильтров, заберем его для того, чтобы можно было выполнить по ним цикл и не прописывать его вручную. Кто знает, может быть в будущем, если использовать запрос повторно, количество записей уже будет больше или меньше и снова придется менять код. А так, мы будем получать количество записей уже независимо от того, сколько из в фильтре.
Создадим функцию get_record_count() -> int. На вход она ничего не принимает, а возвращает целое число, которое и содержит количество записей. Для начала пишем пользователю, что получаем количество записей, чтобы ему не было скучно. Затем вставляем данные json, которые будем отправлять в запросе. Здесь offset укажем 0, а limit 20, так как нам нет необходимости получать 1000 для того, чтобы забрать общее количество. Выполняем запрос с отключенной верификацией, сообщаем пользователю, сколько записей получили и возвращаем их из функции + одну запись. Это нужно будет для цикла. В json уже содержится int, потому дополнительное преобразование в данном случае не нужно.
Итерация по запросам, получение и сохранение записей
Создадим функцию get_json(). В ней мы будем выполнять запросы и забирать из ответов нужные данные, которые будем складывать в список, после чего, этот список запишем в json.
Объявим список и выполним функцию для получения общего количества записей.
Теперь запустим цикл, в котором будем итерироваться по диапазону от 0 до полученного количества записей с шагом 1000. Сообщаем пользователю на каждой итерации, где мы сейчас находимся, для того лишь, чтобы ему не было скучно, ну и чтобы он видел, что хоть что-то происходит.
В параметрах, которые мы передаем в запросе, offset установим равным i, то есть, данное значение будет подставляться из цикла, а limit выставим в 1000.
Указывать полный json я не стал, только те параметры, которые меняются. После этого выполняем запрос и получаем json с данными:
Запускаем цикл от 0 до 1000, так как в запросе 1000 строк. Заключаем последующий код в блок try –except, чтобы обработать ошибку, так как в последнем запросе записей может быть меньше. В том json, что прилетает с данными, они содержаться в списках, каждый из которых представляет данные по определенному столбцу. Поэтому, забирать будем данные из каждого списка и сводить их вместе. Данные списки лежат по пути: result – data – values и далее указываем индекс, в котором нужно искать данные. Забираем дату и конвертируем ее в нужный формат. Забираем назначение, target. Наименование участка недр – name_sector и сведения о пользователе – name. После добавляем их в список в виде словаря. Таким образом у нас должен получиться список словарей. После того, как будет обработана последняя запись, выходим из цикла по срабатыванию исключения.
После того, как будут выполнены все запросы, сообщаем пользователю, сколько данных удалось собрать в список. Сообщаем, что сохраняем данные в файл и собственно сохраняем их в json.
Запуск функции сбора данных
Ну и последнее, это функция main() и обработка условия.
Теперь запустим написанный код и посмотрим, что удалось получить. Итак, вот результат выполнения код в терминале:
И заглянем внутрь сохраненного json с собранными нами данными:
Как видим, все замечательно сохранилось. На самом деле, сохранить можно в самых разных форматах. Тут уже все зависит от того, в каком формате желает получить данные заказчик.
Вот такой вот небольшой парсер.
Спасибо за внимание. Надеюсь, что информация была вам полезна
Дисклеймер: Все данные, предоставленные в данной статье, взяты из открытых источников, не призывают к действию и являются только лишь данными для ознакомления, и изучения механизмов используемых технологий.
Сбор данных и анализ запросов
К сожалению, так как прошло уже довольно много времени, страница с заданием недоступна, а я просто не догадался сохранить документ с ним. Тем не менее, так как я задание это выполнял, суть его я помню. И, даже если оно несколько различается в деталях фильтров, суть от этого не меняется.
Итак, в задании был указан сайт
Ссылка скрыта от гостей
. Зайдя на него мы видим, что это «Государственный реестр участков недр…». Необходимо было выставить фильтры по «Вид ПИ лицензии». Все, что касается подземных вод. В данный момент таких пунктов два. А вот сколько их было, точно не скажу, может быть больше. Но, на сам по себе парсинг это не влияет, а влияет только на количество результатов запроса. Фильтры представлены на картинке ниже.Таблица на данном сайте просто гигантская. Да и данных достаточно много. Вот, для примера, то, что влезло на страницу браузера.
После того, как были выставлены фильтры и нажата кнопка «Применить», отфильтровалось 129 751 записей. Что же, теперь настало время заглянуть в «Инструменты разработчика». В Google Chrome щелкаем правой кнопкой по странице и выбираем пункт «Просмотреть код».
Выбрав таблицу мы видим, что она отображается во вкладке «Элементы», но находиться в <iframe>, что не есть особо хорошо.
Что же, ладно. Уже неплохо. Давайте попробуем получить код страницы и посмотреть, что же прилетает нам в запросе. Для этого напишем небольшой код:
Python:
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 '
'Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,'
'application/signed-exchange;v=b3;q=0.9 '
}
url = "https://rfgf.ru/ReestrLic/"
req = requests.get(url=url, headers=headers)
print(req.text)
После его выполнения посмотрим на выведенные в терминал результаты. Что мы имеем? Да практически ничего.
Сама таблица, а вернее элемент <iframe> в запросе присутствует. Вот только данных нет никаких. Это печально. Нужно копать глубже. Можно было бы попробовать использовать selenium, но, мы еще не проверили все варианты, а значит, пока еще рано.
Идем во вкладку «Network» инструментов разработчика и фильтруем запросы по «Fetch/XHR». Для того, чтобы понять, что вообще и откуда прилетает, удалим все, что есть в фильтре и обновим страницу. Картинку показывать не буду, но скажу, запросов там просто тьма. Тем более, что при обновлении слетели фильтры, которые были выставлены по недрам. Удалим все, что прилетело в запросах при обновлении страницы. После чего выставим фильтры заново. После того, как мы выставили фильтры и нажали кнопку «Применить», видим, что выполнилось четыре запроса. Проверяем каждый из них, и видим, что по крайней мере два из них, имеют данные, которые нам необходимы, да еще и в формате JSON.
Единственное, что не совсем хорошо, это то, что в запросе присутствуют только первые 20 записей. Но, это все из-за того, что в таблице есть фильтр, выставив который мы можем выводить от 20 до 1000 записей.
Так как мы все же не будем просматривать записи вручную, фильтр можно выставить на максималку. Но сделаем мы это чуть позже.
Щелкнем по запросу правой кнопкой мыши и выберем пункт «Open in new tab», чтобы посмотреть, возможно ли получить данные обычным GET запросом.
Но увы, попытка получения данных не принесла результата. В ответе от сервера мы увидели ошибку. И в принципе это правильно, так как тип запроса не GET, а POST, то есть, необходимо передать некоторые параметры.
Значит нужно попробовать действовать иначе. Щелкаем правой кнопкой мыши по запросу, выбираем пункт «Copy -> Copy all us cURL(bash)».
Теперь, после того, как запрос скопирован, идем на сайт
Ссылка скрыта от гостей
, который переводит данные запросы в код на нескольких языках программирования, один из которых Python. Выбираем тип запроса «POST» и вставляем скопированный запрос в поле ввода.В итоге мы получили довольно длинную «портянку» с кодом, основная часть кода, это параметры, которые передаются в запросе. Копируем то, что получилось, вставляем в IDE и пробуем выполнить.
Python:
import requests
headers = {
'authority': 'bi.rfgf.ru',
'accept': 'application/json, text/javascript, */*; q=0.01',
'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
'authorization': 'Bearer NoAuth',
# Already added when you pass json=
# 'content-type': 'application/json',
'origin': 'https://bi.rfgf.ru',
'referer': 'https://bi.rfgf.ru/viewer/public?dashboardGuid=ae176f70a6df4e81ba10247f44fb1191&showNav=false&fit=false',
'sec-ch-ua': '"Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36',
'x-requested-with': 'XMLHttpRequest',
}
json_data = {
'QueryType': 'GetOlapData+Query',
'OlapSettings': {
'olapGroups': [
{
'measures': [
{
'id': 'id_lc',
'sort': 0,
'aggregator': 0,
},
],
'attributeGroupsInRows': [],
'attributeGroupsInColumns': [],
'filters': [
[
{
'selectedFilterValues': {
'Values': [
[
'В - Подземные воды (за исключением подземных минеральных вод)',
],
[
'М - Подземные минеральные воды, лечебные грязи, специфические минеральные ресурсы (рапа лиманов и озер, сапропель и другие)',
],
],
'Range': {
'Min': None,
'Max': None,
},
'UseRange': False,
'UseExcluding': False,
},
'attribute': {
'id': 'S_rasshifrovkoi',
'dimensionOrDimensionRoleId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
},
},
],
],
'dateRangeFilters': [],
'calculatedMeasures': [],
'id': 'a0c78f5a0e8a4dc69b1f384ab49caa99',
'measureGroupId': 'R_Reestr_litsenzii',
'rowTotal': False,
'columnTotal': False,
'showAllDimensionValues': False,
},
],
'databaseId': 'Main',
'limit': {
'rowsLimit': 100,
'columnsLimit': 10,
},
},
'CalculationQueries': [],
'AdditionalLogs': {
'widgetGuid': 'cd42f192ca53498dae511d0638a81829',
'dashboardGuid': 'ae176f70a6df4e81ba10247f44fb1191',
'sheetGuid': '23760aa1c3844e9686946dfac2d0e8a0',
},
'WidgetGuid': 'cd42f192ca53498dae511d0638a81829',
}
response = requests.post('https://bi.rfgf.ru/corelogic/api/query', headers=headers, json=json_data)
Вот только допишем распечатку того, что получаем.
Python:
print(response.json())
Однако при выполнении кода IDE ругается на SSL-сертификат. Печально, но, попробуем обойти данную проблему.
Добавляем в запрос параметр verify=False, чтобы код запроса выглядел примерно так:
Python:
response = requests.post('https://bi.rfgf.ru/corelogic/api/query', headers=headers, json=json_data, verify=False)
и снова пробуем его выполнить. И да, теперь запрос выполняется, вот только нужных нам данных все равно в нем нет.
Копаем дальше. Так как запрос бьется на страницы, с определенным количеством записей, очистим запросы и попробуем перейти на одну из них. Посмотрим, какие данные там есть и есть ли они вообще.
И да, при переходе на вторую страницу мы получаем POST-запрос, ответом на который является JSON с данными. Попробуем его преобразовать в код и выполнить в IDE. Копируем cURL и идем на сайт для преобразования в код Python. Выполняем и… да, это то, что нужно. В ответе прилетели нужные данные в количестве 20 строк. Ну да ладно. С этим мы попробуем разобраться.
Давайте посмотрим в те данные, которые передаются в запросе. Здесь есть параметры «offset» и «limit». Первый, очевидно, означает смещение, то есть строку, с которой нужно выводить данные, а второй – количество записей на странице. Таким образом, поменяв offset на 0, а limit на 1000 мы получим первые 1000 строк. А вот для того, чтобы получить следующую порцию данных, offset нужно постепенно увеличивать на 1000.
Если мы заглянем в JSON, который прилетает в ответ на запрос, то мы увидим не только необходимые данные, но и ключ, в котором содержится полное количество записей по выставленным фильтрам.
Что же, теперь у нас есть все необходимые данные и параметры, чтобы начать сбор данных. Клиенту нужно было получить следующие данные: «Дата присвоения государственного регистрационного номера лицензии», «Целевое назначение лицензии», «Наименование участка недр…» и «Сведения о пользователе недр». Вот их и будем забирать из получаемого JSON.
Что потребуется?
Установим, если вы этого еще не сделали, библиотеку requests, с помощью которой будем выполнять запросы и получать данные. Пишем в терминале:
pip install requests
Собственно и все. Устанавливать ничего больше не нужно. Импортируем библиотеки, которые понадобятся в коде:
Python:
import time
import json
from datetime import datetime as dt
import requests
time будет необходим для небольшой задержки между выполнением запросов, json – для сохранения полученных данных, а datetime для конвертации даты в нужный формат.
Теперь нужно написать небольшую строку кода, которая подавит предупреждение от библиотеки requests о том, что мы выполняем запросы без верификации по SSL. Это не обязательно, но если можно, почему бы нет.
Python:
requests.packages.urllib3.disable_warnings()
Заголовки, которые нужны для запросов, сделаем глобальными, чтобы к ним был доступ из любой функции. В итоге, на начальном этапе должно получиться следующее:
Python:
import time
import json
from datetime import datetime as dt
import requests
requests.packages.urllib3.disable_warnings()
headers = {
'authority': 'bi.rfgf.ru',
'accept': 'application/json, text/javascript, */*; q=0.01',
'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
'authorization': 'Bearer NoAuth',
'origin': 'https://bi.rfgf.ru',
'referer': 'https://bi.rfgf.ru/viewer/public?dashboardGuid=ae176f70a6df4e81ba10247f44fb1191&showNav=false&fit=false',
'sec-ch-ua': '"Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 '
'Safari/537.36',
'x-requested-with': 'XMLHttpRequest',
}
Получение общего количества записей по фильтрам
Двигаемся дальше. Так как у нас в JSON содержится количество записей при применении фильтров, заберем его для того, чтобы можно было выполнить по ним цикл и не прописывать его вручную. Кто знает, может быть в будущем, если использовать запрос повторно, количество записей уже будет больше или меньше и снова придется менять код. А так, мы будем получать количество записей уже независимо от того, сколько из в фильтре.
Создадим функцию get_record_count() -> int. На вход она ничего не принимает, а возвращает целое число, которое и содержит количество записей. Для начала пишем пользователю, что получаем количество записей, чтобы ему не было скучно. Затем вставляем данные json, которые будем отправлять в запросе. Здесь offset укажем 0, а limit 20, так как нам нет необходимости получать 1000 для того, чтобы забрать общее количество. Выполняем запрос с отключенной верификацией, сообщаем пользователю, сколько записей получили и возвращаем их из функции + одну запись. Это нужно будет для цикла. В json уже содержится int, потому дополнительное преобразование в данном случае не нужно.
Python:
def get_record_count() -> int:
print("Получаю количество записей")
json_data = {
'QueryType': 'GetRawOlapData+Query',
'RawOlapSettings': {
'databaseId': 'Main',
'measureGroup': {
'columns': [
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'c64a19c50c394605981e3ebe87918ae1',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'rcart_link',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '7c57c1b9e5b74322b5976eff809e3c20',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'lc_number_full',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'b146527321b840668975b16507194d33',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'has_doc',
},
{
'type': 'RawOlapDimensionRoleAttributeColumnDto',
'kind': 'RawOlapDimensionRoleAttributeColumnDto',
'guid': '3766824573874611bc87322d41091f2f',
'dimensionRoleId': {
'type': 'DimensionRoleIdDto',
'kind': 'DimensionRoleIdDto',
'value': 'date_reg_start',
},
'attributeId': 'DATE',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'dd98c164c5234359b58909f9b96f10da',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'purpose',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '0c54fe57b031469686122724a10fca61',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'lc_cat_type_abr_with_pi',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '7514ae69824d42fb999a6630fc6b902d',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'area_nedr_name',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'f5cd97e6be364c82a48e8bad88682e32',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'region_name_sf',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '15dfc1c77ad34713acd832b0b28cba33',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'koord',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '89880068c9514a94b74dd1ac6326c4ee',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'stat_uch',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '55928c0365254e989776b22c2ad91133',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'company_name',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '30e8b6fcda754620b37a8808eaad9822',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'organ',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '1565bb98bd09479dbb14db2a63d04ad6',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'doc_lic',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '8b0d834417a14859940ba47a37420323',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'doc_dopoln',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '86006ff857ee48c0b94c27a5c82743e4',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'lc_number_full_new',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '24644a616cba4dcba8369c584ba1cfea',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'annul_doc',
},
{
'type': 'RawOlapDimensionRoleAttributeColumnDto',
'kind': 'RawOlapDimensionRoleAttributeColumnDto',
'guid': '17bd7207b3b14433803e97480f5dc2a4',
'dimensionRoleId': {
'type': 'DimensionRoleIdDto',
'kind': 'DimensionRoleIdDto',
'value': 'annul_date',
},
'attributeId': 'DATE',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '930bb47ba3b242b2b91e81d457692a54',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'pause_dates',
},
{
'type': 'RawOlapDimensionRoleAttributeColumnDto',
'kind': 'RawOlapDimensionRoleAttributeColumnDto',
'guid': 'a46c3241eeae4c5e9d81d71f84c03eab',
'dimensionRoleId': {
'type': 'DimensionRoleIdDto',
'kind': 'DimensionRoleIdDto',
'value': 'date_reg_end',
},
'attributeId': 'DATE',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'd02af800021e4f559a1120bae16e692f',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'num_rann_lic',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '1fb7dfc7907545899a8bf78dc7c655b2',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'asln_link',
},
],
'filters': [
[
{
'selectedFilterValues': [
'В - Подземные воды (за исключением подземных минеральных вод)',
'М - Подземные минеральные воды, лечебные грязи, специфические минеральные '
'ресурсы (рапа '
'лиманов и озер, сапропель и другие)',
],
'attributeId': 'S_rasshifrovkoi',
'dimensionOrDimensionRoleId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'useExcluding': False,
},
],
],
'times': [],
'id': 'R_Reestr_litsenzii',
},
'lazyLoadOptions': {
'columnSorts': [
{
'columnindex': 3,
'order': 1,
},
],
'offset': 0,
'limit': 20,
},
},
'CalculationQueries': [],
'AdditionalLogs': {
'widgetGuid': '245d52ef1fdc474a8795fc37a8d03d02',
'dashboardGuid': 'ae176f70a6df4e81ba10247f44fb1191',
'sheetGuid': '23760aa1c3844e9686946dfac2d0e8a0',
},
'WidgetGuid': '245d52ef1fdc474a8795fc37a8d03d02',
}
resp = requests.post('https://bi.rfgf.ru/corelogic/api/query', headers=headers, json=json_data, verify=False).json()
print(f"Записей в таблице с данной выборкой: {resp['result']['recordCount']}\n")
return resp['result']['recordCount']+1
Итерация по запросам, получение и сохранение записей
Создадим функцию get_json(). В ней мы будем выполнять запросы и забирать из ответов нужные данные, которые будем складывать в список, после чего, этот список запишем в json.
Объявим список и выполним функцию для получения общего количества записей.
Python:
res_dict = []
count = get_record_count()
Теперь запустим цикл, в котором будем итерироваться по диапазону от 0 до полученного количества записей с шагом 1000. Сообщаем пользователю на каждой итерации, где мы сейчас находимся, для того лишь, чтобы ему не было скучно, ну и чтобы он видел, что хоть что-то происходит.
Python:
for i in range(0, count, 1000):
print(f'\rПолучаю записи. Смещение: {i}/{count-1}', end="")
В параметрах, которые мы передаем в запросе, offset установим равным i, то есть, данное значение будет подставляться из цикла, а limit выставим в 1000.
json_data = {… 'offset': i, 'limit': 1000, …}
Указывать полный json я не стал, только те параметры, которые меняются. После этого выполняем запрос и получаем json с данными:
Python:
resp = requests.post('https://bi.rfgf.ru/corelogic/api/query', headers=headers, json=json_data, verify=False).\
json()
Запускаем цикл от 0 до 1000, так как в запросе 1000 строк. Заключаем последующий код в блок try –except, чтобы обработать ошибку, так как в последнем запросе записей может быть меньше. В том json, что прилетает с данными, они содержаться в списках, каждый из которых представляет данные по определенному столбцу. Поэтому, забирать будем данные из каждого списка и сводить их вместе. Данные списки лежат по пути: result – data – values и далее указываем индекс, в котором нужно искать данные. Забираем дату и конвертируем ее в нужный формат. Забираем назначение, target. Наименование участка недр – name_sector и сведения о пользователе – name. После добавляем их в список в виде словаря. Таким образом у нас должен получиться список словарей. После того, как будет обработана последняя запись, выходим из цикла по срабатыванию исключения.
Python:
for it in range(0, 1000):
try:
date = dt.strptime(resp['result']['data']['values'][3][it], "%Y-%m-%d").strftime("%d.%m.%Y")
target = resp['result']['data']['values'][4][it]
name_sector = resp['result']['data']['values'][6][it]
name = resp['result']['data']['values'][10][it]
res_dict.append({
"date": date,
"target": target,
"name_sector": name_sector,
"name": name
})
except IndexError:
break
После того, как будут выполнены все запросы, сообщаем пользователю, сколько данных удалось собрать в список. Сообщаем, что сохраняем данные в файл и собственно сохраняем их в json.
Python:
print(f"\n\nПолучено записей: {len(res_dict)}")
print('Сохраняю данные в файл "result_ReestrLic.json"')
with open('result_ReestrLic.json', 'w', encoding='utf-8') as file:
json.dump(res_dict, file, indent=4, ensure_ascii=False)
print("Данные успешно сохранены")
Запуск функции сбора данных
Ну и последнее, это функция main() и обработка условия.
Python:
def main():
get_json()
if __name__ == "__main__":
main()
Теперь запустим написанный код и посмотрим, что удалось получить. Итак, вот результат выполнения код в терминале:
И заглянем внутрь сохраненного json с собранными нами данными:
Как видим, все замечательно сохранилось. На самом деле, сохранить можно в самых разных форматах. Тут уже все зависит от того, в каком формате желает получить данные заказчик.
Вот такой вот небольшой парсер.
Python:
import time
import json
from datetime import datetime as dt
import requests
requests.packages.urllib3.disable_warnings()
headers = {
'authority': 'bi.rfgf.ru',
'accept': 'application/json, text/javascript, */*; q=0.01',
'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
'authorization': 'Bearer NoAuth',
'origin': 'https://bi.rfgf.ru',
'referer': 'https://bi.rfgf.ru/viewer/public?dashboardGuid=ae176f70a6df4e81ba10247f44fb1191&showNav=false&fit=false',
'sec-ch-ua': '"Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 '
'Safari/537.36',
'x-requested-with': 'XMLHttpRequest',
}
def get_record_count() -> int:
print("Получаю количество записей")
json_data = {
'QueryType': 'GetRawOlapData+Query',
'RawOlapSettings': {
'databaseId': 'Main',
'measureGroup': {
'columns': [
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'c64a19c50c394605981e3ebe87918ae1',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'rcart_link',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '7c57c1b9e5b74322b5976eff809e3c20',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'lc_number_full',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'b146527321b840668975b16507194d33',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'has_doc',
},
{
'type': 'RawOlapDimensionRoleAttributeColumnDto',
'kind': 'RawOlapDimensionRoleAttributeColumnDto',
'guid': '3766824573874611bc87322d41091f2f',
'dimensionRoleId': {
'type': 'DimensionRoleIdDto',
'kind': 'DimensionRoleIdDto',
'value': 'date_reg_start',
},
'attributeId': 'DATE',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'dd98c164c5234359b58909f9b96f10da',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'purpose',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '0c54fe57b031469686122724a10fca61',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'lc_cat_type_abr_with_pi',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '7514ae69824d42fb999a6630fc6b902d',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'area_nedr_name',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'f5cd97e6be364c82a48e8bad88682e32',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'region_name_sf',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '15dfc1c77ad34713acd832b0b28cba33',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'koord',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '89880068c9514a94b74dd1ac6326c4ee',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'stat_uch',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '55928c0365254e989776b22c2ad91133',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'company_name',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '30e8b6fcda754620b37a8808eaad9822',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'organ',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '1565bb98bd09479dbb14db2a63d04ad6',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'doc_lic',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '8b0d834417a14859940ba47a37420323',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'doc_dopoln',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '86006ff857ee48c0b94c27a5c82743e4',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'lc_number_full_new',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '24644a616cba4dcba8369c584ba1cfea',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'annul_doc',
},
{
'type': 'RawOlapDimensionRoleAttributeColumnDto',
'kind': 'RawOlapDimensionRoleAttributeColumnDto',
'guid': '17bd7207b3b14433803e97480f5dc2a4',
'dimensionRoleId': {
'type': 'DimensionRoleIdDto',
'kind': 'DimensionRoleIdDto',
'value': 'annul_date',
},
'attributeId': 'DATE',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '930bb47ba3b242b2b91e81d457692a54',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'pause_dates',
},
{
'type': 'RawOlapDimensionRoleAttributeColumnDto',
'kind': 'RawOlapDimensionRoleAttributeColumnDto',
'guid': 'a46c3241eeae4c5e9d81d71f84c03eab',
'dimensionRoleId': {
'type': 'DimensionRoleIdDto',
'kind': 'DimensionRoleIdDto',
'value': 'date_reg_end',
},
'attributeId': 'DATE',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'd02af800021e4f559a1120bae16e692f',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'num_rann_lic',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '1fb7dfc7907545899a8bf78dc7c655b2',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'asln_link',
},
],
'filters': [
[
{
'selectedFilterValues': [
'В - Подземные воды (за исключением подземных минеральных вод)',
'М - Подземные минеральные воды, лечебные грязи, специфические минеральные '
'ресурсы (рапа '
'лиманов и озер, сапропель и другие)',
],
'attributeId': 'S_rasshifrovkoi',
'dimensionOrDimensionRoleId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'useExcluding': False,
},
],
],
'times': [],
'id': 'R_Reestr_litsenzii',
},
'lazyLoadOptions': {
'columnSorts': [
{
'columnindex': 3,
'order': 1,
},
],
'offset': 0,
'limit': 20,
},
},
'CalculationQueries': [],
'AdditionalLogs': {
'widgetGuid': '245d52ef1fdc474a8795fc37a8d03d02',
'dashboardGuid': 'ae176f70a6df4e81ba10247f44fb1191',
'sheetGuid': '23760aa1c3844e9686946dfac2d0e8a0',
},
'WidgetGuid': '245d52ef1fdc474a8795fc37a8d03d02',
}
resp = requests.post('https://bi.rfgf.ru/corelogic/api/query', headers=headers, json=json_data, verify=False).json()
print(f"Записей в таблице с данной выборкой: {resp['result']['recordCount']}\n")
return resp['result']['recordCount']+1
def get_json():
res_dict = []
count = get_record_count()
for i in range(0, count, 1000):
print(f'\rПолучаю записи. Смещение: {i}/{count-1}', end="")
json_data = {
'QueryType': 'GetRawOlapData+Query',
'RawOlapSettings': {
'databaseId': 'Main',
'measureGroup': {
'columns': [
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'c64a19c50c394605981e3ebe87918ae1',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'rcart_link',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '7c57c1b9e5b74322b5976eff809e3c20',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'lc_number_full',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'b146527321b840668975b16507194d33',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'has_doc',
},
{
'type': 'RawOlapDimensionRoleAttributeColumnDto',
'kind': 'RawOlapDimensionRoleAttributeColumnDto',
'guid': '3766824573874611bc87322d41091f2f',
'dimensionRoleId': {
'type': 'DimensionRoleIdDto',
'kind': 'DimensionRoleIdDto',
'value': 'date_reg_start',
},
'attributeId': 'DATE',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'dd98c164c5234359b58909f9b96f10da',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'purpose',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '0c54fe57b031469686122724a10fca61',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'lc_cat_type_abr_with_pi',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '7514ae69824d42fb999a6630fc6b902d',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'area_nedr_name',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'f5cd97e6be364c82a48e8bad88682e32',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'region_name_sf',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '15dfc1c77ad34713acd832b0b28cba33',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'koord',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '89880068c9514a94b74dd1ac6326c4ee',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'stat_uch',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '55928c0365254e989776b22c2ad91133',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'company_name',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '30e8b6fcda754620b37a8808eaad9822',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'organ',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '1565bb98bd09479dbb14db2a63d04ad6',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'doc_lic',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '8b0d834417a14859940ba47a37420323',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'doc_dopoln',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '86006ff857ee48c0b94c27a5c82743e4',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'lc_number_full_new',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '24644a616cba4dcba8369c584ba1cfea',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'annul_doc',
},
{
'type': 'RawOlapDimensionRoleAttributeColumnDto',
'kind': 'RawOlapDimensionRoleAttributeColumnDto',
'guid': '17bd7207b3b14433803e97480f5dc2a4',
'dimensionRoleId': {
'type': 'DimensionRoleIdDto',
'kind': 'DimensionRoleIdDto',
'value': 'annul_date',
},
'attributeId': 'DATE',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '930bb47ba3b242b2b91e81d457692a54',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'pause_dates',
},
{
'type': 'RawOlapDimensionRoleAttributeColumnDto',
'kind': 'RawOlapDimensionRoleAttributeColumnDto',
'guid': 'a46c3241eeae4c5e9d81d71f84c03eab',
'dimensionRoleId': {
'type': 'DimensionRoleIdDto',
'kind': 'DimensionRoleIdDto',
'value': 'date_reg_end',
},
'attributeId': 'DATE',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': 'd02af800021e4f559a1120bae16e692f',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'num_rann_lic',
},
{
'type': 'RawOlapDimensionAttributeColumnDto',
'kind': 'RawOlapDimensionAttributeColumnDto',
'guid': '1fb7dfc7907545899a8bf78dc7c655b2',
'dimensionId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'attributeId': 'asln_link',
},
],
'filters': [
[
{
'selectedFilterValues': [
'В - Подземные воды (за исключением подземных минеральных вод)',
'М - Подземные минеральные воды, лечебные грязи, специфические минеральные '
'ресурсы (рапа '
'лиманов и озер, сапропель и другие)',
],
'attributeId': 'S_rasshifrovkoi',
'dimensionOrDimensionRoleId': {
'type': 'DimensionIdDto',
'kind': 'DimensionIdDto',
'value': 'R_litsenziya',
},
'useExcluding': False,
},
],
],
'times': [],
'id': 'R_Reestr_litsenzii',
},
'lazyLoadOptions': {
'columnSorts': [
{
'columnindex': 3,
'order': 1,
},
],
'offset': i,
'limit': 1000,
},
},
'CalculationQueries': [],
'AdditionalLogs': {
'widgetGuid': '245d52ef1fdc474a8795fc37a8d03d02',
'dashboardGuid': 'ae176f70a6df4e81ba10247f44fb1191',
'sheetGuid': '23760aa1c3844e9686946dfac2d0e8a0',
},
'WidgetGuid': '245d52ef1fdc474a8795fc37a8d03d02',
}
resp = requests.post('https://bi.rfgf.ru/corelogic/api/query', headers=headers, json=json_data, verify=False).\
json()
for it in range(0, 1000):
try:
date = dt.strptime(resp['result']['data']['values'][3][it], "%Y-%m-%d").strftime("%d.%m.%Y")
target = resp['result']['data']['values'][4][it]
name_sector = resp['result']['data']['values'][6][it]
name = resp['result']['data']['values'][10][it]
res_dict.append({
"date": date,
"target": target,
"name_sector": name_sector,
"name": name
})
except IndexError:
break
time.sleep(0.3)
print(f"\n\nПолучено записей: {len(res_dict)}")
print('Сохраняю данные в файл "result_ReestrLic.json"')
with open('result_ReestrLic.json', 'w', encoding='utf-8') as file:
json.dump(res_dict, file, indent=4, ensure_ascii=False)
print("Данные успешно сохранены")
def main():
get_json()
if __name__ == "__main__":
main()
Спасибо за внимание. Надеюсь, что информация была вам полезна
Вложения
Последнее редактирование: