【Powershell】httpsのサイトに Invoke-WebRequestしたら「接続が切断されました」と出た時にした対処方法

常駐している現場の業務で、社内向けサイトのhtmlソースを取得できたら業務効率化できるなーと思いついたのですが、
常駐先ですし、客先から配布されているWindows PCなので、好き勝手にツールは入れられないのがお約束です。
そんな訳でやり方を色々検討してたら「そうだ、Powershellでやってみよう」と思い立ちました。

Powershellは触ったことありませんでしたが、最近のWindowsなら標準実装だし、
使えるようになったらどこの現場行っても自動化できるようになるし、いっちょやってみようかなと。

とりあえず調べてやってみます。
Linuxのcurlに相当するコマンドがInvoke-WebRequestなのはすぐ分かったのですが、なぜかエラーが出ちゃう……。
でたのは下記のようなエラーです。

Invoke-WebRequest -Uri "https://xxxxxxx/xxxxx/"
Invoke-WebRequest : 接続が切断されました: SSL/TLS のセキュリティで保護されているチャネルに対する信頼関係を確立できませんでした。
発生場所 行:1 文字:1
+ Invoke-WebRequest -Uri "https://xxxxxxx/xxxxx/" -Cre ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest]、WebExce
    ption
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

これに対処すべく、色々調べました。
それで、調べたことを書く前に、社内向けサイトの詳細を書いておきますね。

htmlソースを取得したい社内向けサイトの詳細
①BASIC認証がかかっているサイトである
②httpsのサイトである
③サイトが自己証明書を使用しているためブラウザでアクセス「この接続ではプライバシーが保護されません」メッセージが表示される

それでは上記①~③への対処を書いていきます。

①BASIC認証への対処法

$USER = "username"
$PASS = "password"
$secpasswd = ConvertTo-SecureString $PASS -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($USER, $secpasswd)

こんな感じに書けばBASIC認証のUsernameとPasswordを自動入力できます。
UsernameとPasswordをそれぞれの変数にセットして、
Passwordをコンバートして$secpaswdに格納。
そんで$USERと$secpaswdを使って$credに格納、って感じです。

②httpsへの対応

以下のコマンドでSSL / TLSの各種プロトコルを全部有効にします。

・事前設定確認

[Net.ServicePointManager]::SecurityProtocol
Ssl3, Tls

このコマンド打つと、デフォルトだとssl3とTLSのみ対応になってます。
これに加えてTLS1.1、TLS1.2も対応させるには以下のコマンドです。

・設定変更

[Net.ServicePointManager]::SecurityProtocol = @([Net.SecurityProtocolType]::Ssl3,[Net.SecurityProtocolType]::Tls,[Net.SecurityProtocolType]::Tls11,[Net.SecurityProtocolType]::Tls12)

・事前設定確認

[Net.ServicePointManager]::SecurityProtocol
Ssl3, Tls, Tls11, Tls12

はい、こんな感じで表示されて、TLS1.1、TLS1.2も追加されました。

③サイトが自己証明書の使用している時の対応

中身の詳細はよく分からないんですが、以下のソースをコピペすればいけました。

add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

これで課題だった①~③には全て対応できました。
次にやりたいこととしては、スクレイピングしてきたdataをテキストファイルに保存することです。

以下のような2行で対処できました。

$response = Invoke-WebRequest -Uri "https://xxxxxxx/xxxxx/" -Credential $cred
$response.Links  | Out-File -Append -FilePath website_data.txt -Encoding Ascii  -width 1000

1行目でサイトのデータを$responseに格納して、
2行目で$responseの中のLinksってフィールドをOut-Fileでテキストファイルに書きこんでます。

-Appendは、ファイルに追記するオプションです。
ひとつのファイルにどんどん追記していきたい場合に指定します。

-Encoding Ascii は、文字コードを指定してAscii形式でテキストファイルに保存するオプションです。

-width 1000は、テキストファイル内の1行の横幅を指定します。
これを多めに設定しておかないと、長いURLとかデータをテキストファイルに書き込む時、
変なところで改行されちゃうんですよね。
私の場合は1000で大丈夫でしたが、ここは適切な値を入れてみてください。


という感じで色々解説してきましたが、ひとつのスクリプトにまとめると以下のようになります。
URLにyahoo.co.jpを指定しているので、そのままコピペして動作確認もできます。
取得したデータは、Cドライブ直下にwebsite_dataというフォルダを作って、そこに入れる形になってます。
ここらへんは適宜修正してみてください。

# ①BASIC認証への対処法(ユーザー名・パスワードの設定)
$USER = "username"
$PASS = "password"
$secpasswd = ConvertTo-SecureString $PASS -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($USER, $secpasswd)


# ②httpsへの対応 (SSL / TLSの各種プロトコルを全部有効にする)
[Net.ServicePointManager]::SecurityProtocol = @([Net.SecurityProtocolType]::Ssl3,[Net.SecurityProtocolType]::Tls,[Net.SecurityProtocolType]::Tls11,[Net.SecurityProtocolType]::Tls12)


# ③サイトが自己証明書の使用している時の対応
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy


# サイトのソース情報をダウンロードして、ファイルに保存する
mkdir c:\website_data
cd C:\website_data

$response = Invoke-WebRequest -Uri "https://yahoo.co.jp" -Credential $cred
$response.Links  | Out-File -Append -FilePath website_data.txt -Encoding Ascii  -width 1000

$response = Invoke-WebRequest -Uri "https://yahoo.co.jp" -Credential $cred
$response.Links  | Out-File -Append -FilePath website_data.txt -Encoding Ascii  -width 1000

$response = Invoke-WebRequest -Uri "https://yahoo.co.jp" -Credential $cred
$response.Links  | Out-File -Append -FilePath website_data.txt -Encoding Ascii  -width 1000

こんな感じでPowershellだと、WindowsPCさえあれば環境に依存せず業務の自動化ができます。
当然ですが、WEBのスクレイピングだけじゃなくて、他の色んな事もできますので、
自分の作業でやるのかったるいなーと思ってる自動化したいことがある人は。
是非Powershellを調べてみましょう!