PowerShellで2chビューワ

http://engineers-be-ambitious.blogspot.com/2007/10/2ch.html

なんていうのが紹介されていたので、さっそくPowerShell2chビューワを作ってみた。

板一覧を取得するスクリプト

Get-BoardList.ps1

param([string]$boardCategory="*", [string]$boardName="*")

if($args[0] -eq "-?") {
    $commandName = [IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
	Write-Host @"

Name:
    $commandName

Description:
    Gets list of board from 2ch BBS.
    
Usage:
    $commandName [[-boardCategory] <string>] [[-boardName] <string>]

    -boardCategory <string>
        The category of board to get.
        
    -boardName <string>
        The name of board to get.
        
Require:
    New-PSObject

"@ -foregroundColor Yellow
    exit 1
}

$webReq = [Net.HttpWebRequest]::Create(
    "http://azlucky.s25.xrea.com/2chboard/2channel.brd"
)
$webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows XP)"
$webRes = $webReq.GetResponse()

$sr = New-Object IO.StreamReader(
    $webRes.GetResponseStream(), [Text.Encoding]::GetEncoding("Shift-JIS")
)

[void]($sr.ReadLine())  # 一行目は飛ばす

$buf = New-Object IO.StringReader($sr.ReadToEnd())
$sr.Close()
$webRes.Close()

while(($line = $buf.ReadLine()) -ne $null) {
    # フォーマット
    # カテゴリ名
    # (TAB)サーバドメイン(TAB)ディレクトリ名(TAB)板の名前
    if($line.StartsWith("`t") -and $category -ne $null) {
        $values = $line.Split("`t")
        
        if($values[3] -like $boardName) {
            New-PSObject @{
                Category=$category; Name=$values[3]; Directory=$values[2]; Domain=$values[1]
            }
        }
    } else {
        $category = $line.Split("`t")[0]
        
        if($category -notlike $boardCategory) { $category = $null }
    }
}

指定した板のスレッド一覧を取得するスクリプト

Get-ThreadList.ps1

param([object[]]$board, [string]$threadName="*")

$board += @($input)

if($args[0] -eq "-?" -or $board.Count -eq 0) {
    $commandName = [IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
	Write-Host @"

Name:
    $commandName

Description:
    Gets list of thread from 2ch BBS.

Usage:
    $commandName [[-board] <object[]>] [[-threadName] <string>]
    
    -board <object[]>
        The board to get.
        
    -threadName <string>
        The name of thread to get.
        
Require:
    New-PSObject

"@ -foregroundColor Yellow
    exit 1
}

$board | % {
    $webReq = [Net.HttpWebRequest]::Create(
        ("http://{0}/{1}/subject.txt" -f $_.Domain, $_.Directory)
    )
    $webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows XP)"
    $webRes = $webReq.GetResponse()

    $sr = New-Object IO.StreamReader(
        $webRes.GetResponseStream(), [Text.Encoding]::GetEncoding("Shift-JIS")
    )
    $buf = New-Object IO.StringReader($sr.ReadToEnd())
    $sr.Close()
    $webRes.Close()
    
    while(($line = $buf.ReadLine()) -ne $null) {
        # フォーマット
        # (THREAD_ID).dat<>(スレッド名 + 半角スペース + レス数)
        $values = $line.Split("<>")
        $threadID = $values[0]
        $pos = $values[2].LastIndexOf(" ")
        $name = $values[2].Substring(0, $pos)
        $count = [int]::Parse($values[2].Substring($pos+1).Trim("(", ")"))
        if($name -like $threadName) {
            New-PSObject @{
                ID=$threadID; Name=$name; Count=$count; Domain=$_.Domain; Directory=$_.Directory
            }
        }
    }
}

指定したスレッドのレス一覧を取得するスクリプト

Get-ResList.ps1

param([object[]]$thread)

$thread += @($input)

if($args[0] -eq "-?" -or $thread.Count -eq 0) {
    $commandName = [IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
	Write-Host @"

Name:
    $commandName

Description:
    Gets list of response from 2ch BBS.

Usage:
    $commandName [[-thread] <object[]>]
    
    -thread <object[]>
        The thread to get.
        
Require:
    New-PSObject

"@ -foregroundColor Yellow
    exit 1
}

# 余計なタグを削除する正規表現
$escapePtrn = New-Object Text.RegularExpressions.Regex("(<.*?>|\&nbsp;)")

function Escape([string]$value) {
    return $escapePtrn.Replace($value, "").Trim()
}

function NewObject([string]$line) {
    $values = $line.Split([string[]]"<>", [StringSplitOptions]::RemoveEmptyEntries)

    if($values.Length -gt 2) {
        return New-PSObject @{
            Title=Escape($values[0]); Date=Escape($values[1]); Body=Escape($values[2])
        }
    }
}

$thread | % {
    $webReq = [Net.HttpWebRequest]::Create(
        ("http://{0}/{1}/dat/{2}" -f $_.Domain, $_.Directory, $_.ID)
    )
    $webReq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows XP)"
    $webRes = $webReq.GetResponse()

    $sr = New-Object IO.StreamReader(
        $webRes.GetResponseStream(), [Text.Encoding]::GetEncoding("Shift-JIS")
    )
    $buf = New-Object IO.StringReader($sr.ReadToEnd())
    $sr.Close()
    $webRes.Close()
    # フォーマット
    # 1行目: (名前)<>(メール欄)<>(書き込み日時)<>(半角スペース) + (本文)<>(スレ名)
    if(($line = $buf.ReadLine()) -ne '<?xml version="1.0" encoding="Shift_JIS"?>') {
        NewObject($line)
        
        exit 0
    }
    [void]($buf.ReadLine()) # "<text><![CDATA[" を飛ばす
    
    while(($line = $buf.ReadLine()) -ne $null) { NewObject($line) }
}

例えば、「痛いニュース」板から「社会」を含むスレッドでレス数が「300」以上のスレッドの一件目から全てのレスを取得する場合

使い方

PS > (Get-BoardList -boardName 痛い* | Get-ThreadList -threadName *社会* | ? { $_.Count -gt 300 })[0] | Get-ResList | fl Date,Body | more

出力

Date : 2007/02/24(土) 23:40:08 0 BE:10980443-2BP(77)
Body : 24日午前10時半ごろ、大阪市旭区太子橋2丁目の淀川に架かる豊里大橋の下に男性が  浮いているのを釣り人が見つけ、
       110番通報した。男性は死亡しており、頭部に斧(おの)  で切られたような裂傷が5、6カ所あった。川底にはバイクが
       沈んでおり、男性の腰部分と  ワイヤでつながれていた。近くの河川敷には複数の血だまりがあり、大阪府警は殺人・死体
       遺棄事件とみて旭署に捜査本部を置き、男性の身元の特定を急いでいる。     調べでは、男性は死後1日以内とみられ、両
       足のひざ下に打撲痕もあった。ズボンのベルト穴  にさびた金属製のワイヤ(直径約1センチ)が通され、その先が水深約9
       0センチの川底に沈んだ  古いバイクに結びつけられていた。遺体はバイクに覆いかぶさるような形で、川岸の川面にうつ
       ぶせで浮いていたという。     近くの河川敷で直径約40センチの血だまりが複数見つかり、いずれもまだ乾燥していなか
       った。  府警は何者かが23日夜から24日朝にかけ、河川敷で男性を殺害後、現場近くに放置されて  いたバイクを重し代
       わりに使って、川に捨てたとみている。     男性は20歳前後。身長160〜170センチで、白いダウンジャケットと茶
       色いパーカーを羽織り、  黒いジーンズ、茶色のスニーカーなどを身に着けていた。頭髪の一部が金色に染められた長髪  で
       、左耳にピアス、左手首に銀色のブレスレットをしていた。たばことライター以外の所持品は  なかったという。     現場
       は、市営地下鉄谷町線太子橋今市駅の北西約300メートルで、国道479号豊里大橋の  下にある淀川河川公園。遊歩道や
       駐車場があり、早朝から犬の散歩やジョギングをする人が多い。  公園の管理人によると、23日午後5時に駐車場を閉めた
       が、遺体は見当たらなかったという。   http://www.asahi.com/national/update/0224/OSK200702240028.html?ref=rss

Date : sage
Body : 2007/02/24(土) 23:40:47 0

Date : 2007/02/24(土) 23:41:32 0
Body : また大阪かと華麗に2ゲット

Date : sage
Body : 2007/02/24(土) 23:41:48 0

Date : 2007/02/24(土) 23:44:43 O
Body : 荒川で殺された広告デザイナーはどうなったの?  続報ないし、スレも立ってないし…

PowerShellが即席の2chビューワーに早変わり!!これの利点はコマンドプロンプトなので仕事しているように見えるところw