アプリのインストールなんて要らない!!開発ソフト無しでファイルのスクレイピングをやってみよう

技術者に愛されし業界 ― InformationTechnology

隆盛を極めた彼の地は、技術者たちによって築かれた

だが――

いまや技術者はICT機器運用ルールによってその輝きを失い

そして仕事がしづらくなる企業もでてきた

NaoAkakura 著 「回顧録『情報のテクノロジー』」より*1

はじめに

わたしについて

はじめまして、ソーシャルゲーム事業部バックエンドエンジニアのあかくらです

初めてこちらに寄稿させていただきます、よろしくお願い致します

本記事の概要

本記事をご閲覧頂いている方の中には、就労先で貸与されているPCについて、 ソフトウェアのインストールがICT運用ルール等で許可されていない のようなルールの為、任意のアプリをインストールしたり、実行したりすることができないことはございませんでしょうか?

その為に、業務改善の為に開発環境をインストールしたくても、そもそも管理者権限が無い為、インストールができなかったり、インストール許可を貰おうと稟議を通そうとしても、却下されるなど、パターンとしてはあるかと思います (私も経験したことがあります)

そんなあなたに、「わざわざ開発環境をインストールしなくても、どうにかする方法」を2つほどご紹介いたします

今回の例題

今回の問題として、以下を用意し、それを解決に導く方法を解説致します

問題

  • 次のような文字列が書き込まれたファイルを用意する
  • このファイルの中に存在する、重複した行を削除する
あいうえお
あいうえお
かきくけこ
さしすせそ
かきくけこ
あいうえお
たちつてと

解決例

あいうえお
かきくけこ
さしすせそ
たちつてと

方法その1. powershell scriptを使用する方法

前提

必ず、以下を確認してください

  • ※ 以下記述する > 文字は、powershellのプロンプト文字ですので、実際に実行される場合は入力をしないようにご注意ください
  • ※もし、下記が実行できないなどございます場合は、方法その2. をご参照ください

  • > Get-ExecutionPolicy の結果が以下のいずれかであること

  • 上記以外の場合、次のコマンドが実行できること
    • > Set-ExecutionPolicy RemoteSigned

また、使用するのは以下です

  1. powershell (Win+Sで powershell で検索すると、出てきます)
  2. powershell_ise (同じくWin+Sで powershell_ise で検索すると、出てきます)

実際のコード

# 実行時引数として重複行を削除した後のファイルパスを取る
Param($srcFilePath)

if ([String]::IsNullOrWhiteSpace($srcFilePath))
{
    return
}

# 出力先は実行時引数の拡張子を変えたものにする
$destFilePath = $srcFilePath + ".noduplicate"

$sw = $null
$sr = $null
try
{
    # 書き込むやつと読み込むやつ準備
    $sw = [System.IO.StreamWriter]::new($destFilePath)
    $sr = [System.IO.StreamReader]::new($srcFileName)

    # 過去にどんな行を読んだか覚えさせる
    $lineHistoryList = @()

    # 読み込みが完了するまで永久ループ
    while(($line = $sr.ReadLine()) -ne $null)
    {
        # 重複行ならスキップ
        $found = ($lineHistoryList | where { $_ -eq $line })
        if (![String]::IsNullOrWhiteSpace($found))
        {
            continue
        }

        # 行を書き込みして、今の行を履歴に覚えさせておく
        $sw.WriteLine($line)
        $lineHistoryList += $line
    }
}
finally
{
    # 読み込むやつ解放
    if ($sr -ne $null -and $sr -is [System.IDisposable])
    {
        $sr.Dispose()
    }
    # 書き込むやつ解放
    if ($sw -ne $null -and $sw -is [System.IDisposable])
    {
        $sw.Dispose()
    }
}

コードの解説

まず、前提として powershellは、.NET Frameworkのクラスや関数が使用できます

なんならpowershell script自体がC#の構文を一部踏襲しているところもありますので、shellscriptを書いたことが無い人にとっては、shellscriptの中ではかなり理解しやすいモノになっていると思います

try-catch-finally 構文があるのも、その為です

やっていることは、

  1. スクリプトの実行時に渡された引数のファイルパスをチェック (L1-L10くらいまで)
  2. 渡されたファイルパスからファイルの内容を1行ずつ読む (L16-L24くらいまで)
  3. 読んだ行が過去に現れた行であれば、スキップする (L26-L31くらいまで)
  4. 読んだ行が新しく表れた行なら、ファイルに書き込む (L33-L35くらいまで)
  5. すべての行を読んだなら、ファイルストリームをそれぞれ閉じる (L38-L50くらいまで)

と、非常にシンプルです

方法その2. javascriptを使用する方法

え?本当にできるの?と思ったそこのあなた、 できます

前提

使用するのは以下です

  1. 最新のchromeブラウザ (たぶん最新のモノであればどれでも大丈夫だとは思いますが、筆者の動作検証環境はchromeのVer.103あたりでした)
  2. あなたのお気に入りのテキストエディタ、あるいはメモ帳

実際のコード

<html>
    <head>
    </head>
    <body>
        <!-- ファイル選択ボタン -->
        <input type="file" id="file">
        <!-- コピーボタン -->
        <input type="button" value="copy to clipboard" id="copy">
        <div style="display: none" id="message">
            コピーしたよ
        </div>
        <!-- ファイルの重複削除結果が表示されるエリア -->
        <div id="output">
        </div>

        <script type="text/javascript" defer>
            // DOM操作のために使用するDOMのElementの用意
            const inputElement = document.getElementById('file')
            const outputElement = document.getElementById('output')
            const copyElement = document.getElementById('copy')
            const messageElement = document.getElementById('message')

            // copyボタンがクリックされたときの動作の定義
            copyElement.addEventListener('click', function () {
                // 修正後ファイルの内容をクリップボードに書き込む
                const text = outputElement.innerText
                navigator.clipboard.writeText(text)
                // 画面に3秒間だけ「コピーしたよ」を表示する
                messageElement.style = 'display: block'
                setTimeout(() => {
                    messageElement.style = 'display: none'
                }, 3000)
            })

            // ファイルの選択ボタンがクリックされ、ファイルが選択された時の動作の定義
            inputElement.addEventListener('change', function () {
                // 変更後ファイルの内容を表示するエリアの初期化
                outputElement.innerText = ''
                // 読み込むファイルの決定 (最初に選択されたファイルのみを参照する)
                let file = null
                for (let f of inputElement.files) {
                    file = f
                    break
                }

                // ファイルが無かった時は終了
                if (file === null) {
                    return
                }

                // javascriptでのファイルの読み込みが完了したときの動作の定義
                const reader = new FileReader()
                reader.onload = () => {
                    // 読み込んだ結果のファイルの中身を取得
                    const raw = reader.result

                    // ファイル内で使用している改行コードの取得
                    let nlChara = '\n'
                    if (raw.includes('\r\n')) {
                        nlChara = '\r\n'
                    } else if (raw.includes('\r')) {
                        nlChara = '\r'
                    }

                    // ファイル内データを改行コードで行ごとに配列化
                    const rawList = raw.split(nlChara)

                    // 行ごとの配列に対して、重複チェックを実施して、飛ばすまたは書き込むを決定
                    let list = []
                    for (const line of rawList) {
                        // 重複チェック、重複している場合は飛ばす
                        if (list.includes(line)) {
                            continue
                        }

                        // 重複していないやつは書き込み対象に含める
                        list.push(line)
                    }

                    // 画面の出力エリアに表示させる
                    outputElement.innerText = list.join(nlChara)
                }

                // ファイルオブジェクトから、ファイルの読み込みの実施
                reader.readAsText(file)
            })
        </script>
    </body>
</html>

コードの解説

やっていることのメインどころとしては

  1. ファイル選択がされた後にそのファイルの内容を読み込む (L37-L52くらいまで)
  2. 渡されたファイルの内容を精査しやすいように、行別に配列化する (L55-L66くらいまで)
  3. ファイルの内容を1行ずつ読む (L70-L78くらいまで)
  4. 読んだ行が過去に現れた行であれば、スキップする (L72-L74くらいまで)
  5. 読んだ行が新しく表れた行なら、ファイルに書き込む (L77)
  6. すべての行を読んだなら、読んだ結果を画面に出力する (L81)

これだけです

というわけで

というわけで、パッと思い浮かんだ「ファイルの内容を、特別なプログラミング環境を用意せずに読み書きする方法」でした

他にもラクな方法はあると思いますが、例えば唐突に上司等から「このファイルのデータの精査、今週末までに頼むよ~~~」みたいな事を おしつけられ 依頼された場合などに使用してみては如何でしょうか?

ちなみになんでこんな記事書いたの?

昔、技術者派遣されていた会社でそういえばこんなことがあったなぁとふと思い出す機会がありました

その当時の私は、このような方法を思い浮かばせることができず、今ならいろいろ手段があるよね、と思い直したため、記述致しました

本音は?

ある知り合いが「あかくらさ~~~ん!!職場でデータ整理依頼されたんだけど、データの量がウン万行あって、一人じゃ整理できないんだ~~~なんとかしてよ~~~~!!!」とヘルプを依頼してきたものの、 前述の通り、あらゆるソフトウェアのインストールが禁じられた環境でして、「しょうがないなぁ~~~」と言いながら、その中でどのようにすれば解決できるかを考えた経験を記述してみました

さいごに

また、弊社では様々なポジションのエンジニアを募集しています。

今回の記事ではICT運用ルールによる縛りに対して技術で回答をするものとなりましたが、

弊社のICTルールは「なんでもダメ!」という感じではなく、技術者に寄り添ったルールとなっております のでご安心ください

まずはカジュアルにお話を、という形でも結構ですので是非気軽にご応募ください!

reazon.jp

*1:そんなものはない