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

Шпаргалка

Перевод 8 лучших практик предотвращения SQL-инъекций

26.05.2021 14:12:47 | Автор: admin

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

К старту курса "Этичный хакер" мы решили поделиться именно этой статьёй, потому что она не только предлагает памятку по инъекциям, но и знакомит с платформой snyk.io сканером на уязвимости, который для проектов с открытым исходным кодом можно использовать совершенно бесплатно, а значит, он будет полезен для пет-проектов многих разработчиков и читателей Хабра. По этой причине публикация попала и в хаб Программирование.


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

Например, SQL-инъекции могут быть такими:

  • добавление к фразе where булева оператора, который делает это условие всегда выполняющимся ' OR 1=1;

  • удаление части запроса путём ввода строчных комментариев --;

  • завершение исходного запроса и запуск нового запроса '; DROP TABLE USERS;

  • соединение данных из нескольких таблиц с помощью оператора UNION.

В этой памятке я подробно рассмотрю 8 оптимальных методов, которые можно применять в любом приложении в целях предотвращения атак через SQL-инъекции. Итак, давайте начнём и сделаем ваше приложение действительно защищённым от SQL-инъекций.

Загрузить памятку

  1. Не полагайтесь на проверку вводимых данных на стороне клиента.

  2. Ограничивайте полномочия пользователя базы данных.

  3. Используйте предварительно подготовленные опраторы и параметризацию запросов.

  4. Сканируйте код, чтобы обнаружить уязвимость к SQL-инъекциям

  5. Используйте слой ORM.

  6. Не полагайтесь на чёрные списки.

  7. Выполняйте проверку вводимых пользователем данных.

  8. С осторожностью используйте хранимые процедуры.

1. Не полагайтесь на проверку вводимых данных на стороне клиента

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

Но применительно к SQL-инъекциям это не тот метод защиты, на который действительно можно положиться. Проверку вводимых данных на стороне клиента легко можно отключить, изменив некоторый код javascript, загруженный в ваш браузер. Кроме того, в архитектуре клиент-сервер очень легко выполнить простейший HTTP-вызов серверного приложения, используя в нём параметр, вызывающий SQL-инъекцию. Также это можно сделать, используя различные инструментальные средства, такие как postman, или старые добрые команды curl.

Следует выполнять проверку на серверной стороне желательно как можно ближе к источнику данных. В рассматриваемом случае там, где вы создаёте SQL-запрос. Все данные, передаваемые со стороны клиента, должны рассматриваться как потенциально опасные. Ввиду вышеизложенного возлагать надежды на проверку вводимых данных на стороне клиента в целях защиты от SQL-инъекций плохая идея.

2. Ограничивайте полномочия пользователя базы данных

Как уже было упомянуто, существует несколько типов атак с использованием SQL-инъекций. Некоторые из них более опасны, чем другие. Представьте, к примеру, что мой SQL-запрос имеет вид "SELECT * FROM USER WHERE USERID = '" + userid +"'". Инъекция " foo' OR '1'='1 " предоставит доступ ко всем пользователям, и это само по себе очень опасно. Однако инъекция " '; UPDATE message SET password = 'EVIL приведет к ещё большим проблемам, поскольку злоумышленник в этом случае изменит все записи.

Создавая пользователя в базе данных для вашего приложения, вы должны подумать о его полномочиях. Действительно ли приложению необходима возможность считывать, записывать и обновлять все базы данных? Должны ли быть у него полномочия для выполнения команд truncate и drop в отношении таблиц? Если вы сами ограничите полномочия своего приложения при работе с базой данных, то таким образом можно снизить и потенциальный вред от SQL-инъекций. Наверное, правильным будет создание для приложения нескольких пользователей базы данных и назначение им определённых ролей. Проблемы с безопасностью, как правило, вызывают эффект цепной реакции, поэтому необходимо контролировать все звенья вашей цепи, чтобы предотвратить тяжёлые последствия.

3. Используйте предварительно подготовленные операторы и параметризацию запросов

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

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

Пример на Java:

String query = "SELECT * FROM USERS WHERE username LIKE ?";PreparedStatement statement = connection.prepareStatement(query);statement.setString(1, parameter);ResultSet result = statement.executeQuery();

Пример на Python с коннектором MySQL:

cursor = conn.cursor(prepared=True)params = ("foo",)cursor.execute("SELECT * FROM USERS WHERE username = %s", params)

Пример на JavaScript с mysql2:

connection.query("SELECT * FROM USERS WHERE username = ?",[     req.body.username    ],function(error, results){}); //emulates a prepared statement//ORconnection.execute("SELECT * FROM USERS WHERE username = ?",[     req.body.username    ],function(error, results){});//prepared statement

Тем не менее существует несколько способов сделать это в JavaScript при использовании, например, базы данных MySQL. Учитывайте, что при использовании функции .query() подготовленный оператор не выполняется. В этом случае подстановка параметров осуществляется на стороне клиента, то есть происходит эмуляция подготовленного оператора. Чтобы сделать настоящий подготовленный запрос к базе данных, следует использовать функцию .execute().

4. Сканируйте код, чтобы обнаружить уязвимость к SQL-инъекциям

Писать собственный код совсем не сложно. Но при этом нередко случаются ошибки. Для проверки кода у вас могут быть предусмотрены такие процессы, как ревью кода и парное программирование. Но является ли тот, кто проверяет ваш код или программирует в паре с вами, также и специалистом в вопросах безопасности? Сможет ли он заметить в коде ошибку, опасную с точки зрения SQL-инъекций? В любом случае будет полезно автоматически проверять собственный код на предмет потенциальных угроз безопасности, таких как SQL-инъекции.

Используя такой инструмент SAST (технология статического тестирование безопасности приложений), как Snyk Code, вы можете автоматически проверять свой код и обнаруживать в нём угрозы безопасности, включая SQL-инъекции. Этот процесс можно легко автоматизировать в SDLC, например, подключив к Snyk ваш Git-репозиторий.

5. Используйте слой ORM

Также необходимо рассмотреть возможность использования уровня объектно-реляционного отображения (ORM). Слой ORM преобразует данные из базы данных в объекты, а также выполняет обратное преобразование. Применение библиотеки ORM уменьшает количество явных SQL-запросов, а значит, делает приложение менее уязвимым к SQL-инъекциям.

Одним из ярких примеров использования библиотек ORM является применение решений Hibernate для Java и Entity Framework для C#. Эти языки строго типизированы, а значит, в них есть возможность устанавливать соответствие между объектами и таблицами в базах данных. Таким образом, у вас есть возможность полностью отказаться от самостоятельного формирования каких-либо SQL-запросов.

Тем не менее проблема всё равно возникнет, если потребуется написать свои собственные запросы. В библиотеке Hibernate для Java предусмотрен даже собственный язык написания запросов HQL (Hibernate Query Language). При создании запросов на HQL опять же нужно помнить о возможных инъекциях и использовать функцию createQuery(), которая работает аналогично подготовленным операторам.

Для языка JavaScript также существуют хорошо известные библиотеки ORM, такие как sequelize. С помощью sequelize вы сможете определить, как значения будут отображаться в определённые типы в базе данных. Но давайте смотреть правде в глаза, в конечном счёте любой библиотеке ORM требуется преобразование её внутренних логических команд в операторы SQL. Мы верим, что в библиотеках экранирование параметров реализовано правильно.

Чтобы быть уверенным, что в вашей библиотеке ORM нет проблем с SQL-инъекциями, необходимо выполнить её сканирование с целью выявления известных уязвимостей. Работа с некорректной, устаревшей версией библиотек sequelize или hibernate может стать причиной больших проблем. Используя Snyk Open Source для проверки своего проекта, вы сможете защититься от скрытых SQL-инъекций в применяемых библиотеках, а также от многих других потенциальных угроз.

Использование Snyk совершенно бесплатно

Вы можете сразу приступать к работе со Snyk Open Source. Всё, что для этого нужно, пройти регистрацию на получение бесплатного аккаунта.

6. Не полагайтесь на чёрные списки

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

Например, мы можем блокировать все запросы, где содержится слово OR. Возможно, в этом даже есть какой-то смысл, но на практике оказывается, что Or это очень часто встречающееся в Израиле имя. И это означает, что множество моих коллег будут блокироваться при попытке ввода своих имён. То же самое справедливо для одиночных кавычек '. Этот символ содержится в огромном количестве имён. Подумайте о пользователях с фамилиями ONeill и ODonnell. А ведь есть ещё и имена, такие как Donta.

7. Выполняйте проверку вводимых данных

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

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

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

8. С осторожностью используйте хранимые процедуры

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

И, так же как и в случае с SQL-запросами, вам следует использовать в хранимой процедуре параметризованные запросы вместо добавления параметров. SQL-инъекции в хранимой процедуре достаточно легко предотвратить. Но не пытайтесь сделать это в MySQL:

DELIMITER //CREATE PROCEDURE `FindUsers`(    IN Username VARCHAR(50))BEGIN    SET @Statement = CONCAT('SELECT * FROM User WHERE username = ', Username, ' );    PREPARE stm FROM @Statement;    EXECUTE stm;END //DELIMITER ;

Вместо этого лучше используйте в своих хранимых процедурах параметризованные запросы:

DELIMITER //CREATE PROCEDURE `First`(    IN Username VARCHAR(50))BEGIN    PREPARE stm FROM 'SELECT * FROM User WHERE username = ?';    EXECUTE stm USING Username;END //DELIMITER ;

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

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

HTML-теги и атрибуты, о которых вы, возможно, не знали

01.05.2021 14:19:19 | Автор: admin

image


Эта статья шпаргалка по HTML-тегам. Поэтому не будем разглагольствовать, сразу к делу.


abbr


Тег abbr определяет аббревиатуру или акроним. Аббревиатура или акроним расшифровываются с помощью атрибута title.


<abbr title="HyperText Markup Language">HTML</abbr> был разработан британским ученым Тимом Бернерсом-Ли приблизительно в 19861991 годах.

abbr часто используется совместно с тегом dfn, идентифицирующим понятие или термин:


<p><dfn><abbr title="Cascading Style Sheets">CSS</abbr></dfn> - формальный язык описания внешнего вида документа (веб-страницы).</p>

address


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


<address>Автор статьи: <a href="mailto:ivan@mail.com">Иван Иванов</a><br />Официальный сайт: <a href="http://personeltest.ru/away/example.com" target="_blank" rel="noopener noreferrer">Example.com</a><br />Адрес: некоторое царство, некоторое государство<address>

audio


Тег audio используется для встраивания аудио-контента (музыка и др.) в веб-страницу.


Для определения аудио-источника используется либо атрибут src, либо тег source. Последний используется для определения нескольких источников, из которых браузер выбирает наиболее подходящий (для определения типа аудио-контента используется атрибут type).


Текст между <audio> и </audio> отображается только в случае, когда браузер не поддерживает элемент audio.


В настоящее время поддерживается три формата аудио: MP3, WAV и OGG.


Атрибуты:


  • autoplay автовоспроизведение (блокируется большинством браузеров)
  • controls панель управления (без этого атрибута элемент audio, скорее всего, не будет отображаться на странице)
  • loop определяет, что воспроизведение, после завершения, начнется сначала
  • muted воспроизведение без звука (позволяет преодолеть блокировку autoplay)
  • preload определяет, должен ли аудио-контент загружаться после загрузки страницы. Возможные значения: auto, metadata, none. Значение none не позволит работать с аудио с помощью JavaScript
  • src путь к аудиофайлу

<audio controls>

  Ваш браузер не поддерживает элемент "audio".</audio><!-- или --><audio src="music.mp3" preload="metadata" controls muted loop>Ваш браузер не поддерживает элемент "audio".</audio>

video


Тег video используется для встраивания видео-контента (видеоклип и др.) в веб-страницу.


Для определения видео-источника используется либо атрибут src, либо тег source. Последний используется для определения нескольких источников, из которых браузер выбирает наиболее подходящий (для определения типа видео-контента используется атрибут type).


Текст между <video> и </video> отображается только в случае, когда браузер не поддерживает элемент video.


В настоящее время поддерживается три формата видео: MP4, WebM и OGG.


Атрибуты:


  • autoplay автовоспроизведение (блокируется большинством браузеров)
  • controls панель управления
  • loop определяет, что воспроизведение, после завершения, начнется сначала
  • muted воспроизведение без звука (позволяет преодолеть блокировку autoplay)
  • preload определяет, должен ли видео-контент загружаться после загрузки страницы. Возможные значения: auto, metadata, none. Значение none не позволит работать с видео с помощью JavaScript
  • src путь к видеофайлу
  • poster изображение, отображаемое при загрузке видео или до нажатия пользователем кнопки воспроизведения
  • width ширина элемента в пикселях
  • height высота элемента в пикселях

<video width="320" height="240" controls>

  Ваш браузер не поддерживает элемент "видео".</video><!-- или --><!-- Такой вариант может использоваться для воспроизведения видео в качестве фона страницы --><video src="movie.mp4" autoplay muted loop>Ваш браузер не поддерживает элемент "видео".</video>

DOM API предоставляет множество свойств, методов и событий для работы с элементами audio и video.


base


Тег base определяет основной путь (URL) и/или цель (target) для всех относительных путей в документе. Он должен размещаться в теге head и иметь хотя бы один из следующих атрибутов:


  • href основной путь
  • target определяет значение по умолчанию атрибута target всех гиперссылок и форм на странице. Возможные значения: _blank, _parent, _self и _top

<head>  <base href="http://personeltest.ru/away/example.com/" target="_blank"></head><body>  <header>    <nav>      <!-- http://example.com/product.html -->      <a href="product.html">Продукт</a>    </nav>  </header>  <main>    <!-- http://example.com/images/happy_face.png -->    <img src="images/happy_face.png" alt="" />  </main>  <footer>    <nav>      <!-- http://example.com/contacts.html -->      <a href="contacts.html">Контакты</a>    </nav>  </footer></body>

blockquote и cite


Тег blockquote определяет раздел страницы, заимствованный из другого источника. Источник указывается в атрибуте cite.


<blockquote cite="http://personeltest.ru/aways/ru.wikipedia.org/wiki/JavaScript">JavaScript - мультипарадигменный язык программирования. Поддерживает объектно-ориентированный, императивный и функциональный стили. Является реализацией спецификации ECMAScript (стандарт ECMA-262).</blockquote>

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


<p>Для более глубокого изучения JavaScript советую взглянуть на <cite>"Вы не знаете JS"</cite> Кайла Симпсона.</p>

code


Тег code используется для определения части компьютерного кода:


<p>HTML-тег `button` определяет кликабельную кнопку.</p><p>CSS-свойство `background-color` определяет цвет фона элемента.</p>

Для форматирования блока кода code часто используется совместно с тегом pre:


<pre>  `    const name = prompt('Как Вас зовут?')    if (name?.trim()) alert(`Привет, ${name}!`)    else console.info('Пользователь пожелал остаться неизвестным')  `</pre>

datalist


Тег datalist определяет список возможных вариантов для заполнения поля для ввода текста. Он позволяет реализовать "автозавершение" для элемента input: при установке фокуса на такое поле пользователь видит выпадающий список.


Атрибут id тега datalist должен совпадать с атрибутом list тега input.


<!-- Атрибут `for` тега `label` должен совпадать с атрибутом `id` тега `input` --><label for="browser">Выберите Ваш браузер из списка:</label><input list="browsers" id="browser"><datalist id="browsers">  <option value="Edge">  <option value="Firefox">  <option value="Chrome">  <option value="Opera">  <option value="Safari"></datalist>

Свойство options объекта Datalist возвращает коллекцию всех элементов списка.


dl, dt и dd


Тег dl определяет список описаний (определений, извиняюсь за тавтологию). Он используется совместно с тегами dt, определяющим понятие или термин, и dd, определяющим описание термина.


Внутри dd могут размещаться параграфы, изображения, ссылки, списки и т.д.


<dl>  <dt>Кофе</dt>  <dd>Черный горячий напиток</dd>  <dt>Молоко</dt>  <dd>Белый холодный напиток</dd></dl>

details


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


По умолчанию details находится в закрытом состоянии.


Внутри details могут размещаться любые теги.


Для отображения заголовка details используется тег summary.


Индикатором открытого состояния details является атрибут open (этот атрибут может использоваться в качестве CSS-селектора details[open] или JavaScript-селектора document.querySelector('[open]')).


<details>  <summary>Заголовок - видимая часть элемента "details"</summary>  <p>Скрытый контент - дополнительная информация</p></details>

dialog


Тег dialog определяет диалоговое окно. Он используется для создания "попапов" и модальных окон.


По умолчанию dialog находится в неактивном состоянии.


Индикатором активного состояния dialog является атрибут open.


<dialog open>Открытое диалоговое окно</dialog>

Для управления объектом Dialog с помощью JavaScript используются такие методы как show(), close() и showModal(), а также свойство open.


figure


Тег figure определяет обособленный (автономный) контент, такой как иллюстрации, диаграммы, фотографии, примеры кода и т.д.


Несмотря на то, что контент элемента figure формально относится к основному потоку (main flow), его позиция (местонахождение) не зависит от этого потока. Поэтому удаление элемента figure не должно влиять на поток документа.


Для добавление подписи к figure используется тег figcaption.


<figure>  <img src="v8-compiler-pipeline.png" alt="V8 compiler pipeline" style="width:100%">  <figcaption>Рис. 1 - Процесс компиляции кода "движком" JavaScript V8.</figcaption></figure>

meter


Тег meter определяет скалярное значение в пределах известного диапазона или дробного значения. Другими словами, meter определяет меру чего-либо (gauge).


Этот тег не должен использоваться в качестве индикатора прогресса.


Для обеспечения доступности совместно с meter рекомендуется использовать тег label.


Атрибуты:


  • value текущее числовое значение между min и max
  • min нижняя числовая граница диапазона
  • max верхняя числовая граница диапазона
  • low верхняя числовая граница нижнего предела диапазона. Должна быть больше min, но меньше high и max
  • high нижняя числовая граница верхнего предела диапазона
  • optimum оптимальное числовое значение между min и max. Расположение этого атрибута определяет предпочтительную часть диапазона. Например, если optimum находится между min и low, значит, предпочтительным является нижний диапазон
  • form определяет элемент form, с которым связан meter

<label for="disk_d">Использование диска "D":</label><meter id="disk_d" min="0" max="100" value="60">60%</meter>

Так можно записать уровень заряда батареи вашего устройства в значение meter:


// <meter id="meter" max="100"></meter>if ('getBattery' in navigator) {  navigator.getBattery()    .then(({ level }) => {      meter.value = level * 100    })}

progress


Тег progress определяет процесс выполнения задачи.


Этот тег не должен использоваться для определения меры чего-либо.


Для обеспечения доступности совместно с progress рекомендуется использовать тег label.


Атрибуты:


  • max максимальное значение. По умолчанию равняется 1
  • value текущее значение

<label for="file">Процесс загрузки:</label><progress id="file" max="100" value="32">32%</progress>

Так можно реализовать десятисекундный таймер:


// <progress id="progress" value="0" max="10"></progress>const timerId = setInterval(() => {  progress.value += 1  if (progress.value === progress.max) {    progress.remove()    clearInterval(timerId)  }}, 1000)

output


Тег output используется для представления результата вычислений.


Атрибуты:


  • for определяет связь между результатом и элементами, используемыми для его выичсления
  • form определяет элемент form, которому принадлежит output
  • name название элемента output

<form oninput="x.value=parseInt(a.value)+parseInt(b.value)">  <input type="number" id="a" value="25" />  +  <input type="number" id="b" value="50" />  =  <output name="x" for="a b">75</output></form>

picture


Тег picture предоставляет возможность использования нескольких источников для изображения (нескольких изображений).


Этот тег позволяет использовать разные изображения в зависимости от ширины области просмотра (viewport width) вместо масштабирования одного изображения.


Элемент picture содержит два тега: один или более source и один img.


Браузер выбирает элемент source с медиа-запросом, совпадающим с текущей шириной области просмотра. Элемент img указывается в качестве последнего дочернего элемента на случай отсутствия совпадений с source.


Путь к изображению указывается в атрибуте srcset, а медиа-запрос в атрибуте media.


<picture>

  <img src="default.jpg" alt="" style="width:auto;"></picture>

template


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


Этот тег предназначен для хранения разметки, которая используется часто, но в определенных случаях (по запросу).


Контент внутри template может быть отрендерен с помощью JavaScript.


<template>  <h1>Заголовок</h1>  <p>И какой-то текст</p></template><button id="button">Это не кнопка</button>

((body, { content }) => {  button.onclick = () => {    body.append(content.cloneNode(true))  }})(document.body, document.querySelector('template'))

time


Тег time определяет конкретное время (или дату и время).


Атрибут datetime используется для представления времени в машиночитаемом формате.


<p>Некоторые люди искренне верили в то, что в <time datetime="2000-01-01 00:01">полночь 2000 года</time> наступит конец света, но, как видите, свет продолжается. Возможно, он закончится в <time datetime="3000-01-01 00:01">полночь 3000 года</time>, но это не точно</p>

noscript


Тег noscript определяет резервный контент, который отображается в случае, если в браузере отключен JavaScript. Он может использоваться как в теге head, так и в теге body. В первом случае noscript может содержать только такие теги как link, style и meta.


<script>document.write('Одни дивы да спаны... Где семантика?')</script><noscript>Пожалуйста, включите JavaScript</noscript>

Другие теги в форме шпагралок


Семантическое "секционирование" страницы


<body>  <header>    <h1>Page Title</h1>    <nav>      <ul>        <li><a href="#">Link1</a></li>        <li><a href="#">Link2</a></li>      </ul>    </nav>    <img src="images/logo.png" alt="" />  </header>  <aside>    <h2>Aside Title</h2>    <p>Aside Content</p>  </aside>  <main>    <article>      <h2>Atricle Title</h2>      <section>        <h3>Section Title</h3>        <p>Section Content</p>      </section>    </article>    <div>      <section>        <h2>Section2 Title</h2>        <p>Section2 Content</p>      </section>      <section>        <p>Section3 Content</p>      </section>    </div>  </main>  <footer>    <nav>      <ol>        <li><a href="#">Link3</a></li>        <li><a href="#">Link4</a></li>      </ol>    </nav>    <div>      <p>Block text &amp; <span>inline text</span></p>    </div>  </footer></body>

Стилизация текста


<p>  Текст может быть    <b>полужирным</b>,    <strong>полужирным и "важным"</strong>,    <i>"наклонным"</i>,    <em>наклонным и важным</em>,    <small>маленьким</small>,    <del>удаленным из документа</del>,    <ins>вставленным в документ</ins>,    <u>подчеркнутым</u>    <s>неправильным</s>.  Он может представлять собой    <q>короткую цитату</q>,    <kbd>ввод с клавиатуры</kbd>,    <samp>вывод программы</samp>.  Текст может находиться    <sup>над строкой</sup> и    <sub>под ней</sub>.  Наконец, он может быть <mark>помеченным</mark>.  Длинныйтекстможноразделять<wbr />внужномместе.  Его можно переносить<br />на новую строку и разделять<hr />горизонтальной чертой.</p>

Форма


Символ / означает или.


<form  action="URL"  autocomplete="on / off"  enctype=""  id="form"  method="GET / POST"  name="form"  novalidate>  <fieldset>    <legend>Title</legend>    <label for="first_name">Имя:</label>    <input type="text" id="first_name">    <label>Фамилия:      <input type="text">    </label>  </fieldset>  <select    autofocus    disabled    form="form"    multiple    name="select"    required    size="10"  >    <optgroup label="Group1">      <option        value="option1"        disabled        label="option1"      ></option>      <option value="option2" selected></option>    </optgroup>    <optgroup label="Group2" disabled>      <option value="option3"></option>    </optgroup>    <option value="option4"></option>  </select>  <textarea    autofocus    cols="30"    disabled    form="form"    maxlength="50"    name="textarea"    placeholder="Введите текст..."    readonly    required    rows="10"    wrap="hard / soft"  ></textarea>  <button    type="submit"    autofocus    disabled    form="form"    formaction="URL"    formenctype=""    formmethod="GET / POST"    formnovalidate    name="submit"  >    Кнопка для отправки формы  </button>  <button type="reset">Кнопка для сброса формы (очистки полей для ввода данных)</button>  <button type="button">Просто кнопка</button></form>

Поля для ввода данных


<input type="button"><input type="checkbox"><input type="color"><input type="date"><input type="datetime-local"><input type="email"><input type="file"><input type="hidden"><input type="image"><input type="month"><input type="number"><input type="password"><input type="radio"><input type="range"><input type="reset"><input type="search"><input type="submit"><input type="tel"><input type="text"> (значение по умолчанию)<input type="time"><input type="url"><input type="week">

Атрибуты:


  • accept MIME-тип принимаемых файлов (только для type="file"). Значения: расширение, например, .png, audio/*, video/*, image/*, медиа тип, например, json
  • alt подпись
  • autocomplete on / off
  • autofocus
  • checked только для type="checked" или type="radio"
  • disabled
  • form
  • formaction только для type="submit" или type="image"
  • formenctype только для type="submit" или type="image". Значения: application/x-www-form-urlencoded, multipart/form-data, text/plain
  • formmethod только для type="submit" или type="image" (get / post)
  • formnovalidate отключает проверку элементов формы
  • formtarget определяет, где отображается ответ, полученный после отправки формы (только для type="submit" или type="image"). Значения: _blank, _self, _parent, _top, название фрейма
  • height высота в пикселях (только для type="image")
  • list
  • max максимальное значение (число или дата)
  • maxlength максимальная длина строки (количество символов)
  • min минимальное значение (число или дата)
  • minlength минимальная длина строки (количество символов)
  • multiple позволяет пользователю выбирать несколько значений или отправлять несколько файлов
  • name
  • pattern регулярное выражение для проверки значения поля
  • placeholder
  • readonly
  • required
  • size количество символов, определяющее ширину поля
  • src путь к изображению, используемому в качестве кнопки для отправки формы (только для type="image")
  • step интервал (шаг) между min и max
  • type
  • value
  • width ширина поля в пикселях (только для type="image")

Пример валидации адреса электронной почты и пароля:


<form>  <input    type="email"    id="email"    name="email"    pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"    required  />  <input    type="password"    id="password"    name="password"    pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"    title="Пароль не может быть меньше 8 символов и должен содержать одну цифру, одну прописную (заглавную) букву и одну строчкую букву"    required  />  <input type="submit" /></form>

Таблица


<table>  <caption>    Table Title  </caption>  <colgroup>    <col span="2" />    <col />  </colgroup>  <thead>    <tr>      <th        abbr="H1"        colspan="2"        rowspan="2"        scope="col / colgroup / row / rowgroup"      >        Heading1      </th>      <th>Heading2</th>    </tr>  </thead>  <tbody>    <tr>      <td colspan="2" rowspan="2">1-1</td>      <td>1-2</td>    </tr>    <tr>      <td>2-1</td>      <td>2-2</td>    </tr>  </tbody>  <tfoot>    <tr>      <td>3-1</td>      <td>3-2</td>    </tr>  </tfoot></table>

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


Послесловие


Мы рассмотрели почти все существующие HTML-теги. С полным списком можно ознакомиться здесь или здесь. Во многих публикациях упоминаются теги map и area, но с их использованием сопряжено несколько проблем (сложность реализации карты, зависимость областей от координат и т.д.), поэтому я оставил их без внимания. Также существуют такие теги, как svg и canvas, предназначенные для работы с графикой, но для рассказа о каждом из них требуется отдельная статья (и даже не одна).




Наши виртуалки можно использовать для разработки веб-сайтов.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Подробнее..

Перевод Напишем и поймем Decision Tree на Python с нуля! Часть 2. Основы Python, необходимые для генерации Decision Tree

11.09.2020 18:10:30 | Автор: admin
Привет, Хабр! Представляю вашему вниманию перевод статьи "Python02. Python".

Данная статья вторая в серии. Первую вы можете найти здесь.

2.1 Комментарии обозначаются # или ''' (три одинарные кавычки)


# Комментарийa = 1 # Комментарий ''' Это тоже комментарийb = cc = d'''

2.2 Использование динамической типизации (тип определяется автоматически)


# динамическая типизация переменных# = копирование значения справа налевоi = 1 #  целое число (int)f = 2.1 # число с плавающей запятой (float)s = "a" # строковый тип (string)b = True # логический тип (boolean)l = [0,1,2] # массив,список (array) t = (0,1,2) # кортеж (tuple)d = {"a":0, "b":1} # словарь, ассоциативный массивprint(i,f,s,b,l,t,d)# 1 2.1 a True [0, 1, 2] (0, 1, 2) {'a': 0, 'b': 1}# Когда хотим определить тип, используем typeprint(type(i)) # Вывод <class 'int'># Переменная не сохраняет, а содержит фактическое значение# Это, своего рода, переменная-ссылка, указывающая на местоположение значения# Можно получить идентификатор актуального значения через idprint(id(l)) # 00000000000000 (меняется от исполнения к исполнениюц)l2 = l # Приведу в пример копию массива, где ссылаюсь на 2 его элемента, а фактический массив - 1. print(id(l2)) # 00000000000000 (то же значение, что у вышеуказанного id(l))# Поскольку существует только один фактический массив, кажется, что он был добавлен в массив l, даже если вы добавили элемент со ссылкой на l2.l2.append(1)

2.3 Арифметические операторы (Вычисление и присвоение значений. Результат вычисления, как правило, помещается в переменную)


a = 1 # переменная а соответствует значению 1b = a # b так же соответствует 1 a = 2 # теперь а соответствует 2, но b не изменяетсяa += 1 # значение а увеличивается на 1 (на данном этапе а равно 3)a -= 1 # значение а уменьшается на 1 (на данном этапе а равно 2)a *= 2 # значение а увеличивается в два раза (на данном этапе а равно 4)a /= 3 # значение а составляет  предыдущего значения (на данном этапе а равно 1.3333333333333333)a = 1+2-3*4/5 # Вычисление в следующем порядке: умножение, деление, сложение, вычитание  a равно 0,6. (Однако, если учитывать погрешность, а равно 0.6000000000000001)

2.4 Создание группы программ через отступ


a = 0# Указываем условие выполнения группы программ, обозначенной отступом.# Далее прописываются условия, циклы, функционализация и т.д.if a == 1:    # Строка, которая смещена вправо на такую же величину в левой части строки от этой строки, составляет одну группу программ.    print(1)    print(2)    pass # Указание на конец группы с отступом    # при выполнении условия if выполняется группа программ до строки вышеprint(3) # вывод независимо от значения а, не подпадающий под условие if# вывод 3

2.4.1 Сгруппированным программам даётся контроль над выполнением операций


# Условие, позволяющее программе понять, запускаться или нетif условие :    # program group# Выполнение повторяется только для элементов массива# Элементы массива присваиваются переменной v в прямом порядкеfor v in массив    # program group# Выполнение продолжается только при соблюдении условийwhile условие:    # program group# Тайминг выполнение группы программ определяется позже# (в первую очередь нужно создать группу программ и дать им имена)def имя группы():    # program group    # После выполнения группы программ, возвращаем результат с помощью return    # Или можно вернуться к началу    return# Ниже описан пример с ошибкой# print(2) с измененным отступом будет относиться к другой группе программ# однако, program group 2 не имеет под собой никакого объясненияif a == 1:    # выполнение группы program group 1 регулируется условием if a==1:    print(1)        # в program group 2 нет регулирующей части, поэтому компилятор выдает ошибку.        print(2)

2.4.2 Формулировка условий


a = 1b = 2c = 2# переменные a и b одинаковы, но a и c разныеif a==b and a!=c:    print("отличается только с")# а больше или равно b (включает а=b) или а>c (не включает а=с)elif a>=b or a>c:    print("а больше или равно b или больше с")# не подходит под условия if и elifelse:    print("кроме этого")# выводит "кроме этого"

2.4.2.1 Другой пример написания условий


a = 1b = 2# если а=b, то v=0, если нет, то v=1v = 0 if a==b else 1print(v)# вывод 1 

2.4.3 Формирование циклов (for)


for v in [0,1,2,3]: # Следующая обработка повторяется для элементов [0,1,2,3]в массиве.    # Проходимся по массиву. Элементы массива выводятся через переменную v в прямом поряке   print(v)   pass # Обозначаем конец отступа# вывод 0 1 2 3# Используя enumerate, можно получить индекс и значение массива в итеративном процессе.for i,v in enumerate([5,3,7,8]):    # i - индекс, v - значение элемента    print("(",i,v,")")# вывод ( 0 5 ) ( 1 3 ) ( 2 7 ) ( 3 8 )# С помощью zip два массива могут быть объединены в один и обработаны итеративно.for v0,v1 in zip([1,2,3,4],[5,6,7,8]):    # v0 содержит элементы массива первого аргумента zip, а v1 содержит элементы второго аргумента.    print("(",v0,v1,")")# вывод ( 1 5 ) ( 2 6 ) ( 3 7 ) ( 4 8 )

2.4.4 Цикл while


a = 3# Пока a больше либо равно 0while a>=0:    print(a)    # Уменьшаем значение a на 1    a -= 1# вывод 3 2 1 0

2.4.5 Функции и методы


# Определение функции: Название - function_name, аргументы: param1 и param2, param2 - аргумент по умолчанию# В случае, если аргумент не установлен, при вызове функции его значение будет равно 1 (значению по умолчанию)def function_name(param1,param2=1):    print("p1:",param1,"p2",param2)    # Эта функция возращает сумму аргументов param1 и param2    return param1 + param2# вызов функции (на данном этапе впервые запускается function_name)# значение param1 = 5,  param2 не устанавливается (используется аргумент по умолчанию)v = function_name(5)# вывод (вывод с помощью оператора print в функции function_name) p1: 5 p2 1print("возвращаемое значение",v)()# вывод "возвращаемое значение 6"

2.4.5.1 Лямба-выражение, пример написания функции


# название функции = lambda (входные аргументы) : (возвращаемое значение) - по такому принципу описывается функция.f = lambda x: x*x# Вызов функции происходит так же, как и в примере с defv = f(2)print(v) # отобразится 4

2.5 Преобразование строки в число


si = "1"   #Строкаsf = "2.3" #Строкаi = 4      #Целое число# Преобразовываем целое число i в строку, происходит конкатенация строкprint(str(i)+si)# 41# Обратная ситуация - преобразовываем строку si в целое число(int), происходит арифметическое сложениеprint(i+int(si))# 5# Преобразовываем строку в число с плавающей запятой (float), опять же происходит сложение (число при выводе автоматически заменяется на float)print(i+float(sf))# 6.3

2.6 Массив, список


# Создание массиваa = [1,1,2,3,2]              # a=[1,1,2,3,2]b = [n for n in range(8)]  # b=[0, 1, 2, 3, 4, 5, 6, 7]# Ссылка# Отрицательные значения становятся индексами, которые выссчитываются с -1 в качестве последнего элементаv = a[0]  # v=1v = a[-1] # v=2# Добавлениеa += [4]     # a=[1, 1, 2, 3, 2, 4]a.append(5)  # a=[1, 1, 2, 3, 2, 4, 5]# Извлечение (перед выполнением a=[1, 1, 2, 3, 2, 4, 5])v = a.pop(0) # v=1, a=[1, 2, 3, 2, 4, 5]# Вырезание (перед выполнением a=[1, 2, 3, 2, 4, 5])# a[первый индекс: предпоследний индекс]# если индексы не прописывать, а оставить просто a[:], первоначальный индекс автоматически станет  равен 0, последний - количество элементов)c = a[1:3] # c=[2, 3]# Максимумминимум (перед выполнением а= [1, 2, 3, 2, 4, 5])mx,mi = max(a),min(a) # mx=5, mi=1# Среднее значение(mean)Медианное значение(median)Наиболее частое значение(mode)Стандартное отклонение(stdev)Дисперсия(variance)# (перед выполнением a=[1, 2, 3, 2, 4, 5])from statistics import mean,median,mode,stdev,variancev = mean(a) # v=2.8333333333333335v = median(a) # v=2.5v = mode(a) # v=2v = stdev(a) # v=1.4719601443879744v = variance(a) #v=2.1666666666666665# Удаление повторений(перед выполнением a=[1, 2, 3, 2, 4, 5])c = set(a) # c={1, 2, 3, 4, 5}# SortedСоздается новый упорядоченный массив(перед выполнением a=[1, 2, 3, 2, 4, 5])c = sorted(a) # c=[1, 2, 2, 3, 4, 5] (массив a остается без изменений)# Sortупорядочивает элементы массива(перед выполнением a=[1, 2, 3, 2, 4, 5])a.sort()      # a=[1, 2, 2, 3, 4, 5]# Одинаково преобразует все элементы массиваmapping (перед выполнением a=[1, 2, 2, 3, 4, 5])# используем лямбда-выражение x: x+2.Значение элемента x меняется на x+2.# list преобразует вывод функции map в массивc = list(map(lambda x:x+2,a)) #c=[3, 4, 4, 5, 6, 7]# Фильтр(перед выполнением a=[1, 2, 2, 3, 4, 5])# используем лямбда-выражение x: x%2==0# Ищется элемент xкоторый делится на два без остатка. Подходящие элементы образуют новый массивc = list(filter(lambda x:x%2==0,a)) #c=[2, 2, 4]# Из всех элементов массива создается одно значение по заданным условиям# x - изначальное значениеесли запускаем цикл, х - значение первого элемента массива# xi - объект текущей операцииfrom functools import reduce# задаем x+xi, получаем сумму всех элементов массиваперед выполнением a=[1, 2, 2, 3, 4, 5]c = reduce(lambda x,xi:x+xi,a) #c=17# задаем max(xi,x), получаем наибольшее значение среди элементов массиваперед выполнением a=[1, 2, 2, 3, 4, 5]c = reduce(lambda x,xi:max(x,xi),a) #c=5

2.7 Словарь (dictionary)ассоциативный массив


# Создание. Ключи - Родной язык, Математика, Английский язык, а значения - массивыd = {    "Родной язык" : [81, 60, 97, 96],    "Математика" : [80, 78, 75, 96],    "Английский язык" : [76, 85, 65, 88],}print(d)# вывод {'Родной язык': [81, 60, 97, 96], 'Математика': [80, 78, 75, 96], 'Английский язык': [76, 85, 65, 88]}# Инициализация с использованием ford1 = {name:[0,1] for name in ["Родной язык","Математика","Английский язык"]}# вывод {'Родной язык': [0, 1], 'Математика': [0, 1], 'Английский язык': [0, 1]}# Получение значений через указание ключаa = d["Математика"] # a=[80, 78, 75, 96]# Замена значений через укзание ключаПерезаписьd["Английский язык"] = [77, 61, 91, 87] # d["Английский язык"]=[77, 61, 91, 87]# Цикл данныхfor k,v in d.items():    print(k,v)# вывод Родной язык [81, 60, 97, 96] Математика [80, 78, 75, 96] Английский язык [77, 61, 91, 87]# Максимальное значениеМинимальное значениев случае с ключом Родной языкjmx,jmi = max(d["Родной язык"]),min(d["Родной язык"])#print(jmx,jmi)# Находим название предмета с наивысшим средним значениемfrom statistics import mean# В массивах типа словарь при использовании max можно в качестве ключевого аргумента указать функцию, определяющую, среди чего искать максимальное значение# В этом случае лямбда-выражение используется для вычисления среднего значения из аргумента k(значение ключа в словаре)kmx = max(d,key=lambda k:mean(d[k])) # kmx="Родной язык"

2.8 Файловая операция with open


# with, файловая операция# with автоматизирует завершение процессов, требующих запуска и завершения.# Например, файл, открытый с помощью open, необходимо закрыть с помощью вызова функции close # Если использовать with и открыть файл с помощью open, при выходе из диапазона with функция close вызовется автоматически.# mode - r: чтениеw: записьa: дополнительная запись# Возвращаемое значение, которое мы получаем с помощью open, назовем fwith open("09_memo.txt",mode="a") as f:    # Записываем в файл    f.write("\n")# Чтение файлаwith open("09_memo.txt","r") as r:    print(r.read())

2.9 Случайное число random


# Использование случайных чиселimport random# Инициализация генерации случайных чисел# Случайные числа позволяют генерировать одну и ту же последовательность случайных чисел несколько раз для экспериментальной воспроизводимости.# Если аргумент seed указать численно, всегда будет выводиться одно и то же число.random.seed(0)# Генерация случайных чисел больше или равных 0 и меньше 1.print(random.random()) # Вывод 0.8444218515250481# Создает случайные целые числа больше первого аргумента и меньше второго аргумента.print(random.randint(1,3)) # Вывод одного числа из 1,2,3# Произвольно выбирает число из массиваprint(random.choice([0,1,2])) # Вывод одного числа из 0,1,2# Извлечение нескольких данных из массива# Вторым аргументом указывается количества выводимых данныхprint(random.sample([0,1,2],2)) # Выводимый массив [1, 2]# Перемешиваем данные в массиве# Новый массив не создается - данные перемешиваются непосредственно в массиве аa = [0,1,2]random.shuffle(a)print(a) # вывод [0, 2, 1]print(random.sample(a,len(a))) # Для создания нового массива можно использовать sample


Спасибо за прочтение!

Мы будем очень рады, если вы расскажете нам, понравилась ли вам данная статья, понятен ли перевод, была ли она вам полезна?
Подробнее..

Перевод Знакомство с PromQL Cheatsheet

13.06.2021 12:23:01 | Автор: admin

Скачать Cheatsheet по запросам PromQL

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

Поскольку Prometheus хранит данные в виде временных рядов (time-series data model), запросы PromQL радикально отличаются от привычного SQL. Понимание, как работать с данными в Prometheus, является ключом к тому, чтобы научиться писать эффективные запросы.

Не забудьте скачать Cheatsheet по запросам PromQL!

Как работают time-series databases

Временные ряды это потоки значений, связанных с меткой времени.

Каждый временной ряд можно идентифицировать по названию метрики и меткам, например:

mongodb_up{}

или

kube_node_labels{cluster="aws-01", label_kubernetes_io_role="master"}

В приведенном выше примере присутствует имя метрики (kube_node_labels) и метки (cluster и label_kubernetes_io_role). На самом деле, метрики тоже являются метками. Приведенный выше запрос можно записать так:

{__name__ = "kube_node_labels", cluster="aws-01", label_kubernetes_io_role="master"}

В Prometheus есть четыре типа метрик:

  • Gauges(Измеритель) значения, которые могут меняться. Например, метрика mongodb_up позволяет узнать, есть ли у exporter соединение с экземпляром MongoDB.

  • Counters(Счетчик) показывают суммарные значения и обычно имеют суффикс _total. Например, http_requests_total.

  • Histogram (Гистограмма) это комбинация различных счетчиков, используется для отслеживания размерных показателей и их продолжительности, таких как длительность запросов.

  • Summary (Сводка)работает как гистограмма, но также рассчитывает квантили.

Знакомство с выборкой данных PromQL

Выбрать данные в PromQL так же просто, как указать метрику, из которой вы хотите получить данные. В этом примере мы будем использовать метрику http_requests_total.

Допустим, мы хотим узнать количество запросов по пути / api на хосте 10.2.0.4. Для этого мы будем использовать метки host и path из этой метрики:

http_requests_total{host="10.2.0.4", path="/api"}

Запрос вернет следующие значения:

name

host

path

status_code

value

http_requests_total

10.2.0.4

/api

200

98

http_requests_total

10.2.0.4

/api

503

20

http_requests_total

10.2.0.4

/api

401

1

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

Это называетсяinstant vector, самое раннее значение для каждого потока на указанный в запросе момент времени. Поскольку семплы берутся в случайное время, Prometheus округляет результаты. Если длительность не указана, то возвращается последнее доступное значение.

Кроме того, вы можете получить instant vector из другого отрезка времени (например, день назад).

Для этого вам нужно добавить offset (смещение), например:

http_requests_total{host="10.2.0.4", path="/api", status_code="200"} offset 1d

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

http_requests_total{host="10.2.0.4", path="/api"}[10m]

Запрос вернет следующие значения:

name

host

path

status_code

value

http_requests_total

10.2.0.4

/api

200

641309@1614690905.515

641314@1614690965.515

641319@1614691025.502

http_requests_total

10.2.0.5

/api

200

641319@1614690936.628

641324@1614690996.628

641329@1614691056.628

http_requests_total

10.2.0.2

/api

401

368736@1614690901.371

368737@1614690961.372

368738@1614691021.372

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

Это называется range vector все значения для каждой серии в пределах указанного временного интервала.

Знакомство с агрегаторами и операторами PromQL

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

Представим, что у нас есть метрика node_cpu_cores с меткой cluster. Мы могли бы, например, суммировать результаты, объединяя их по определенной метке:

sum by (cluster) (node_cpu_cores)

Запрос вернет следующие значения:

cluster

value

foo

100

bar

50

С помощью этого простого запроса мы видим, что имеется 100 ядер ЦП для кластера cluster_foo и 50 для cluster_bar.

Кроме того, мы можем использовать в запросах PromQL арифметические операторы. Например, используя метрику node_memory_MemFree_bytes, которая возвращает объем свободной памяти в байтах, мы могли бы получить это значение в мегабайтах с помощью оператора деления:

node_memory_MemFree_bytes / (1024 * 1024)

Мы также можем получить процент доступной свободной памяти, сравнив предыдущую метрику с node_memory_MemTotal_bytes, которая возвращает общий объем памяти, доступной на узле:

(node_memory_MemFree_bytes / node_memory_MemTotal_bytes) * 100

Теперь мы можем использовать этот запрос для создания оповещения, когда на узле остается менее 5% свободной памяти:

(node_memory_MemFree_bytes / node_memory_MemTotal_bytes) * 100 < 5

Знакомство с функциями PromQL

PromQL поддерживает большое количество функций, которые мы можем использовать для получения более сложных результатов. Например, в предыдущем примере мы могли бы использовать функцию topk, чтобы определить, какой из двух узлов имеет больший объем свободной памяти (в процентах):

topk(2, (node_memory_MemFree_bytes / node_memory_MemTotal_bytes) * 100)

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

Представьте, что вы хотите узнать, сколько свободного места будет доступно на диске в следующие 24 часа. Вы можете применить функцию pred_linear к результатам за прошлую неделю из метрики node_filesystem_free_bytes, которая возвращает доступное свободное место на диске. Это позволяет прогнозировать объем свободного дискового пространства в гигабайтах в ближайшие 24 часа:

predict_linear(node_filesystem_free_bytes[1w], 3600 * 24) / (1024 * 1024 * 1024) < 100

При работе со счетчиками Prometheus удобно использовать функцию rate. Она вычисляет среднюю скорость увеличения временного ряда в векторе диапазона в секунду, сбросы счетчика автоматически корректируются. Кроме того, вычисление экстраполируется к концам временного диапазона.

Что делать, если нам нужно создать оповещение, которое срабатывает, если мы не получали запрос в течение 10 минут. Мы не можем просто использовать метрику http_requests_total, потому что при сбросе счетчика в течение указанного временного диапазона результаты были бы неточными:

http_requests_total[10m]

name

host

path

status_code

value

http_requests_total

10.2.0.4

/api

200

100@1614690905.515

300@1614690965.515

50@1614691025.502

В приведенном выше примере после сброса счетчика мы получаем отрицательные значения от 300 до 50, поэтому нам недостаточно только этой метрики. Мы можем решить проблему с помощью функции rate. Поскольку он считает сбросы счетчика, результаты фиксируются, как если бы они были такими:

name

host

path

status_code

value

http_requests_total

10.2.0.4

/api

200

100@1614690905.515

300@1614690965.515

350@1614691025.502

rate(http_requests_total[10m])

name

host

path

status_code

value

http_requests_total

10.2.0.4

/api

200

0.83

Независимо от сбросов за последние 10 минут в среднем было 0,83 запроса в секунду. Теперь мы можем настроить оповещение:

rate(http_requests_total[10m]) = 0

Что дальше?

В этой статье мы узнали, как Prometheus хранит данные, рассмотрели примеры запросов PromQL для выборки и агрегирования данных.

Вы можете скачать Cheatsheet по PromQL, чтобы узнать больше об операторах и функциях PromQL. Вы также можете проверить все примеры из статьи и Cheatsheet с нашим сервисом Prometheus playground.

Подробнее..

Из песочницы Блокнот-шпаргалка для быстрого Data preprocessing

14.07.2020 16:07:05 | Автор: admin
Часто люди, заходящие в область Data Science, имеют не совсем реалистичные представления о том, что их ждет. Многие думают, что сейчас они будут круто писать нейросети, создавать голосового помощника из Железного Человека или обыгрывать всех на финансовых рынках.
Но работа Data Scientist завязана на данных, и один из важнейших и время затратных моментов это обработка данных перед тем, как их подавать в нейросеть или анализировать определенным способом.

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

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

Получили dataset. Что делать дальше?


Итак, стандарт: нужно понять, с чем имеем дело, общую картину. Для этого используем pandas, чтобы просто определить разные типы данных.

import pandas as pd #импортируем pandasimport numpy as np  #импортируем numpydf = pd.read_csv("AB_NYC_2019.csv") #читаем датасет и записываем в переменную df

df.head(3) #смотрим на первые 3 строчки, чтобы понять, как выглядят значения



df.info() #Демонстрируем информацию о колонках



Смотрим на значения колонок:

  1. Соответсвует ли количество строчек каждой колонки общему количеству строчек?
  2. Какова суть данных в каждой колонке?
  3. Какую колонку мы хотим сделать target, чтобы делать предсказания для нее?

Ответы на эти вопросы позволят проанализировать датасет, и примерно нарисовать план ближайших действий.

Также, для более глубокого взгляда на значения в каждой колонке, можем воспользоваться функцией pandas describe(). Правда, недостаток этой функции в том, что она не дает информацию про колонки со значениями string. С ними мы разберемся позже.

df.describe()



Волшебная визуализация


Посморим на то, где у нас отсутствуют значения вообще:

import seaborn as snssns.heatmap(df.isnull(),yticklabels=False,cbar=False,cmap='viridis')



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

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

df = df[[c for c        in list(df)        if len(df[c].unique()) > 1]] #Перезаписываем датасет, оставляя только те колонки, в которых больше одного уникального значения

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

df.drop_duplicates(inplace=True) #Делаем это, если считаем нужным.                                 #В некоторых проектах удалять такие данные с самого начала не стоит.

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


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

df_numerical = df.select_dtypes(include = [np.number])df_categorical = df.select_dtypes(exclude = [np.number])

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

Работаем с количественными данными


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

Как нам их определить? Конечно, все зависит от природы данных, которые Вы анализируете, но в основном такие колонки могут иметь мало уникальных данных (в районе 3-10 уникальных значений).

print(df_numerical.nunique())


После того, как мы определимся с колонками-шпионами, мы переместим их из количественных данных в качественные:
spy_columns = df_numerical[['колонка1', 'колока2', 'колонка3']]#выделяем колонки-шпионы и записываем в отдельную dataframedf_numerical.drop(labels=['колонка1', 'колока2', 'колонка3'], axis=1, inplace = True)#вырезаем эти колонки из количественных данныхdf_categorical.insert(1, 'колонка1', spy_columns['колонка1']) #добавляем первую колонку-шпион в качественные данныеdf_categorical.insert(1, 'колонка2', spy_columns['колонка2']) #добавляем вторую колонку-шпион в качественные данныеdf_categorical.insert(1, 'колонка3', spy_columns['колонка3']) #добавляем третью колонку-шпион в качественные данные

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

for i in df_numerical.columns:    print(i, df[i][df[i]==0].count())

На этом этапе важно понять, в каких колонках нули могут означать отсутствующие значения: связано ли это с тем, как собирались данные? Или это может быть связано со значениями данных? На эти вопросы нужно отвечать в каждом отдельном случае.

Итак, если мы все-таки решили, что данные у нас могут отсутствовать там, где есть нули, следует заменить нули на NaN, чтобы было легче потом работать с этими утерянными данными:

df_numerical[["колонка 1", "колонка 2"]] = df_numerical[["колонка 1", "колонка 2"]].replace(0, nan)

Теперь посмотрим, где у нас пропущены данные:

sns.heatmap(df_numerical.isnull(),yticklabels=False,cbar=False,cmap='viridis') # Можно также воспользоваться df_numerical.info()



Здесь должны быть отмечены желтым цветом те значения внутри столбцов, которые отсутствуют. И самое интересное начинается теперь как вести себя с этими значениями? Удалить строчки с этими значениями или столбцы? Или заполнить эти пустые значения какими-нибудь другими?

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



0. Удаляем ненужные колонки

df_numerical.drop(labels=["колонка1","колонка2"], axis=1, inplace=True)

1. Количество пустых значений в этой колонке больше 50%?

print(df_numerical.isnull().sum() / df_numerical.shape[0] * 100)

df_numerical.drop(labels=["колонка1","колонка2"], axis=1, inplace=True)#Удаляем, если какая-то колонка имеет больше 50 пустых значений

2. Удаляем строчки с пустыми значениями

df_numerical.dropna(inplace=True)#Удаляем строчки с пустыми значениями, если потом останется достаточно данных для обучения

3.1. Вставляем случайное значение

import random #импортируем randomdf_numerical["колонка"].fillna(lambda x: random.choice(df[df[column] != np.nan]["колонка"]), inplace=True) #вставляем рандомные значения в пустые клетки таблицы

3.2. Вставляем константное значение

from sklearn.impute import SimpleImputer #импортируем SimpleImputer, который поможет вставить значенияimputer = SimpleImputer(strategy='constant', fill_value="<Ваше значение здесь>") #вставляем определенное значение с помощью SimpleImputerdf_numerical[["новая_колонка1",'новая_колонка2','новая_колонка3']] = imputer.fit_transform(df_numerical[['колонка1', 'колонка2', 'колонка3']]) #Применяем это для нашей таблицыdf_numerical.drop(labels = ["колонка1","колонка2","колонка3"], axis = 1, inplace = True) #Убираем колонки со старыми значениями


3.3. Вставляем среднее или макисмально частое значение

from sklearn.impute import SimpleImputer #импортируем SimpleImputer, который поможет вставить значенияimputer = SimpleImputer(strategy='mean', missing_values = np.nan) #вместо mean можно также использовать most_frequentdf_numerical[["новая_колонка1",'новая_колонка2','новая_колонка3']] = imputer.fit_transform(df_numerical[['колонка1', 'колонка2', 'колонка3']]) #Применяем это для нашей таблицыdf_numerical.drop(labels = ["колонка1","колонка2","колонка3"], axis = 1, inplace = True) #Убираем колонки со старыми значениями

3.4. Вставляем значение, вычисленное другой моделью

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

Итак, пока повествование о количественных данных прервется, потому что есть множество других нюансов о том, как лучше делать data preparation и preprocessing для разных задач, и базовые вещи для количественных данных были учтены в этой статье, и сейчас самое время вернуться в качественным данным, которые мы отделили несколько шагов назад от количественных. Вы же можете изменять этот notebook так, как Вам угодно, подстраивая его под разные задачи, чтобы data preprocessing проходил очень быстро!

Качественные данные


В основном для качественных данных используется метод One-hot-encoding, для того, чтобы отформатировать их из string (или object) в число. Перед тем как перейти к этому пункту, воспользуемся схемой и кодом сверху, для того, чтобы разобраться с пустыми значениями.

df_categorical.nunique()

sns.heatmap(df_categorical.isnull(),yticklabels=False,cbar=False,cmap='viridis')



0. Удаляем ненужные колонки

df_categorical.drop(labels=["колонка1","колонка2"], axis=1, inplace=True)


1. Количество пустых значений в этой колонке больше 50%?

print(df_categorical.isnull().sum() / df_numerical.shape[0] * 100)

df_categorical.drop(labels=["колонка1","колонка2"], axis=1, inplace=True) #Удаляем, если какая-то колонка                                                                           #имеет больше 50% пустых значений

2. Удаляем строчки с пустыми значениями

df_categorical.dropna(inplace=True)#Удаляем строчки с пустыми значениями,                                    #если потом останется достаточно данных для обучения

3.1. Вставляем случайное значение

import randomdf_categorical["колонка"].fillna(lambda x: random.choice(df[df[column] != np.nan]["колонка"]), inplace=True)

3.2. Вставляем константное значение

from sklearn.impute import SimpleImputerimputer = SimpleImputer(strategy='constant', fill_value="<Ваше значение здесь>")df_categorical[["новая_колонка1",'новая_колонка2','новая_колонка3']] = imputer.fit_transform(df_categorical[['колонка1', 'колонка2', 'колонка3']])df_categorical.drop(labels = ["колонка1","колонка2","колонка3"], axis = 1, inplace = True)

Итак, наконец мы разобрались с пустыми значениями в качественнных данных. Теперь время произвести one-hot-encoding для значений, которые есть в вашей базе данных. Этот метод крайне часто используется для того, чтобы Ваш алгоритм мог обучаться с учетом качественных данных.

def encode_and_bind(original_dataframe, feature_to_encode):    dummies = pd.get_dummies(original_dataframe[[feature_to_encode]])    res = pd.concat([original_dataframe, dummies], axis=1)    res = res.drop([feature_to_encode], axis=1)    return(res)

features_to_encode = ["колонка1","колонка2","колонка3"]for feature in features_to_encode:    df_categorical = encode_and_bind(df_categorical, feature))

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

new_df = pd.concat([df_numerical,df_categorical], axis=1)

После того, как мы соединили вместе датасеты в один, под конец мы можем использовать трансформацию данных с помощью MinMaxScaler из библиотки sklearn. Это сделает наши значения в пределах от 0 до 1, что поможет при обучении модели в будущем.

from sklearn.preprocessing import MinMaxScalermin_max_scaler = MinMaxScaler()new_df = min_max_scaler.fit_transform(new_df)

Эти данные теперь готовы ко всему к нейронным сетям, стандартным ML алгоритмам и тд!

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

Категории

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

  • Имя: Макс
    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