夏休みの自由研究 - uniana_comparator

KONAMI社のBEMANIシリーズを韓国で稼働させるための提携先にUNIANA社があり、それは下記サイトに詳しい。
韓国の音ゲー事情
そして、ローカライズや運営しているサイトのなかに稼働店舗一覧のページがあるようで、


というツイートを見かけた。
そもそもハングル文字読めないので、どこから稼働店舗一覧のページに遷移できるかを探すのに苦労した。マウスを動かしてリンクを表示させて、というのをひたすらやるしかない。
http://beat.uniana.com/store/store_list.php
ようやく見つけたので、しばらく動かしてみて、HTMLソースも眺めてみたりして、

<tr>
	<td>21세기 게임랜드</td>
	<td><span>서울시 관악구 신림9동 1538-1&nbsp;</span><a href="http://map.naver.com/?level=2&lat=37.4810215&lng=126.9516009&query=7ISc7Jq47Yq567OE7IucIOq0gOyVheq1rCDsi6Drprw564%2BZIDE1MzgtMQ%3D%3D&menu=location&stab=ADDRESS%3B1&queryRank=1&mapMode=0&enc=b64" target="_blank"><img src="http://webimage.uniana.com/beatmania2015/common/btn_store.jpg" alt="위치보기" /></a></td>	
</tr>
<tr class="detail_view" style="text-align:center;">
	<td colspan="2"><img src="http://webimage.uniana.com/uniana/store/jubeat/seoul06.png" alt="게임장 사진" /></td>
</tr>
<tr>
	<td>건대 해피 게임천국</td>
	<td><span>서울시 광진구 화양동 9-43&nbsp;</span><a href="http://map.naver.com/?dlevel=11&lat=37.5420939&lng=127.0687863&searchCoord=127.069268%3B37.5404038&query=7ISc7Jq47IucIOq0keynhOq1rCDtmZTslpHrj5kgOS00Mw%3D%3D&menu=location&tab=1&mapMode=0&enc=b64" target="_blank"><img src="http://webimage.uniana.com/beatmania2015/common/btn_store.jpg" alt="위치보기" /></a></td>	
</tr>
<tr class="detail_view" style="text-align:center;">
	<td colspan="2"><img src="http://webimage.uniana.com/uniana/store/beat/seoul/seoul_6.png" alt="게임장 사진" /></td>
</tr>

この時点で25時だし夜も遅いし明日も仕事だし、


とヒントだけ書いておいた。

数日経って時間に余裕も出来たので、ちょっと試しにつくって見ます。

0. 結論

以下のURLで試験運用中。いまのところ差分ないけど。
uniana.com shop comer/gone detector

1. クロール

先ほどのサイトでは、Flashで地域を選ぶとその地域内の店舗をページング表示する作りになっており、以下のようなURL構成になっている。

http://beat.uniana.com/store/store_list.php?p=1&area=1

パラメータ「p」はページで、1-originで指定できる。パラメータ「area」は地域を表し、1~16まで指定できる。
店舗が無かったり、ページ指定が誤り(全2ページなのに3ページ目を指定するなど)だったりすると、

<tr><td colspan='2'>매장이 없습니다.</td></tr>

のようになるので、これを検知すればいいかな。

10wget.sh

指定のゲームに対して、全ページを取得する。
引数に、ゲームの種別、今日の年月日を(yyyyMMdd)書式で指定する。

#!/bin/sh
GAME=$1
TODAY=$2

# 地域は1~16まで
for (( area=1; area<=16; area++ ))
do
  # ページ番号は1から
  page=1
  continueflag=1
  while [ ${continueflag} -gt 0 ]
  do
    url="http://${GAME}.uniana.com/store/store_list.php?p=${page}&area=${area}"
    zpage=`echo 000${page} | sed -e 's/.*\(..\)$/\1/'`
    zarea=`echo 000${area} | sed -e 's/.*\(..\)$/\1/'`
    # 保存するファイルは、beat/20160827a01p01.html のように保存。(地域=01、ページ=01)
    wgetfile=${GAME}/${TODAY}a${zarea}p${page}.html

    # HTMLを取得する処理は別ファイルに記述
    ./11wget.sh "${url}" "${wgetfile}"
    # 処理結果を取得。0なら成功(次ページを取得)、それ以外なら次の地域へ
    result=$?
    if [ ${result} -ne 0 ] ; then
      continueflag=0
    fi
    # 無限ループにならないよう、3ページ取得したら次の地域へ
    if [ ${page} -ge 3 ] ; then
      continueflag=0
    fi

    # ページ番号をインクリメント
    page=$(($page + 1))
  done
done
11wget.sh

実際にwgetでHTML文書を取得する。
引数には、アクセスするURLと、保存するファイル名を指定。
戻り値が0だと成功。それ以外だと何らかの失敗。そのエリアではこれ以上HTML取得しない、と判断する。

#!/bin/sh
WGETURL=$1
OUTFILE=$2
TMPFILE=${OUTFILE}.tmp
# パラメータチェック
if [ "${WGETURL}" = "" ] ; then
  exit 11
fi
if [ "${OUTFILE}" = "" ] ; then
  exit 12
fi

# ユーザーエージェント偽装
useragent="Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36"
# wgetコマンドでHTML取得
wget -v --append-output=wget.log \
--output-document="${TMPFILE}" \
--user-agent="${useragent}" ${WGETURL}
# 文字コードをShift_JISからUTF-8に置換
nkf -w "${TMPFILE}" > "${OUTFILE}"
rm -rf "${TMPFILE}"
# 店舗情報がこれ以上ないかどうか、<td colspan='2'> の有無で検知
COUNT=`grep "<td colspan='2'>" ${OUTFILE} | wc -l`
if [ ${COUNT} -ge 1 ] ; then
  # 店舗情報がない場合は、いま取得したファイルは削除
  rm ${OUTFILE}
fi

exit ${COUNT}

2. 店舗名と住所の抽出

10wget.shで保存したHTMLファイルから、店舗名を取得して1ファイルにまとめて保存。同様に、住所を取得する。
引数に、ゲームの種別、今日の年月日を(yyyyMMdd)書式で指定する。

30sed.sh
#!/bin/sh
GAME=$1
TODAY=$2

NAMEFILE=${GAME}/m_name${TODAY}.txt # 1行1店舗名
ADDRFILE=${GAME}/m_addr${TODAY}.txt # 1行1店舗住所
MERGFILE=${GAME}/m_list${TODAY}.txt # 1行に店舗名称と店舗住所をまとめる

rm -f ${NAMEFILE}
rm -f ${ADDRFILE}

# 保存したHTMLファイルのうち今日の年月日を含むファイルについて順次実施
for TARGET in `ls -1 ${GAME}/${TODAY}*`
do
# 店舗名をgrepで抽出し、sedで整形して保存
  cat ${TARGET} \
  | grep -e "<td>[^<]\+</td>" \
  | sed -e "s:^.\+<td>\([^<]\+\)</td>.\+$:\1:g" \
  >> ${NAMEFILE}
# 住所をgrepで抽出し、sedで整形して保存
  cat ${TARGET} \
  | grep -e "<td><span>[^&]\+&nbsp;</span>" \
  | sed -e "s:^.\+<td><span>\([^&]\+\)&.\+$:\1:g" \
  >> ${ADDRFILE}
done

# 店舗名だけのファイルと、住所だけのファイルをひとまとめにする
php -f ./31merge.php ${NAMEFILE} ${ADDRFILE} ${MERGFILE}
31merge.php

引数に、店舗名だけのファイル名、住所だけのファイル名、出力用ファイル名を指定

<?php
// $argc, $argv : 予約語
if ($argc < 4) {
  echo "$argv[0] in-shopname-file in-shopaddr-file out-merge-file\n";
  exit(1);
}

$fpName = fopen($argv[1], "r");
$fpAddr = fopen($argv[2], "r");
$fpMerg = fopen($argv[3], "w");

while (!feof($fpName) && !feof($fpAddr)) {
  // read each files by line
  // and delete CR/LF
  $shopname = trim( fgets($fpName) );
  $shopaddr = trim( fgets($fpAddr) );

  // write line
  if (!empty($shopname) && !empty($shopaddr)) {
    fwrite($fpMerg, "<tr><td>${shopname}</td><td>${shopaddr}</td></tr>\n");
  }
}

fclose($fpMerg);
fclose($fpAddr);
fclose($fpName);
?>

3.昨日との比較

31merge.phpで保存した m_listYYYYMMDD.txt を、昨日のものと今日のものとで比較することで、昨日なくて今日ある店舗と、昨日あって今日ない店舗を抽出する。

50diff.sh

引数に、ゲームの種別、今日の年月日(yyyyMMdd)書式、昨日の年月日(yyyyMMdd)書式、を指定する。

#!/bin/sh
GAME=$1
TODAY=$2
YESTERDAY=$3

TFILE=${GAME}/m_list${TODAY}.txt
YFILE=${GAME}/m_list${YESTERDAY}.txt
RHTML=${GAME}/result${TODAY}.html

rm -f ${RHTML}
touch ${RHTML}

# HEADER
cat html1.html >> ${RHTML}

# TITLE
echo "<h1>${GAME} - ${TODAY}</h1>" >> ${RHTML}

# NEW COMER
echo "<h2>NEW COMER</h2>" >> ${RHTML}
echo "<table>" >> ${RHTML}
echo "<tr><th>ShopName</th><th>ShopAddress</th></tr>" >> ${RHTML}

diff ${YFILE} ${TFILE} | grep -e "^> " | sed -e "s/^> //g" >> ${RHTML}

echo "</table>" >> ${RHTML}
# GONE
echo "<h2>GONE</h2>" >> ${RHTML}
echo "<table>" >> ${RHTML}
echo "<tr><th>ShopName</th><th>ShopAddress</th></tr>" >> ${RHTML}

diff ${YFILE} ${TFILE} | grep -e "^< " | sed -e "s/^< //g" >> ${RHTML}

echo "</table>" >> ${RHTML}

# LINK

echo "<hr>" >> ${RHTML}
echo "&gt;&gt; <a href=\"result${YESTERDAY}.html\">result${YESTERDAY}.html</a>" >> ${RHTML}

# FOOTER
cat html2.html >> ${RHTML}

4. Webサーバ

50diff.shで作成したHTMLファイルを、Webサーバのドキュメントディレクトリへコピー。
index.htmlのエイリアスとして最新の日付のファイルをシンボリックリンク設定する。

60html.sh
#!/bin/sh
GAME=$1
TODAY=$2

RFILE=result${TODAY}.html
RPATH=${GAME}/${RFILE}
WWWPATH=/var/www/html/pub/uniana
DESTPATH=${WWWPATH}/${GAME}/index.html
REALPATH=${WWWPATH}/${GAME}/${RFILE}

cp -p ${RPATH} ${REALPATH}
ln -s -f ${RFILE} ${DESTPATH}

5. 定時実行

上記のシェルスクリプトをシーケンシャルに実行する

00all.sh
#!/bin/sh
cd /usr/local/uniana

TODAY=`date +%Y%m%d`
YESTERDAY=`date --date '1 days ago' +%Y%m%d`
for GAME in beat
do
  ./10wget.sh ${GAME} ${TODAY}
  ./30sed.sh  ${GAME} ${TODAY}
  ./50diff.sh ${GAME} ${TODAY} ${YESTERDAY}
  ./60html.sh ${GAME} ${TODAY}
done
crontab
## uniana wget
59 7 * * * /usr/local/uniana/00all.sh

C90-3rd(8/14(Sun.))

昔は6:00入りしてたもんだ。もう体力はないぜ。購入欲は抑えてる。カタログ買ってサークルチェックすれば欲しい物が増える一方なので、買ってもほとんど読まないんだぜ。

11:30 東京臨海高速鉄道国際展示場駅到着

買ったもの


AFEEのやつ。認識し始めたのがここ数ヶ月の出来事なので、まずは読むことから始めていきたい


新刊を。国鉄から第三セクターなどに移管された本州と四国の特定地方交通線についてまとめられた本。1982年や1986年当時のスジも簡易ながら描かれていて見ていて楽しい


世界樹の迷宮でミキサーなどやられているquad氏の新譜を。


新譜を。買ったらスーパーブラックシリカちゃんシールをもらい、塩飴つかみどりサービスがあり、そしてカード引き(プリキュアプリキュア、なんかの特撮?、万能文化猫娘)の4択なら万猫えらぶしかないじゃろ そしてやっぱりこの4択なら万猫が人気だったんだとか 客層の年齢層の高さよ


新譜を。かつての勝手なイメージと違って顔がスリムになられたような片岡氏。


新刊を。SQLiteの話だけして「壁おめでとうございます!」っていうの忘れた……

お疲れさまでした。

C90-2nd(8/13(Sat.))

昔は6:00入りしてたもんだ。もう体力はないぜ。購入欲は抑えてる。カタログ買ってサークルチェックすれば欲しい物が増える一方なので、買ってもほとんど読まないんだぜ。

13:30 ゆりかもめ国際展示場正門駅到着

買ったもの


KEYCRASHさんはシンクロニカのレーティングランキングでも見かけたことがあるのでホントに多機種勢なのですね。
多機種でゲーセンめぐりしてる/してた身としては、あるある感たっぷりで共感しまくりであります。

行脚できると「実績」とか「称号」がもらえるゲーム
 ・beatmania IIDXKONAMI
 ・maimai(SEGA
 ・CHUNITHM(SEGA
 ・GROOVE COASTER(TAITO)
 ・crossbeats REV.(CAPCOM
   リフレクもあった(過去形)

行脚?何それ美味しいの?(p4)より抜粋

Dance Dance Revolution に一文字も触れてないのは、単に忘れてるだけなのか、エリアレーダーは実績じゃない認定なのだと思います。

正解のバス乗り場から乗らないと死ぬ……
一度青森で失敗して一時間半循環だけしたのは結構トラウマ

行脚?何それ美味しいの?(p9)より抜粋

ラウンドワンに行こうとして浜田循環線に乗るべきところを横内循環線に乗ってしまったということだろうか。それはキツい……
運行経路・運賃案内/青森市



ばーるさんのところのいつものやつ


山田太郎参議院議員の特集が気になりまして


中央線と山手線と小田急線の終電ちゃんを買いました。東海道新幹線は売り切れてました。

お疲れさまでした

Synchronica青のレーティング計算式3

lmtak.hateblo.jp

仮説

  • 計算式は、(スコアレート+ランク補正値+フルコンボ補正値)÷曲レベルに応じた定数
    • スコアレートはパーセンテージ表記での70~100。70に満たないSTAGE FAILEDの場合、レーティングはゼロ
      • COMBO BONUSとRELEASE BONUSは用いない
    • ランク補正値は、C=0, B=0, A=15, AA=25, AAA=70, Rz=105
    • フルコンボ補正値は、フルコンボマークが付いたら10
    • 曲レベルに応じた定数は、Lv1=800/1、Lv7=3750/7 。他のレベルはデータが足りない
  • 曲ごとに、計算式で求めた値の「トップ値+セカンド値÷2」がその曲のレーティング
  • 譜面毎のFC+AAAボーナスは0.10。NORMAL, ADVANCED, TECHNICALでそれぞれ計上可能。PANDORAは対象外かな?

Synchronica青のレーティング計算式2

lmtak.hateblo.jp

今日もデータを取って来た

この辺りも参考にしつつ、素点を追加で記録してみた

夜の踊り子 ADVANCED/7 NOTES:212

SCORE % +BONUS % R
1448 70.1 1817 85.7 C 1.36 +0.13 =1.49 +0.13
1486 70.0 1839 86.7 C 1.49 +0.07 =1.56 +0.20
1696 80.0 2071 97.6 B 1.56 +0.02 =1.58 +0.22
1698 80.0 2073 97.7 B 1.58 +0.01 =1.59 +0.23
1908 90.0 2303 108.6 A 1.59 +0.04 =1.63 +0.27
1912 90.1 2307 108.8 A 1.63 +0.02 =1.65 +0.29
1952 92.0 2351 110.9 AA 1.65 +0.02 =1.67 +0.31
1952 92.0 2349 110.8 AA 1.67 +0.00 =1.67 +0.31
2084 98.3 2506 118.2 AAA 1.67 +0.11 =1.78 +0.42
2078 98.0 2491 117.5 AAA 1.78 +0.04 =1.82 +0.46

朱の旋律 NORMAL/7 NOTES:164

SCORE % +BONUS % R
1154 70.3 1378 84.0 C 1.82 +0.13 =1.95 +0.13
1154 70.3 1383 84.3 C 1.95 +0.06 =2.01 +0.19
1314 80.1 1576 96.1 B 2.01 +0.02 =2.03 +0.21
1486 90.6 1774 108.1 A 2.03 +0.05 =2.08 +0.26
1480 90.2 1786 108.9 A 2.08 +0.03 =2.11 +0.29
1614 98.4 1934 117.9 AAA 2.11 +0.11 =2.22 +0.40
1612 98.2 1932 117.8 AAA 2.22 +0.05 =2.27 +0.45
1436 87.5 1655 100.9 A+FC 2.27 +0.13 =2.40 +0.48(+0.10)

にじいろ NORMAL/1 NOTES:65

SCORE % +BONUS % R
460 70.7 533 82.1 C 2.40 +0.08 =2.48 +0.08
460 70.7 533 82.1 C 2.48 +0.04 =2.52 +0.12
590 90.7 700 107.7 A 2.52 +0.04 =2.56 +0.16
590 90.7 700 107.7 A 2.56 +0.02 =2.58 +0.18
640 98.4 769 118.3 AAA 2.58 +0.08 =2.66 +0.26
640 98.4 769 118.3 AAA 2.66 +0.04 =2.70 +0.30
554 85.2 628 96.6 B+FC 2.70 +0.12 =2.82 +0.32(+0.10)
650 100.0 780 120.0 Rz 2.82 +0.05 =2.87 +0.37(+0.10)
650 100.0 780 120.0 Rz 2.87 +0.01 =2.88 +0.38(+0.10)

検証はまた今度