フランスの最も美しい村 twitter botをphpで作りました

既に私をTwitterでフォローしてくださっている方はご存知なのですが、先月からこれまで投稿した「フランスの最も美しい村」関連の投稿をランダムでツイートするTwitter botが稼働しております。

今回は簡単にこれをどのように作ったのかを書いてみます。以下、少々技術的な話となります。といっても私はエンジニアではないのでその道の人がみたら「なっていない」書き方かもしれませんがそれはご容赦ください。また、当ブログはWordPressを利用しています。その関係でphpを使ってボットを作っています。

どうして自作したのか

程よいWordPressのプラグインがなかったからです。多くのプラグインでは「特定のカテゴリーを選ぶ」ではなく「特定のカテゴリーを除く」という仕様が多かったからです。また、サムネイル画像をツイートするプラグインもなかったがなかったのも一因です。せっかくなので勉強を兼ねて自作してみることにしました。

基本的な流れ

作成の基本的な流れは以下のとおり。

  1. Twitter Application Management より、新しいアプリを作成
  2. Twitter Application Management より、作成したアプリの各種トークンを取得
  3. TwitterOAuthライブラリーをダウンロード
  4. WordPressのデータベース(MySQL)から直接データ(タイトルとそのアイキャッチ画像)を取得するsqlを書く
  5. ツイートを行うためのphpを書く
  6. レンタルサーバーのcronを設定する

1. Twitter Application Management より新しいアプリを作成

初めにTwitter Application Managementに行き、“Create New App”のボタンを押して新しいアプリを作成します。指示に従えば簡単に手続きが完了し、トップページの「Twitter Apps」に追加されたアプリが表示されるはずです。

2. Twitter Application Management よりアプリの各種トークンを取得

続いて作成したアプリの各種トークン」を取得します。トップ画面から作成したアプリを選択、“Keys and Access Tokens”というタブがあるのでそれを選択。

以下の情報が格納されているので記録しておきます。

$consumer_key = "######################";
$consumer_secret = "###########################################";
$access_token = "########-#####################################";
$access_token_secret = "########################################";

なお“Permission”は、書き込みができるタイプ(Read and Write / Read, Write and Access direct messages)を選んでおきましょう。

3. TwitterOAuthライブラリーをダウンロード

こちらからTwitterOAuthライブラリーをダウンロードして適当な名前のフォルダ名(僕は“twitteroauth”にしました)で保存します。

4. WordPressのデータベースから直接データを取得するsqlを書く

WordPressのデータベースから「フランスの最も美しい村」に属するカテゴリーを取得します。僕の場合はダイレクトにWordPressのデータベース(MySQL)からsqlで直接投稿データ(タイトル、URL、アイキャッチ画像)を取得する方法を選びました。

サーバー環境にもよるのかもしれませんが、WordPressがインストールされているフォルダーにボット用phpを置いていないせいなのか、もともとWordPress内で定義されている関数(query_postsやget_post_thumbnail_idなど)がうまく機能しなかったからです。これを行うためには、WordPressのデータベースの構造を知らなければなりません。

僕は次の方法で投稿データを取得することにしました。

4-1. wp_term_taxonomyからカテゴリーIDとタクソノミーIDを照合
4-2. wp_term_relationshipsから特定のタクソノミーIDからランダムに1つのレコードを取得
4-3. wp_postmetaからアイキャッチ画像のIDを取得
4-4. wp_postsから抽出した記事の投稿タイトルとURLを取得
4-5. wp_postsから抽出した記事のアイキャッチ画像のURLを取得

以下、ひとつひとつ見ていくことにしましょう。

4–1. wp_term_taxonomyからカテゴリーIDとタクソノミーIDを照合

まず下準備として、WordPressのダッシュボードなどでTweetの対象となるカテゴリーNoを取得します。僕のフランスの最も美しい村の例ではcat = 696となります。なお「カテゴリーID」はURLからも調べられます。“https://www.yuu-koma.jp/?cat=696”←このようになっていますよね。

そして、この次からは具体的にWordPressのデータベースを直に見る必要が出てきます。phpmyadminもしくはsshなどでデータベースにアクセスしましょう。僕はsshでアクセスしています。

さて、まずここでやっかいなのは、WordPressのデータベースから特定のカテゴリーの投稿をピックアップするためには、ダイレクトに「カテゴリーID」を指定するのではなく、そのカテゴリーの上位概念である「タクソノミーID」を指定しなければならないことです。なお、タクソノミーおよびカテゴリーの関係がどのようなものかはここでは説明を省略します。

下準備で調べた「カテゴリーID」とそれに対応する「タクソノミーID」の関係は、「wp_term_taxonomy」というテーブルで調べることができます。このテーブルでは、

  • カテゴリーID: term_id
  • タクソノミーID: term_taxonomy_id

に対応します。ということで、term_taxonomy_idを調べるには、次のようなsqlを実行してみればOKです。(以下の例ではterm_id=696とします。)

mysql> select * from wp_term_taxonomy where term_id=696;
+------------------+---------+----------+-------------+--------+-------+
| term_taxonomy_id | term_id | taxonomy | description | parent | count |
+------------------+---------+----------+-------------+--------+-------+
|              739 |     696 | category |             |    324 |   102 | 
+------------------+---------+----------+-------------+--------+-------+
1 row in set (0.00 sec)

2コラム(フィールド)目がカテゴリーID: term_idなので、対応するタクソノミーID: term_taxonomy_id=739となります。

4–2. 特定のタクソノミーIDからランダムに1つのレコードを取得

続いて、「wp_term_relationships」でterm_taxonomy_id=739を抽出。これでterm_taxonomy_id=739に属する投稿記事IDを取得することができます。どうせ1つしか使わないので、この段階でrandomに取得して一番上のみを取ってくることにしました。

mysql> select object_id from wp_term_relationships where term_taxonomy_id=739 order by rand() limit 1;
+-----------+
| object_id |
+-----------+
|      7896 | 
+-----------+
1 row in set (0.00 sec)

このobject_idが投稿のIDとなります。以後、post_id=7896として話をすすめていきます。

4–3. アイキャッチ画像のIDを取得

アイキャッチ画像を取得するのは少々やっかいでした。まず上で取得した投稿ID(この例ではpost_id=7896)に対応するアイキャッチ画像のID: _thumbnail_id」を「wp_postmeta」から取得する必要があります。

mysql> select meta_value from wp_postmeta where meta_key='_thumbnail_id' and post_id=7896;
+------------+
| meta_value |
+------------+
| 9789       | 
+------------+
1 row in set (0.00 sec)

このmeta_valueがアイキャッチ画像のID(ここではmeta_value=9789)に対応します。とりあえず控えておきます。

詳細は前述のデータベース構造のサイトを見ていただくとして、その仕組をざっくり言いますと、一つの投稿にはいろいろな情報、すなわち投稿内容はもちろんのこと、投稿された時間、投稿した人、カテゴリーやタグ、そしてアイキャッチ画像情報、が含まれています。つまり一つの投稿IDにはいろいろな情報(より上位の概念としてはカスタムフィールドと呼ばれています。)に関するIDが紐付いているわけです。それを管理しているテーブルが「wp_postmeta」、投稿ID(post_id)に関連する情報の属性を格納しているフィールドが「meta_key」、そしてこの中でアイキャッチ画像に該当するのが「meta_key=’_thumbnail_id’」というわけです。

4–4. wp_postsからツイートで必要なものをピックアップ

ここまでくれば後はシンプル。投稿に関するデータを格納しているwp_postsから必要なものをピックアップするだけです。フィールド名(ここではpost_id=IDとなっています。)に注意して以下のようなsqlを実行。

mysql> select post_title, guid from wp_posts where ID=7896;
+-----------------------------------------------------------------------------+--------------------------------+
| post_title                                                                  | guid                           |
+-----------------------------------------------------------------------------+--------------------------------+
| Riquewihr(リクヴィール)- フランスで最も美しい村巡り2011 No.44 -★★★★☆ | http://www.yuu-koma.jp/?p=7896 | 
+-----------------------------------------------------------------------------+--------------------------------+
1 row in set (0.00 sec)

これでタイトルとURLが取得できます。なお、僕の場合はMySQLデータベースのエンコードをShift-JISに指定ため、エンコードを変換しなければいけないという問題が生じています。UTF–8でデータベースを構築していれば問題はないはずです。WordPressを始めた当初、こうした知識がなくて未だに苦労しています。実際エンコード(文字化け)の問題を解決するのに苦労しました。(解決方法は後述するphpを御覧ください。)

また2015年秋からSSL対応(https)をしたので、httpをhttpsに変換するという処理もしています。(こちらも後述のphpをご参照)

4–5. wp_postsから抽出した記事のアイキャッチ画像のURLを取得

最後にアイキャッチ画像のURLを取得します。このIDは投稿IDではなくアイキャッチ画像のID(ここではmeta_value=9789)を使います。

mysql> select guid from wp_posts where ID=9789;
+---------------------------------------------------------------------------------+
| guid                                                                            |
+---------------------------------------------------------------------------------+
| https://www.yuu-koma.jp/wp/wp-content/uploads/2011/09/6123327639_8d2f4fd009.jpg | 
+---------------------------------------------------------------------------------+
1 row in set (0.00 sec)

WordPressのデータベースにアクセスして必要なものをピックアップするのはこれで終了です。

5. ツイートを行うためのphpを書く

次のようなphpを書きました。オマケ処理として、冒頭のメッセージはランダムに、ハッシュタグ(#フランス)をつけるなどしました。

なお、phpでMySQLに接続する方法はphpのバージョンによって異なりますので、各自の環境に応じて適宜変更してください。僕はphp5.5です。(多分古いらしいです…)

<?php
    // MySQLに接続
    require("dbinfo.php");

    $con=mysql_connect($sv, $uid, $pwd);
    if (!$con) {
        die('Not connected : ' . mysql_error());
    }
    $db_selected = mysql_select_db($db, $con);
    if (!$db_selected) {
        die ('Can\'t use db : ' . mysql_error());
    }
    mysql_select_db($db, $con);
    
    // term_taxonomy_idを指定して、post_idをランダムに1つ取得
    $sql = "select object_id from wp_term_relationships where term_taxonomy_id=### order by rand() limit 1;";
    $result = mysql_query($sql);
    $row = mysql_fetch_assoc($result);
    
    $post_id=$row['object_id'];
    
    // アイキャッチ画像のIDを取得
    $sql = "select meta_value from wp_postmeta where meta_key='_thumbnail_id' ";
    $sql = $sql . "and post_id=" . $post_id . ";";
    $result = mysql_query($sql);
    $row = mysql_fetch_assoc($result);
    
    $thumbnail_id=$row['meta_value'];
    
    // wp_postsから抽出した記事の投稿タイトルとURLを取得
    $sql = "select post_title, guid from wp_posts ";
    $sql = $sql . "where ID=" . $post_id . ";";
    $result = mysql_query($sql);
    $row = mysql_fetch_assoc($result);
    
    // エンコード対策
    $post_title = $row['post_title'];
    $post_title = mb_convert_encoding($post_title, "utf-8");
    
    // http -> httpsに変換
    $post_link  = $row['guid'];
    $post_link=str_replace("http://", "https://", $post_link);
    
    // wp_postsから抽出した記事のアイキャッチ画像のURLを取得
    $sql = "select guid from wp_posts ";
    $sql = $sql . "where ID=" . $thumbnail_id . ";";
    $result = mysql_query($sql);
    $row = mysql_fetch_assoc($result);
    
    $eye_img = $row['guid'];
    
    // 画像付きテキスト用
    $text = array();
    $text[0] = "フランスの美しい村の投稿から:";
    $text[1] = "こんな村にご訪問はいかが?:";
    $text[2] = "コンプリートした美しい村をご紹介:";
    $text[3] = "村ごとに「色」が違うんです:";
    $text[4] = "ひとつひとつの訪問が財産になっています:";
    
    if(shuffle($text)) {
        $message = $text[0];
    }
    
    // OAuthスクリプトの読み込み
    require_once('twitteroauth/autoload.php');
    use Abraham\TwitterOAuth\TwitterOAuth;
    
    $consumer_key = "######################";
    $consumer_secret = "###########################################";
    $access_token = "########-#####################################";
    $access_token_secret = "########################################";
    
    // つぶやく
    $connection = new TwitterOAuth($consumer_key,$consumer_secret,$access_token,$access_token_secret);
    
    if($connection) {
        $content = $connection->get("account/verify_credentials");
        $message = $message . $post_title . " " . $post_link . " #フランス";
        $media = $connection->upload("media/upload",array('media' => $eye_img));
        
        $parameters = array(
                            'media_ids' => implode("," , array($media -> media_id_string)),
                            'status' => $message
                            );
        $result = $connection->post("statuses/update",$parameters);        
    }
?>

6. レンタルサーバーのcronを設定する

最後にレンタルサーバーのcronを設定します。path、設定方法はサーバー(レンタルサーバー)ごとに違いますので、適宜変更してください。以下は私が使っているエックスサーバー
での例です。

/usr/bin/php5.5 /home/path/villagebot.php

bot作成方法は以上となります。

TwitterOAuthの仕組みが正直よくわかっていないのですが、今のところ順調にbotは稼働しているようです。やはり写真付きのツイートのほうが評判はよいようです。ただし、sqlのrandom処理がどうもイマイチな気もします。乱数は突き詰めると難しいのですが、余裕があればさらに一工夫してみたいです。

なお、フランスの美しい村botの他に「世界遺産bot」も検討中です。乞うご期待。

参考にしたサイト

  • 自動ツイートのTwitter Botを自作してみた
  • プロフィール

    都内の会社に務める傍ら、休暇を利用して旅行をしたり音楽活動をしているビジネスマン。趣味は、旅行、音楽など。旅行はヨーロッパが中心、現地でレンタカーを借りて旅することにはまっています。フランスの最も美しい村全156箇所を完全制覇!音楽はクラシックが中心。ヴァイオリンの演奏もします。最近は健康のためにランニングを開始。マラソンも。Marathon du Médoc 2014含む数回のフルマラソンを完走しています。