夏休みの自由研究 - 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