Skip to content

Instantly share code, notes, and snippets.

@countb
Last active September 4, 2021 01:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save countb/6249095 to your computer and use it in GitHub Desktop.
Save countb/6249095 to your computer and use it in GitHub Desktop.
YahooWeather.pl for samurize
#!perl
use warnings;
use strict;
use utf8;
use Encode;
use 5.014;
use LWP::Simple;
use Time::Piece;
use File::Spec;
no warnings 'experimental';
# YahooWeather.pl for samurize (2013/08/16)
#
# このスクリプト自体は Unicode(UTF-8) で保存してあること
# license: CC0
#
# Yahoo!天気情報を samurize 用に変換して保存する perl スクリプトです。
# perl がインストールされている必要があります。
# samurize の "Add Meter" で "Add Console Program" から メーターを追加し、
# Command Line を「perl "このスクリプトへのフルパス"」とすることで実行できます。
# 実行間隔(Update Every X Minutes)は60分くらいで十分だと思います。
#
# 少なくとも以下の項目をお使いの環境に合わせて設定する必要があります。
#
# ピンポイント天気のURL、週間天気を取得するURL、天気画像が入っているフォルダ名、
# 作業フォルダ、および getImage() ルーチン内の画像ファイル名(660行目付近)
#debug 用
my $debug_ConsoleOut = 1; #process_day() と process_week() の配列をコンソールに出力
my $debug_DontGetFromRemote = 0; #Yahoo からファイルを取得しない(ローカル保存されているものを使う)
my $debug_DontWriteToFile = 0; #最終結果をファイルに書き出さない
#ピンポイント天気のURL(3時間ごとの予報があるページ)
#例: "https://weather.yahoo.co.jp/weather/jp/47/9410/47207.html"
my $yahooWeatherDay = "https://weather.yahoo.co.jp/weather/jp/47/9410/47207.html";
#週間天気を取得するURL(1日ごとの予報があるページ)
#例: "https://weather.yahoo.co.jp/weather/jp/1a/1100.html"
my $yahooWeatherWeek = "https://weather.yahoo.co.jp/weather/jp/1a/1100.html";
#ローカルに一時保存するピンポイント天気元データのファイル名
my $localDay = "YahooWeatherDayOriginal.txt";
#ローカルに一時保存する週間天気元データのファイル名
my $localWeek = "YahooWeatherWeekOriginal.txt";
#最終的に出力するファイル名
my $outFile = "YahooWeather.txt";
#天気画像が入っているフォルダ名のフルパス
#(ファイル名は getImage サブルーチンで指定している)
my $imageDir = 'C:\\Program\\Samurize\\Icons\\Shiny\\';
#作業フォルダ(すべてのファイルはここに作成される)
my $workingDir = "c:\\Program\\Samurize\\Scripts\\YahooWeather";
#天気画像の設定で昼夜の判別をするかどうか(0:判別しない 1:判別する)
my $useDaytime = 1;
#最終出力データを入れる変数
my $samurize_text;
#3時間ごとの予報が2日分(今日の0時から3時間ごとに計16件*8項目)入る2次元配列
#時間、天気、気温、湿度、降水量、風向、風速、天気画像ファイルのパス
my @day;
#週間天気(今日を入れて8日分*7項目)が入る2次元配列
#日付、曜日、天気、最高気温、最低気温、降水確率、天気画像ファイルのパス
my @week;
#週間天気ページから生成された、3時間ごとの降水確率が入る配列
#(今日の0時から3時間ごとに計16件)
my @precipitation;
#週間天気ページから生成された、今日明日のデータのみに存在する項目が入る2次元配列(2日*4項目)
#最高気温前日比、最低気温前日比、風、波
my @todayAdditional;
#予報の更新日時が入る配列(3件)
#ピンポイント予報発表日時、今日明日の天気発表日時、週間天気発表日時
my @updateTime;
#ピンポイント予報の地域
my $pinpointLocation;
# 現在時刻の取得
my $t = Time::Piece::localtime();
#my $year = $t->year;
#my $month = $t->mon;
#my $day = $t->mday;
my $date = $t->ymd("/");
my $time = $t->hms;
my $now = "$date $time";
binmode STDOUT, ":encoding(cp932)";
binmode STDERR, ":encoding(cp932)";
chdir $workingDir;
#エラーをファイルに書き出す(無限ループとか起こすと大変なことになる)
#デフォルトはコメントアウト
#open STDERR, ">>error.txt";
#実際のデータ取得、生成サブルーチンを呼び出す
@day = process_day();
@week = process_week();
#今日の予報の書き出し開始地点を決める
#現時刻を含む予報が最初になるようにする
my $dayStartPos = int(($t->hour) / 3);
#データ整形→ファイル書き出し開始
#書き出しフォーマットは "YahooWeather.vbs" に準じる(たぶん)
##----- 最終出力フォーマット start -----##
$samurize_text .= "$pinpointLocation\n"; #ピンポイント予報の地域
$samurize_text .= "$updateTime[0]\n"; #ピンポイント予報発表時刻
$samurize_text .= "\n"; #1行空ける
#3時間ごとの予報を8件分書き出す
for ( my $i = $dayStartPos; $i < $dayStartPos + 8; $i++)
{
$samurize_text .= "$day[$i][0]\n"; #時間
$samurize_text .= "$day[$i][1]\n"; #天気
$samurize_text .= "$day[$i][2]\n"; #気温
$samurize_text .= "$day[$i][3]\n"; #湿度
$samurize_text .= "$day[$i][4]\n"; #降水量
$samurize_text .= "$precipitation[$i]\n"; #降水確率
$samurize_text .= "$day[$i][5]\n"; #風向
$samurize_text .= "$day[$i][6]\n"; #風速
$samurize_text .= "$day[$i][7]\n"; #天気画像のパス
$samurize_text .= "\n"; #1行空ける
}
#週間予報を7件分書き出す
$samurize_text .= "$updateTime[1]\n"; #今日と明日の天気発表時刻
$samurize_text .= "\n"; #1行空ける
#今日と明日
for (my $i = 0; $i < 2; $i++)
{
$samurize_text .= "$week[$i][0]($week[$i][1])\n"; #日付(曜日)
$samurize_text .= "$week[$i][1]\n"; #曜日
$samurize_text .= "$week[$i][2]\n"; #天気
$samurize_text .= "$week[$i][3]\n"; #最高気温
$samurize_text .= "$todayAdditional[$i][0]\n"; #最高気温前日比
$samurize_text .= "$week[$i][4]\n"; #最低気温
$samurize_text .= "$todayAdditional[$i][1]\n"; #最低気温前日比
$samurize_text .= "$week[$i][5]\n"; #降水確率
$samurize_text .= "$todayAdditional[$i][2]\n"; #風
$samurize_text .= "$todayAdditional[$i][3]\n"; #波
$samurize_text .= "$week[$i][6]\n"; #天気画像のパス
$samurize_text .= "\n"; #1行空ける
}
$samurize_text .= "$updateTime[2]\n"; #週間予報発表時刻
$samurize_text .= "\n"; #1行空ける
#明後日以降
for (my $i = 2; $i < 7; $i++)
{
$samurize_text .= "$week[$i][0]($week[$i][1])\n"; #日付(曜日)
$samurize_text .= "$week[$i][1]\n"; #曜日
$samurize_text .= "$week[$i][2]\n"; #天気
$samurize_text .= "$week[$i][3]\n"; #最高気温
$samurize_text .= "$week[$i][4]\n"; #最低気温
$samurize_text .= "$week[$i][5]\n"; #降水確率
$samurize_text .= "$week[$i][6]\n"; #天気画像のパス
$samurize_text .= "\n"; #1行空ける
}
##----- 最終出力フォーマット end -----##
#ファイルに書き出し
if(!$debug_DontWriteToFile)
{
open OUT, ">$outFile" or
die "$now Cannot open out file: $!";
print OUT encode('cp932', $samurize_text);
}
#コンソールに出力
#print "$samurize_text\n";
#3時間ごとの予報を取得するサブルーチン
sub process_day {
#時間、天気、気温、湿度、降水量、風向、風速、天気画像のパス
my @dayArray;
# 1日予報を取得
if(!$debug_DontGetFromRemote)
{
getstore($yahooWeatherDay, $localDay) or
die "$now Cannot get YahooWeatherDay: $!";
}
# 保存した1日予報を開く
open (DAYSOURCE, "<:utf8", $localDay) or
die "$now Cannot open localDay: $!";
my $originalText = join '', <DAYSOURCE>;
# font タグを取り除く
$originalText =~ s%</?font[^>]*?>%%g;
#タイトル行から地域を取り出す
if ( $originalText =~ /<title>(.+?)の天気/m )
{
$pinpointLocation = "$1";
}
#発表日時を取り出す
if ( $originalText =~ m%<p class="yjSt yjw_note_h2"\s*>.+?</p>%s )
{
if ( $& =~ /(\d+年\d+月\d+日).+?(\d+時\d+分)発表/m )
{
push (@updateTime, "$1 $2");
}
}
#今日・明日の天気 (3時間ごと)
#今日の分と明日の分で2回ループ
for (my $dailyloop = 0; $dailyloop < 2; $dailyloop++)
{
my $divText;
my $dayArrayStartPos;
if ($dailyloop == 0)
{
$divText = "yjw_pinpoint_today";
$dayArrayStartPos = 0;
} else {
$divText = "yjw_pinpoint_tomorrow";
$dayArrayStartPos = 8;
}
if ( $originalText =~ m%<div id="${divText}">.+?</div>%s )
{
#print "$&\n";
my $todayPart = $&;
#<tr>タグで分割して配列に格納
my @trArray = $todayPart =~ m%<tr.+?</tr>%gs;
for ( my $j = 0; $j < 6; $j++)
{
my $i = $dayArrayStartPos;
if (! defined $trArray[$j] )
{
die "Error Parsing $divText part";
}
#時間、気温、湿度、降水量
elsif ($j == 0 || $j == 2 || $j == 3 || $j == 4)
{
my @smallArray = $trArray[$j] =~ m%<small>(.+?)</small>%g;
die "Error Parsing Part \$j=$j" if (! defined $smallArray[8]);
for (my $k = 1; $k < 9; $k++)
{
$dayArray[$i][$j] = $smallArray[$k];
$i++
#print "$dayArray[$dayArrayStartPos][$j] ";
}
#print "\n";
}
#天気
elsif ($j == 1)
{
my @tdArray = $trArray[$j] =~ m%<td[^>]*?>(.+?)</td>%gs;
for (my $k = 1; $k < 9; $k++)
{
my @smallArray = $tdArray[$k] =~ m%<small>(.+?)</small>%gs;
if (defined $smallArray[1])
{
$dayArray[$i][1] = $smallArray[1];
$i++
}
else
{
$dayArray[$i][1] = $smallArray[0];
$i++
}
}
}
#風向、風速
elsif ($j == 5)
{
my @tdArray = $trArray[$j] =~ m%<td[^>]*?>(.+?)</td>%gs;
#改行を削除する
@tdArray = map { chomp($_); $_} @tdArray;
#空白を削除する
@tdArray = map { $_ =~ s/\s+//g; $_ } @tdArray;
#空白行を削除する
@tdArray = grep { !/^\s*$/ } @tdArray;
#print "tdArray --";
#print "@tdArray";
for (my $k = 1; $k < 9; $k++)
{
$tdArray[$k] =~ m%<small>(.+?)<br>\s*(.+?)</small>%gs;
$dayArray[$i][5] = $1;
$dayArray[$i][6] = $2;
$i++
#print "$dayArray[$dayArrayStartPos][5] ";
#print "$dayArray[$dayArrayStartPos][6]\n";
}
}
}
}
}
# 天気に応じた画像ファイルパスを追加
foreach my $array (@dayArray)
{
push (@$array, &getImage(@$array[1], @$array[0]) );
}
#気温等に記号を付加
for ( my $i = 0; $i < 16; $i++)
{
$dayArray[$i][2] .= "℃"; #気温
$dayArray[$i][3] .= "%"; #湿度
$dayArray[$i][4] .= "mm/h"; #降水量
$dayArray[$i][6] .= "m/s"; #風速
}
#デバッグ用コンソール出力
if($debug_ConsoleOut)
{
print "----- \@dayArray start -----\n";
foreach my $debug1 (@dayArray)
{
foreach my $debug2 (@$debug1)
{
print "$debug2 ";
}
print "\n";
}
print "----- \@dayArray end -----\n";
}
return @dayArray;
}
#週間予報を取得するサブルーチン
sub process_week {
#日付、曜日、天気、最高気温、最低気温、降水確率、天気画像のパス
my @weekArray;
#週間予報を取得
if(!$debug_DontGetFromRemote)
{
getstore($yahooWeatherWeek, $localWeek) or
die "$now Cannot get YahooWeatherWeek: $!";
}
#保存した週間予報を開く
open (WEEKSOURCE, "<:utf8", $localWeek) or
die "$now Cannot open localWeek: $!";
my $originalText = join '', <WEEKSOURCE>;
#発表日時を取り出す
if ( $originalText =~ m%<!--今日・明日の天気-->.+?</p>%s )
{
if ( $& =~ /(\d+年\d+月\d+日).+?(\d+時\d+分)発表/m )
{
push (@updateTime, "$1 $2");
}
}
if ( $originalText =~ m%<!---週間の天気--->.+?</p>%s )
{
if ( $& =~ /(\d+年\d+月\d+日).+?(\d+時\d+分)発表/m )
{
push (@updateTime, "$1 $2");
}
}
#今日・明日の天気
if ( $originalText =~ m%<div class="forecastCity">.+?<!-- /forecastToday -->%s )
{
my $thisPart = $&;
#print $thisPart;
# img タグを取り除く
$thisPart =~ s%<img[^>]*?>%%g;
# 熱中症情報を取り除く
#$thisPart =~ s%<dd class="hstrokeexp">.+?</dd>%%g;
my $weekArrayStartPos = 0;
my $debugCount = 0;
while ( $thisPart =~ m%<div>.+?</div>%gs )
{
#debug: 無限ループ抑制
$debugCount++;
die "maybe infinity loop" if $debugCount > 100;
my $part = $&;
#print $part;
#日付と曜日
if ( $part =~ m%<p class="date">\d+月(\d+日)\((.+?)\)</p>%m )
{
$weekArray[$weekArrayStartPos][0] = "$1"; #日付
$weekArray[$weekArrayStartPos][1] = "$2"; #曜日
#print "$weekArray[$weekArrayStartPos][0] $weekArray[$weekArrayStartPos][1]";
}
#天気
# この表現は改行があった場合に取り除くため → (?:\s*)
if ( $part =~ m%<p class="pict">(?:\s*)(.+?)(?:\s*)</p>%m )
{
$weekArray[$weekArrayStartPos][2] = $1;
#print "$weekArray[$weekArrayStartPos][1] ";
}
#最高気温
if ( $part =~ m%<li class="high"><em>(.+?)</em>.+?\[(.+?)\]%m )
{
$weekArray[$weekArrayStartPos][3] = "$1℃";
push (@{$todayAdditional[$weekArrayStartPos]}, "$2"); #最高気温前日比
}
#最低気温
if ( $part =~ m%<li class="low"><em>(.+?)</em>.+?\[(.+?)\]%m )
{
$weekArray[$weekArrayStartPos][4] = "$1℃";
push (@{$todayAdditional[$weekArrayStartPos]}, "$2"); #最低気温前日比
}
#降水確率
if ( $part =~ m%<tr class="precip">.+?</tr>%s )
{
my $tmp = $&;
my @precipArray;
while ( $tmp =~ m%<td>(.+?)</td>%gs )
{
push (@precipArray, $1);
}
#24時間の値になっているときはそれを使う。それ以外は12-18時の値を使う
#ただし値が "---" のとき(当日の18時を過ぎている)は18-24時の値を使う
if (! defined $precipArray[1] )
{
$weekArray[$weekArrayStartPos][5] = $precipArray[0];
}
elsif ( $precipArray[2] ne "---" )
{
$weekArray[$weekArrayStartPos][5] = $precipArray[2];
}
else
{
$weekArray[$weekArrayStartPos][5] = $precipArray[3];
}
#print "$weekArray[$weekArrayStartPos][5] ";
#今日と明日の3時間ごとの降水確率はここで作って @precipitation に入れる
if (! defined $precipArray[1] )
{
for (my $i = 0; $i < 8; $i++ )
{
push (@precipitation, $precipArray[0]);
}
}
else
{
for (my $i = 0; $i < 4; $i++ )
{
for (my $j = 0; $j < 2; $j++)
{
push (@precipitation, $precipArray[$i]);
}
}
}
}
#風と波
if ( $part =~ m%<dl>.+?(<dt class="hstroke">|</dl>)%s )
{
my $tmp = $&;
#print $tmp;
while ( $tmp =~ m%<dd>(.+?)</dd>%gs )
{
push (@{$todayAdditional[$weekArrayStartPos]}, "$1");
}
} else {
print "not muched";
}
$weekArrayStartPos++;
}
}
#明後日以降の天気
if ( $originalText =~ m%<table[^>]+?class="yjw_table">.+?</table>%s )
{
my $thisPart = $&;
# span、font タグを取り除く
$thisPart =~ s%</?span[^>]*?>%%g;
$thisPart =~ s%</?font[^>]*?>%%g;
my $weekArrayStartPos = 2;
for (my $j = 0; $j < 4; $j++ )
{
if ( $thisPart =~ m%<tr.+?</tr>%sg )
{
my $part = $&;
my $i = $weekArrayStartPos;
#print "---$part\n";
#日付と曜日
if ( $j == 0 )
{
while ( $part =~ m%<small>\d+?月(\d+?日).+?\((.+?)\)</small>%g )
{
$weekArray[$i][0] = "$1"; #日付
$weekArray[$i][1] = "$2"; #曜日
#print "$weekArray[$i][0] $weekArray[$i][1]\n";
$i++;
}
}
#天気
elsif ( $j == 1 )
{
my $flag = 0;
while ( $part =~ m%<small>(.+?)</small>%g )
{
if ( $flag == 0 )
{
$flag = 1;
next;
}
$weekArray[$i][2] = $1;
#print "$weekArray[$i][2] ";
$i++;
}
}
#気温
elsif ( $j == 2 )
{
my $flag = 0;
while ( $part =~ m%<td(.+?)</td>%sg )
{
my $tdPart = $&;
if ( $flag == 0 )
{
$flag = 1;
next;
}
# この表現は改行がある場合に取り除くため → (?:\s*)
while ( $tdPart =~ m%<small>(?:\s*)(.+?)<br>(?:\s*)(.+?)(?:\s*)</small>%g )
{
#print "$tdPart\n";
$weekArray[$i][3] = "$1℃";
$weekArray[$i][4] = "$2℃";
#print "$weekArray[$i][3] $weekArray[$i][4]\n";
}
$i++;
}
}
#降水確率
elsif ( $j == 3 )
{
my $flag = 0;
while ( $part =~ m%<small>(.+?)</small>%g )
{
if ( $flag == 0 )
{
$flag = 1;
next;
}
$weekArray[$i][5] = "$1%";
#print "$weekArray[$i][5] ";
$i++;
}
}
}
}
}
# 天気に応じた画像ファイルパスを追加
foreach my $array (@weekArray)
{
push (@$array, &getImage(@$array[2]) );
}
#天気の "時々" を "/" に "後" を "→" に置換(お好みで)
foreach my $array (@weekArray)
{
@$array[2] =~ s/時々/\//;
@$array[2] =~ s/一時/\//;
@$array[2] =~ s/後/→/;
@$array[2] =~ s/のち/→/;
}
#デバッグ用コンソール出力
if($debug_ConsoleOut)
{
print "----- \@weekArray start -----\n";
foreach my $debug1 (@weekArray)
{
foreach my $debug2 (@$debug1)
{
print "$debug2 ";
}
print "\n";
}
print "----- \@weekArray end -----\n";
print "----- \@todayAdditional start -----\n";
foreach my $debug1 (@todayAdditional)
{
foreach my $debug2 (@$debug1)
{
print "$debug2 ";
}
print "\n";
}
print "----- \@todayAdditional end -----\n";
print "----- \@precipitation start -----\n";
print "@precipitation\n";
print "----- \@precipitation end -----\n";
}
return @weekArray;
}
#天気画像ファイルを指定するサブルーチン
sub getImage {
my $daytime;
my $image;
my($givenWeather, $givenTime);
($givenWeather, $givenTime) = @_;
#昼夜の判別
if ( (! defined $givenTime) || (! $useDaytime) )
{
$daytime = 1;
}
else
{
$givenTime =~ /^\d+/ ;
my $time = $&;
if ( $time < 6 || $time > 18 )
{
$daytime = 0;
}
else
{
$daytime = 1;
}
}
if ( $daytime )
{
#昼間の画像 (昼夜の判別をしない場合もこちら)
given( $givenWeather ) {
when ( "晴れ" ) { $image = "32.png" }
when ( "曇り" ) { $image = "26.png" }
when ( "弱雨" ) { $image = "11.png" }
when ( /^(雨|強雨|大雨)$/ ) { $image = "12.png" }
when ( "暴風雨" ) { $image = "1.png" }
when ( /^(雪|湿雪|乾雪)$/ ) { $image = "15.png" }
when ( "暴風雪" ) { $image = "43.png" }
when ( "みぞれ" ) { $image = "05.png" }
when ( /^(晴のち曇|曇のち晴)/ ) { $image = "28.png" }
when ( /^(曇時々晴|晴時々曇)/ ) { $image = "30.png" }
when ( /^(曇のち雨|雨のち曇|曇時々雨|雨時々曇|曇一時雨)/ ) { $image = "11.png" }
when ( /^(晴のち雨|雨のち晴|晴時々雨|雨時々晴|晴一時雨)/ ) { $image = "39.png" }
when ( /^(曇のち雪|雪のち曇|曇時々雪|雪時々曇|曇一時雪)/ ) { $image = "13.png" }
when ( /^(晴のち雪|雪のち晴|晴時々雪|雪時々晴|晴一時雪)/ ) { $image = "13.png" }
when ( /^(雨のち雪|雪のち雨|雨時々雪|雪時々雨)/ ) { $image = "07.png" }
default { $image = "default.png" }
}
}
else
{
#夜間の画像
given( $givenWeather ) {
when ( "晴れ" ) { $image = "31.png" }
when ( "曇り" ) { $image = "26.png" }
when ( "弱雨" ) { $image = "11.png" }
when ( /^(雨|強雨|大雨)$/ ) { $image = "12.png" }
when ( "暴風雨" ) { $image = "1.png" }
when ( /^(雪|湿雪|乾雪)$/ ) { $image = "15.png" }
when ( "暴風雪" ) { $image = "43.png" }
when ( "みぞれ" ) { $image = "05.png" }
when ( /^(晴のち曇|曇のち晴)/ ) { $image = "27.png" }
when ( /^(曇時々晴|晴時々曇)/ ) { $image = "29.png" }
when ( /^(曇のち雨|雨のち曇|曇時々雨|雨時々曇|曇一時雨)/ ) { $image = "11.png" }
when ( /^(晴のち雨|雨のち晴|晴時々雨|雨時々晴|晴一時雨)/ ) { $image = "45.png" }
when ( /^(曇のち雪|雪のち曇|曇時々雪|雪時々曇|曇一時雪)/ ) { $image = "13.png" }
when ( /^(晴のち雪|雪のち晴|晴時々雪|雪時々晴|晴一時雪)/ ) { $image = "46.png" }
when ( /^(雨のち雪|雪のち雨|雨時々雪|雪時々雨)/ ) { $image = "07.png" }
default { $image = "default.png" }
}
}
my $imagePath = File::Spec->catfile($imageDir, $image);
return $imagePath;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment