PowerShell и ЖЖ: простейшее кэширование результата HTTP(S)-запроса

Feb 23, 2023 08:23

Ранее в этой серии постов:
...
19. PowerShell: улучшаю преобразование данных из хеш-таблицы в XML-RPC
20. PowerShell и ЖЖ: аутентификация с «cookie», интерфейс «XML-RPC»
21. PowerShell: улучшаю преобразование данных от ЖЖ (интерфейс «flat») в хеш-таблицу

Окружение: операционная система «Windows 10», программа-оболочка «PowerShell» версии 7.

Напомню одно из требований ЖЖ («Живого Журнала») к роботам (программам, которые совершают обращения к ЖЖ), а точнее, к создателям таких программ:

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

Говоря проще, мы должны при первом HTTP(S)-запросе сохранить полученный в HTTP(S)-ответе результат, чтобы, если в будущем нам опять понадобится результат такого же HTTP(S)-запроса, взять сохраненный ранее результат, а не обращаться повторно к программе-серверу ЖЖ.

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

Особенно использование кэширования актуально при получении от программы-сервера ЖЖ кусков данных большого размера. Я сейчас экспериментирую с удалённой функцией «syncitems» программы-сервера ЖЖ, обращаясь к ней по интерфейсу «flat» (подробнее об этой функции я напишу отдельно). Даже для относительно небольшого журнала «vbgtut.livejournal.com» со 147 постами я получаю довольно большого размера тело HTTP(S)-ответа. А учитывая то, что этот журнал давным-давно не ведется, то разумно получить данные один раз, если в будущем они, скорее всего, не изменятся.

Для хранения данных HTTP(S)-ответа я обычно использую переменную $Response класса «Microsoft.PowerShell.Commands.BasicHtmlWebResponseObject». Объект такого класса возвращает командлет «Invoke-WebRequest», которым я пользуюсь для отправки HTTP(S)-запроса программе-серверу ЖЖ. Свойство «Content» этого объекта содержит тело HTTP(S)-ответа, представляющее собой многострочную строку (multiline string).

PS C:\> ($Response.Content).GetType().FullName
System.String
PS C:\> ($Response.Content).Length
14441

Длина строки, показанная в блоке кода выше, выражается в количестве символов, составляющих эту строку. Напомню, количество символов не равно количеству байтов, в которых эта строка хранится, так как количество байтов, в которых хранятся символы, зависит от используемой для хранения строки кодировки. Как видно из блока кода выше, я получил в теле HTTP(S)-ответа строку длиной в 14 тысяч 441 символ.

По умолчанию программа-оболочка «PowerShell» версии 7 работает с кодировкой UTF-8. В этой кодировке для хранения разных символов может использоваться разное количество байтов. Для хранения символа из таблицы ASCII (в том числе символов-букв латинского алфавита) в этой кодировке используется 1 байт. Для хранения символа-буквы русского алфавита в этой кодировке используется 2 байта.

Итак, сохраним тело HTTP(S)-ответа в файл, передав его по конвейеру командлету «Out-File»:

$Response.Content | Out-File -FilePath "C:\Users\Илья\Desktop\syncItems.txt" `
-NoNewline -NoClobber

Как можно видеть из блока кода выше, я сохранил тело HTTP(S)-ответа в текстовый файл с названием «syncItems.txt» на свой рабочий стол. Если вы работаете в отдельной папке проекта, вы можете организовать для хранения результатов HTTP(S)-запросов отдельную папку внутри папки проекта и сохранять текстовые файлы туда, используя не абсолютный путь (как в примере выше), а относительный (я не буду тут приводить пример такой работы, но вы можете легко разобраться в этом сами).

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

По умолчанию командлет «Out-File» вставляет в конец данных, записываемых в файл, символ новой строки. Чтобы этого избежать, я использую параметр -NoNewline этого командлета.

В итоге на своём рабочем столе я получил текстовый файл с названием «syncItems.txt» и объемом в 14441 байт. Исходя из изложенного выше, можно понять, что все символы, полученные в теле HTTP(S)-ответа в данном случае входят в состав таблицы ASCII, поскольку количество символов совпало с количеством байтов, в которых они хранятся. Текст сохранен в кодировке UTF-8 (без метки BOM).

Когда нам снова понадобится сохраненный результат HTTP(S)-запроса, можно получить его из файла, воспользовавшись командлетом «Get-Content»:

PS C:\> $fromCache = Get-Content -Path "C:\Users\Илья\Desktop\syncItems.txt" -Raw
PS C:\> $fromCache.GetType().FullName
System.String
PS C:\> $fromCache.Length
14441

По умолчанию командлет «Get-Content» считывает из файла строки в массив, используя в качестве разделителя символ новой строки. Так как в нашем случае в файле сохранена многострочная строка (multiline string), то мы получим в переменной $fromCache массив линий многострочной строки. При этом символы новой строки будут откинуты. Чтобы этого избежать и получить именно то, что изначально было извлечено из тела HTTP(S)-ответа, я использую параметр -Raw этого командлета. Как видно в блоке кода выше, при этом в переменную $fromCache записалась строка длиной в 14441 символ, что мы и хотели получить.

Инструмент, Образование, Программирование, Английский язык, ЖЖ

Previous post Next post
Up