KONAMI社のBEMANIシリーズを韓国で稼働させるための提携先にUNIANA社があり、それは下記サイトに詳しい。
韓国の音ゲー事情
そして、ローカライズや運営しているサイトのなかに稼働店舗一覧のページがあるようで、
そんなわけで、誰かunianaの公式から店舗の更新情報を逐一調べてくれるツール作って下さい。キリッ!
— 安心の建山さんは合格じゃ (@xiaoli_tateyama) 2016年8月23日
というツイートを見かけた。
そもそもハングル文字読めないので、どこから稼働店舗一覧のページに遷移できるかを探すのに苦労した。マウスを動かしてリンクを表示させて、というのをひたすらやるしかない。
http://beat.uniana.com/store/store_list.php
ようやく見つけたので、しばらく動かしてみて、HTMLソースも眺めてみたりして、
<tr> <td>21세기 게임랜드</td> <td><span>서울시 관악구 신림9동 1538-1 </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 </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時だし夜も遅いし明日も仕事だし、
unianaの店舗情報は、<td>~</td>の部分が店舗名で、<td><span>~</span>の部分が住所っぽいので、解析するなら割と簡単にできるんじゃないかな。がんばれるひとはがんばってください
— MTAK (@lmtak) 2016年8月23日
とヒントだけ書いておいた。
数日経って時間に余裕も出来たので、ちょっと試しにつくって見ます。
0. 結論
以下のURLで試験運用中。いまのところ差分ないけど。
uniana.com shop comer/gone detector
1. クロール
先ほどのサイトでは、Flashで地域を選ぶとその地域内の店舗をページング表示する作りになっており、以下のようなURL構成になっている。
パラメータ「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>[^&]\+ </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 ">> <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