Русский
Русский
English
Статистика
Реклама

Opensource

PyOpenRPA туториал. Управление WEB приложениями

16.08.2020 12:14:15 | Автор: admin

Долгожданный туториал по управлению сторонними WEB приложениями с помощью pyOpenRPA. Во 2-й части мы разберем принципы роботизированного воздействия на HTML/JS. А также своими руками сделаем небольшого, но очень показательного робота.


Этот робот будет полезен тем, для кого актуальна тема покупки/продажи недвижимости.


pyOpenRPA туториал. Управление WEB приложениями


Для тех, кто с нами впервые


pyOpenRPA это open source RPA платформа, которая в полной мере позволяет заменить топовые коммерческие RPA платформы.


Подробнее про то, чем же она полезна, можно почитать здесь.


Навигация по туториалам pyOpenRPA


Туториал сверстан в виде серии статей, в которых будут освещаться ключевые технологии, необходимые для RPA.


Освоив эти технологии, у вас появится возможность углубиться в специфику той задачи, которая поставлена перед вами.


Перечень статей-туториалов (опубликованные и планируемые):



А теперь перейдем к самому туториалу.


Немного теории и терминов


[Из википедии]


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


Веб приложения стали широко использоваться в конце 1990-х начале 2000-х годов.


Ссылка на источник


Ок, с выдержкой из вики все #КрутоУмно, но от этого не легче (для тех, кто в этой теме дилетант). Продемонстрирую устройство WEB приложения на примере "Что видим мы?"/"Что видит робот?". Для этого отправимся на сайт одной известной WEB площадки по объявлениям по недвижимости


Что видим мы?


Мы видим красиво сверстанный сайт с интуитивно понятным интерфейсом, на котором можно найти интересные объявления о продаже/сдаче в аренду недвижимости.


Как мы видим WEB приложение


Что видит робот?


Робот видит огромную гипертекстовую разметку HTML с примесью алгоритмического кода JS и завернутого в каскадную таблицу стилей CSS. Увлекательно, правда? :)


Как робот видит WEB приложение


Интерпретация


WEB приложения это один из самых легко роботизируемых классов приложений. Обилие инструментов + технологий позволяют реализовывать практически любую поставленную задачу в кооперации с ними.


Управлять WEB страницей можно с помощью разных технологий адресации: CSS, XPath, id, class, attribute. Мы будем взаимодействовать со страницей с помощью CSS селекторов.


(По шагам) робот своими руками


В этом туториале мы будем заниматься разработкой робота, который оперирует на одном из самых популярных порталов по объявлениям по недвижимости в РФ (тема одна из актуальных для многих).


В качестве примера поставим себе следующую задачу: Разработать робота, который будет извлекать список всех объявлений по ранее преднастроенному фильтру. Все извлеченные объявления сохранить как датасет в файл .json со следующей структурой:


{    "SearchKeyStr": "МСК_Тверской", # Ключевое слово поиска    "SearchTitleStr": "Москва, район Тверской", # Заголовок поиска    "SearchURLStr": "https://www.cian.ru/cat.php?deal_type=sale&engine_version=2&in_polygon%5B1%5D=37.6166_55.7678%2C37.6147_55.7688%2C37.6114_55.7694%2C37.6085_55.7698%2C37.6057_55.77%2C37.6018_55.77%2C37.5987_55.77%2C37.5961_55.7688%2C37.5942_55.7677%2C37.5928_55.7663%2C37.5915_55.7647%2C37.5908_55.7631%2C37.5907_55.7616%2C37.5909_55.7595%2C37.5922_55.7577%2C37.5944_55.7563%2C37.5968_55.7555%2C37.6003_55.7547%2C37.603_55.7543%2C37.6055_55.7542%2C37.6087_55.7541%2C37.6113_55.7548%2C37.6135_55.756%2C37.6151_55.7574%2C37.6163_55.7589%2C37.6179_55.7606%2C37.6187_55.7621%2C37.619_55.7637%2C37.6194_55.7651%2C37.6193_55.7667%2C37.6178_55.7679%2C37.6153_55.7683%2C37.6166_55.7678&offer_type=flat&polygon_name%5B1%5D=%D0%9E%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D1%8C+%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&room1=1&room2=1", # URL of the CIAN search [str]    "SearchDatetimeStr": "2020-08-01 09:33:00.838081", # Дата, на которую была сформирована выгрузка    "SearchItems": { # Перечень извлеченных ценовых объявлений        "https://www.cian.ru/sale/flat/219924574/:": { # URL ссылка на ценовое объявление            "TitleStr": "3-комн. кв., 31,4 м, 5/8 этаж", # Заголовок ценового объявления            "PriceFloat": 10000000.0, # Стоимость общая            "PriceSqmFloat": 133333.0, # Стоимость на 1 кв. м.            "SqMFloat": 31.4, # Кол-во кв. м.            "FloorCurrentInt": 5, # Этаж лота по объявлению            "FloorTotalInt": 8, # Этажей в доме всего            "RoomCountInt": 3 # Кол-во комнат        }    }}

Шаг 0. Подготовим проект для нового робота (развернем pyOpenRPA)


В отличии от подавляющего большинства RPA платформ, в pyOpenRPA реализован принципиально иной подход по подключению к проекту, а именно: если в них структуру проекта определяет сама RPA платформа, то в pyOpenRPA структуру проекта определяете Вы и только Вы. Это дает больше гибкости и возможности по использованию этой RPA технологии в других направлениях (использовать pyOpenRPA как обычную библиотеку Python).


Доступно несколько вариантов загрузки pyOpenRPA:


  • Вариант 1, простой. Скачать преднастроенную портативную версию с GitLab страницы проекта
  • Вариант 2, сложный. Установить pyOpenRPA в свою версию интерпретатора Python 3 (pip install pyOpenRPA)

Я рекомендую воспользоваться простым вариантом (вариант 1). Преднастроенная версия не требуется каких-либо настроек инфраструктуры. Здесь в лучших традициях pyOpenRPA реализован принцип, когда пользователь скачивает репозиторий, и у него уже все настроено из коробки пользователю остается лишь писать скрипт робота. #Enjoy :)


Шаг 1. Создать проект робота


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


Ниже приведу зависимости проекта от сторонних компонентов:


  • Selenium WebDriver
  • Google Chrome или Mozilla Firefox или Internet Explorer
  • Python 3

Если вы пошли по варианту 1 (см. шаг 0), то у Вас все эти компоненты уже будут развернуты и настроены внутри скачанного репозитория pyOpenRPA (#Удобно). Репозиторий pyOpenRPA уже содержит все необходимые portable версии требуемых программ (Google Chrome, Mozilla Firefox, Python3 32|64 и т.д.).


Вы наверняка заметили, что в pyOpenRPA используется Selenium. Этот компонент является одним из лучших отказоустойчивых компонентов по внедрению в WEB. Именно поэтому мы его и будем использовать в pyOpenRPA.


Создадим следующую структуру проекта:


  • Репозиторий pyOpenRPA > Wiki > RUS_Tutorial > WebGUI_Habr:
    • Файл "3. MonitoringCIAN_Run_64.py" скрипт робота, который мониторит WEB площадку
    • Файл "3. MonitoringCIAN_Run_64.cmd" скрипт запуска робота с 1-го клика по аналогии с .exe файлами

Ниже приведу пример "3. MonitoringCIAN_Run_64.cmd" файла:


cd %~dp0..\..\..\Sources..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe "..\Wiki\RUS_Tutorial\WebGUI_Habr\3. MonitoringCIAN_Run_64.py"pause >nul

Для инициализации Selenium WebDriver воспользуемся следующей функцией:


########################### Init the Chrome web driver###########################def WebDriverInit(inWebDriverFullPath, inChromeExeFullPath, inExtensionFullPathList):    # Set full path to exe of the chrome    lWebDriverChromeOptionsInstance = webdriver.ChromeOptions()    lWebDriverChromeOptionsInstance.binary_location = inChromeExeFullPath    # Add extensions    for lExtensionItemFullPath in inExtensionFullPathList:        lWebDriverChromeOptionsInstance.add_extension (lExtensionItemFullPath)    # Run chrome instance    lWebDriverInstance = None    if inWebDriverFullPath:        # Run with specified web driver path        lWebDriverInstance = webdriver.Chrome(executable_path = inWebDriverFullPath, options=lWebDriverChromeOptionsInstance)    else:        lWebDriverInstance = webdriver.Chrome(options = lWebDriverChromeOptionsInstance)    # Return the result    return lWebDriverInstance

Шаг 2. Запустить WEB инструменты разработчика и сформировать CSS селекторы


В нашем случае WEB инструменты разработчика мы будем использовать из Google Chrome, который предустановлен в репозитории pyOpenRPA (вариант 1 из шага 0).


Откроем Google Chrome и инструменты разработчика (pyOpenRPA repo\Resources\GoogleChromePortable\App\Chrome-bin\chrome.exe, после чего Ctrl + Shift + i)
Portable Google Chrome + Dev Tools


Откроем в браузере сайт, который мы будем анализировать. Сформируем область поиска и отобразить обнаруженные ценовые предложения в виде списка.


Пример поискового запроса


Список ценовых предложений по фильтру


Для того, чтобы подобрать CSS селектор нам помогут инструменты разработчика Google Chrome. Подробнее узнать про устройство CSS селекторов можно здесь по ссылке
Для проверки правильности CSS селектора я буду делать следующую проверку в инструментах разработчика на вкладке "Console". На картинке представлен пример того, как проводится проверки правильности CSS селектора для извлечения списка ценовых предложений.


Пример проверки CSS селектора


Подберем CSS селектор для выборки списка ценовых предложений на странице.


При составлении селектора выяснилось, что в список объявлений встроены рекламные баннеры, которые не содержат информацию о ценовом предложении.


И таких видов рекламных баннеров было обнаружено несколько видов:


  • div[data-name="BannerServicePlaceInternal"]
  • div[data-name="getBannerMarkup"]
  • div[data-name="AdFoxBannerTracker"]

В связи с этим CSS селектор должен быть скорректирован таким образом, чтобы исключить из выборки такие виды баннеров. Ниже приведен готовый CSS селектор.


  • CSS селектор, Список ценовых предложений: div[data-name="Offers"] > div:not([data-name="BannerServicePlaceInternal"]):not([data-name="getBannerMarkup"]):not([data-name="AdFoxBannerTracker"])

Подберем CSS селекторы по извлечению параметров ценового предложения: Заголовок, Стоимость общая, URL ссылка на карточку.


  • CSS селектор, Заголовок: div[data-name="TopTitle"],div[data-name="Title"]
  • CSS селектор, Стоимость общая: div[data-name="Price"] > div[class="header"],div[data-name="TopPrice"] > div[class="header"]
  • CSS селектор, URL ссылка на карточку: a[class*="--header--"]

Подберем CSS селектор для извлечения кнопки на следующую страницу.


  • CSS селектор, Указатель на следующую страницу: div[data-name="Pagination"] li[class*="active"] + li a

Шаг 3. Обработать/преобразовать получаемые данные


На предыдущем шаге мы успешно подобрали все необходимые CSS селекторы. Теперь нам нужно грамотно извлечь информацию, а потом и обработать ее.


В результате обработки ценового предложения у нас будет сформирована структура следующего вида:


lOfferItemInfo = { # Item URL with https    "TitleStr": "3-комн. кв., 31,4 м, 5/8 этаж", # Offer title [str]    "PriceFloat": 10000000.0, # Price [float]    "PriceSqmFloat": 133333.0, # CALCULATED Price per square meters [float]    "SqMFloat": 31.4, # Square meters in flat [float]    "FloorCurrentInt": 5, # Current floor [int]    "FloorTotalInt": 8, # Current floor [int]    "RoomCountInt": 3  # Room couint [int]}

Для начала получим список элементов ценовых предложений.


lOfferListCSSStr = 'div[data-name="Offers"] > div:not([data-name="BannerServicePlaceInternal"]):not([data-name="getBannerMarkup"]):not([data-name="AdFoxBannerTracker"])'lOfferList = inWebDriver.find_elements_by_css_selector(css_selector=lOfferListCSSStr)

Далее циклическая обработка каждого ценового предложения.


for lOfferItem in lOfferList:

Извлечем параметры из WEB страницы: Заголовок, Стоимость общая, URL на карточку.


lTitleStr = lOfferItem.find_element_by_css_selector(css_selector='div[data-name="TopTitle"],div[data-name="Title"]').text # Extract title textlPriceStr = lOfferItem.find_element_by_css_selector(css_selector='div[data-name="Price"]  > div[class*="header"],div[data-name="TopPrice"] > div[class*="header"]').text # Extract total pricelURLStr = lOfferItem.find_element_by_css_selector(css_selector='a[class*="--header--"]').get_attribute("href") # Extract offer URLlOfferItemInfo["TitleStr"] = lTitleStr # set the titlelPriceStr = lPriceStr.replace(" ","").replace("","") # Remove some extra symbolslOfferItemInfo["PriceFloat"] = round(float(lPriceStr),2) # Convert price to the float type

Извлечем недостающие параметры алгоритмическим путем.


  • Если в заголовке содержится слово "Апартаменты"


    lREResult = re.search(r".*, (\d*,?\d*) м, (\d*)/(\d*) эта.", lTitleStr)  # run the relOfferItemInfo["RoomCountInt"] = 1 # Room countlSqmStr = lREResult.group(1)lSqmStr= lSqmStr.replace(",",".")lOfferItemInfo["SqMFloat"] = round(float(lSqmStr),2) # sqm countlOfferItemInfo["FloorCurrentInt"] = int(lREResult.group(2)) # Floor currentlOfferItemInfo["FloorTotalInt"] = int(lREResult.group(3)) # Floor totallOfferItemInfo["PriceSqmFloat"] = round(lOfferItemInfo["PriceFloat"] / lOfferItemInfo["SqMFloat"],2) # Sqm per M
    

  • Если в заголовке не содержится слово "Апартаменты"


    lREResult = re.search(r".*(\d)-комн. .*, (\d*,?\d*) м, (\d*)/(\d*) эта.", lTitleStr) # run the relOfferItemInfo["RoomCountInt"] = int(lREResult.group(1)) # Room countlSqmStr = lREResult.group(2)lSqmStr= lSqmStr.replace(",",".")lOfferItemInfo["SqMFloat"] = round(float(lSqmStr),2) # sqm countlOfferItemInfo["FloorCurrentInt"] = int(lREResult.group(3)) # Floor currentlOfferItemInfo["FloorTotalInt"] = int(lREResult.group(4)) # Floor totallOfferItemInfo["PriceSqmFloat"] = round(lOfferItemInfo["PriceFloat"] / lOfferItemInfo["SqMFloat"],2) # Sqm per M
    


В примере выше применяется магия регулярных выражений


Для подбора правильных регулярных выражений я пользуюсь online валидаторами типа таких


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


Выше (на шаге 2) мы уже находили CSS селектор указателя на следующую страницу. Нам нужно выполнить действие клика .click() по этому элементу.
Но при тестировании выяснилось, что функция .click от Selenium отрабатывает некорректно для этой страницы (не происходит переключение). В связи с этим у нас есть уникальная возможность использовать функциональность JavaScript на самой странице через Selenium. А уже из JavaScript выяснилось, что функция нажатия по указателю страницы отрабатывает корректно. Для этого выполним следующую команду:


inWebDriver.execute_script("""document.querySelector('div[data-name="Pagination"] li[class*="active"] + li a').click()""")

После того как был отправлен сигнал на переключение страницы, необходимо дождаться ее загрузки. Только после появления новой страницы мы сможем перейти к обработке новых ценовых предложений.


# wait while preloader is activelDoWaitBool = Truewhile lDoWaitBool:    lPreloaderCSS = inWebDriver.find_elements_by_css_selector(css_selector='div[class*="--preloadOverlay--"]') # So hard to catch the element :)    if len(lPreloaderCSS)>0: time.sleep(0.5) # preloader is here - wait    else: lDoWaitBool = False # Stop wait if preloader is dissappear

Итоговую структуру сохраним в .json файл.


# Check dir - create if not existsif not os.path.exists(os.path.join('Datasets',lResult['SearchKeyStr'])):    os.makedirs(os.path.join('Datasets',lResult['SearchKeyStr']))# Save result in filelFile = open(f"{os.path.join('Datasets',lResult['SearchKeyStr'],lDatetimeNowStr.replace(' ','_').replace('-','_').replace(':','_').replace('.','_'))}.json","w",encoding="utf-8")lFile.write(json.dumps(lResult))lFile.close()

Шаг 4. Обработка нештатных ситуаций


Этап тестирования это один из самых важных этапов, который позволяет конвертировать вложенные усилия в реальный эффект. При тестировании кода на этом WEB приложении выяснилось, что могут происходить некоторые сбои. Ниже привожу те виды сбоев, которые встретились у меня:


  • Зависает ползунок загрузки при переключении на сл. страницу
  • При переключении на следующую страницу открывается совсем не следующая страница (иногда, но случалось :) )

Но роботы не боятся таких проблем (на то они и роботы :) ).


Для каждого вида сбоя мы предусмотрим сценарий восстановления, который позволит роботы доделать свою работу до конца.


  • Зависает ползунок загрузки при переключении на сл. страницу


    # wait while preloader is active. If timeout - retry all joblTimeFromFLoat = time.time() # get current time in float (seconds)lDoWaitBool = Truewhile lDoWaitBool:lPreloaderCSS = inWebDriver.find_elements_by_css_selector(css_selector='div[class*="--preloadOverlay--"]')if len(lPreloaderCSS)>0: time.sleep(0.5) # preloader is here - waitelse: lDoWaitBool = False # Stop wait if preloader is dissappearif (time.time() - lTimeFromFLoat) > 15: # check if timeout is more than 15 seconds    lRetryJobBool = True # Loading error on page - do break, then retry the job    if inLogger: inLogger.warning(f"Ожидание загрузки страницы более {15} с., Робот повторит задание сначала")    break # break the loopif lRetryJobBool == True: # break the loop if RetryJobBool is truebreak
    

  • При переключении на следующую страницу открывается совсем не следующая страница (иногда, но случалось :) )


    lPageNumberInt = int(inWebDriver.find_element_by_css_selector(css_selector='li[class*="--active--"] span').text) # Get the current page int from web and check with iterator (if not equal - retry all job)if lPageNumberInt == lPageCounterInt:... Код робота ...else:lRetryJobBool = Trueif inLogger: inLogger.warning(    f"Следующая страница по списку не была загружена. Была загружена страница: {lPageNumberInt}, Ожидалась страница: {lPageCounterInt}")
    


Шаг 5. Консолидировать код в проекте робота


Соберем все блоки воедино.


Получим следующий пакет (открыть на GitLab):


# Init Chrome web driver with extensions (if applicable)# Import sectionfrom selenium import webdriverimport timeimport re # Regexp to extract info from stringimport jsonimport datetimeimport osimport reimport copyimport logging# Store structure (.json)"""{    "SearchKeyStr": "МСК_Тверской",    "SearchTitleStr": "Москва, район Тверской", # Title of the search [str]    "SearchURLStr": "https://www.cian.ru/cat.php?deal_type=sale&engine_version=2&in_polygon%5B1%5D=37.6166_55.7678%2C37.6147_55.7688%2C37.6114_55.7694%2C37.6085_55.7698%2C37.6057_55.77%2C37.6018_55.77%2C37.5987_55.77%2C37.5961_55.7688%2C37.5942_55.7677%2C37.5928_55.7663%2C37.5915_55.7647%2C37.5908_55.7631%2C37.5907_55.7616%2C37.5909_55.7595%2C37.5922_55.7577%2C37.5944_55.7563%2C37.5968_55.7555%2C37.6003_55.7547%2C37.603_55.7543%2C37.6055_55.7542%2C37.6087_55.7541%2C37.6113_55.7548%2C37.6135_55.756%2C37.6151_55.7574%2C37.6163_55.7589%2C37.6179_55.7606%2C37.6187_55.7621%2C37.619_55.7637%2C37.6194_55.7651%2C37.6193_55.7667%2C37.6178_55.7679%2C37.6153_55.7683%2C37.6166_55.7678&offer_type=flat&polygon_name%5B1%5D=%D0%9E%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D1%8C+%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&room1=1&room2=1", # URL of the CIAN search [str]    "SearchDatetimeStr": "2020-08-01 09:33:00.838081", # Date of data extraction,  [str]    "SearchItems": {        "https://www.cian.ru/sale/flat/219924574/:": { # Item URL with https            "TitleStr": "3-комн. кв., 31,4 м, 5/8 этаж", # Offer title [str]            "PriceFloat": 10000000.0, # Price [float]            "PriceSqmFloat": 133333.0, # CALCULATED Price per square meters [float]            "SqMFloat": 31.4, # Square meters in flat [float]            "FloorCurrentInt": 5, # Current floor [int]            "FloorTotalInt": 8, # Current floor [int]            "RoomCountInt": 3 # Room couint [int]        }    }}"""########################### Init the Chrome web driver###########################gChromeExeFullPath = r'..\Resources\GoogleChromePortable\App\Chrome-bin\chrome.exe'gExtensionFullPathList = []gWebDriverFullPath = r'..\Resources\SeleniumWebDrivers\Chrome\chromedriver_win32 v84.0.4147.30\chromedriver.exe'def WebDriverInit(inWebDriverFullPath, inChromeExeFullPath, inExtensionFullPathList):    # Set full path to exe of the chrome    lWebDriverChromeOptionsInstance = webdriver.ChromeOptions()    lWebDriverChromeOptionsInstance.binary_location = inChromeExeFullPath    # Add extensions    for lExtensionItemFullPath in inExtensionFullPathList:        lWebDriverChromeOptionsInstance.add_extension (lExtensionItemFullPath)    # Run chrome instance    lWebDriverInstance = None    if inWebDriverFullPath:        # Run with specified web driver path        lWebDriverInstance = webdriver.Chrome(executable_path = inWebDriverFullPath, options=lWebDriverChromeOptionsInstance)    else:        lWebDriverInstance = webdriver.Chrome(options = lWebDriverChromeOptionsInstance)    # Return the result    return lWebDriverInstancefrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as EC# def to extract list of offers from one jobdef OffersByJobExtractDict(inLogger, inWebDriver, inJob):    # BUG 0 - if timeout - retry the job +    # BUG 1 - do mouse scroll to to emulate user activity - cian can hold the robot    # BUG 2 - check the page to retry job offer if page is not next +    # BUG 3 - RE fall on Апартаменты-студия, 85,6 м, 4/8 этаж +    lRetryJobBool = True # Init flag if some error is raised - retry    while lRetryJobBool:        lRetryJobBool = False  # Set false until some another action will appear        lResult = copy.deepcopy(inJob) # do copy the structure        lFilterURLStr = lResult["SearchURLStr"]        inWebDriver.get(lFilterURLStr) # Open the URL        lDatetimeNowStr = str(datetime.datetime.now())        lResult.update({            "SearchDatetimeStr": lDatetimeNowStr, # Date of data extraction,  [str]            "SearchItems": {} # prepare the result        })        # Get List of the page        lNextPageItemCSS = 'div[data-name="Pagination"] li[class*="active"] + li a'        lNextPageItem = inWebDriver.find_element_by_css_selector(lNextPageItemCSS)        lPageCounterInt = 1 # Init the page counter        while lNextPageItem:            lPageNumberInt = int(inWebDriver.find_element_by_css_selector(css_selector='li[class*="--active--"] span').text) # Get the current page int from web and check with iterator (if not equal - retry all job)            if lPageNumberInt == lPageCounterInt:                lOfferListCSSStr = 'div[data-name="Offers"] > div:not([data-name="BannerServicePlaceInternal"]):not([data-name="getBannerMarkup"]):not([data-name="AdFoxBannerTracker"])'                lOfferList = inWebDriver.find_elements_by_css_selector(css_selector=lOfferListCSSStr)                for lOfferItem in lOfferList: # Processing the item, extract info                    lOfferItemInfo = { # Item URL with https                        "TitleStr": "3-комн. кв., 31,4 м, 5/8 этаж", # Offer title [str]                        "PriceFloat": 10000000.0, # Price [float]                        "PriceSqmFloat": 133333.0, # CALCULATED Price per square meters [float]                        "SqMFloat": 31.4, # Square meters in flat [float]                        "FloorCurrentInt": 5, # Current floor [int]                        "FloorTotalInt": 8, # Current floor [int]                        "RoomCountInt": 3  # Room couint [int]                    }                    lTitleStr = lOfferItem.find_element_by_css_selector(css_selector='div[data-name="TopTitle"],div[data-name="Title"]').text # Extract title text                    if inLogger: inLogger.info(f"Старт обработки предложения: {lTitleStr}")                    lPriceStr = lOfferItem.find_element_by_css_selector(css_selector='div[data-name="Price"]  > div[class*="header"],div[data-name="TopPrice"] > div[class*="header"]').text # Extract total price                    lURLStr = lOfferItem.find_element_by_css_selector(css_selector='a[class*="--header--"]').get_attribute("href") # Extract offer URL                    lOfferItemInfo["TitleStr"] = lTitleStr # set the title                    lPriceStr = lPriceStr.replace(" ","").replace("","") # Remove some extra symbols                    lOfferItemInfo["PriceFloat"] = round(float(lPriceStr),2) # Convert price to the float type                    #Check if Апартаменты                    if "АПАРТАМЕНТ" in lTitleStr.upper():                        lREResult = re.search(r".*, (\d*,?\d*) м, (\d*)/(\d*) эта.", lTitleStr)  # run the re                        lOfferItemInfo["RoomCountInt"] = 1 # Room count                        lSqmStr = lREResult.group(1)                        lSqmStr= lSqmStr.replace(",",".")                        lOfferItemInfo["SqMFloat"] = round(float(lSqmStr),2) # sqm count                        lOfferItemInfo["FloorCurrentInt"] = int(lREResult.group(2)) # Floor current                        lOfferItemInfo["FloorTotalInt"] = int(lREResult.group(3)) # Floor total                        lOfferItemInfo["PriceSqmFloat"] = round(lOfferItemInfo["PriceFloat"] / lOfferItemInfo["SqMFloat"],2) # Sqm per M                    else:                        lREResult = re.search(r".*(\d)-комн. .*, (\d*,?\d*) м, (\d*)/(\d*) эта.", lTitleStr) # run the re                        lOfferItemInfo["RoomCountInt"] = int(lREResult.group(1)) # Room count                        lSqmStr = lREResult.group(2)                        lSqmStr= lSqmStr.replace(",",".")                        lOfferItemInfo["SqMFloat"] = round(float(lSqmStr),2) # sqm count                        lOfferItemInfo["FloorCurrentInt"] = int(lREResult.group(3)) # Floor current                        lOfferItemInfo["FloorTotalInt"] = int(lREResult.group(4)) # Floor total                        lOfferItemInfo["PriceSqmFloat"] = round(lOfferItemInfo["PriceFloat"] / lOfferItemInfo["SqMFloat"],2) # Sqm per M                    lResult['SearchItems'][lURLStr] = lOfferItemInfo # Set item in result dict                # Click next page item                lNextPageItem = None                lNextPageList = inWebDriver.find_elements_by_css_selector(lNextPageItemCSS)                if len(lNextPageList)>0:                    lNextPageItem = lNextPageList[0]                    try:                        #lNextPageItem = WebDriverWait(lWebDriver, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, 'div[data-name="Pagination"]')))                        #lNextPageItem.click()                        inWebDriver.execute_script("""document.querySelector('div[data-name="Pagination"] li[class*="active"] + li a').click()""")                    except Exception as e:                        print(e)                    time.sleep(0.5) # some init operations                    # wait while preloader is active. If timeout - retry all job                    lTimeFromFLoat = time.time() # get current time in float (seconds)                    lDoWaitBool = True                    while lDoWaitBool:                        lPreloaderCSS = inWebDriver.find_elements_by_css_selector(css_selector='div[class*="--preloadOverlay--"]')                        if len(lPreloaderCSS)>0: time.sleep(0.5) # preloader is here - wait                        else: lDoWaitBool = False # Stop wait if preloader is dissappear                        if (time.time() - lTimeFromFLoat) > 15: # check if timeout is more than 15 seconds                            lRetryJobBool = True # Loading error on page - do break, then retry the job                            if inLogger: inLogger.warning(f"Ожидание загрузки страницы более {15} с., Робот повторит задание сначала")                            break # break the loop                    if lRetryJobBool == True: # break the loop if RetryJobBool is true                        break                lPageCounterInt = lPageCounterInt + 1 # Increment the page counter            else:                lRetryJobBool = True                if inLogger: inLogger.warning(                    f"Следующая страница по списку не была загружена. Была загружена страница: {lPageNumberInt}, Ожидалась страница: {lPageCounterInt}")        if lRetryJobBool == False:  # break the loop if RetryJobBool is true            # Check dir - create if not exists            if not os.path.exists(os.path.join('Datasets',lResult['SearchKeyStr'])):                os.makedirs(os.path.join('Datasets',lResult['SearchKeyStr']))            # Save result in file            lFile = open(f"{os.path.join('Datasets',lResult['SearchKeyStr'],lDatetimeNowStr.replace(' ','_').replace('-','_').replace(':','_').replace('.','_'))}.json","w",encoding="utf-8")            lFile.write(json.dumps(lResult))            lFile.close()# Инициализировать Google Chrome with selenium web driverlWebDriver = WebDriverInit(inWebDriverFullPath = gWebDriverFullPath, inChromeExeFullPath = gChromeExeFullPath, inExtensionFullPathList = gExtensionFullPathList)lFilterURLStr = "https://www.cian.ru/cat.php?deal_type=sale&engine_version=2&in_polygon%5B1%5D=37.6166_55.7678%2C37.6147_55.7688%2C37.6114_55.7694%2C37.6085_55.7698%2C37.6057_55.77%2C37.6018_55.77%2C37.5987_55.77%2C37.5961_55.7688%2C37.5942_55.7677%2C37.5928_55.7663%2C37.5915_55.7647%2C37.5908_55.7631%2C37.5907_55.7616%2C37.5909_55.7595%2C37.5922_55.7577%2C37.5944_55.7563%2C37.5968_55.7555%2C37.6003_55.7547%2C37.603_55.7543%2C37.6055_55.7542%2C37.6087_55.7541%2C37.6113_55.7548%2C37.6135_55.756%2C37.6151_55.7574%2C37.6163_55.7589%2C37.6179_55.7606%2C37.6187_55.7621%2C37.619_55.7637%2C37.6194_55.7651%2C37.6193_55.7667%2C37.6178_55.7679%2C37.6153_55.7683%2C37.6166_55.7678&offer_type=flat&polygon_name%5B1%5D=%D0%9E%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D1%8C+%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0&room1=1&room2=1"lJobItem = {    "SearchKeyStr": "МСК_Тверской",    "SearchTitleStr": "Москва, район Тверской",  # Title of the search [str]    "SearchURLStr": lFilterURLStr,    # URL of the CIAN search [str]}OffersByJobExtractDict(inLogger = logging, inWebDriver = lWebDriver, inJob = lJobItem)

Подведем итоги


Уважаемые роботизаторы.


Мы успешно преодолели вторую серию туториалов по созданию роботов в WEB приложениях с помощью open source pyOpenRPA. Готовый проект робота Вы можете найти в репозитории pyOpenRPA по ссылочке.
В нашем аресенале уже имеются изученные технологии упраления Desktop и WEB приложениями. В следующей статье-туториале мы остановимся на особенностях роботизированного управления мышью и клавиатурой.


Пишите комменты, внедряйте бесплатных роботов, будьте счастливы :)


До скорых публикаций!

Подробнее..

Дефицит цветов в современных фильмах

26.10.2020 02:07:54 | Автор: admin

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

Well, enough said. Источник: https://gridfiti.com/visually-stunning-movies/Well, enough said. Источник: https://gridfiti.com/visually-stunning-movies/

Я в таких своих наблюдениях оказался совсем не одинок, вот, например, критическая статья о злоупотреблении этими вашими Голливудами оранжевым и голубовато-зелёным. Однако, одиночные кадры - это не серьёзно для доказательства. Тут нужен научный подход. Как астроном-любитель я знаю, что информацию о цвете чего-либо принято сравнивать в виде спектра.

Эмиссионный спектр бора. Источник: https://commons.wikimedia.org/wiki/File:Boronemissionspectrum.pngЭмиссионный спектр бора. Источник: https://commons.wikimedia.org/wiki/File:Boronemissionspectrum.png

Хочется получить такой вот примерно "спектр" для целого видео. Осталось придумать, как это сделать.

Идея

Упрощённо, видео есть последовательность изображений. Каждое изображение - массив RGB значений цветов пикселей. Можно заметить, что в спектре есть две оси: цвет (или длина волны) по горизонтали и уровень сигнала по вертикали. Значит, необходимо превратить трёхмерный (RGB) цвет в одномерный. И тут на помощь приходит цветовая модель HSL, где для цвета есть только одно измерение - Hue, в дополнение к Saturation и Lightness.

Теперь, для каждого пикселя видео и соответствующего ему значения "чистого цвета" (Hue) нужно определить вес чистого цвета на основе значений Saturation и Lightness. В случае с Saturation всё понятно и просто - вес должен быть пропорционален насыщенности цвета. С Lightness немножко сложнее: начения 0% и 100% будут соответствовать чёрному и белому цветам вне зависимости от значения Hue, поэтому максимальный вес (1) должен быть для значения Lightness = 50%, снижаясь "по краям" до 0, так как совсем светлые и совсем тёмные оттенки должны иметь меньший вес. Таким образом, можно вывести формулу уровня цветового сигнала:

W (S, L) = S * (0.52 - (0.5 - L)2) / 0.52

Это значение W (weight) будет численной мерой, показывающей, насколько данный цвет выглядит ярким и выраженным. Далее, для получения спектра достаточно сложить все полученные из пикселей веса (W). Стоит отдельно заметить то, что полученный "вес выразительности" цвета всё-таки субъективен, так как зависимость от яркости (L) может быть иной, например линейной. Или какой-нибудь ещё. Но я считаю свой вариант довольно честным.

Реализация

Осталось реализовать всё это в коде и я решил сделать это на Golang. К счастью, все необходимые биндинги для превращения видео в отдельные кадры, конвертации RGB в HSL и прочего уже доступны. Исходное видео конвертируется в кадры с разрешением 256х144, то есть ~37 килопикселей. Чтобы избежать слишком задумчивого переваривания при скармливании больших видеофайлов код может пропускать промежуточные кадры, чтобы не превысить лимит количества обрабатываемых кадров (хард код 2712 на данный момент). То есть любое видео будет ограничено более-менее равномерной выборкой из 100 миллионов пикселей. Для цветовой статистики вроде бы должно быть достаточно. Есть небольшая проблема, что не все промежуточные кадры одинаково полезны могут быть успешно сконвертированы. Судя по всему это происходит при попадании на не-ключевые кадры. Это может немного уменьшать выборку из-за ошибок.

Изначально это была консольная утилита, получающая на вход имя видеофайла и сохраняющая файл спектра в файл с тем же именем плюс разрешение ".svg". Позже я также добавил простейший web-cервис и форму для upload видео файла в газоанализатор для получения "спектра". Сервис раздеплоен временно-бесплатно в первом попавшемся облаке, так что желающие могут попробовать немножко поэкспериментировать: https://moviespectrum.azurewebsites.net/.

На размер загружаемого видеофайла стоит ограничение 128 Мб, поэтому рекомендуется использовать не самое лучшее разрешение, тем более что оно слабо влияет на цветовую статистику и видео всё равно конвертируется в кадры 256х144 перед обработкой.

В облаке сервис работает на минимальных ресурсах и никак не приспособлен для масштабирования, так что если он вдруг приляжет вследствие хабраэффекта или по какой другой причине, то вы всегда можете собрать его из безисходников и запустить локально, например в докере: https://github.com/akurilov/moviespectrum

Также сначала была идея прикрутить это всё к тытубу, чтобы пользователь мог просто кинуть ссылку на видео, однако из-за инцидента с youtube-dl я решил не рисковать и отпилить всю подобную функциональность. Даже вспомнилось Unix-заклинание "один инструмент = одна функция" в качестве оправдания. Так что теперь придётся сначала скачать видео, а потом уже скармливать его.

Результаты

Возьмём в качестве baseline пару олдскульных фильмов и посмотрим на их "спектры".

1991 - Терминатор 2:

1994 - Форрест Гамп:

Теперь возьмём фильмы посвежее и сравним:

1999 - Матрица aka 50 оттенков зелёного, если не считать фиолетового артефакта:

2003 - Властелин колец:

2005 - Город грехов. В качестве малоцветного baseline:

2007 - Трансформеры. Очень малоцветный фильм, не считая фиолетового артефакта:

2009 - Аватар:

2013 - Обливион:

2019 - Джокер. Всё почти в красном цвете, но есть немножно зеленовато-голубого.

Выводы вы можете сделать самостоятельно. На мой взгляд, есть тенденция к обеднению цветов со временем.

Подробнее..
Категории: Работа с видео , Rgb , Golang , Svg , Ffmpeg , Opensource , Movies , Hsl

Сборка логов в kubernetes. Установка EFK стека с LDAP интеграцией. (Bitnami, opendistro)

26.01.2021 10:19:42 | Автор: admin

Постепенно эволюционируя, каждая организация переходит от ручного grep логов к более современным инструментам для сбора, анализа логов. Если вы работаете с kubernetes, где приложение может масштабироваться горизонтально и вертикально, вас просто вынуждают отказаться от старой парадигмы сбора логов. В текущее время существует большое количество вариантов систем централизованного логирования, причем каждый выбирает наиболее приемлемый вариант для себя. В данной статье пойдет речь о наиболее популярном и зарекомендовавшем себя стэке Elasticsearch + Kibana + Fluentd в связке с плагином OpenDistro Security. Данный стэк полностью open source, что придает ему особую популярность.

Проблематика

Нам необходимо было наладить сборку логов с кластера kubernetes. Из требований:

  • Использовать только открытые решения.

  • Сделать очистку логов.

  • Необходимо прикрутить LDAP, для разграничения прав.

Пререквизиты

Данный материал предполагает:

  • Имеется установленный kuberenetes 1.18 или выше (ниже версию не проверяли)

  • Установленный пакетный менеджер helm 3

Немного о helm chart

Наиболее популярным способом установки приложения в kubernetes является helm. Helm это пакетный менеджер, с помощью которого можно подготовить набор компонентов для установки и связать их вместe, дополнив необходимыми метриками и конфигурацией.

В своей практике мы используем helm chart от компании bitnami(подразделение vmware)

  • собраны open source продукты на все случаи жизни.

  • корпоративные стандарты по разработке чатов и докер образов

  • красивая документация

  • проект живой и активно поддерживается сообществом

Выбор стека

Во многом выбор стека технологий определило время. С большой долей вероятностью два года назад мы бы деплоили ELK стек вместо EFK и не использовали helm chart.

Fluentd часто упоминается в документации, широко распространен, имеет большое количество плагинов, на все случаи жизни. К тому же у нас есть человек, который после обучение в rebrain и очень хотел внедрить fluentd.

Elasticsearch и kibana поставляются с открытой лицензией, однако плагины для security и других вкусностей идут под иной лицензией. Однако компания Amazon выпустила плагины Open Distro, которые покрывают оставшийся функционал под открытой лицензией.

Схема выглядит примерно так

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

Минимальный деплой EFK стека (без Security)

Сборка EFK стека была произведена по статье Collect and Analyze Log Data for a Kubernetes Cluster with Bitnami's Elasticsearch, Fluentd and Kibana Charts. Компоменты упакованы в отдельный чат. Исходники можно взять здесь и произвести командой

helm dependency updatehelm upgrade --install efk . -f values-minimal.yaml

Из исходников values-minimal.yaml

elasticsearch:  volumePermissions:    enabled: truekibana:  volumePermissions:    enabled: true  elasticsearch:    hosts:    - "efk-elasticsearch-coordinating-only"    port: 9200# Пропишите свой хост, если используете ингресс  ingress:    enabled: true    hostname: kibana.local# Либо   service:    type: NodePort    port: 30010fluentd:  aggregator:    enabled: true    configMap: elasticsearch-output    extraEnv:    - name: ELASTICSEARCH_HOST      value: "efk-elasticsearch-coordinating-only"    - name: ELASTICSEARCH_PORT      value: "9200"  forwarder:#    Чтение логов с диска /var/log/containers/* отключено    enabled: false    configMap: apache-log-parser    extraEnv:    - name: FLUENTD_DAEMON_USER      value: root    - name: FLUENTD_DAEMON_GROUP      value: root

Кибана будет доступна по адресу http://minikube_host:30010/, либо http://kibana.local.

Как видим компонент fluentd forwarder не включен. Перед включением парсинга, я рекомендовал бы настроить на определенные логи, иначе еластику может быть послано слишком большое количество логов. Правила для парсинга описаны в файле apache-log-parser.yaml.

Как отправить логи в EFK

Существует много способов, для начала предлагаем либо включить fluentd forwarder, либо воспользоваться простейшим приложением на python. Ниже пример для bare metal. Исправьте FLUENT_HOST FLUENT_PORT на ваши значения.

cat <<EOF | kubectl apply -f -apiVersion: apps/v1kind: Deploymentmetadata:  name: helloworld  labels:    app: helloworldspec:  replicas: 1  template:    metadata:      name: helloworld      labels:        app: helloworld    spec:      containers:        - name: helloworld          image: sergbs/django-hello-world:1          imagePullPolicy: Always          ports:            - containerPort: 8000          env:            - name: FLUENT_HOST              value: "efk-fluentd-headless"            - name: FLUENT_PORT              value: "24224"  selector:    matchLabels:      app: helloworld---apiVersion: v1kind: Servicemetadata:  name: helloworldspec:  selector:    app: helloworld  ports:    - port: 8000      nodePort: 30011  type: NodePortEOF

По ссылке http://minikube_host:30011/ Будет выведено "Hello, world!" И лог уйдет в elastic

Пример

Включить fluentd forwarder, он создаст daemon set, т.е. запустится на каждой ноде вашего кубернетеса и будет читать логи docker container-ов.

Добавить в ваш докер контейнер драйвер fluentd, тем более можно добавлять более одного драйвера

Добавить в ваше приложение библиотеку, которая будет напрямую логировать. Пример приложения на python. Используйте его, как отладочное средство при установке EFK.

И как показывает практика, логировать напрямую эффективный, но далеко не самый надежный способ. Даже если вы логируете сразу в fluentd и в консоль. В случае потери конекта, во fluentd часть логов просто не смогут попасть и будут потеряны для него навсегда. Поэтому наиболее надежный способ, это считывать логи с диска для отправки в EFK.

Очистка логов.

Для очистки логов используется curator. Его можно включить, добавив в yaml файл:

elasticsearch:  curator:    enabled: true

По умолчанию его конфигурация уже предусматривает удаление через индекса старше 90 дней это можно увидеть внутри подчата efk/charts/elasticsearch-12.6.1.tgz!/elasticsearch/values.yaml

configMaps:  # Delete indices older than 90 days  action_file_yml: |-      ... unit: daysunit_count: 90

Security

Как обычно security доставляет основную боль при настройке и использовании. Но если ваша организация чуть подросла, это необходимый шаг. Стандартом де факто при настройке безопасности является интеграция с LDAP. Официальные плагины от еластика выходят не под открытой лицензией, поэтому приходится использовать плагин Open Distro. Далее продемонстрируем, как его можно запустить.

Сборка elasticsearch c плагином opendistro

Вот проект в котором собирали docker images.

Для установки плагина, необходимо, чтобы версия elasticsearch соответствовала версии плагина.

В quickstart плагина рекомендуется установить install_demo_configuration.sh с демо сертификатами.

FROM bitnami/elasticsearch:7.10.0RUN elasticsearch-plugin install -b https://d3g5vo6xdbdb9a.cloudfront.net/downloads/elasticsearch-plugins/opendistro-security/opendistro_security-1.12.0.0.zipRUN touch /opt/bitnami/elasticsearch/config/elasticsearch.ymlUSER rootRUN /bin/bash /opt/bitnami/elasticsearch/plugins/opendistro_security/tools/install_demo_configuration.sh -y -iRUN mv /opt/bitnami/elasticsearch/config/elasticsearch.yml /opt/bitnami/elasticsearch/config/my_elasticsearch.ymlCOPY my_elasticsearch.yml /opt/bitnami/elasticsearch/config/my_elasticsearch.ymlUSER 1001

Есть небольшая магия, ввиду, того что плагин дополняет elasticsearch.yml, а контейнеры bitnami только при старте генерируют этот файл. Дополнительные же настройки они просят передавать через my_elasticsearch.yml

В my_elasticsearch.yml мы изменили настройку, это позволит нам обращаться к рестам elasticsearch по http.

# turn off REST layer tlsopendistro_security.ssl.http.enabled: false

Сделано это в демонстрационных целях, для облегчения запуска плагина. Если вы захотите включить Rest Layer tls придется добавлять соответствующие настройки во все компоненты, которые общаются с elasticsearch.

Запуск docker-compose с LDAP интеграцией

Запустим проект с помощью docker-compose up , и залогинимся на http://0:5601 под admin/admin

Теперь у нас есть вкладка Security

Можно посмотреть настройку LDAP через интерфейс кибаны

Настройки должны совпадать с файлами конфигурации. Стоит обратить внимание, что плагин хранит свои данные в индексе еластика, и файлы конфигурации применяет при инициализации, т.е. если индекс не создан. Если вы измените файлы позже то вам придется воспользоваться утилитой securityadmin.sh

docker exec -it elasticsearch /bin/bashI have no name!@68ac2255bb85:/$ ./securityadmin_demo.shOpen Distro Security Admin v7Will connect to localhost:9300 ... doneConnected as CN=kirk,OU=client,O=client,L=test,C=deElasticsearch Version: 7.10.0Open Distro Security Version: 1.12.0.0Contacting elasticsearch cluster 'elasticsearch' and wait for YELLOW clusterstate ...Clustername: elasticsearchClusterstate: YELLOWNumber of nodes: 1Number of data nodes: 1.opendistro_security index already exists, so we do not need to create one.Populate config from /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/Will update '_doc/config' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/config.yml    SUCC: Configuration for 'config' created or updatedWill update '_doc/roles' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/roles.yml    SUCC: Configuration for 'roles' created or updatedWill update '_doc/rolesmapping' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/roles_mapping.yml    SUCC: Configuration for 'rolesmapping' created or updatedWill update '_doc/internalusers' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/internal_users.yml    SUCC: Configuration for 'internalusers' created or updatedWill update '_doc/actiongroups' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/action_groups.yml    SUCC: Configuration for 'actiongroups' created or updatedWill update '_doc/tenants' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/tenants.yml    SUCC: Configuration for 'tenants' created or updatedWill update '_doc/nodesdn' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/nodes_dn.yml    SUCC: Configuration for 'nodesdn' created or updatedWill update '_doc/whitelist' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/whitelist.yml    SUCC: Configuration for 'whitelist' created or updatedWill update '_doc/audit' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/audit.yml    SUCC: Configuration for 'audit' created or updatedDone with successI have no name!@68ac2255bb85:/$ 

настройка Ldap

Для настройки интеграции с LDAP необходимо заполнить соответствующие секции в elastisearch-opendistro-sec/config.yml, ссылка на официальную документацию по authentication

config:  dynamic:    authc:      # тут можно настроить авторизацию          authz:      # а здесь аутентификацию

В случае с Active Directory, необходимо будет изменить конфигурационный файл примерно так:

      # тут можно настроить авторизацию          ldap:        ...            hosts:              - ldaphost:389            bind_dn: cn=LDAP,ou=Example,dc=example,dc=ru            password: CHANGEME            userbase: 'DC=example,DC=ru'            usersearch: '(sAMAccountName={0})'            username_attribute: sAMAccountName

Не забудьте воспользоваться securityadmin.sh после изменения конфигурации. Процедура была описана в предыдущем параграфе.

Установка EFK + Opendistro в kubernetes

Вернемся к проекту с kubernetes, установим проект командой

helm upgrade --install efk . -f values.yaml  

Нам необходимо будет

Для настройка OpenDistro Security plugin мы скопировали файл конфигурации, которые поместим в секреты kubernetes.

  extraVolumes:  - name: config    secret:      secretName: opendistro-config      items:        - key: config.yml          path: config.yml  - name: roles-mapping    secret:      secretName: opendistro-config      items:        - key: roles_mapping.yml          path: roles_mapping.yml  extraVolumeMounts:    - mountPath: /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/config.yml      subPath: config.yml      name: config    - mountPath: /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/roles_mapping.yml      subPath: roles_mapping.yml      name: roles-mapping

Чтобы настройки применялись при команде helm upgrade, мы сделали job, который будет запускаться при каждой команде helm upgrade

apiVersion: batch/v1kind: Jobmetadata:    name: opendistro-config-reload    labels:        app.kubernetes.io/managed-by: {{.Release.Service | quote }}        app.kubernetes.io/instance: {{.Release.Name | quote }}    annotations:        "helm.sh/hook": post-upgrade        "helm.sh/hook-delete-policy": hook-succeededspec:    template:        metadata:            name: config-reload            labels:                app.kubernetes.io/managed-by: {{.Release.Service | quote }}                app.kubernetes.io/instance: {{.Release.Name | quote }}        spec:            initContainers:                - name: "wait-for-db"                  image: "alpine:3.6"                  command:                      - 'sh'                      - '-c'                      - >                          until nc -z -w 2 efk-elasticsearch-coordinating-only 9300 && echo elastic is ok; do                            sleep 2;                          done;            containers:                - name: opendistro-config-reload                  image: "{{ .Values.elasticsearch.image.registry }}/{{ .Values.elasticsearch.image.repository}}:{{ .Values.elasticsearch.image.tag }}"                  imagePullPolicy: {{ .Values.elasticsearch.image.pullPolicy | quote }}                {{- if .Values.elasticsearch.master.securityContext.enabled }}                  securityContext:                    runAsUser: {{ .Values.elasticsearch.master.securityContext.runAsUser }}                 {{- end }}                  command:                    - 'bash'                    - '-c'                    - >                       "/opt/bitnami/elasticsearch/plugins/opendistro_security/tools/securityadmin.sh" -h efk-elasticsearch-coordinating-only -cd "/opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig" -icl -key "/opt/bitnami/elasticsearch/config/kirk-key.pem" -cert "/opt/bitnami/elasticsearch/config/kirk.pem" -cacert "/opt/bitnami/elasticsearch/config/root-ca.pem" -nhnv            restartPolicy: Never    backoffLimit: 1

Итоги

Если вы собираетесь организовать централизованную сборку логов для приложений под управление kubernetes, данный вариант мог бы стать вполне обоснованным решением. Все продукты поставляются под открытыми лицензиями. В статье приведена заготовка из которой можно создать production ready решение. Спасибо за внимание!

Подробнее..

Из песочницы Свободная веб-энциклопедия для любых IT-проектов на собственном движке

06.08.2020 02:22:56 | Автор: admin

Введение


Когда я пытался продвигать свой последний проект (веб-чат), я столкнулся с тем, что о нем фактически негде написать. Ситуация касается не только wiki-проектов, а вообще всего рунета. Крупные сайты ведут такую политику, что Вы не можете написать о своем проекте Вы должны либо купить рекламу, либо о Вас должен написать кто-то еще. Если рассматривать конкретно wiki-сайты, то даже на Лурк или Викиреальность попасть проблематично, не говоря уже о таком гиганте как Википедия. Хотя потом я нашел пару небольших wiki-порталов без цензуры, для которых написал два уникальных обзора на свой чат. Они меня и вдохновили на создание свободного wiki-ресурса для IT-разработок.

Зачем писать свой wiki-движок когда есть готовые?


Ну во-первых мне это было интересно. Создать готовый сайт по шаблону в несколько кликов скучно и не заслуживает внимания аудитории. А новая разработка со своим интерфейсом вероятно активнее будет привлекать интерес. К тому же мне кажется проще написать код под определенные задачи с нуля, взяв за основу какой-либо фреймворк для упрощения разработки, чем переделывать нагромождения чужого кода под себя.

image

Подробнее о wikiclick.ru


Wikiclick.ru это свободная веб-энциклопедия об IT-разработках. Здесь Вы можете написать о своем сайте, приложении, сервисе, игре или другой разработке, например о своем фреймворке или библиотеке и Ваша статья не будет расценена как спам. А навигатор автоматически создаст древовидное оглавление для Вашей страницы по h1-h6 заголовкам. На странице с Вашим проектом также будет доступно обсуждение в виде дерева комментариев. Созданную страницу можно будет найти в журнале версий, в поиске по сайту, выборке по указанным тегам, или в соответствующем разделе, к которому относится Ваша разработка. В обзоре проекта можно ограничиться лишь небольшим описанием, а сообщество со временем дополнит статью. Также Вы можете прикрепить картинки.

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

О движке WikiClick


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

Мне нравится, каким получился движок WikiClick он компактный, красивый, быстро работает, код сервера уложился в 1000 строк. В журнал версий новая запись идет только в том случае если страница написана с другого ip или в другой день этот механизм позволяет сохранять все ключевые версии страницы, не набирая кучу идентичных дубликатов с правками от одного пользователя. Свои комментарии сохраняются в сессию, и пока она не истекла, их можно отредактировать. Хорошая адаптивная верстка почти сходу налезла на мобильную версию. Обсуждение в виде дерева комментариев внизу страницы кажется более удобным чем на Википедии или на Лурке (Хотя я не являюсь активным участником этих проектов и мое мнение спорное). Использование HTML-кода в содержании страниц выглядит более гибким и скорее всего будет более привычно разработчикам. А навигатор по заголовкам получился гораздо проще тех, что я нашел в интернете, и работает на стороне клиента, не создавая нагрузку на сервер. Он выполнен на jquery и подходит для повторного использования, хотя требует от автора соблюдения последовательности заголовков:

if ($('.cords').length) {  $h = 'h1, h2, h3, h4, h5, h6';  $ah = $('article').find($h);  if ($ah.length >=3) {    $('.cords').html('<div>Справка</div><ul class="level0" data-level="0"><ul>');    $ol = $('.level0');    $str = '';    $.each($ah, function(i, elm) {      if (i < $ah.length-1) {        a = $ah.eq(i).prop('tagName'); b = $ah.eq(i+1).prop('tagName');      }      $(this).html('<selection id="H'+i+'">'+$(this).html()+'</selection>');      $str += '<li><a href="#H'+i+'">'+$(this).text()+'</li>';      if (i < $ah.length-1 && a>b) $str+='</ul>';      if (a<b && !(a=='H1' && b=='H2')) $str += '<ul>';      if (i == $ah.length-1) {        for (i = parseInt($(this).prop('tagName').substring(1)); i>=3; i--) {          $str += '</ul>';        }        console.log($str);        $ol.html($str);      }    });  }}

P.S.: надеюсь, мой велосипед смог Вас заинтересовать (будем считать это бета-тестом).

Ссылки


wikiclick.ru
Проект на GitHub
Подробнее..

Обзор возможностей Kubespray Отличие оригинальной версии и нашего форка

20.09.2020 14:22:27 | Автор: admin

23 сентября 20.00 МСК Сергей Бондарев проведёт бесплатный вебинар Обзор возможностей Kubespray, где расскажет, как готовят kubespray, чтобы получилось быстро, эффективно и отказоустойчиво.


Сергей Бондарев расскажет отличие оригинальной версии и нашего форка:



Отличие оригинальной версии и нашего форка.


Те, кто с кубспреем уже сталкивался, наверное сейчас недоумевают, почему я kubeadm противопоставляю кубспрею, ведь кубспрей для создания кластера, как раз и вызывает кубадм и на первый взгляд выглядит, как скрипт установки пакетов и автоматизированного запуска.


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


  • собирал кластер etcd;
  • устанавливал кублеты, генерил сертификаты, конфиги и токены доступа для статик подов контролплейна и прочих служебных компонентов;
  • создавал сервис аккаунты для рабочих узлов и подключал их в кластер.

Но в позапрошлом году они этот функционал выпилили, оставив только кубадм. Который в то время был не очень. Мне стало обидно и я сделал свой форк, в котором сохранил классический режим установки, и собственно теперь поддерживаю этот форк в актуальном состоянии, черри-пикая коммиты из оригинального кубспрея к себе. Попутно допиливая классический режим под новые изменения.


В итоге разница между кластерами, созданными моим форком и оригинальным это kube-proxy и сроки действия сертификатов.


В моем форке все осталось, как было раньше куб-прокси запускается, как статик под, сертификаты выписываются на 100 лет.


В Kubeadm куб-прокси запускается, как daemonset, а сертификаты выписываются на 1 год, и их надо периодически продлевать. kubeadm наконец-то научился это делать одной командой.


Разница небольшая, и на сегодня мы используем оба варианта.


Особенности (недостатки) при промышленной эксплуатации:


Сценарий универсальный, поэтому не очень быстрый. Собственный можно значительно ускорить, выкинув проверки, запускаясь из готового образа.


Сценарий сложный, есть нелогичные места, тяжелое наследие legacy. Установка доп. контроллеров и софта через кубспрей хороша для обучения и тестов. В пром. эксплуатации зависеть от кубспрея не очень здравая идея, плюс обновление софта реализовано методом "убил-сделал новый" значит перерыв в обслуживании.


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


Например у меня была проблема с кубадмом, когда он падал в момент добавления второго и третьего мастера, и после этого кубспрей делал kubeadm reset на узле, и пробовал добавить мастер еще раз.


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


Opensource как он есть.


Всё это и многое другое на бесплатном вебинаре Обзор возможностей Kubespray 23 сентября 20.00 МСК.


Присоединяйтесь!

Подробнее..

Перевод Решение проблем связанности микросервисов с помощью сетевых журналов

25.12.2020 14:07:02 | Автор: admin


Сеть является основой для распределенных приложений. Распределенное приложение имеет несколько микросервисов, каждый из которых работает в наборе Подов, часто расположенных на разных Нодах. Проблемные области в распределенном приложении могут быть на уровне сети (Flow, сетевые журналы), или на уровне недоступности ресурсов приложения (Метрики) или недоступности компонентов (трассировка).

На связанность на сетевом уровне могут влиять различные факторы, такие как настройки маршрутизации, пула IP-адресов, сетевые политики и т.д. Когда сервис А не может общаться с сервисом В по сети или внешнее приложение не может подключиться к сервису А, сетевые журналы становятся важным источником данных, необходимых для устранения неполадок, связанных с подключениями. Как и в традиционной сети, сетевые журналы позволяют администраторам кластеров мониторить сеть микросервисов Kubernetes.



Сетевые Журналы Имеют Несколько Сценариев Использования


Сетевые журналы могут использоваться для выполнения уникальных требований различных команд (DevOps, SecOps, Platform, Network). Ценность сетевых журналов Kubernetes заключается в собранной информации, такой как подробный контекст о конечной точке / endpoint (например, поды, метки, пространства имен) и сетевые политики, развернутые при настройке соединения. В ИТ-среде команды DevOps, SecOps, Network и Platform могут использовать сетевые журналы для своих сценариев использования, которые полезны в их домене знаний. Ниже мы покажем несколько примеров.



Генерирование Политик и Обнаружение Угроз с Помощью Журналов Потоков Данных в Calico Enterprise.


Calico Enterprise генерирует контекстные журналы сетевых потоков при каждом подключении к поду. Журнал сохраняется в файл на ноде, который затем обрабатывается агентом fluentd и отправляется в настроенное место назначения.



Как показано на приведенной выше диаграмме, журналы по умолчанию отправляются в движок Elasticsearch, который поставляется с Calico Enterprise. Вы можете настроить пересылку журналов потоков на вашу SOC платформу. Мы рекомендуем Вам иметь единую платформу для всех ваших журналов. Журналы это важнейший инструмент мониторинга и анализа для команды эксплуатации, которая уже имеет четко определенные процессы, построенные на централизованной платформе журналирования. Это важно для вашего планирования.

Характеристики Журнала Потока Данных


Журналы потоков имеют много накладных расходов на хранение. По умолчанию, Calico Enterprise хранит только данные за последние семь дней. Это позволяет Calico Enterprise предоставлять функции, специально предназначенные для упрощения разработки политик и устранения неполадок подключения практически в режиме реального времени.

  • Предлагаемые рекомендации для создания политик позволяют разработчикам автоматически генерировать сетевые политики для защиты своих сервисов
  • Рабочий процесс для политик (рекомендации, предварительный просмотр, поэтапные политики) позволяет командам SecOps и DevOps эффективно создавать политики и развертывать их ненавязчивым образом
  • Обнаружение угроз позволяет командам SecOps отследить каждый flow по определенному IP-адресу или домену и выявить угрозы


Стандартный журнал потока в Calico Enterprise содержит в себе всю необходимую контекстную информацию:

  • Контекст Kubernetes (под, пространство имен, метки, политики)
  • IP-адрес отправителя для внешних источников, если он доступен через ingress
  • Start_time, end_time, action, bytes_in, bytes_out, dest_ip, dest_name, dest_name_aggr, dest_namespace, dest_port, dest_type, dest_labels, reporter, num_flows, num_flows_completed, num_flows_started, http_requests_allowed_in, http_requests_denied_in, packets_in, packets_out, proto, policies, source_ip, source_name, source_name_aggr, source_namespace, source_port, source_type, source_labels, original_source_ips, num_original_source_ips


Журнал DNS агрегируется для каждого пода с течением времени и содержит следующую информацию:

  • Start_time, end_time, type, count, client_ip, client_name, client_name_aggr, client_namespace, client_labels, qname, qtype, qclass, rcode, rrsets, servers


Записи журналов доступны в Kibana в виде индексов Elasticsearch. Вы полностью контролируете параметры индексации, время хранения и хранилище. Сохранение настроек по умолчанию хорошо подходит для большинства развертываний.

Сократите операционные расходы за счет оптимизации хранилища журнала потоков



Calico Enterprise собирает различные журналы (network / flow, audit, DNS). Журналы потоков являются самыми дорогими с точки зрения хранения, занимая более 95% общего объема хранилища. Нередко на каждую полностью загруженную ноду приходится 5k flow в секунду При скромных 200 байтах на flow это превращается в 1 МБ/с (мегабайт). Суточная потребность в хранилище для каждой ноды составляет 86 ГБ для журналов потоков. Для кластера из 100 узлов ежедневное требование к журналу потоков становится 8 ТБ+!!! Очевидно, что это не масштабируется. И что еще более важно, действительно ли вам нужно хранить так много данных? Как правило, ценность данных, содержащихся в журналах, экспоненциально уменьшается с течением времени и имеет значение только для устранения неполадок и обеспечения соответствия требованиям.

По этой причине Calico Enterprise имеет по умолчанию агрегацию, которая снижает требования к хранению журнала потоков более чем в 100 раз! Мы делаем это без ущерба для данных (видимость, мониторинг, поиск и устранение неисправностей), которые получают наши клиенты из журналов потоков. Журналы агрегируются по порту назначения в течение определенного периода времени. Таким образом, вам не нужно беспокоиться о стоимости хранения журналов потоков при использовании настроек по умолчанию в Calico Enterprise. Еще один способ, которым Calico Enterprise может помочь вам снизить требования к объему хранилища, это исключение. Вы можете легко настроить определенные пространства имен или инсталляции, которые будут исключены из создания журналов потоков.

Заинтересованы в детальном изучении журналов потоков?


Журналы потоков включены по умолчанию. Обратитесь к следующей документации для детальной настройки:

Подробнее..

Шифрование передаваемых данных в Calico Enterprise

25.12.2020 18:10:30 | Автор: admin


Мы рады сообщить, что Calico Enterprise, ведущее решение для сетей Kubernetes, безопасности и видимости в гибридных и мультиоблачных средах, теперь включает в себя шифрование передаваемых данных.

Calico Enterprise известен своим богатым набором средств сетевой безопасности для защиты контейнерных рабочих нагрузок путем ограничения трафика К и ОТ доверенных источников. Они включают в себя, но не ограничиваются, внедрением существующих практик контроля безопасности в Kubernetes, контролем egress с помощью DNS политик, расширением межсетевого экрана на Kubernetes, а также обнаружением вторжений и защитой от угроз. Однако по мере развития Kubernetes мы наблюдаем потребность в еще более глубоком подходе к защите конфиденциальных данных, который подпадает под требования нормативно-правового соответствия / compliance.

Не все угрозы исходят снаружи компании. По данным Gartner, почти 75% нарушений происходят из-за действий персонала внутри компании: сотрудников, бывших сотрудников, подрядчиков или деловых партнеров, которые имеют доступ к внутренней информации о средствах безопасности компании, данных и компьютерных системах. Такой уровень уязвимости данных неприемлем для организаций, которые имеют строгие требования к их защите и соблюдению нормативных требований. Независимо от того, откуда исходит угроза, доступ к зашифрованным данным есть только у законного владельца ключа шифрования, что обеспечивает защиту данных в случае попытки несанкционированного доступа.

Некоторые нормативные стандарты устанавливают требования к защите данных и соблюдению требований к организациям и указывают на использование средств шифрования, в том числе SOX, HIPAA, GDPR и PCI. Например, стандарт безопасности данных индустрии платежных карт (PCI DSS) применяется к организациям, которые работают с фирменными кредитными картами, и был создан для усиления контроля за данными держателей карт с целью сокращения случаев мошенничества. PCI DSS требует от организаций шифрования номеров счетов кредитных карт, хранящихся в их базах данных, и обеспечения безопасности данных при передаче. Проверка соответствия проводится ежегодно или ежеквартально.



Calico Enterprise решает эту проблему, используя WireGuard для внедрения шифрования передаваемых данных. WireGuard согласуется с подходом Tigera batteries-included" к сетям Kubernetes, безопасности и наблюдаемости. WireGuard работает в качестве модуля ядра Linux и обеспечивает лучшую производительность и более низкую загрузку процессора, чем протоколы туннелирования IPsec и OpenVPN. Независимые тесты производительности Kubernetes CNI показали, что Calico с включенной функцией шифрования работает в 6 раз быстрее, чем любое другое решение на рынке.

WireGuard работает как модуль внутри ядра Linux и обеспечивает лучшую производительность и более низкую загрузку процессора, чем протоколы туннелирования IPsec и OpenVPN. Включить шифрование передаваемых данных на Calico Enterprise очень просто все, что вам нужно это развернутый на операционной системе хоста кластер Kubernetes с WireGuard. Полный список поддерживаемых операционных систем и инструкции по установке вы найдете на веб-сайте WireGuard.

Тесты производительности CNI


Являясь отраслевым стандартом для сетей Kubernetes и сетевой безопасности, Calico обеспечивает работу более миллиона нод Kubernetes каждый день. Calico единственный CNI, имеющий возможность поддерживать три data plane из одной, унифицированой контрольной панели. Независимо от того, что вы используете eBPF, Linux или Windows data plane; Calico обеспечивает невероятно высокую производительность и исключительную масштабируемость, что подтверждают последние бенчмарк-тесты.

Последний бенчмарк сетевых плагинов Kubernetes (CNI) по сети 10 Гбит/с опубликовал Алексис Дюкастель, CKA/CKAD Kubernetes и основатель InfraBuilder. Тест основывался на версиях CNI, которые были актуальны и обновлены по состоянию на август 2020 года. Были протестированы и сравнены только те CNI, которые можно настроить с помощью одного yaml-файла, включая:

  • Antrea V. 0. 9. 1
  • Calico v3. 16
  • Canal v3.16 (сетевые политики Flannel + Calico)
  • Cilium 1.8.2
  • Flanel 0.12.0
  • Kube-router последняя версия (2020-08-25)
  • WeaveNet 2.7.0

Среди всех протестированных CNI Calico стал явным победителем, продемонстрировав превосходство практически во всех категориях и получив превосходные результаты, которые приведены в таблице ниже. Фактически, Calico является предпочтительным выбором CNI в случаях первичного использования, представленных автором в резюме отчета.



Ознакомьтесь с полными результатами последних бенчмарк-тестов Kubernetes CNI. Вы также можете запустить бенчмарк на собственном кластере, с помощью инструмента Kubernetes Network Benchmark Tool от InfraBuilder.
Подробнее..

Подборка статей о машинном обучении кейсы, гайды и исследования за декабрь 2020

11.01.2021 18:14:50 | Автор: admin


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

MuZero


DeepMind неожиданно опубликовали статью о MuZero, алгоритме, который способен играть как в популярные логические настольные игры вроде шахмат, Сёги и Го, так и в видеоигры Atari вроде Pac-Man.

MuZero пытается смоделировать не всю среду, а только отдельные аспекты, которые важны для процесса принятия стратегических решений агентом. Алгоритм постоянно собирает информацию о текущем и предыдущем состоянии игры изучая таким образом запреты и награды. Так, например, модель понимает, что в шахматах цель игры поставить мат, а в пакмане съесть желтую точку.

Есть еще одно важное преимущество: MuZero многократно использует изученную модель для улучшения планирования, а не для сбора новых данных о среде. Например, в играх Atari со сложной изменяющейся средой алгоритм использовал изученную модель в 90% случаев чтобы перепланировать то, что должно было быть сделано в прошлых игровых сессиях.

Почему это важно. По сути, MuZero модель общего назначения, которую можно будет применять для решения сложных проблем реального мира, которые трудно свести к простым правилам. DeepMind предлагают такую аналогию новый подход похож на то, как человек в пасмурную погоду решает взять зонтик, чтобы остаться сухим, в то время как прежние подходы пытались бы смоделировать, в каком порядке будут падать капли дождя.



Infinite Nature


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

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

Новизна подхода в том, что он способен синтезировать изображения с учетом геометрии сцены, которая покрывает большие расстояния на сотни кадров. Датасет уже доступен, а вот исходники придется подождать.



Time Travel Rephotography


Нейросеть для реставрации и колоризации старых фото, напоминающая DeOldify. В отличие от обычных фильтров восстановления изображений, которые применяют независимые операции, такие как шумоподавление, раскрашивание и апскейлинг, здесь используется StyleGAN2, чтобы синтезировать близкое к оригиналу лицо. На выходе получаются портретные фотографии сразу в цвете и хорошем разрешении. Код тоже обещают выкатить позже.



pi-GAN


Еще одна GAN-модель, которая генерирует 3D представление объекта из нескольких неразмеченных двухмерных изображений. В демо показано, как модель можно использовать для вращения головы, подобно тому как ранее демонстрировали Nvidia в Maxine.



Neural Scene Flow Fields


Новый NeRf-метод, который строит представление динамической сцены из видео, снятого на обычную камеру. Это позволяет, например, остановить кадр и сдвинуть камеру или наоборот зафиксировать камеру, но будто перемотать время. Алгоритм дорисовывает окружение со сложной структурой, например, с тонкими объектами вроде решеток, и движущимися объектами вроде мыльных пузырей.



YolactEdge


Первый метод мгновенной сегментации изображений, который в режиме реального времени работает на слабых устройствах. Исходный код уже доступен.

ModNet


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

Svoice


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


Hypersim


Apple опубликовали датасет с сегментационными масками для искусственно созданных сцен. Почти два терабайта рендеров помещений в сверхвысоком разрешении. Маркировка данных здесь на уровне отдельных пикселей.

ArtLine


Открытая модель, которая превращает фотопортрет в карандашный набросок. Пока что плохо справляется с текстурами одежды и с тенями, но в целом выдает достойные результаты. За основу взята архитектура DeOldify, которая позволяет хорошо распознавать черты лица.

На этом все, вот таким на удивление насыщенным оказался декабрь. Начало года тоже обещает быть интересным. Нам уже не терпится посмотреть, что в январе появится на основе Dall-E от OpenAI. Как говорится, stay tuned!
Подробнее..

Новости Yii 2021, выпуск 1

05.03.2021 02:11:27 | Автор: admin

Привет, сообщество!

Это первый выпуск новостей в 2021. Начало года вышло продуктивным. Мы начали активно релизить пакеты Yii 3, есть значительный прогресс с пока не релизнутыми пакетами. Улучшили инструментарий разработки, много всего исправили, убили лишние пакеты. И всё это параллельно с поддержкой Yii 2 и решением проблем с финансированием.

Команда и фонд

Несмотря на то, что 2020 был через край долбанутым, для Yii всё вышло неплохо. Удалось договориться о продлении поддержки Yii 1.1 и других вещах в обмен на единовременное или постоянное пополнение фонда.

Эти соглашения и, конечно же,наши любимые спонсоры, позволили команде меньше работать над коммерческими проектами и больше над фреймворком. Фонд удалось использовать полностью.

Я был чрезвычайно воодушевлён возросшей скоростью разработки, окунулся в неё с головой и немного подзабыл о финансовой части. В январе меня ждал неприятный сюрприз. Оказывается, что забыл я про очень важный факт. А именно, что немаленькая часть средств фонда поступила не рекуррентными платежами. Соответственно, бюджета на всё не хватило. Чтобы не случилось полной катастрофы, я перестал забирать деньги фонда с OpenCollective и начал активно искать партнёров среди компаний, которые используют Yii и PHP. Результаты пока спорные, но, надеюсь, всё наладится.

Если хотите пообщаться на тему партнёрства, пишите вsam@rmcreative.ru.

Yii 2

Вышел Yii 2.0.41. Сильно помогли с релизомPawe Brzozowski, недавно присоединившийся к команде Yii 2, иRobert Korulczykс его тщательными ревью всего вливаемого в master кода. Много часов было потрачено на безопасность фреймворка. Удалось перебрать текущие сообщения о предполагаемых уязвимостях иулучшить безопасность.

Yii 3

Прежде всего, релизы:

В каждом пакете есть документация, отличное покрытие тестами, код вычищен и, конечно же, публичное API достаточно стабилен.

Ну а теперь время рассмотреть интересные изменения с предыдущего выпуска новостей. Для удобства изменения сгруппируем по пакетам.

Arrays

  • ДобавленArrayHelper::pathExists().

  • ДобавленArrayHelper::group(). Это алиасArrayHelper::index().

  • Удалены модификаторы.

Data

Auth

Config plugin

После большого количества попыток улучшитьComposer config pluginстало очевидно что он стал слишком сложным: AST, мёрж конфигов и всё что там творилось под капотом. Мы приняли решение сделать его менее зависимым от пакетов yiisoft и сделатьновый, более простой и производительный, пакет.

Все шаблоны приложений уже были переведены на новые конфиги. Скорее всего нужно будет исправлять баги и повышать удобство использования пакета.

Error handler

  • Рефакторинг, ридми.

  • Поддержка Xdebug 3 для получения более подробных стектрейсов.

  • ExceptionResponder, при помощи которого можно формировать HTTP-ответ в зависимости от пойманной ошибки. Это может быть полезно для исключений вродеNotFoundException. Будут ли такие исключения из коробки пока обсуждаем.

  • Renderer-ы теперь могут отдавать HTTP-заголовки, добавлен renderer, который выводит ошибку в заголовках. Полезно если показывать ошибки текстом неудобно. Например, для некоторых API или при генерации картинок.

  • Элементы stacktrace для классов изvendorтеперь отображаются отдельной группой и по умолчанию свёрнуты. Так как ошибки обычно в самом приложении, это позволяет не отвлекаться на нерелевантную информацию.

Обработка ошибок в общем

  • В пакетyiisoft/yii-eventдобавленрежим отладки. В нём проверяются все обработчики событий сразу.

  • Улучшены ошибки DI контейнера.

Strings

  • ДобавленNumericHelper::isInteger().

  • Добавлена поддержкаboolвNumericHelper::normalize().

  • Был переработан матчинг по wildcard. Вместо довольно большого количества опций добавили**, совпадающий, в том числе, с сепараторами.

Var dumper

Html

Пакет был значительно переработан. Главных изменений два:

  1. Вывод по умолчанию экранируется.

  2. Теги реализованы как отдельные объекты, создаваемые через статические методы-фабрики из Html. Для пользователя это значит, что конфигурация теперь производится через вызов методов. То есть теперь IDE это дополняют и проверяют.

Валидатор

Много рефакторинга. Самое интересное:

Формы

Перевод сообщений

Почти готов к релизу. Можете почитатьего readme.

Mailer

Mailerиадаптер для SwiftMailerбыли вычищены и отрефакторены. Добавлена документация. Релизнем как только будут готовы зависимости.

DB и ActiveRecord

В обоих пакетах были исправления, оба ещё больше покрыли тестами. Wilmer, член команды, который занимался этими пакетами, уже проверил из в бою на Telegram боте с 120 тысячами запросов в сутки.

Оба пакета изначально были портированы из Yii 2 почти как есть. Была убрана магия, добавлены типы, покрытие тестами доведено почти до 100%. Но, несмотря на то что пакеты стали даже лучше, чем Yii 2, дизайн сохранился примерно в том же виде. Нам предстоит трудный выбор, релизить ли первую версию как есть или же сначала затеять гигантский рефакторинг.

Скорее всего выберем первое.

yii-web

User

Значительно переделали пакет:

Всё ещё не до конца довольны. Скорее всего будем переделывать ещё.

Bootstrap и Bulma

Консоль

Появиласьвозможность добавлять команды со своим именем.

Debugger

Есть прогресс как в API, так и на фронтенде.

Docker

Добавлена поддержка PHP 8.

Инструментарий разработки фреймворка

Улучшилиинструментарийчтобы можно было удобно работать даже не поставив все пакеты. Улучшили точность фиксера зависимостей. Добавили новую командыrelease/what. Она подсказываем какой пакет релизить следующим. Приоритет отдаётся пакетам без нестабильных зависимостей и, при этом, блокирующих как можно больше релизов других пакетов.

Шаблоны приложений

Новая и обновлённая документация

Почитать-посмотреть

Спасибо!

Хочу сказать спасибо всем спонсорам и разработчикам, благодаря которым стала возможна разработка Yii 3. Вместе у нас всё получится.

Я понял, что ранее не писал о компаниях, которые поддерживают разработку Yii. Вот самые большие спонсоры с коротким описанием:

  • CraftCMS- Отличная OpenSource CMS на Yii2.

  • Onetwist Software- Услуги качественной разработки приложений.

  • SkillShare- Обучение новым навыкам.

  • Betteridge- Ювелирные изделия.

  • dmstr- Облачные решения на основе Docker.

  • HumHub- OpenSource решения для общения команды.

  • JetBrains- Отличные инструменты для разработки.

  • Skin.Club- Рынок скинов для CS:GO.

  • ЭФКО- фудтех, производство продуктов питания, и венчурные инвестиции. С недавнего времени ещё и ЭФКО-тех, отдельная растущая сервисная IT-компания, которая планирует заниматься не только внутренними проектами ЭФКО.

Кроме перечисленных нас поддерживаютотдельные разработчики и другие компании.

Отдельное спасибо тем, кто помог Yii 3 кодом:

Подробнее..
Категории: Php , Yii , Фреймворк , Yii2 , Yii3 , Opensource , Yii framework

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru