{"id":14293,"url":"\/distributions\/14293\/click?bit=1&hash=05c87a3ce0b7c4063dd46190317b7d4a16bc23b8ced3bfac605d44f253650a0f","hash":"05c87a3ce0b7c4063dd46190317b7d4a16bc23b8ced3bfac605d44f253650a0f","title":"\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0435 \u043f\u043e\u0442\u0440\u0430\u0442\u0438\u0432 \u043d\u0438 \u043a\u043e\u043f\u0435\u0439\u043a\u0438","buttonText":"","imageUuid":""}

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

Хочу познакомить читателя с таким ресурсом, как «Единый федеральный реестр сведений о фактах деятельности юридических лиц» (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, там можно посмотреть больше кода.

А у меня на этом все!

0
Комментарии
-3 комментариев
Раскрывать всегда