Edited at

【Line Bot 】ムフフなランキング 教えてDMM総合Rannking


はじめに:やりたいこと


  • 日頃お世話になることが多いDMM(18禁のほう)改めFANZAですが、人気女優ランキングがあります。

  • そのランキングは、DVD、レンタル、ビデオの分類別にあります。

    DVDのランキングURL(https://www.dmm.co.jp/mono/dvd/-/ranking/=/mode=actress/term=monthly/)

  • とてもありがたいのですが、それぞれを詳しく見ている時間もありません。

1 (Custom).jpg

そこで、それぞれのランキングデータから、足し算の独自ルールで総合ランキング(DVD、レンタル、ビデオ)を自動でつくり、ランキング順位を数字で問いかけたら、情報を返してくれるLine Botを作成しようと思いました。


前提


  • Line message APIのアクセストークンを取得+Webhook URLにPHPプログラムを指定する。


使用技術


  • Line message API(https://developers.line.biz/ja/services/messaging-api/)

  • PHP

  • PHPスクレイピング:phpQuery(恥ずかしながらスクレイピングってPythonでやるもの、と勝手に思っていましたが、PHPでもできることが今回わかりました)


実際のもの

実際の画面

11.jpg  10.jpg

QRコード(Bot DMM M-Rank)

1.jpg

よかったら使ってみてください。


実現方法


①WEBをスクレイピングして、ランキングデータを取得する(Cronにて、1日1回の実行)


  • DVD、レンタル、ビデオの3つのランキングをスクレイピングして、下記の情報をDB(MySQL)に格納する。

 ・分類(DVD、レンタル、ビデオ)

 ・ランキング

 ・女優名前

 ・画像URL

 ・作品リストURL

 ・最新作URL


getRnak.php


<?php
// PHPによるスクレイピングの実施
require_once("./phpQuery/phpQuery-onefile.php");

// ループを実施し、スクレイピングを実施する
$i = 1;
while($i < 16) {

// 取得するURLを設定する(dvd:5, video:5, rental:5)
switch ($i) {
case 1:
$html = file_get_contents("https://www.dmm.co.jp/mono/dvd/-/ranking/=/mode=actress/term=monthly/");
break;
case 2:
//以下、省略
};

// 分類設定
if($i < 6) {
$class = 'dvd';
} elseif($i >= 6 && $i < 11) {
$class = 'video';
} else {
$class = 'rental';
};

// phpQueryの実行
$dochtml = phpQuery::newDocument($html);

// タグとクラスにて絞り込み
$tdtag = $dochtml->find('td')->find('.bd-b');

$headUrl = 'https://www.dmm.co.jp';

foreach ($tdtag as $value) {

// ランキングの取得(クラス)
$rank = pq($value)->find('.rank')->text();
// 名称の取得(imgタグ->altタグ)
$name = pq($value)->find('img')->attr('alt');
// 画像の取得(imgタグ->srcタグ)
$image = pq($value)->find('img')->attr('src');
// Listの取得(pタグ->aタグ->href属性)
$list_url = pq($value)->find('p')->find('a')->attr('href');
$list_url = $headUrl . $list_url;
// 最新作の取得(2個めのaタグ->href属性)
$new_url = pq($value)->find('a:eq(2)')->attr('href');
$new_url = $headUrl . $new_url;

// データのセット
$setData[] = [
':class' => $class,
':rank' => $rank,
':name' => $name,
':imgUrl' => $image,
':listUrl' => $list_url,
':newUrl' => $new_url
];
}
$i++;
};

// 取得したデータをSQLにてMYSQLに格納
$host = "host情報";
$dbname = "DB名称";
$user = "ユーザID";
$pass = "パスワード";

try {
$db = new PDO('mysql:host=' . $host . 'dbname=' . $dbname . 'charset=utf8', $user, $pass);
} catch(PDOException $e) {
echo 'エラーの発生:' . $e->getMessage();
}

// SQL文の格納(DELETE:洗替えのため)
$sqldelete = "delete from dmm_ranks";
$stmt = $db->prepare($sqldelete);
$stmt->execute();

// SQL文の格納(INSERT)
$sqlinsert = "insert into dmm_ranks (class, rank, name, imgUrl, listUrl, newUrl) VALUES (:class, :rank, :name, :imgUrl, :listUrl, :newUrl)";
$stmt = $db->prepare($sqlinsert);

foreach ($setData as $data) {
$stmt->execute($data);
}
?>



②ランキングデータをもとに、総合ランキングを作成する(Cronにて、1日1回の実行)

総合ランキングのルールは下記のようにしました。


  • それぞれの分類のランキング順位を足して、総合ランキングポイントとする。

  • 総合ポイントが少ないほうが順位が上位となる。

  • それぞれの分類(DVD、レンタル、ビデオ)で、どこか1つに登場しない場合は、101を足し、2つ登場しない場合は、202を足す。(3つの分類のすべてに登場する女優さんは、人気上位となる)

  • どこかの分類に登場すれば、総合ランキングにでてくることになる。(これを書いている時点では、181位まであります)


makeRank.php

<?php

$host = "host情報";
$dbname = "DB名称";
$user = "ユーザID";
$pass = "パスワード";

try {
$db = new PDO('mysql:host=' . $host . 'dbname=' . $dbname . 'charset=utf8', $user, $pass);

// SQL文の格納(rankデータの取得)
$sql = 'SELECT count(id) as cnt,
name,
sum(rank) as point
FROM dmm_ranks
GROUP BY name
ORDER BY cnt DESC, point ASC'
;
$stmt = $db->prepare($sql);
$stmt->execute();

// foreach文で配列の中身を1件ずつ処理
foreach ($stmt as $row) {

// データが3つならなにもしない・データが2つなら101を足す・データが2つなら202を足す
if($row['cnt'] == 3){
$point = $row['point'];
} elseif($row['cnt'] == 2) {
$point = $row['point'] + 101;
} else {
$point = $row['point'] + 202;
};

$pointData[] = [
'point' => $point,
'name' => $row['name'],
];

}

// ソート処理(昇順)
asort($pointData);

} catch(PDOException $e) {
$content = 'エラーの発生:' . $e->getMessage();
}

$i = 1;
// ランキングポイントデータをループして、ランキングの作成とデータの取得
foreach ($pointData as $row1) {

$v_image = NULL;
$v_list_url = NULL;
$v_new_url = NULL;
$v_rank = 0;
$d_image = NULL;
$d_list_url = NULL;
$d_new_url = NULL;
$d_rank = 0;
$r_image = NULL;
$r_list_url = NULL;
$r_new_url = NULL;
$r_rank = 0;

// SQL文の格納(rankデータの取得)
$sqlurl = "SELECT * FROM dmm_ranks WHERE name = '" . $row1['name'] . "'";
$stmt1 = $db->prepare($sqlurl);
$stmt1->execute();

// foreach文で配列の中身を1件ずつ処理
foreach ($stmt1 as $row2) {

if($row2['class'] == 'video'){
$v_image = $row2['imgUrl'];
$v_list_url = $row2['listUrl'];
$v_new_url = $row2['newUrl'];
$v_rank = $row2['rank'];
};

if($row2['class'] == 'dvd'){
$d_image = $row2['imgUrl'];
$d_list_url = $row2['listUrl'];
$d_new_url = $row2['newUrl'];
$d_rank = $row2['rank'];
};

if($row2['class'] == 'rental'){
$r_image = $row2['imgUrl'];
$r_list_url = $row2['listUrl'];
$r_new_url = $row2['newUrl'];
$r_rank = $row2['rank'];
};

};

// 画像・listUrl・newUrlのセット
if($v_image == NULL && $d_image == NULL) {
$image = $r_image;
$list_url = $r_list_url;
$new_url = $r_new_url;
} elseif($v_image == NULL && $d_image != NULL) {
$image = $d_image;
$list_url = $d_list_url;
$new_url = $d_new_url;
} else {
$image = $v_image;
$list_url = $v_list_url;
$new_url = $v_new_url;
};

// rankに値が設定されていない場合は、101を設定する
if($v_rank == 0) {
$v_rank = 101;
}
if($d_rank == 0) {
$d_rank = 101;
}
if($r_rank == 0) {
$r_rank = 101;
}

// ポイントの内訳を設定する
$bdpoint = 'video:' . $v_rank . ' + ' . 'dvd:' . $d_rank . ' + ' . 'rental:' . $r_rank;

// DBに格納するデータのセット
$setData[] = [
':rank' => $i,
':point' => $row1['point'],
':name' => $row1['name'],
':imgUrl' => $image,
':listUrl' => $list_url,
':newUrl' => $new_url,
':bdpoint' => $bdpoint
];
$i++;
}

// SQL文の格納(DELETE:洗替えのため)
$sqldelete = "delete from dmm_makeranks";
$stmt2 = $db->prepare($sqldelete);
$stmt2->execute();

// SQL文の格納(Insert処理)
$sqlinsert = "insert into dmm_makeranks (rank, point, name, imgUrl, listUrl, newUrl, bdpoint) VALUES (:rank, :point, :name, :imgUrl, :listUrl, :newUrl, :bdpoint)";
$stmt3 = $db->prepare($sqlinsert);
foreach ($setData as $data) {
$stmt3->execute($data);
};
?>



③ Line送られてきた数字に対する ランキング順位 の情報を返す


  • 文字が送られてきた場合や総合ランキングの順位よりの大きい数字がきたら、「1」~「DBカウント数」の間でランダム数を設定し、その情報を返す。

  • 返答する情報は、以下のとおり。

 ・画像

 ・ランキング:女優名性

 ・作品リストURL

 ・最新作URL

 ・ランキングポイント内訳

9.jpg


lineReply.php


<?php

// Lineチャンネルアクセストークン設定
$channelAccessToken = 'アクセストークンを設定する';

// ユーザーからのメッセージ取得
$inputData = file_get_contents('php://input');

// 受信したJSON文字列をデコード
$jsonObj = json_decode($inputData);

// イベントタイプの取得
$eventType = $jsonObj->{"events"}[0]->{"type"};

// メッセージイベントだった場合(テキスト、画像、スタンプなどの場合「message」になる)
if ($eventType == 'message') {
// メッセージタイプ取得
$messageType = $jsonObj->{"events"}[0]->{"message"}->{"type"};
// ReplyToken取得(受信したイベントに対して返信を行うために必要)
$replyToken = $jsonObj->{"events"}[0]->{"replyToken"};
// メッセージタイプがtextの場合の処理
if ($messageType == 'text') {
// メッセージテキスト取得
$messageText = $jsonObj->{"events"}[0]->{"message"}->{"text"};
// DB接続情報
$host = "host情報";
$dbname = "DB名称";
$user = "ユーザID";
$pass = "パスワード";

// DB接続情報設定
$db = new PDO('mysql:host=' . $host . 'dbname=' . $dbname . 'charset=utf8', $user, $pass);

// SQL文の格納・実行(DBカウント)
$sqlcnt = 'SELECT count(*) from dmm_makeranks';
$stmt = $db->query($sqlcnt);
$dbcnt = $stmt->fetchColumn();

// int型に変換(文字などが来た場合は、0となる)
$rank = intval($messageText);

// rankが0 or DBカウント数より大きかったら、1~DBカウント数のランダム数を設定する
if ($rank == 0 || $rank > $dbcnt) {
$rank = rand(1, $dbcnt);
}
// SQL文の格納(rankによるDB データ取得)
$sql = 'SELECT * from dmm_makeranks where rank = "' . $rank . '"';
// SQLを実行し、結果を変数に格納
$stmt = $db->query($sql);

// foreach文で配列の中身を一行ずつ出力
foreach ($stmt as $row) {
// 画像
$imgUrl = $row['imgUrl'];
// rank + name + リストURL + 最新作URL + ポイント(3つの総合得点)
$rankname = $row['rank'] . '位: ' . $row['name'] . "\n" . "\n"
. '作品リスト: ' . $row['listUrl'] . "\n" . "\n"
. '最新作: ' . $row['newUrl'] . "\n" . "\n"
. 'ポイント: ' . $row['point'] . '(' . $row['bdpoint'] .')';
}

// 画像データの格納
$response1 = [
"type" => "image",
"originalContentUrl" => "$imgUrl",
"previewImageUrl" => "$imgUrl"
];
// ランキングデータの格納
$response2 = [
"type" => "text",
"text" => "$rankname"
];

$post_data = [
"replyToken" => $replyToken,
"messages" => [$response1, $response2]
];

//上記以外のメッセージタイプ(画像やスタンプなどの場合)
} else {
$response1 = [
"type" => "text",
"text" => "メッセージ以外は受け取れません!数字を送ってください"
];
$post_data = [
"replyToken" => $replyToken,
"messages" => [$response1]
];
}
}

//Reply message用のURLに対して HTTP requestを行う
$ch = curl_init("https://api.line.me/v2/bot/message/reply");

curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json; charser=UTF-8',
'Authorization: Bearer ' . $channelAccessToken
));

$result = curl_exec($ch);
curl_close($ch);

?>



まとめ


  • ①スクレイピング ②ランキングを作成する ③Lineによる返答は、それぞれの別々の3つのプログラムを用意しました。

  • プログラムの目的がわかりやすいので、初心者の私には頭の整理がしやすく、作成に集中できたと思います。

  • これでわざわざWEBページにアクセスしたり、それぞれのランキングをみる、といったことも必要はなくなりました。また、現時点の総合的に人気女優さんがわかるな、と思います。


参考URL