Примечания:
- ООС — общероссийский официальный сайт (Государственные закупки).
- Требуемые знания для понимания статьи: VBA (MS Excel).
Введение
Цитата:
«Официальный сайт Российской Федерации в сети «Интернет» для размещения информации о размещении заказов на поставки товаров, выполнение работ, оказание услуг … предназначен для обеспечения свободного и безвозмездного доступа к полной и достоверной информации о контрактной системе в сфере закупок и закупках товаров, работ, услуг, отдельными видами юридических лиц, а также для формирования, обработки и хранения такой информации.»
Для многих коммерческих и иных компаний система «госзакупок» является основным способом привлечения в хозяйственную деятельность государственных (бюджетных) средств. Информация о закупках товаров, услуг, исследовательских работ, осуществляемых компаниями с государственным участием (и другими отдельными видами компаний) в обязательном порядке (согласно федеральным законам №№ 223, 94, 44) размещается на сайте госзакупок. Коммерческие компании также зачастую публикуют свои тендеры именно на официальном сайте госзакупок.
Ссылки на упоминаемые законы, информация на сайте «КонсультантПлюс»:
- Федеральный закон от 21.07.2005 N 94-ФЗ (ред. от 02.07.2013) "О размещении заказов на поставки товаров, выполнение работ, оказание услуг для государственных и муниципальных нужд"
- Федеральный закон от 18.07.2011 N 223-ФЗ (ред. от 12.03.2014, с изм. от 29.12.2014) "О закупках товаров, работ, услуг отдельными видами юридических лиц" (с изм. и доп., вступ. в силу с 01.01.2015)
- Федеральный закон от 05.04.2013 N 44-ФЗ (ред. от 08.03.2015) "О контрактной системе в сфере закупок товаров, работ, услуг для обеспечения государственных и муниципальных нужд"
Таким образом, информация о конкурсах, публикуемая на сайте госзакупок, является центральным источником актуальных сведений о возможных «государственных» заказах по профилю для множества компаний из самых разных сфер деятельности — от услуг охраны до геофизических изысканий. Поэтому потребность иметь регулярно обновляемые сведения о проходящих конкурсах, попадающих под определенные критерии, возникает у каждой организации, участвующей в госзакупках. В настоящей статье рассмотрим различные способы и практический пример реализации указанной потребности.
Варианты получения сведений о госзакупках
Чаще всего задача ставится руководством перед маркетинговым или IT-подразделением компании примерно в следующей формулировке: «необходима ежедневная сводка по конкурсам госзакупок, удовлетворяющим следующим критериям…». Именно ежедневная, так как иногда между публикацией конкурсной документации и окончанием подачи заявок на него объявляется срок менее 10 дней, в среднем — около двух недель. Времени для сбора всей необходимой документации для участия в конкурсе совсем не много, каждый день «на счету».
Перечислим варианты реализации задачи.
1. Сайт ООС (госзакупок)
Сам сайт http://zakupki.gov.ru/ дает возможности настраиваемого поиска и отбора конкурсов по параметрам.
Недостатки:
- Поиск работает недостаточно корректно, иногда конкурсы «не находятся». Я свидетель долгой рабы именно через официальный сайт, утверждение мной проверено;
- Сайт зачастую не работает по ночам и праздникам;
- Ограничение в количестве результатов. Если поиск выдает более, чем 500 строк, скачивание данных о конкурсах (в виде таблицы .csv формата) средствами сайта невозможно.
Для частичной автоматизации процесса я в своё время написал плагин для браузера FireFox, который должен был делать ежедневное скачивание отобранных конкурсов.
В двух словах плагин формировал адрес в виде примерно такой строки:
"http://zakupki.gov.ru/epz/order/extendedsearch/search.html?"
+ "placeOfSearch=FZ_44"
+ "&orderPriceFrom=" + priceArray[i] + "&orderPriceTo=" + (priceArray[i+1]-1)
+ "&orderPriceCurrencyId=-1"
+ "&orderPublishDateFrom=" + OrderDate + "&orderPublishDateTo=" + OrderDate
+ "&headAgencyWithSubElements=true&matchingWordPlace44=NOTIFICATIONS"
+ "&law44.okpd.withSubElements=true”
+ "&law44.okpd.ids=31301%2C37097%2C50876%2C51122" + "&law44.advantages[MP44]=I&law44.advantages[UG44]=I"
+ "&law44.advantages[IN44]=I&law44.advantages[MPSP44]=I"
+ "&morphology=false&strictEqual=false";
Естественно, для 94 и 223 закона параметры запроса другие. Как видно, ОКПД, диапазон цен и т.п. приходится «зашивать» в запрос, так как иначе количество найденных конкурсов будет слишком большим и результаты поиска можно будет скачать только частично.
2. Специализированные web — сервисы поиска конкурсов
В ответ на запрос рынка несколько компаний организовали сайты поиска информации о проводимых конкурсах. Зачастую такие сайты дают не только сведения о госзакупках, но и о «частных» тендерах с электронных площадок.
Несколько таких систем:
- Мультитендер.ру: «бесплатная специализированная поисковая система, позволяющая отслеживать госзакупки и коммерческие тендеры»©;
- TenderCAT: «Каталог TenderCAT предназначен для облегчения поиска государственных и коммерческих тендеров и аукционов в огромной массе ежедневно публикуемых на портале госзакупок (http://zakupki.gov.ru) заказов по всем регионам и направлениям деятельности»©;
- Ист Бюджет: «Сайт госзакупок и тендеров — лучший поиск и аналитика от Ист Бюджет»©;
- Закупки 360
И другие.
Отдельно упомяну Госзатраты, о которых на Хабре была статья.
Целью настоящей статьи не является сравнение или анализ указанных ресурсов, так что ни их достоинства, ни недостатки здесь не приводятся.
3. Самостоятельная разработка для скачивания конкурсов.
В некоторых (я знаю несколько) случаях руководство компании, участвующей в конкурсах, ставит очень специфические задачи, касающиеся параметров отбора информации, регулярности поиска или оформления результата поиска. В таких случаях обращаются к IT специалистам, «ручная работа» по мониторингу и отбору конкурсов становится слишком трудоемкой.
Встречаются примеры заказа описанных работ у фрилансеров. Например, на SQL.ru, на Weblancer. Наконец, можно приобрести готовое решение у «проверенных временем» исполнителей. Однако основная часть настоящей статьи описывает процедуру самостоятельного исполнения задачи.
Скачивание данных о конкурсах с http://zakupki.gov.ru средствами Excel VBA
Исходные данные
Основное необходимое знание об источнике данных: у ООС (общероссийский официальный сайт госзакупок) имеется публичный ftp-сервер. При этом если http регулярно (обычно в ночное время и праздничные дни) не доступен по причине «технического обслуживания», то ftp работает (согласно моей практике) вполне надежно.
Адреса ftp серверов разделены по федеральным законам, применяющимся для размещения тендеров:
ФЗ №223: ftp://ftp.zakupki.gov.ru/out/
ФЗ №94 и ФЗ №44: ftp://zakupki.gov.ru
Структура каталогов для 223 закона вполне прозрачна. Что же касается 94 и 44, здесь расположены следующие интересующие нас подкаталоги:
- 94fz каталог, содержащий данные публичных выгрузок в соответствии с 94ФЗ (остальные каталоги — 44ФЗ),
- fcs_regions каталог, содержащий данные полной региональной выгрузки опубликованной на ООС информации в соответствии с 94ФЗ.
Остальные каталоги содержат нормативно-справочную информацию, информацию по банковским гарантиям, выгрузки по правилам и в рамках решения задачи не представляют интереса.
Далее необходимые выдержки из разъяснений по процедуре выгрузки сведений об опубликованных документах по региону на FTP-сервер Общероссийского официального сайта, набор цитат:
Полная региональная выгрузка включают в себя все опубликованные на ООС документы следующих типов:
• опубликованные извещения;
• опубликованные изменения извещений;
• опубликованные протоколы;
• опубликованные сведения о контрактах;
• опубликованные изменения контрактов;
• опубликованные сведения об исполнении/прекращении действия контрактов.
Отметим, что в наших целях интересны только извещения (notice). Все остальные виды документов в рамках задачи не используются!
Выгрузка осуществляется в архивируемые файлы формата XML.
В одном файле могут находиться документы только одного типа в количестве, не превышающем 3000 записей. Если количество документов, подлежащих выгрузке, превышает 3000 записей, система формирует несколько файлов и каждый помещает в отдельный архив.
Все сформированные и зархивированные XML-файлы выгружаются на FTP-сервер… файлы в выгрузке разделены по каталогам, соответствующим региону. В каждом каталоге региона есть еще 3 каталога: notifications, protocols и contracts. В каждом из каталогов notifications, protocols и contracts дополнительно есть каталог daily.
Опубликованные документы выгружаются на FTP-сервер в следующем порядке:
· Каждый календарный день (ежедневно) выгружается список документов, опубликованных за предыдущий календарный день. При этом…выгрузка извещений по региону делается в каталог <Наименование региона>/notifications/daily;
· Каждый календарный месяц (ежемесячно) выгружается список документов, опубликованных за предыдущий календарный месяц. При этом…выгрузка извещений по региону делается в каталог <Наименование региона>/notifications;
В ежедневной и ежемесячной выгрузках всегда выгружаются все типы документов, опубликованных за прошедший календарный день или календарный месяц соответственно.
Если на момент формирования выгрузки за истекший период не было ни одного опубликованного документа какого-нибудь типа, то XML-файл с данным типом документов выгружается пустым.
После завершения ежемесячной выгрузки, каталоги с ежедневными выгрузками за истекший месяц очищаются.
Имена файлов региональной выгрузки имеют следующую структуру:
<вид-документа_регион_начало-периода_конец-периода_номер.xml.zip>,
где:
• вид-документа – принимает значение notification, protocol или contract для извещений, протоколов и сведений о контрактах соответственно;
• регион – название региона выгрузки;
• начало-периода – дата начала периода для отбора документов по дате-времени публикации выгружаемых документов в формате yyyyddmm_hhmmss, где yyyy – год, mm – месяц (номер), dd – день, hh – час, mm – минуты, ss – секунды;
• конец-периода – дата конца периода для отбора документов по дате-времени публикации выгружаемых документов в формате yyyyddmm_hhmmss, где yyyy – год, mm – месяц (номер), dd – день, hh – час, mm – минуты, ss – секунды;
• номер – порядковый номер сформированного файла;
Актуальную версию приведенной информации можно скачать с ООС в виде документов «Схемы информационного обмена…».
Описание программы автоматического скачивания конкурсной информации (VBA MS Excel, Windows).
Подготовка
- Подключаем скриптинг Microsoft Scripting Runtime. Для функциональности FSO (WindowsSystem32scrrun.dll)
- Подключаем скриптинг Microsoft XML, v.6
Для запроса диапазона дат, в котором следует скачать конкурсы, я сделал userForm с использованием элемента MonthView. Его настройки позволяют развернуть два месяца рядом (предположим, что диапазон не длиннее, чем месяц), показывать текущую дату (красная рамочка), установить выбранный диапазон дат по умолчанию.
На примере выбран диапазон с 26 февраля по 4 марта. Выбранные даты считываются со свойств Формы:
MonthView.SelStart и MonthView.SelEnd
Вот такой фильтр позволяет выбирать файлы (структура имен которых нам известна) в заданном диапазоне дат:
fltr = "*_" & Format(targetDate, "yyyymmdd") & "*" & Format(targetDate + 1, "yyyymmdd") & "*.zip;" & "*_" & Format(targetDate, "yyyymmdd") & "*" & Format(targetDate, "yyyymmdd") & "*.zip"
Обратим внимание на то, что здесь точка с запятой разделяет альтернативные варианты, поэтому указанный фильтр подходит для файлов, формируемых по всем трем законам.
Скачивание файлов я осуществляю в заданную директорию, которую предварительно очищаю от старых закачек, применяя методы FSO:
Dim FSO As FileSystemObject
Set FSO = New FileSystemObject
Далее выбираем целевую папку bFld = FSO.GetFolder(...) и уничтожаем поддиректории:
For Each SubFolder In bFld.SubFolders
SubFolder.Delete
Next
Совершенно не обязательным, но крайне удачным «фантиком» к программе оказалось использование
Application.Speech.Speak
Удобно не наблюдать за прогрессом долгой процедуры, а занимаясь своими делами регулярно слышать сообщения (приятным женским голосом) типа:
Application.Speech.Speak "Downloading purchase notices", True
Второй параметр — асинхронное выполнение.
Скачивание файлов
Начинаем скачивание. Убеждаемся, что целевая папка существует с помощью
FSO.FolderExists
и при необходимости создаем её функцией MkDir.
Создаем шелл — объект
Set myShell = CreateObject("Shell.Application")
и применяем основную «фишку» описываемого подхода — метод namespace:
Set ftpItems = myShell.Namespace(адресFTP).Items
Указанная строка реализует обращение к FTP, возвращая папки и файлы. Обратите внимание на то, что передаваемый параметр должен иметь тип Variant, а не String.
Выбрать все директории можно так:
ftpItems.Filter 32, "*".
Выбрать поддиректории и файлы (96=32+64) и применить фильтр вроде того, который был построен в начале параграфа для отбора фалов по дате — так:
ftpItems.Filter 96, fltr.
Остается указать целевую (локальную) папку для скачивания аналогичным способом:
set tFolder = myShell.Namespace(tgtFolder)
И запустить «копирование» (оно же «скачивание») следующим образом:
tFolder.CopyHere ftpItems, 20
Эта команда запускает внешний процесс (в Windows отображается стандартный прогресс-бар копирования файлов), её выполнение из vba не контролируется напрямую. Однако нам необходимо дождаться окончания её выполнения, для чего мы следующим методом в цикле проверяем наличие последнего из копируемых файлов:
While Len(Dir$(tgtFolder & "" & ftpItems.Item(ftpItems.Count - 1).Name)) = 0
Sleep 1: DoEvents
Wend
Так устроенное «ожидание» длится в точности столько, сколько копируются файлы.
Распаковка (разархивация) скачанных файлов
Перебрав все директории и поддиректории и скачав все отфильтрованные файлы, приступаем к их обработке на локальной машине:
Application.Speech.Speak "Unzipping archives", True.
Для этого снова пользуемся методом namespace.
Перебираем все архивы в папке
For Each fl In tFolderItems
и пользуемся тем, что эти архивы в Windows видны как поддиректории! Соответственно, всё содержимое архива доступно так:
Set flItems = myShell.Namespace(CVar(tgtFolder & "" & fl.Name)).Items
(Снова обращаю внимание на то, что параметр должен быть Variant, из-за чего необходимо преобразование типа из строки).
И опять та же команда CopyHere позволяет «скопировать» (на самом деле извлечь) из архива все фалы в целевую папку:
myShell.Namespace(tgtFolder).CopyHere flItems, 20
а асинхронное выполнение заставляет нас в цикле ждать окончания выполнения этой команды тем же образом, который описывался выше.
Среди разархивированных файлов немало «мусора». Например, по какому-то региону в определенный день не было никаких закупок, ООС генерирует пустой файл за эту дату. Поэтому прежде, чем парсить xml, я предпочитаю удалить лишнее. Используя FSO перебираем файлы
Set fold = FSO.GetFolder(tgtFolder)
For Each fl In fold.Files
«Отсев» легко произвести по размеру файла (fl.Size <= 198) и фильтруя его название Not (LCase(fl.Name) Like "*noti*")).
Удаление файла осуществляется предельно просто: fl.Delete
Расшифровка XML с данными о конкурсах
Расшифровка xml зависит от их схемы, которая на ООС иногда меняется. Поэтому далее приведены основные приемы, без концентрации на отдельных полях и данных. Начинаем, конечно, с
Application.Speech.Speak "Decoding files", True.
В этой части помимо FSO для работы с файлами нам понадобится XML:
Dim xml As MSXML2.DOMDocument60
Set xml = New DOMDocument60: xml.async = False: xml.validateOnParse = True
И, конечно, целевой лист (ActiveSheet) в книге Excel, куда мы будем записывать информацию.
Начнем с того, что остановим отрисовку Excel на время, чтобы «не мельтешило»:
Application.ScreenUpdating = False
Принципиальным моментом является то, что
«XPath обрабатывает пустой префикс как пространство имен null. Другими словами, в запросах XPath можно использовать только префиксы, сопоставленные с пространствами имен. Это значит, что если нужно построить запрос к пространству имен в XML-документе, то даже если оно является пространством имен по умолчанию, для него необходимо определить префикс.»
Поэтому для успешного разбора полей скачанных документов для пространства имен по умолчанию добавляем некий префикс. Например, «q»:
xml.setProperty "SelectionNamespaces", " xmlns:q= 'http://zakupki.gov.ru/oos/export/1'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:oos='http://zakupki.gov.ru/oos/types/1'"
Так выглядит определения пространства имен для разбора xml, сформированных по 94 ФЗ. Для 44 она будет немного другой:
xml.setProperty "SelectionNamespaces", "xmlns:q='http://zakupki.gov.ru/oos/types/1'
xmlns:ns2='http://zakupki.gov.ru/oos/export/1'
xmlns:ns3='http://zakupki.gov.ru/oos/printform/1'"
Собственно все приведенные данные берутся из заголовка любого скачанного xml — файла, только для пространства имен по умолчанию добавляется q.
Далее определяются поля (точнее, xpath, указывающий на них), которые нужно скачать. На текущий момент для 94 ФЗ они выглядят примерно так: ".//oos:notificationNumber", ".//oos:lot", ".//oos:orderName", ".//oos:maxPrice". Для 44 ФЗ — другая структура (спасибо программистам ООС): ".//q:purchaseNumber", ".//q:lot", ".//q:purchaseObjectInfo", ".//q:maxPrice|.//q:price|.//q:totalSum".
Видим, что однозначности нет, настраивать парсинг приходится не только опираясь на опубликованные схемы, но и на собственный практический опыт расшифровки данных.
Проверить, «читается» ли xml -файл можно двойным условием:
If Not xml.Load(tgtFolder & "" & fl.Name) then…If (xml.parseError.ErrorCode <> 0) then…
Если же файл прочитался (до сих пор у меня с закачанными с ООС файлами проблем не было), можно собственно разбирать его содержание. Прежде всего рекомендую прочитать поле, описывающее состав сообщения (notice).
purchaseType = LCase(xml.DocumentElement.ChildNodes(0).BaseName)
documentType = LCase(xml.DocumentElement.BaseName)
И проверить, что содержимое файла — это именно объявление о конкурсе, а не отмена его, уведомление о публикации протокола и т.п. примерно так:
If Not (purchaseType Like "*cancel*" Or purchaseType Like "*protocol*" Or documentType Like "*cancel*") Then
Так как в xml, сформированных по 223 ФЗ, название типа документа «спрятано», то можно добавить:
If (purchaseType Like "*notification*" Or documentType Like "*notice*") Then
Далее идет собственно запись данных из xml в ячейки листа:
Range("A" & i) = xml.DocumentElement.SelectSingleNode(строкаXPathсоотвПоля).Text
И т.д.
Если XPath может давать несколько вариантов (указание, например, на названия лотов) и все их мы хотим сохранить, поможет такая конструкция:
For Each it In lot.SelectNodes(строкаXPathсоотвПолей)
Range("E" & i) = Range("E" & i) & it.Text & "; "
Next
Некоторые поля в документе могут отсутствовать, тогда пропускаем их условием:
If Not xml.DocumentElement.SelectSingleNode(строкаXPathсоотвПоля) Is Nothing Then
Заключение
Скачивание файлов с ftp можно реализовать разными способами. Я описал один, с использованием shell.namespace, работающий и ОЧЕНЬ просто реализуемый.
После скачивания, автоматического ранжирования (отбора) и форматирования списка конкурсов моя ежедневная подборка конкурсов (по всем ФЗ) выглядит примерно так:
Описанный выше подход позволяет скачивать данные о конкурсах, контрактах, планах закупок и т.п. с ООС, ведь вся эта информация публикуется на открытом ftp. Весь код программы я не привожу и не могу привести, так как он является «интеллектуальной собственностью». Однако восстановить программу по приведённым ключевым участкам кода может любой, владеющий основами vba и, что важнее, терпением.
Терпение понадобится, во-первых, при разборе фалов и поддиректорий на ftp: надо не скачать лишнего и не упустить нужное. И во-вторых, при парсинге xml. Однако здесь уже вопрос поставленной задачи: какие именно поля, в какой последовательности, как отформатированные хочет видеть заказчик.
Всем удачи и побед: — в конкурсах — и личных!
Автор: Rodres