powershellでRSSフィード取得・単語検索(RSS1.0, 2.0対応)

## はじめに
私事だが、inoreaderからRSSの情報収集をすることが多い。幅広く情報収集をしたいときにはinoreader(フリー版)の使用感にはおおむね満足しているのだが、特定の情報を知りたい時に効率が悪いことがネックだった。そこでRSSの購読している一覧の記事の中から特定の単語を含む記事を検索するプログラムを書いてみた。

## 仕様
- 引数
    - search_word
        - 検索したい単語
        - 入力必須
        - 正規表現に対応
        - 大文字小文字は区別しない
    - rss_list_arg
        - RSSを記載したテキストファイル
        - 入力必須
    - search_days_arg
        - プログラム実行時から何日前まで検索するか指定
        - 指定しなければ30日前まで検索
- アウトプット
 - 検索したrssのタイトル
 - 検索にヒットした際は記事のタイトル、URLを出力
 - そのままだと標準出力するので適宜、">"や”|”で加工

### プログラム実行例
RSSの一覧をファイルに記載

```samplerss.txt
https://wings.msn.to/contents/rss.php
```

"hoge"という単語を7日前までの記事から検索

```powershell:
.\sample.ps1 -search_word hoge -rss_list_arg samplerss.txt -search_days_arg 7
========================================
blog_title:サーバサイド技術の学び舎 - WINGS
----------------------------------------
========================================
```

"vue.js"という単語を12日前までの記事から検索

```powershell:
.\sample.ps1 -search_word vue.js -rss_list_arg samplerss.txt -search_days_arg 12
========================================
blog_title:サーバサイド技術の学び舎 - WINGS
----------------------------------------
url:https://wings.msn.to/out.php?c=rss&id=2986
title:「翔泳社 CodeZine（Webページの状態を集中管理できる「Nuxt.js」のVuexストアを使いこなそう）」記事掲載
----------------------------------------
========================================
```

引数名を短縮
"hoge"という単語を30日前までの記事から検索

```powershell:
.\sample.ps1 -search_word hoge -rss samplerss.txt
========================================
blog_title:サーバサイド技術の学び舎 - WINGS
----------------------------------------
========================================
```

## プログラム
```powershell:sample.ps1
Param(
    [parameter(mandatory=$true)][string]$search_word, # 検索ワード
    [int]$search_days_arg = 30, # 何日前まで取得するか指定, デフォルトは30日
    [parameter(mandatory=$true)]$rss_list_arg # rssのリスト(txt)
)

# webクライアント用変数の準備
$wc = New-Object System.Net.WebClient
$wc.Encoding = [System.Text.Encoding]::UTF8

function Find-Match{
    # 日付の比較
    If($search_days -le $date){
        $wc_all = $wc.DownloadString($item.link.Trim())
        if($wc_all | Select-String -Pattern $search_word){
           "url:" + $item.link.Trim()
           "title:" + $item.title
            "----------------------------------------"
        }
    }
}

# rssを配列で格納
[String]$encoding = 'UTF-8'
[string[]]$rss_list = (Get-Content $rss_list_arg) -as [string[]]

If([string]::IsNullOrEmpty($search_days_arg)){
}else{
    $search_days = (Get-Date).AddDays(-$search_days_arg)
}

"========================================"
foreach($rss in $rss_list){
    $rss = [xml]$wc.DownloadString($rss)
    if($rss.rss -ne $null){ # rss=2.0
        "rss_title:" + $rss.rss.channel.title + ""
        "----------------------------------------"
        foreach($item in $rss.rss.channel.item){
            $date = [DateTime]$item.pubDate
            Find-Match
        }
    }
    else{ # rss=1.0
        "rss_title:" + $rss.RDF.channel.title + ""
        "----------------------------------------"
        foreach($item in $rss.RDF.item){
            $date = [DateTime]$item.date
            Find-Match
        }
    }
    "========================================"
}
```

### プログラム実行内容の説明
プログラム内のコメントで簡単に実行内容などは記載しているので、細かい部分まで気にしていない方は読まなくても問題ないはずです。

#### 引数
`[parameter(mandatory=$true)]`があると入力が必須になる。
何日前まで検索するかについては、プログラム実行時の指定がなければ30日が入力される。
プログラム実行例に記載したように引数名を短縮することが可能。

```powershell
Param(
    [parameter(mandatory=$true)][string]$search_word, # 検索ワード
    [int]$search_days_arg = 30, # 何日前まで取得するか指定, デフォルトは30日
    [parameter(mandatory=$true)]$rss_list_arg # 調査rssのリスト(txt), 必須
)
```

#### 何日前まで検索するか
引数で指定がなければ、ifに分岐し何もしない。指定があればelseに分岐し、プログラム実行時から何日前なのか情報を取得する。

```powershell
If([string]::IsNullOrEmpty($search_days_arg)){
}else{
    $search_days = (Get-Date).AddDays(-$search_days_arg)
}
```

#### rssの巡回
`if($rss.rss -ne $null)`でrss1.0か2.0によって条件分岐している。2.0ならifに分岐し、1.0ならelseに分岐する。
`"rss_title:"`でrssのタイトルを出力している。
`foreach($item in ・・・`でそれぞれのRSSの記事を巡回している。
`$date = ・・・`で記事が掲載された日付情報を取得している。

```powershell
if($rss.rss -ne $null){ # rss=2.0
    "rss_title:" + $rss.rss.channel.title + ""
    "----------------------------------------"
    foreach($item in $rss.rss.channel.item){
        $date = [DateTime]$item.pubDate
        Find-Match
    }
}
else{ # rss=1.0
    "rss_title:" + $rss.RDF.channel.title + ""
    "----------------------------------------"
    foreach($item in $rss.RDF.item){
        $date = [DateTime]$item.date
        Find-Match
    }
}
```

#### 検索ワードを含む記事の取得
`If($search_days -le $date)`で記事が検索対象かどうか判断している。
`$wc_all = $wc.DownloadString($item.link.Trim())`で記事の情報を取得している。
`if($wc_all | Select-String -Pattern $search_word)`で記事内に検索ワードが含まれているか確認している。含まれていた場合、記事のURLとタイトルを出力している。

```powershell
function Find-Match{
    # 日付の比較
    If($search_days -le $date){
        $wc_all = $wc.DownloadString($item.link.Trim())
        if($wc_all | Select-String -Pattern $search_word){
           "url:" + $item.link.Trim()
           "title:" + $item.title
            "----------------------------------------"
        }
    }
}
```