Introduction to Time Series Data
Time series data management is an essential procedure for storing, analyzing, and visualizing time-stamped data. Time series data is a type of data that is collected and stored over time; e.g. sensor, financial, and log data. These are often gathered at regular intervals, and the time aspect of the data is an essential part of its analysis and visualization.
To work with time series data, we need a database that can handle the specific needs of time series data, such as high-performance write, read, and data retention policies. One popular open-source time series database is InfluxDB. InfluxDB is a robust open-source time series database that allows you to easily store, query, and analyze data points collected at frequent intervals. Thus you can use it for monitoring systems, industrial automation, or IoT devices.
Time Series DB or Relational DB Management System?
When it comes to time series data management, it is important to understand the difference between TSDB and RDBMS. InfluxDB, as a time series database, excels in handling and analyzing time-stamped data providing high-write loads, real-time data collection and analysis, and time-based querying. In contrast, traditional relational databases, such as MySQL or PostgreSQL, are not optimized for time series data. They are designed to handle structured data with fixed schemas. In addition, they are not well-suited for handling large amounts of time-stamped data.
Time Series DB Advantages
Here are a few reasons why time series databases are better suited for handling time series data than traditional relational databases:
- High write loads: Time series databases are optimized for handling high write loads. They can take millions of data points per second. This makes them well-suited for use cases where real-time data collection is required, such as monitoring IoT devices.
- Time-based querying: Time series databases provide powerful time-based querying capabilities. For example, retrieving data based on time ranges and performing time-based aggregations. This makes it easy to analyze and visualize time series data.
- Scalability: Time series databases are designed to be horizontally scalable, allowing them to handle large amounts of data. This makes them well-suited for use cases where data volume is expected to grow over time.
- Performance: Time series databases are optimized for time-based queries. Thus they can handle large amounts of data and return results quickly. This makes them well-suited for use cases where real-time data analysis is required.
- Retention policies: Time series databases provide support for retention policies. Consequently, you can automatically expire old data and keep only the data that you need. This helps to keep the size of the database manageable and optimize query performance.
You can further explore the benefits of time series data management and discover why time series databases are a superior option for handling time-stamped data by reading here and here.
Setting Up InfluxDB for Time Series Data Management
Installing InfluxDB
In this blog post, we will focus on a popular time series database: InfluxDB. InfluxDB is an optimized open-source database for storing and querying time series data. It has a SQL-like query language and supports multiple data types, including strings, integers, and floats.
After installing InfluxDB you will need to start the InfluxDB service. In Powershell on Windows 11 machines, navigate into the installation folder (e.g. C:\Program Files\InfluxData\influxdb
) and start InfluxDB by running the influxd
daemon:
Installing InfluxDB-Python Library
Next, you need to install the InfluxDB-Python library. This library provides a Python interface for interacting with InfluxDB. You can install it using pip:
pip install influxdb
pip install influxdb-client
What is the InfluxDB Structure?
InfluxDB organizes data around three key concepts: measurements, tags, and fields.
- Measurements: A measurement is a collection of data points that are stored together and can be queried as a group. Each measurement has a name, and the data points within a measurement are typically related in some way. For example, a measurement called “temperature” might contain data points representing the temperature at different locations and times.
- Tags: Tags are indexed metadata associated with a data point. Thus, you can use tags to filter, search, and group data in an efficient way. Each data point in a measurement can have one or more tags, and each tag has a key and a value. For example, a data point in the “temperature” measurement might have a tag called “location” with the value “New York”.
- Fields: Fields are the actual data and contain the numerical or string values. Each data point in a measurement can have one or more fields, and each field has a key and a value. For example, a data point in the “temperature” measurement might have a field called “value” with the value “72”. InfluxDB organizes fields in a columnar format for efficient and fast data retrieval, without the need for indexing.
With InfluxDB, not only you can store measurements, tags, and fields, but also timestamp each data point with nanosecond precision. By utilizing InfluxDB’s timestamp feature, you can consistently organize and evaluate your data by its generating or receiving time, resulting in streamlined and efficient analysis.
InfluxDB supports SQL-like querying and has a built-in HTTP API for easy data ingestion. It uses a data retention policy to automatically expire old data from the database, which can be specified at the time of database creation, or later on.
How to interact with InfluxDB?
There are several ways to connect to and interact with InfluxDB. All of them provide a simple and easy-to-use API for connecting to InfluxDB, writing data, running queries, and managing databases and measurements:
- InfluxDB-Python library: This is a Python library that allows you to interact with InfluxDB using Python code.
- InfluxDB-JavaScript library: This JavaScript library allows you to interact with InfluxDB using JavaScript code.
- InfluxDB REST API: The InfluxDB REST API allows you to interact with InfluxDB using HTTP requests.
- InfluxDB CLI: The InfluxDB command-line interface (CLI) allows you to interact with InfluxDB using the command line.
- InfluxDB UI: InfluxDB also provides a web-based UI (called Chronograf) that provides a graphical interface for interacting with InfluxDB. You can use it also to create visualizations of your data.
- Other client libraries: InfluxDB also provides libraries for different programming languages such as Go, Java, .Net, and more for interacting with InfluxDB using those languages.
creating a bucket
InfluxDB uses a structure called a bucket to store time series data. A bucket is a container that holds all the data, metadata, and indexes for a specific retention policy. In InfluxDB 2.0 and later versions, you must create a bucket before you can write data to it. You can use InfluxDB CLI, UI, or API to manage buckets.
Writing Real-Time Data to InfluxDB
import psutil
from influxdb_client import InfluxDBClient
from influxdb_client.client.write_api import SYNCHRONOUS
import msvcrt
import time
# You can generate an API token from the "API Tokens Tab" in the UI
myToken = "ZuO6b6Km4B0FdM-iciPWghLftxIs5li7E7QMgGrNNZWznEyeSvRspocs57WKftejGnJ4uxPwZr5Cz7YKooufQQ=="
myOrg = "Unipd"
myBucket = "cpu_ram_usage"
# Connect to the InfluxDB client
client = InfluxDBClient(url="http://localhost:8086", token=myToken , org=myOrg )
write_api = client.write_api(write_options=SYNCHRONOUS)
while True:
# Get the real-time CPU usage
cpu_usage = psutil.cpu_percent()
# Get the real-time RAM usage
ram_usage = psutil.virtual_memory().percent
# Prepare the data in InfluxDB line protocol format
cpu_data = f'status,host=myLaptop cpu_usage={cpu_usage}'
ram_data = f'status,host=myLaptop ram_usage={ram_usage}'
print([cpu_data, ram_data])
# Write the data to InfluxDB
write_api.write(myBucket, myOrg, [cpu_data, ram_data])
# Wait for 3 seconds before writing the next set of data
time.sleep(3)
# Press 'q' to stop
if msvcrt.kbhit() and msvcrt.getch().decode() == 'q':
break
Reading Data From InfluxDB
To read data from InfluxDB, you can use the InfluxDBClient to execute InfluxQL (InfluxDB’s query language) queries. Here is an example of how to retrieve the last 10 minutes of saved data:
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
# You can generate an API token from the "API Tokens Tab" in the UI
myToken = "ZuO6b6Km4B0FdM-iciPWghLftxIs5li7E7QMgGrNNZWznEyeSvRspocs57WKftejGnJ4uxPwZr5Cz7YKooufQQ=="
myOrg = "Unipd"
myBucket = "cpu_ram_usage"
# Connect to the InfluxDB client
client = InfluxDBClient(url="http://localhost:8086", token=myToken , org=myOrg )
# Execute the query
result = client.query_api().query('from(bucket:"cpu_ram_usage") |> range(start: -15m) |> filter(fn: (r) => r._measurement=="status")', myOrg)
# Print the results
for table in result:
for record in table.records:
print(str(record["_time"]) + "\t" + record.get_measurement() + "\t" + str(record["host"]) + "\t" + record.get_field() + "\t" + str(record.get_value()) )
Visualization Using InfluxDB Dashboard
Once you have created a visualization, you can add it to a dashboard, which is a collection of one or more visualizations that you can view together. Dashboards are useful for monitoring and analyzing real-time data, as they allow you to view multiple charts at the same time and quickly switch between different views of your data.
Additionally, you can set alerts and notifications based on a specific condition in the data. This feature can be very useful in real-time monitoring and analytics.
Overall, the InfluxDB UI provides a convenient way to interact with your data and create live graphs and dashboards, which is particularly useful for monitoring and analyzing real-time data. Here are some dashboards examples:
Summary of Time Series Data Management Using InfluxDB
In this tutorial, we have seen how to use InfluxDB to store and analyze real-time CPU and RAM usage data. This is just a taste of what is possible with InfluxDB and time series data. You can also use InfluxDB to store and analyze data from other sources like IoT devices and use the data to power real-time analytics and monitoring application.
Всем привет! В продолжении публикации о возможностях PowerShell, упомянул, что выделю в отдельную статью использование библиотеки Selenium с упомянутым языком. Этот небольшой гайд также может подойти для понимания, что из себя представляет данная библиотека и общее представление, как с ней работать, т.к. не зависимо от выбранного вами языка, принцип работы с Selenium одинаковый. Сразу отвечу на вопрос для тех, кто не знает, что это за библиотека или кому она может понадобиться. Selenium в первую очередь инструмент для функционального тестирования, это когда нужно проделать ряд действий имитируя реального пользователя и убедиться, что функционал Web-приложения работает исправно или неисправно. Реже используется для автоматизации различных задач в браузере (вызов JavaScript функций по средствам нажатий кнопок, заполнение форм и т.п.), для которых не предусмотрено API, а в некоторых случаях может выступать более удобной и полноценной альтернативой. Работая системным администратором, мне было удобно автоматизировать действия в панелях управления различных сервисах, или собирать специфические метрики с отправкой в базу данных. Selenium даже может входить в список инструментов DevOps-инженера. На мой взгляд, инструмент очень интересный с точки зрения творческого подхода к решению различных задач.
Постараюсь разложить все по полочкам, от установки всех зависимостей до примера работы с данным инструментом, также продемонстрирую альтернативный инструмент, который использовал до знакомства с Selenium. Специально для статьи подготовил модуль, который позволяет общаться с бесплатной версией ChatGPT из консоли PowerShell, который вы можете установить, используя всего две команды, а так же покажу, как просто создать такой модуль. Хочу заметить, что осознанно выделяю данную статью в средний уровень сложности Habr, по итогам ознакомления, для людей базово знакомых с PowerShell (для этого у меня есть отдельная работа с заметками), порог вхождения будет минимален. На просторах интернета не так много информации об использовании Selenium с языком PowerShell, я же узнал о данном инструменте из этой статьи, к слову, на этом ресурсе много полезного подчеркнул для себя в процессе изучения PowerShell, но несколько важных нюансов для построения собственных модулей там не было раскрыто. Также ключевой проблемой являлась установка всех зависимостей актуальной версии для дальнейшего масштабирования решений с использованием данной библиотеки, стоит упомянуть модуль selenium-powershell на GitHub, который не поддерживается с 2019 года.
Что требуется для работы:
Браузер. Вариантом несколько: Internet Explorer, Google Chrome, Mozilla Firefox. Важным моментом для меня было использовать конкретный браузер, базирующийся на Chrome.
ChromeDriver. Данный драйвер нужен для возможности Selenium управлять браузером (драйвер может быть другой, в зависимости от выбранного вами браузера). Важным моментом (и изначальной проблемой) является соответствие версии драйвера и текущей версии установленного браузера. За несколько месяцев использования я заметил тенденцию, что версия Google Chrome всегда опережает Chrome Driver на порядок релизов, из-за этого использовать драйвер с текущей версией установленного вами браузера не получится, т.к. он обновляется автоматически в целях поддержания безопасности. В поисках возможности автоматизировать данный процесс, где для сначала нужно удалить браузер из системы (тут вариантом несколько, через WMI/CIM или запускать деинсталлятор в тихом режиме) а потом устанавливать конкретную версию. Удобнее всего управлять данным процессом через менеджер пакетов, например, NuGet, Chocolatey, WinGet или Scoop. Изначально я пошел по этому пути, но тут возникали проблемы, браузер мог самостоятельно обновиться несмотря на различные запреты, а иногда вовсе отсутствовала возможность установки подходящей версии, со временем натыкаясь на различные ошибки в работе, к тому же, этот подход не позволял обновлять сразу все драйверы и приходилось производить много ручных действий. Спустя время меня посетила идея использовать Chromium, после чего весь процесс развертывания получилось очень сильно упростить и никаких проблем уже не возникало, так как у браузера имеется портативная версия. Самое приятное, Chromium имеет открытый исходный код и разрабатывается сообществом, что позволяет использовать такое решение в организации на территории РФ по сей день.
WebDriver. Драйвер Selenium. Так как для работы с драйвером мы используем язык PowerShell, который в свою очередь базируется на платформе .NET, то нам нужна версия для C# (всего вариантов несколько, поддерживаются такие языки, как Python, Java, JavaScript и Ruby). Также присутствует библиотека WebDriver.Support, которая нужна для обработки реже используемых действий.
Процесс установки и обновления вышеперечисленного у меня получилось автоматизировать. Если кратко, то для быстрого развертывания всех зависимостей достаточно воспользоваться одной командой в терминале, которая в свою очередь считает из репозитория GitHub PowerShell скрипт и запустит его в вашей системе:
Invoke-Expression(New-Object Net.WebClient).DownloadString("https://raw.githubusercontent.com/Lifailon/Deploy-Selenium/rsa/Deploy-Selenium-Drivers.ps1")
Данный скрипт установит по пути “домашняя директория пользователя/Документы/Selenium” портативную версию браузера Chromium, chromedriver.exe, WebDriver.dll и WebDriver.Support.dll актуальных версий на текущий момент, а при повторно запуске, обновит все файлы.
Для начала работы определимся с шаблоном, который будем использовать при написании любого скрипта в дальнейшем. В начале необходимо загрузить установленные ранее зависимости:
$path = "$home\Documents\Selenium\"
$log = "$path\ChromeDriver.log"
$ChromeDriver = "$path\ChromeDriver.exe"
$WebDriver = "$path\WebDriver.dll"
$SupportDriver = "$path\WebDriver.Support.dll"
$Chromium = (Get-ChildItem $path -Recurse | Where-Object Name -like chrome.exe).FullName
Add-Type -Path $WebDriver
Add-Type -Path $SupportDriver
try {
$ChromeOptions = New-Object OpenQA.Selenium.Chrome.ChromeOptions # создаем объект с настройками запуска браузера
$ChromeOptions.BinaryLocation = $Chromium # передаем путь до исполняемого файла, который отвечает за запуск браузера
$ChromeOptions.AddArgument("start-maximized") # добавляем аргумент, который позволяет запустить браузер на весь экран
$ChromeOptions.AcceptInsecureCertificates = $True # игнорировать предупреждение на сайтах с не валидным сертификатом
#$ChromeOptions.AddArgument("headless") # скрывать окно браузера при запуске
$ChromeDriverService = [OpenQA.Selenium.Chrome.ChromeDriverService]::CreateDefaultService($ChromeDriver) # создаем объект настроек службы драйвера
$ChromeDriverService.HideCommandPromptWindow = $True # отключаем весь вывод логирования драйвера в консоль (этот вывод нельзя перенаправить)
$ChromeDriverService.LogPath = $log # указать путь до файла с журналом
$ChromeDriverService.EnableAppendLog = $True # не перезаписывать журнал при каждом новом запуске
#$ChromeDriverService.EnableVerboseLogging = $True # кроме INFO и ошибок, записывать DEBUG сообщения
$Selenium = New-Object OpenQA.Selenium.Chrome.ChromeDriver($ChromeDriverService, $ChromeOptions) # инициализируем запуск с указанными настройками
# Далее следует код для обработки действий в браузере
}
finally {
$Selenium.Close()
$Selenium.Quit()
}
Теперь определяемся, что мы хотим выполнить в браузере. Для примера, поставим задачу, где мы откроем онлайн калькулятор и посчитаем сумму двух чисел, а полученный результат выведем на экран консоли. Работа с Selenium всегда начинается с перехода по нужной нам ссылке (url), откуда будем начинать выполнять наши действия, для этого используем дочерний метод навигации – GoToUrl
:
$Selenium.Navigate().GoToUrl("https://google.com")
Если в опциях запуска браузера мы не используем аргумент headless (достаточно его закомментировать), что удобно при отладке (мы можем наблюдать все выполняемые нами действия в окне браузера или наоборот), то в браузере мы увидим, как перейдем по указанной нами ссылке, в нашем случае поисковика Google. Далее нужно передать текст для поиска, например так: “calculator online”. Для этого нужно найти элемент, который отвечает за ввод текста.
Поиск элементов является ключевым моментом при работе с данным инструментом, этот алгоритм аналогичен для всех дальнейших действий. Можно использовать два способа:
1. DevTools – это встроенный набор инструментов Web-разработчиков и тестировщиков. В своем браузере (для удобства в отдельном окне, например, Google Chrome) переходим по нужной нам ссылки и запускаем данный инструмент используя клавишу F12 (или правой кнопкой мыши на странице – Просмотреть код), где нам нужна первая вкладка – «Элементы» (еще иногда может быть полезна вкладки Сеть, что бы отслеживать образующиеся запросы во время взаимодействия с приложением). Универсальным способом будет использовать комбинацию Ctrl+Shift+C, далее используя курсор мыши наводим на нужный нам элемент, в нашем случае, поисковой строки:

Что нас тут может интересовать:
textarea – это элемент управления (он же Tag), который предоставляет пользователю возможность вводить несколько строк текста.
jsname=”yZiJbe” – это атрибут, который указывает на используемый JavaScript-код для данного элемента. В нашем случае может использоваться для поиска через метод
CssSelector
.id=”#APjFqb” – это уникальный идентификатор элемента – селектор (selector), который можно скопировать правой кнопкой мыши, и использоваться для обращения к этому элементу из JavaScript, CSS, а также в нашем случае из PowerShell. Альтернативным вариантом является XPath, который также можно использовать для обращения к элементу.
class=”gLFyf” — атрибут, который определяет класс элемента, который можно использовать для поиска через
ClassName
.name=”q” — атрибут, определяющий имя элемента, которое будет отправлено на сервер при отправке формы, его можно использовать для обращения к элементу по имени.
maxlength=”2048″ — атрибут, указывающий на максимальное количество символов, которые пользователь может ввести в данном поле.
rows=”1″ — атрибут, который определяет количество строк текстового поля ввода.
aria-label=”Найти” — лейбл или этикетка, который соответствует свойству объекта
ComputedAccessibleLabel
и может быть полезен при фильтрации элементов, используя методFindElements
.role=”combobox” — роль элемента, соответствует свойству объекта
ComputedAccessibleRole
.
Исходя из полученных данных, можно составить несколько запросов, которые будут выполнять одинаковое действие, т.е. искать нужный нам элемент с строкой для ввода текста.
Проще всего обращаться к таких элементам по его
Id
:
$Selenium.FindElements([OpenQA.Selenium.By]::Id('APjFqb'))
Если скопировали
XPath
:
$Selenium.FindElements([OpenQA.Selenium.By]::XPath('//*[@id="APjFqb"]'))
Или по имени элемента:
$Selenium.FindElements([OpenQA.Selenium.By]::Name('q'))
В формате XPath, аналогичный запрос будет выглядеть следующим образом (используя соответствующий метод):
$Selenium.FindElements([OpenQA.Selenium.By]::XPath('//*[@name="q"]'))
Используя имя класса:
$Selenium.FindElements([OpenQA.Selenium.By]::ClassName('gLFyf'))
Используя
jsname
для CSS селектора:
$Selenium.FindElements([OpenQA.Selenium.By]::CssSelector('[jsname="yZiJbe"]'))
2. Второй вариант, если вы не хотите пользоваться браузером для поиска элементом (мне иногда так было удобнее), а также если у элемента нет уникальных данных (когда в DevTools мы можем увидеть только название тэга, такое будет в примере с ChatGPT ниже) для обращения к нему или они могут меняться, в таком случае можно использовать тот же метод FindElements
через PowerShell и искать по имени тэга (TagName
):

При таком поиске мы можем получить несколько элементов (практически всегда их несколько), т.к. имя тэга не уникально и понадобится дополнительная фильтрация, тут нам могут помочь упомянутые выше свойства объектов ComputedAccessibleLabel
и ComputedAccessibleRole
, а также Text
. Такой подход может занимать продолжительное время при большом количестве элементов. В нашем случае все просто, мы уже знаем нужный нам элемент, его роль combobox
:
$Selenium.FindElements([OpenQA.Selenium.By]::TagName('textarea')) | Where-Object ComputedAccessibleRole -eq combobox
Еще мы знаем, что значение Lable не является пустым, и даже если бы мы этого не знали, элемента всего два, не сложно догадаться, который из них нам нужен (или просто предварительно проверить оба).
Есть множество популярных тегов, которые используются для работы с CSS и DOM (Document Object Model), и в нашем случае могут быть полезны для поиска элементов. Такие тэги и их описание не сложно найти в интернете, перечислю некоторые из них, которые использовал в данной статье:
textarea – используется для создания многострочного поля ввода текста;
input – используется для добавления различных элементов ввода, таких как текстовые поля, флажки, радиокнопки и т.п.;
div – используется для создания блочного контейнера на веб-странице, который позволяет группировать другие элементы;
span – также используется для группировки элементов, но в отличие от
div
обычно не создает перенос строки и отображается в строке;button – используется для создания кнопок.
Как только мы нашли элемент, помещаем его в переменную, (в нашем случае $Search
), чтобы взаимодействовать с ним на прямую. Для передачи текста, используем метод SendKeys
:
$Search.SendKeys("calculator online")
Теперь, чтобы не повторять поиск элемента с кнопкой поиска, просто используем привычный многим в браузере метод Enter
(мы уже выяснили, что данный элемент не поддерживает несколько строк: rows=1):
$Search.SendKeys([OpenQA.Selenium.Keys]::Enter)
На данном этапе мы выполнили переход на новый url, где получаем содержимое страницы с результатом поиска. В нашем случае, калькулятор уже находится на странице, специально для примера используем второй подход, где вначале находим все элементы с тэгом div
, которые отвечают за нажатие, фильтруем вывод по роли (ComputedAccessibleRole
) – Button
и лейблу (ComputedAccessibleLabel
, также можно использовать свойство Text) – 2. После чего производим два нажатия, используя метод Click()
. Выглядит это так:
Start-Sleep 1
$div = $Selenium.FindElements([OpenQA.Selenium.By]::TagName("div"))
$2 = $div | Where-Object {($_.ComputedAccessibleRole -like "button") -and ($_.ComputedAccessibleLabel -like "2")}
$2.Click()
$2.Click()
Мы нашли цифру 2, и нажали на нее два раза. Далее находим элемент, отвечающий за сложение. У данного элемента свойство Lable
имеет содержимое «сложение», которое отличается от Text
, где содержимым является «+», после чего нажимаем на искомый элемент:
$plus = $div | Where-Object {($_.ComputedAccessibleRole -eq "button") -and ($_.Text -eq "+")}
$plus.Click()
К остальным элементам обратимся по jsname
(которые мы предварительно находим через DevTools). Теперь, нам остается забрать полученный результат, чтобы вывести его в консоль. Для этого достаточно обратиться к нужному элементу и получить значение его свойства Text
.
$3 = $Selenium.FindElement([OpenQA.Selenium.By]::CssSelector('[jsname="KN1kY"]'))
$3.Click()
$3.Click()
$sum = $Selenium.FindElement([OpenQA.Selenium.By]::CssSelector('[jsname="Pt8tGc"]'))
$sum.Click()
$result = $Selenium.FindElement([OpenQA.Selenium.By]::CssSelector('[jsname="VssY5c"]')).Text
Write-Host "Result: $result" -ForegroundColor Green
Обратите внимание, как быстро происходит обращение к элементам на прямую. При фильтрации имеющихся 1092 элементов (в примере) с тэгом div
, для поиска одного элемента уходит порядка 3-4 секунд, что ощутимо дольше и нецелесообразно для использования в конечном скрипте:

Пример целиком опубликован на GitHub.
Для второго случая продемонстрирую пример с ChatGPT. Это альтернатива, т.к. для подобных сервисов (обобщаю в примере с другими модулями) присутствует API, но иногда невозможно получить такой токен бесплатно, а ввиду ограничений в нашей стране даже приобрести напрямую за деньги, что послужило для меня отправной точкой создания подобных модулей. Цель простая, общаться с ИИ используя только консоль PowerShell. Еще, это может оказаться полезным, если вы хотите внедрить такое решение в свой скрипт для генерации ответов (например, произвести парсинг динамических данных) или даже части кода (зависит от задач и потребностей, тенденция растет с каждым днем). И тут действительно все очень просто, нам нужен любой бесплатный сервис, который желательно не требует авторизации, после чего найти элемент, который отвечает за ввод текста и передать в него параметр $Text
с нашим запросом (он и будет использоваться параметром в модуле для задаваемых нами вопросов), после чего дождаться ответа и получить результат. Вот пример для вышеупомянутого шаблона:
$Selenium.Navigate().GoToUrl("$url")
$Search = $Selenium.FindElements([OpenQA.Selenium.By]::TagName('textarea')) | Where-Object ComputedAccessibleRole -eq textbox
$Search.SendKeys("$Text")
$Search.SendKeys([OpenQA.Selenium.Keys]::Enter)
while ($true) {
if ($($Selenium.FindElements([OpenQA.Selenium.By]::TagName('button')).Text) -eq "Clear") {
$Result = $Selenium.FindElements([OpenQA.Selenium.By]::TagName('span'))
return $($Result[-2].Text)
break
}
}
Важным моментом является получение конечного результата ответа на наш вопрос, т.к. ChatGPT не отвечает сразу, а постепенно генерирует текст на экране. Нам всегда надо за что-то зацепиться, для проверки, что ответ был получен полностью, только после этого прочитать его. В примере я использую содержимое свойства кнопки – Text
, когда оно имеет значение Clear
, можно производить новый запрос, соответственно ответ на предыдущий запрос уже был получен. Но это всегда индивидуально, в нашем случае можно было считать кол-во элементов c тэгом div
, его значение вырастает на 7 после каждого полученного ответа. Пример работы с таким модулем выглядит так:

Если вы уже установили зависимости, то для установки модуля Get-GPT из репозитория GitHub можете воспользоваться следующей командой:
Invoke-RestMethod https://raw.githubusercontent.com/Lifailon/Selenium-Modules/rsa/Modules/Get-GPT/Get-GPT.psm1 | Out-File -FilePath "$(New-Item -Path "$($($Env:PSModulePath -split ";")[0])\Get-GPT" -ItemType Directory -Force)\Get-GPT.psm1" -Force
По тому же принципу можно написать модуль, который позволят производить запросы для перевода текста. К слову, я уже писал автоматизированный модуль для перевода текста в консоли с использованием бесплатного публичного API для Google Translate и эмулятора DeepLX, для меня это самый удобный способ, который я использую ежедневно. Планирую написать модуль для bash и отдельную статью, т.к. на мой взгляд такой простое решение может оказаться полезно и другим.
Приведу еще один пример, у меня была задача получать значения измерения скорости интернета от провайдера, используя публичные сервисы, такие как LibreSpeed, OpenSpeedTest и Ookla SpeedTest. Во всех случаях достаточно перейти на нужный url сервиса, нажать кнопку измерения, по завершению получить результат – метрики измерений, и заполнить ими объект System.Collections.Generic.List
.

В большинстве случаев, когда мы хотим получить результат работы, заранее необходимо найти элемент, в котором эти данные будут нам доступны и зафиксировать его в переменной. В дальнейшем, по результатам проделанных нами действий, мы добавляем в бесконечный цикл найденный ранее элемент и сравниваем его значение с текущим значение этого же элемента, тем самым убеждаясь, что данные обновлены, после чего забираем результат и завершаем цикл. Тот же принцип, если мы ожидаем, что по результату текущий url обновится (т.е. мы перейдем на другую страницу), в таком случае проверяем его. Вот пример:
$Url = "https://www.speedtest.net/"
$Selenium.Navigate().GoToUrl($Url)
Start-Sleep 1
$UrlTemp = $Selenium.Url
$span = $Selenium.FindElements([OpenQA.Selenium.By]::TagName("span"))
$Button = $span | Where-Object Text -Match "GO"
$Button.Click()
while ($True) {
if ($Selenium.Url -ne $UrlTemp) {
$UrlResult = $Selenium.Url
break
}
}
$Cont = Invoke-RestMethod $UrlResult
В примере с SpeedTest, если из полученного url нам нужно получить данные всей страницы, то можем просто прочитать его содержимое, используя Invoke-RestMethod (аналог клиента Curl в Windows). Нужные нам значения можно получить по средствам парсинга HTML-страницы, в некоторых случаях можно попытаться вытащить кусок JSON, который PowerShell сразу преобразует в объект и его достаточно только отфильтровать или пересобрать, что очень сильно упрощает процесс:
$Data = ($Cont -split "window.OOKLA.")[3] -replace "(.+ = )|(;)" | ConvertFrom-Json
return $Data.result
Используя модуль Get-SpeedTest, можно прямо в консоли получать результаты измерений, которые в свою очередь возможно настроить на отправку в базу данных временных рядов, например, InfluxDB и отображать в виде графиков в Grafana:

Работу (Ookla-SpeedTest-API) с отправкой метрик измерений скорости интернета я делал еще в Internet Explorer c использованием COM (Component Object Model) интерфейса. Это устаревший вариант, который можно использовать как альтернативу Selenium. Принцип работы у него точно такой же, при этом никаких зависимостей не требуется и работает по сей день. Тем не менее, если станет интересно, я оставлял заметки на тему работы с IE, а также с InfluxDB. Планирую (как только подготовлю тестовый стенд), написать отдельную статью по работе с InfluxDB, используя PowerShell или Bash и настройки графиков в Grafana.
По итогу, если взять за основу представленный выше шаблон и ознакомиться с примерами, можно автоматизировать выполнение процессов в Web-приложениях и даже добавлять такие решения в свои Pipeline.