Как стать автором
Обновить

Проверка контрагентов. Парсинг fedresurs

Время на прочтение6 мин
Количество просмотров5K

В этой статье хочу познакомить читателя с таким ресурсом, как «Единый федеральный реестр сведений о фактах деятельности юридических лиц» (fedresurs.ru), рассказать о его применимости и показать возможность прошерстить ресурс не руками по большому количеству клиентов, а с использованием C# (в вашем случае можете использовать любой другой язык, который вам нравится, подходы, думаю, не изменятся) и море проксей, ну разумеется будем парсить. Возможно, тем кто этим займется, пригодятся статья, идеи или, чем черт не шутит, куски кода, а тем, кто просто проводит проверку контрагентов сможет либо понять, что можно взять с сайта либо придумает, что можно еще проверить.

Периодически возникает потребность в получении данных для анализа клиентов/заемщиков/контрагентов и эти данные мы можем получить из этого сайта. Что же нам предлагается?!

Возникновение обременений (залог, лизинг и т.д.) – посмотреть, например, на возможность заключения фиктивных договоров лизинга или на уже заложенное имущество и т.д.

Различные сообщения по клиенту, например, сообщения о банкротстве и уже с ними – проверка, все ли сообщает клиент и может пора пошевелиться в сторону него и предпринять какие-либо меры.

Торги и т.д.

Составляем запрос для получения желаемого

string strQuery;
        linq2: switch (inn.Length)
            {
                case (10):
                    strQuery = "https://fedresurs.ru/backend/companies?limit=15&offset=0&code=" + inn;
                    break;
                case (12):
                    strQuery = "https://fedresurs.ru/backend/persons?limit=15&offset=0&code=" + inn;
                    break;
                case (9):
                case (11):
                    inn = 0 + inn;
                    goto linq2;
                default:
                    return;
            }

Определяем по длине строки, с кем имеем дело – с физическим или юридическим лицом (10 символов – юрик, 12 символов – физик) от этого зависит запрос. Бывает, что 0 впереди ИНН куда-то теряется, так что можем поставить проверку, что если 9 или 11 символов, то дописываем 0 впереди. В худшем случае добудем избыточную информацию.

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(strQuery);
            request.UserAgent = "Mozila/4.0 (compatible; MSIE 6.0;Windows NT 5.1; SV1; MyIE2; ";
            request.CookieContainer = new CookieContainer();
            request.Accept = "image/gif, image/x-xbitmap, image/jpeg,image / pjpeg, application / x - shockwave - flash, application / vnd.ms - excel, application / vnd.ms - powerpoint,  application / msword,";
            request.Headers.Add("Accept-Language", "ru");
            request.ContentType = "application/json";
            request.KeepAlive = true;
            request.Timeout = 200000;
            request.Referer = "https://fedresurs.ru/search/entity?code=" + inn;
            HttpWebResponse response = new HttpWebResponse();
    	response = (HttpWebResponse)request.GetResponse();

Стоит обратить внимание на Referer – без него, сервер будет отпинывать, сообщая, что страница не найдена.

Берем любой ИНН, например 3327848813 и делаем запрос. Вернется нам json:

{"pageData":[{"guid":"b99c2ded-1a6c-4239-b728-804461ad074b","ogrn":"1103327002393","inn":"3327848813","name":"ООО \"СУЗДАЛЬСКАЯ ПИВОВАРНЯ\"","egrulAddress":"600033, ОБЛАСТЬ ВЛАДИМИРСКАЯ, ГОРОД ВЛАДИМИР, УЛИЦА СУЩЁВСКАЯ, ДОМ 37, ПОМЕЩЕНИЕ 12","status":"Юридическое лицо признано несостоятельным (банкротом) и в отношении него открыто конкурсное производство","statusUpdateDate":"2017-11-28T00:00:00","isActive":true}],"found":1}

При разложении в таблицу выглядит чуть проще:

И вот мы получили guid и еще чуть-чуть информации сверху.

Дальше можем получить список сообщений по данному клиенту и вот запрос:

string strQuery, referer;
                    switch (search.inn.Length)
                    {
                        case (10):
                            strQuery = "https://fedresurs.ru/backend/companies/" + search.guid + "/publications?" +
                                "&limit=" + limit +
                                "&offset=" + offset +
                                "&searchCompanyEfrsb=true" +
                                "&searchAmReport=true" +
                                "&searchFirmBankruptMessage=true" +
                                "&searchFirmBankruptMessageWithoutLegalCase=false" +
                                "&searchSfactsMessage=true" +
                                "&searchSroAmMessage=true" +
                                "&searchTradeOrgMessage=true";
                            referer = "https://fedresurs.ru/company/b99c2ded-1a6c-4239-b728-804461ad074b" + search.guid;
                            break;
                        case (12):
                            strQuery = "https://fedresurs.ru/backend/persons/" + search.guid + "/publications?" +
                                "limit=" + limit +
                                "&offset=" + offset +
                                "&searchPersonEfrsbMessage=true&searchAmReport=true" +
                                "&searchPersonBankruptMessage=true" +
                                "&searchMessageOnlyWithoutLegalCase=false" +
                                "&searchSfactsMessage=true" +
                                "&searchArbitrManagerMessage=true" +
                                "&searchTradeOrgMessage=true";
                            referer = "https://fedresurs.ru/person/b99c2ded-1a6c-4239-b728-804461ad074b" + search.guid;
                            break;
                        default:
                            return pageDatas;
                    }

Ничего неожиданного, все так же надо определять юр или физ лицо мы смотрим, подставлять guid, limit-количество сколько выведет сообщений, offset-сколько пропустить сообщений, все так же необходимо указывать Referer.

Делаем запрос и получаем:

{"pageData":[{"guid":"A10151349AE45D484C24D08CF2451AA7","number":"7344583","datePublish":"2021-09-17T12:37:52.11","isAnnuled":false,"isLocked":false,"title":"Объявление о проведении торгов","publisherName":"Потапов Павел  Викторович","type":"BankruptcyMessage","publisherType":"ArbitrManager","bankruptName":"ООО \"СУЗДАЛЬСКАЯ ПИВОВАРНЯ\"","isRefuted":false},….

Часть данных обрезал, чтоб сильно не захламлять текст. И по традиции табличный вид

А теперь перейду к нюансу, куда же без них иначе было бы неинтересно. По какой-то причине сайт готов выдавать только первые 500 сообщений, не более. Чтобы обойти эту данность, можно начать ставить фильтры либо на типы сообщений, либо на промежуток времени, по которому смотрим, либо и то, и другое Все это записывается в запрос.

После составления запроса добавляется

if (useDate)
                    {
                        endDate = startDate.AddDays(deltDays);
                        strQuery += string.Format("&startDate=" + GetDateStr(startDate) + "T00:00:00.000Z&endDate=" + GetDateStr(endDate) + "T00:00:00.000Z");
                    }

А после части, где получаем ответ от сайта

if (found > 500 && deltDays > 0)
                    {
                        if (useDate)
                        {
                            deltDays /= 2; // меняем кол-во дней если слишком много сообщений
                            break;
                        }
                        else
                        {
                            useDate = true; // включаем поиск по датам если слишком много сообщений
                            break;
                        }
                    }

После получения ответа смотрим сколько всего сообщений есть и если их больше 500, то начинаем добавлять к запросу фильтр по датам Если в ответе все равно много, то снова уменьшаем период времени и так пока не станет <= 500.

Теперь если просто информации что за сообщение, нам недостаточно, и мы хотим (аналитик хочет, а мы вынуждены) получить больше информации, то как и прежде берем guid, только на этот раз это guid сообщения и таким же образом строим запрос:

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://fedresurs.ru/backend/sfactmessages/" + guid);
            request.UserAgent = "Mozila/4.0 (compatible; MSIE 6.0;Windows NT 5.1; SV1; MyIE2; ";
            request.CookieContainer = new CookieContainer();
            request.Accept = "image/gif, image/x-xbitmap, image/jpeg,image / pjpeg, application / x - shockwave - flash, application / vnd.ms - excel, application / vnd.ms - powerpoint,  application / msword,";
            request.Headers.Add("Accept-Language", "ru");
            request.ContentType = "application/json";
            request.Referer = "fedresurs.ru";
            request.Proxy = ProxyClass.GetProxy();
            request.KeepAlive = true;
            request.Referer = "https://fedresurs.ru/sfactmessage/" + guid;

Не стану повторяться, но тут есть момент, а именно, что запрос формируется исходя из типа сообщения. Т.е. sfactmessage (существенные факты) в запросе и в Referer соответственно может принять значения, например, bankruptmessage или какой-либо другой, и под это все json и соответственно конечные таблицы становятся разными. Примерное содержание одного из сообщений:

Осталась рассказать про одну неприятную, но вполне ожидаемую вещь: блокировки по ip((((. За одну айпишку можно успеть сделать от 100 запросов, на некоторых проксях дает сделать лишь один и под замену, и ip улетает в блок примерно на час. Для этой части написал класс подстановки проксей.

public static class ProxyClass
    {
        private static List<WebProxy> Proxys = new List<WebProxy>();// список проксей
        private static int NumPr = 0;//используемая сейчас прокси
        private static int QuerCnt = 0;//сколько запросов сделано с прокси
        private static DateTime lastUseNullProxy=DateTime.Now;//последнее использование без проксли
        private static int minutesForNullProxy = 90;// через какое время начинать обход списка с нуля
        /// <summary>
        /// меняет прокси (в try catch)
        /// </summary>
        public static void NexProxy()
        {
            if (NumPr == 0)
            {
                lastUseNullProxy = DateTime.Now;// меняет дату 
            } // если без прокси(0)
            if (QuerCnt == 1)
            {
                if (NumPr == 0) 
                {
                } // если без прокси(0)
                else
                {
                    Proxys.Remove(Proxys[NumPr]);
                    if (Proxys.Count == 1) { GetFromTxt(); }
                    NumPr--;
                }// если с прокси
            } //если был всего один запрос
            if (NumPr== Proxys.Count-1) 
            { 
                NumPr = 0;
            }//
            else 
            {
                NumPr++;
            }
            if (DateTime.Now.Subtract(lastUseNullProxy).TotalMinutes >= minutesForNullProxy)
            {
                NumPr = 0;
            }
            QuerCnt = 0;
        }
        /// <summary>
        /// возвращает прокси для использования
        /// </summary>
        /// <returns></returns>
        public static WebProxy GetProxy()
        {
            QuerCnt++;
            if (QuerCnt == 2 && NumPr!=0)
            {
                Write(append:true);
            }
            return Proxys[NumPr];
        }
}

В класс собираются списки проксей, и в коде при запросе идет обращение к данному классу, для получения актуальной прокси, в принципе вся работа этого куска описана в комментах. Основная проблема — найти большое количество нормальных проксей, или чтобы хотя бы половина из них были работающие.

Прикладываю также и ссылку на Git, там можно посмотреть больше кода.

Теги:
Хабы:
Всего голосов 3: ↑2 и ↓1+1
Комментарии7

Публикации