Как работать с Invoke-WebRequest в Powershell и алиасом Wget создавая запросы HTTP и HTTPS


01 ноября 2019


Как делать запросы HTTP и HTTPS с Invoke-WebRequest в Powershell и алиасом Wget

Команда Powershell Invoke-WebRequest появилась в 3-ей версии консоли. Основной задачей команды - обращение к ресурсам по HTTP, HTTPS и FTP. У этой команды в Powershell есть алиасы в виде 'iwk' и 'wget'. На примерах ниже мы рассмотрим обычные запросы, работу с json, заполнение форм, аутентификацию и парсинг.

Создание запроса HTTP и HTTPS в Powershell

После выполнения простого запроса мы увидим полученную информацию:

Invoke-WebRequest -Uri fixmypc.ru

powershell wget

Если вы имеете прямую ссылку на файл и хотите его скачать нужно дополнительно указать ключ OutFile:

Invoke-WebRequest -Uri 'https://fixmypc.ru/media/media/news_main_images/PowerShell2_dwtURg2.jpg' -OutFile C:\pic3.jpg

Так же можно скачивать любой файл формата ZIP,MP4 и так далее.

 

Доступ к свойствам

Почти любая команда в Powershell по умолчанию выводит меньше информации чем имеет. Что бы вывести все можно использовать следующий подход:

$wget = Invoke-WebRequest -Uri fixmypc.ru
$wget | SELECT *

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

$wget = Invoke-WebRequest -Uri fixmypc.ru
$wget | Get-Member

powershell запрос http и https

И таким образом выводить данные:

$wget.StatusCode

powershell запрос http и https

Само содержимое веб документа может быть доступно по двум свойствам:

$wget.Content
$wget.RawContent

 

Использование заголовков

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

  1. Отсутствуют заголовки;
  2. Часть информации обрабатывается через JavaScript.

Заголовки указываются в формате хэш-таблиц. Для примера так будет выглядеть запрос с указанными заголовками:

Invoke-WebRequest -Uri fixmypc.ru -Headers @{"accept-encoding"="gzip, deflate, br"; "cache-control"="no-cache";}

powershell скачать файл с сайта

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

powershell wget заголовки

Кстати через это же меню в Chrome можно получить уже сформировавшийся запрос с командой нажав на нужный элемент и выбрав "Copy as Powershell":

powershell wget заголовки

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

powershell wget cookies

Вы можете увидеть вторую кнопку "Copy all as Powershell", которая сформирует объект типа "[object Promise]", но информацию как работать с этим я не нашел.

Такой способ не поможет пройти аутентификацию на сайте так как в Chrome Cookies устанавливаются в параметр Headers, но судя по документации они должны передаваться через параметр WebSession.

Cookies

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

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

Invoke-WebRequest -Uri fixmypc.ru -Headers @{"accept-encoding"="gzip, deflate, br"; "cache-control"="no-cache";} -SessionVariable SavedSession
$SavedSession
Invoke-WebRequest -Uri fixmypc.ru -Headers @{"accept-encoding"="gzip, deflate, br"; "cache-control"="no-cache";} -WebSession $SavedSession

powershell wget cookies

Как вы можете увидеть в этой переменной хранятся и заголовки.

Второй способ это заполнить поля Cookies самим через контейнер .NET, то есть способом аналогичным получению заголовков. Первое что делается - создается объект аналогичный SavedSession:

$s = New-Object Microsoft.PowerShell.Commands.WebRequestSession

powershell wget cookies

Теперь мы должны добавить в поле Cookies данные и передать через командлет, я использовал алиас wget:

$c = New-Object System.Net.Cookie('Password','123')
$s.Cookies.Add($c)

wget 'https://fixmypc.ru/' -WebSession $s

Cookie, которые мы передали соответствуют Password=123.

Работа с формами и загрузка

Когда мы заполняем какую-то форму на сайте или выполняем загрузку мы чаще используем метод POST, а не GET, который стоит по умолчанию. Метод можно увидеть в коде или в описании документации, если используете приложения или API:

powershell wget формы

Чаще всего используются методы описанные выше, но в Powershell доступно больше:

  • Default;
  • Delete;
  • Get;
  • Head;
  • Merge;
  • Options;
  • Patch;
  • Post;
  • Put;
  • Trace.

Ключ, в котором используются эти параметры так и называется Method:

wget 'https://fixmypc.ru/' -WebSession $s -Method POST

Для заполнения форм, в версии Powershell 6 +, можно использовать следующий подход:

$Uri = 'https://fixmypc.ru'
$Form = @{
    firstName  = 'Lora'
    lastName   = 'Palmler'
    email      = 'test@test.ru'
}
$Result = Invoke-RestMethod -Uri $Uri -Method Post -Form $Form

Если вы не установите свой тип в заголовке ContentType то он будет "application/x-www-form-urlencoded". Если это важно, то лучше использовать параметр -ContentType, а не писать это же свойство в заголовке. Синтаксис можно увидеть дальше.

 

Кодировка 

Если вы используете данные, которые отличаются от латиницы вы можете столкнуться с проблемой кодировок. Дело в том, что следующие данные будут отосланы не в UTF-8, хоть это и будет указано:

$r = 'ччч' | `
iwr http://httpbin.org/post `
    -Method 'POST' `
    -Headers @{'Content-Type' = 'application/json; charset=utf-8'}

powershell wget кодировка

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

$r = 'ччч' | `
iwr http://httpbin.org/post `
    -Method 'POST' `
    -ContentType 'application/json; charset=utf-8'

Вы так же можете посылать данные таким образом:

$r = 'ччч' 
$body = [System.Text.Encoding]::UTF8.GetBytes($r);
iwr http://httpbin.org/post `
    -Method 'POST' `
    -ContentType 'application/json; charset=utf-8' `
    -Body $body 

 

Аутентификация

Посмотрим на три способа аутентификации используя:  базовую (Basic), с использованием сертификата, NTLM и Kerberos.

Базовая аутентификация

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

$pass = "Login:Pass"
$creds = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($pass))
iwr 'http://httpbin.org/basic-auth/AzureDiamond/hunter2' `
-Headers @{ 'Authorization' = 'Basic ' + $creds }

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

Используя сертификат

Если у вас установлен сертификат, то вы можете его использовать только указав отпечаток (Thumbprint):

wget 'https://fixmypc.ru' -CertificateThumbprint 28D8AB72A7276FEED46D1DF0B2AC4D4F46B7C4DF

NTLM и Kerberos

Если вам нужно пройти аутентификацию для текущего пользователя вы можете добавить ключ -DefaultCredentials:

wget 'http://domain.local' -UseDefaultCredentials

Этот ключ не будет работать вместе с аутентификацией типа Basic. 

 

Парсинг сайтов

Парсинг, то есть возможность собирать информацию с сайтов, в Powershell реализована достаточно просто. Если мы посмотрим  какие свойства имеет объект команды Invoke-WebRequest, сможем увидеть теги:

powershell парсинг

Картинки

Для примера в HTML картинки хранятся в таком виде:

<IMG title="Как работать с циклами в Powershell ForEach-Object и While на примерах | FixMyPC" class=card-img-top alt="Как работать с циклами в P
            owershell ForEach-Object и While на примерах" src="/media/media/news_main_images/PowerShell2_sUFjjsx.jpg">

То есть все картинки имеют тег IMG и SRC. В Powershell часть тегов уже собрана в отдельные свойства и так мы получим список всех картинок:

$content = wget -Uri fixmypc.ru
$content.Images
$content.Images.src

powershell парсинг сайта

Мы видим только ссылки на картинки, а значит нам нужно выполнить еще по запросу для каждой картинки. К тому же я хочу сохранить существующие имена для каждой картинки, так как у них разные форматы (PNG, JPG). Это будет выглядеть примерно так:

# Массив с картинками
$pics = $content.Images.Src
# Сайт откуда картинки
$site = 'https://fixmypc.ru'
# Папка куда будут сохранятся
$path = 'C:\TestPics\'
foreach ($pic in $pics){
    # Разделяем путь на / , откуда берем только последний элемент (имя)
    $name = $pic.Split('/')[-1]
    # Создаем полную ссылку до картинки
    $pic_uri = ($site + $pic)
    # Полный путь куда картинки будет сохранена
    $full_save_path = ($path + $name)
    iwr -Uri $pic_uri -OutFile $full_save_path
}

Обратите внимание, что если у вас не создана директория куда будут сохранятся картинки, появится ошибка. Iwr не создает директории.

Парсинг других тегов

Парсинг остальной части сайта мало чем отличается от примера с картинками. Так как Images и Links выведены как основные свойства, то поиск, например, заголовков должен делаться через свойство AllElements. Для примера так я найду все теги h2:

$h2 = $content.AllElements | where {$_.TagName -eq 'h2'}
$h2.outerText

powershell парсинг

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

$h2 = $content.AllElements | where {$_.class -eq 'card-title'}
$h2.outerText

JSON

По умолчанию мы не можем работать с JSON объектами. Они будут восприниматься как строки пока мы не конвертируем объект в PSCustomObject. Это делается так:

$data = wget -Uri "https://fixmypc.ru/json"
$data = ConvertFrom-Json $([String]::new($data.Content))

После этого мы можем обращаться к свойствам:

$data.GetType()
$data
$data.Employee.Name

powershell парсинг json

После изменения мы возможно захотим отправить объект. Для этого мы конвертируем из PSCutomObject в JSON:

$data = ConvertTo-Json -InputObject $data
Invoke-WebRequest -Method Post -Body $data

 

Работа с классом Net.WebClient

При работе в Powershell доступен еще один метод для доступа к веб страницам и скачивания файлов Net.WebClient. Этот класс имеет методы все из которых мы можем увидеть так:

$source_site = New-Object Net.WebClient
$source_site | Get-Member

powershell webclient

Скачивание

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

$source_site = New-Object Net.WebClient
$source_site.DownloadString("https://fixmypc.ru")

system net webclient powershell

Как вы видите есть проблема с кодировкой. Ее можно исправить так:

$source_site = New-Object Net.WebClient
$source_site.Encoding = [System.Text.Encoding]::UTF8
$source_site.DownloadString("https://fixmypc.ru")

powershell webrequest

Для скачивания файлов используем метод DownloadFile():

$download = New-Object Net.WebClient
$download.DownloadFile("https://fixmypc.ru/media/media/news_main_images/PowerShell2_dwtURg2.jpg", "C:\pic.jpg")
...

Теги: #powershell


Популярные тэги
О блоге
Этот блог представляет собой конспекты выученного материала, преобретенного опыта и лучшие практики в системном администрировании и программировании.