星とか面白いこととか

暇なのでいろいろ書きます

星とか面白いこととか

スポンサーリンク

食べログ3.6の店だけを表示するマップを作りました【食べログ3.6問題】

スポンサーリンク

スポンサーリンク

こんにちは。まりもです。

 

最近Twitterで盛り上がりを見せている[食べログ3.6問題]

その概要は以下の通りです。

 

食べログ評価3.8以上は年会費を払わなければ3.6に下げられる
食べログが評価を恣意的に操作しているかもしれない

 

ということです。

clean-copy-of-onenote.hatenablog.com

詳しくは上記の検証記事を参照ください。

要は

・食べログの評価が3.8を越えると、年会費を払うことを要求され、払わないと3.6に下げらる

・年会費に関しての真偽は定かではないが、実際に評価た3.6の店が突出して多い。高い確率で事実であることがわかる

 

立論

このことから僕は一つの仮説を生み出しました。

 

食べログ、評価3.8以上の店より、評価3.6の店の方が美味い説

 

です。

評価3.8以上ということは、間違いなく食べログに上納金(年会費)を払っています。

評価をよく見せるためにお金を払う会社は、きっと口コミも捏造している可能性もあります(もちろんそうじゃない店も多いでしょうが)

口コミもお金で捏造しているし、Twitterでたまに流れてくる「まじでこの世のすべての○○好きに教えてあげたいんだが」で始まるクソカスのゲボの宣伝マンにも50~100万円(2019/10現在の依頼価格)払って偽りの褒めをしてもらってバズらせている可能性が高いです。

 

一方食べログ3.6というのは、食べログから上納金を要求されて断った店が多いはずです。

なぜ上納金を断ったかはいろいろ理由があると思いますが、おそらく「年会費を払うくらいならお客さんに少しでも安く美味しいものを食べてもらいたい」という人間の優しい心からきているのではないでしょうか。

 

お店の人間ではないので詳しいことはわかりませんが、とにかく食べログ3.8以上の店よりは、食べログ3.6の店の方か美味しい可能性が高いと考えました。

 

 

やったこと

・食べログの評価3.60~3.69の店を取得する

・場所は東京都。理由は僕が東京在住なので。

 ※〇〇県もやって!という反応があれば随時対応していきます。

・ジャンルはラーメン(ラーメンが好きなので)。あと蕎麦(後輩が蕎麦好きなので)

 

環境

・Python3.6

・BeautifulSoup4

・Mac

・うまいラーメンにありつきたいという強い意思 

 

結果発表

 

まずは気になっていると思うので、結果を貼り付けます。

GoogleMapの共有機能を使っています。

 

drive.google.com

 

皆さんの近くの店に食べログ3.6はあったでしょうか。多分美味しいのでぜひ行ってみてください。

あと、〇〇県でもやってくれ! ラーメン、蕎麦以外もやってくれ! ってのがありましたらコメントとかお願いします。

 

とりあえず記事は以上です。こっから下は、技術的な感じになるので見ないで大丈夫です。

 

 

食べログ3.6の作り方 

 

コード.gs

// xxx部分をChannel Access Token => ISSUE で発行された文字列に置き換える
var channel_access_token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; 

function doPost(e) {
  var posted_json = JSON.parse(e.postData.contents);
  var events = posted_json.events;
   //送られたLINEメッセージを取得
  var json = JSON.parse(e.postData.contents);
  var user_message = json.events[0].message.text;  

  //送られて来た種類
  var receive_message_type = json.events[0].message.type;
  events.forEach(function(event) {
   //発言A ArrayMessage[i]とArrayResponse[i]は対になるように登録してね
    var ArrayMessage = ['明日が本番だから','酔ってると八方美人やめれる','焼きあごラーメン','秋の四角形みたいなやつが同時に見えた','星めっちゃ見た','なにたべるかまじでまよう','とりあえずこたつ買って電車乗っとこ','ちょっと盛った','夏終わる','しおり作りたい','くそかわいい','東京湿度高すぎ','帰ってる','後でまた連絡するね','まじ寝坊しないか不安','7時集合','美味しすぎた','たのしい','とおくね?','1分くらい待って','でんわしよ','いま家?','どへんたいだな','気が向いたらもってくる','でんぱくそわるい','こんどいこ','秋の四角形みたいなやつが同時に見えた'];

   //発言B  発言Aと発言Bは好きな会話を入れてね。ここにあるのは筆者と筆者の友人の会話の一部だよ
    var ArrayResponse = ['気合い入れないと','常に酔ってれば勝てる','たべたい','秋の四辺形わかるの強すぎ','羨ましすぎ','ラーメンが肉か定めよう','そして夜行バス','盛りすぎ','夏らしいことやってないわ','旅行の時しおり作る派だわ','かわいい','まじで暑い','おつ','まってる','モニコするわ','早い','うらやま','いいなあ','遠いわ','まつわ','おけまる水産','家なう','ばれた','#持ってこなそう','無線LAN繋がらなさそう','いこ','秋の四辺形わかるの強すぎ'];

     //レーベンシュタイン距離 
    //ユーザーが入力したテキストと、ArrayMessageのレーベンシュタイン距離を比較。最も小さいものの返答を出力
    var minlevenshtein  = levenshtein(user_message, ArrayMessage[0]);
    var min_i = 0;

    for(var i=1;i<ArrayMessage.length;i++){
      if (levenshtein(user_message, ArrayMessage[i]) < minlevenshtein){
        minlevenshtein = levenshtein(user_message, ArrayMessage[i]); //最小のレーベンシュタイン距離更新
        min_i = i;
      }
    }
     var postData = {
      "replyToken" :event.replyToken,
      "messages" : [
        {
          "type" : "text",
          "text" :  ArrayResponse[min_i]
                         }
      ]
    };
    var options = {
      "method" : "post",
      "headers" : {
        "Content-Type" : "application/json",
        "Authorization" : "Bearer " + channel_access_token
      },
      "payload" : JSON.stringify(postData)
    };
    var reply = UrlFetchApp.fetch("https://api.line.me/v2/bot/message/reply", options);
  });
};

//レーベンシュタイン距離
function levenshtein (s1, s2) {
    // http://kevin.vanzonneveld.net
    // +            original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
    // +            bugfixed by: Onno Marsman
    // +             revised by: Andrea Giammarchi (http://webreflection.blogspot.com)
    // + reimplemented by: Brett Zamir (http://brett-zamir.me)
    // + reimplemented by: Alexander M Beedie
    // *                example 1: levenshtein('Kevin van Zonneveld', 'Kevin van Sommeveld');
    // *                returns 1: 3

    if (s1 == s2) {
        return 0;
    }

    var s1_len = s1.length;
    var s2_len = s2.length;
    if (s1_len === 0) {
      return s2_len;
    }

    if (s2_len === 0) {
        return s1_len;
    }

    // BEGIN STATIC
    var split = false;
    try{
        split=!('0')[0];
    } catch (e){
        split=true; // Earlier IE may not support access by string index
    }

    // END STATIC
    if (split){
        s1 = s1.split('');
        s2 = s2.split('');
    }

    var v0 = new Array(s1_len+1);
    var v1 = new Array(s1_len+1);
    var s1_idx=0, s2_idx=0, cost=0;

    for (s1_idx=0; s1_idx<s1_len+1; s1_idx++) {
       v0[s1_idx] = s1_idx;
    }

    var char_s1='', char_s2='';
    for (s2_idx=1; s2_idx<=s2_len; s2_idx++) {
        v1[0] = s2_idx;
        char_s2 = s2[s2_idx - 1];

        for (s1_idx=0; s1_idx<s1_len;s1_idx++) {
            char_s1 = s1[s1_idx];
            cost = (char_s1 == char_s2) ? 0 : 1;
            var m_min = v0[s1_idx+1] + 1;
            var b = v1[s1_idx] + 1;
            var c = v0[s1_idx] + cost;

            if (b < m_min) {
                m_min = b; }
            if (c < m_min) {
               m_min = c; }
            v1[s1_idx+1] = m_min;
        }

        var v_tmp = v0;
        v0 = v1;
        v1 = v_tmp;
    }

    return v0[s1_len];

}

上記のコードのうち、2行目のトークンを書き換えてください。最低限それで動きます。

ソース上の方にある鬼みたいな配列は、発言Aと発言Bです。最終的には6000アイテムくらい入れる予定なので、配列の要素は外付けで持つように拡張する予定です(このままベタで書いてもソースが汚いだけで普通に動きます)

なお、スタンプと画像が送られて来た際の処理は未実装です。(テキスト以外が送られた処理は非常に面倒っぽかったので)

 

動かしてみた

f:id:Marimofmof:20180820214931p:plain

格納しているデータが少ないこともあり、怪しい箇所もありますがまあまあいい感じに返信してくれてます(返信しやすいようなメッセージを送ったのもありますが)

ちなみに、アイコンがキズナアイちゃんなのは「AI返信したかった(技術が足りなくてできなかった)」という悲しい事情からです。 

 

まとめ

 機械学習もAIも使わずにレーベンシュタイン距離でいい感じの返答ができるLINEBOTを作ることができました。

LINEでの発言をLINEBOTに使うって、なんかエモさがあって楽しかったです。

最後に、今回作成したアカウントを載せておくのでよかったら友達登録してね!!

 

f:id:Marimofmof:20180820215316j:image

 f:id:Marimofmof:20180820214935p:plain