自動で記事が更新される動画まとめサイトを今まで作ってきましたが、さらに運営に時間を掛けないようにカテゴリやタグも自動で付ける仕組みを考えてみたいと思います。この仕組みができれば、動画を視聴して内容に合ったカテゴリやタグを手作業で付ける必要がなくなり、1記事辺り数分の動画視聴の時間が節約できるので運営がかなり簡単になるハズです。
例えば、1日10記事更新して1記事辺り3分節約できるとすれば、月間で約15時間・年間で約182時間も運営に掛ける時間を節約する事ができます。それに毎日コツコツ続けるという面倒なルーチンワークからも解放されるので、精神的にとても楽になると思います。
僕は非常に面倒くさがりの上にコツコツ作業する事も苦手なので、運営に関わる作業は極力プログラムに頑張って貰って、自分は作業結果を監視するだけという環境を作りたいです。この目標を目指して考えたプログラムを今回も解説して行きたいと思います。
ワードプレスにおけるカテゴリとタグページの仕組み
カテゴリとタグを自動付与するには、まずワードプレス内部でカテゴリとタグがどんな仕組みで管理されているのかを調べなければいけません。少々複雑かもしれませんのでゆっくり読み進めてみて下さい。
僕がDIYプログラマーとしてWPをいじり始めた当初、記事に付与したカテゴリやタグは素直に記事テーブルの中に情報が格納されていると思っていたんですが、実は記事テーブルの中にはカテゴリやタグと紐づけるデータが入っていません。最初は戸惑いましたが、記事とカテゴリ・タグの関連は4つのテーブルを組み合わせて管理する仕組みになっています。
ざっくりまとめるとこんな感じです。
テーブル名 | 説明 |
---|---|
wp**_posts | 主に記事データが入ってる |
wp**_terms | 記事を分類するレコードが入ってる(通常はカテゴリとタグ) |
wp**_term_taxonomy | wp**_termsのレコードがカテゴリかタグかを管理 |
wp**_term_relationships | 記事IDとカテゴリ・タグIDを紐づけるレコードを管理 |
記事データを管理するテーブル・記事の分類を管理するテーブル・記事の分類がカテゴリかタグか管理するテーブル・記事と分類を紐づけるテーブルの4種類でもって記事とカテゴリの関連を管理しているんです。ちっとややこしいですよね。
記事レコードの生成と同時にカテゴリを付与する
通常エディタで記事を書く場合は「下書き保存」や「公開ボタン」を押した瞬間に、記事とカテゴリの紐づけレコードが追加されるんですが、今回は外部から直接記事テーブルにデータを挿入しているため、このままでは紐づけデータが作成されません。なので、この紐づけデータを「wp**_term_relationships」に追加するようにプログラムを書いてみましょう。
紐づけデータはYouTube APIの動画データから記事を生成したのと同じように、PHPとMySQLで外部から「wp**_term_relationships」にレコードを挿入することができます。プログラム的にはすでに説明したソースコードの流用し多少修正するだけで作ることが可能です。外部から記事テーブルにレコードを挿入するパターンはこちらの記事で解説してますので読んでみて下さい。
今回は紐づけデータのみを作るプログラムなので、振り分け先のカテゴリやタグページは事前に手動で作成しておく必要があります。ワードプレスの「投稿 > カテゴリ or タグ」から普通に作るだけでOKです。
youtube APIの検索キーワードをカテゴリに使用する
今回作成している動画まとめサイトでは犬種毎にカテゴリを分ける予定なので、動画を収集するキーワードをそのままカテゴリ名として使用する事ができます。つまり、サンプルコードの「アメリカンコッカースパニエル」で検索してヒットした動画データはすべて「アメリカンコッカースパニエル」というカテゴリに入れちゃえばいいんです。
内部的なイメージは「wp**_terms」の中から検索キーワードと一致するレコードを探してterm_idを取得し、さらにterm_idをキーにして「wp**_term_taxonomy」からterm_taxonomy_idを取得、動画データを「wp**_posts」に追加した際に自動付与される記事レコードのIDをlastInsertId();で取得して、term_taxonomy_idとセットで「wp**_term_relationships」に挿入するって感じでできると思います。
ホントにややこしい。。。
カテゴリを外部から付与するサンプルソースコード
仕様の話をすると上のようにややこしくなるのでとりあえず手を動かしてプログラムを書いてみましょう、トライ&エラーで完成に近づけていけばいいんです。カテゴリを付与するには記事データのIDも必要になるので、今まで作ってきたYouTube APIの動画データを記事化するサンプルコードに肉付けする形でこんな風に書いてみました。
<?php
include(dirname(__FILE__) . '/functions.php');
try {
$db = new PDO('mysql:host=localhost;dbname=DB名;charset=utf8', 'DBのユーザー名', 'DBのパスワード');
} catch(PDOException $e) {
die('エラーメッセージ:'.$e->getMessage());
}
$stt = $db->prepare("INSERT IGNORE INTO wp**_posts ( post_author, post_date, post_date_gmt, post_content, post_title, movie_tags, post_status, post_modified, post_modified_gmt, movie_id ) VALUES ( :post_author, :post_date, :post_date_gmt, :post_content, :post_title, :movie_tags, :post_status, :post_modified, :post_modified_gmt, :movie_id )");
$dog_category = array('アメリカンコッカースパニエル', 'コーギー', 'ゴールデンレトリバー');
foreach($dog_category as $value){
$option = array(
//apiキー
'key' => 'APIキー',
//必須パラメータ:id or snippet
'part' => 'snippet',
//フィルタ:最大1個まで
'forContentOwner' => null,
'forMine' => null,
'relatedToVideoId' => null,
//省略可能なパラメータ
'channelId' => null,
'channelType' => null,
'eventType' => null,
'maxResults' => '2',
'onBehalfOfContentOwner' => null,
'order' => 'relevance',
'pageToken' => null,
'publishedAfter' => null,
'publishedBefore' => null,
'q' => $value,
'regionCode' => 'JP',
'safeSearch' => null,
'topicId' => null,
'type' => 'video',
'videoCaption' => null,
'videoCategoryId' => '15',
'videoDefinition' => null,
'videoDimension' => null,
'videoDuration' => null,
'videoEmbeddable' => null,
'videoLicense' => null,
'videoSyndicated' => null,
'videoType' => null
);
$search_url = "https://www.googleapis.com/youtube/v3/search?".http_build_query($option, '&');
$decode_search_url = array(htmlspecialchars_decode( $search_url ));
$api_content = youtube_api_get($decode_search_url);
print_r($api_content);
$video_count = $api_content[0]["pageInfo"]["resultsPerPage"];
for ($j = 0; $j < $video_count; $j++) {
$video_option = array(
//apiキー
'key' => 'APIキー',
//フィルタ
'chart' => null,
'id' => $api_content[0]["items"][$j]["id"]["videoId"],
'myRating' => null,
//任意のフィルタ
'maxResults' => null,
'onBehalfOfContentOwner' => null,
'nextPageToken' => null,
'regionCode' => null,
'videoCategoryId' => null,
//指定可能のレスポンス:
'part' => 'snippet,contentDetails,statistics,player,topicDetails,recordingDetails'
);
$video_url = "https://www.googleapis.com/youtube/v3/videos?".http_build_query($video_option, 'a', '&');
$decode_video_url = array(htmlspecialchars_decode( $video_url ));
$video_api_content = youtube_api_get($decode_video_url);
print_r($video_api_content);
$stt->bindValue(':post_author', 1);
$stt->bindValue(':post_date', date("Y/m/d"));
$stt->bindValue(':post_date_gmt', date("Y/m/d"));
$stt->bindValue(':post_content', $video_api_content[0]["items"][0]["snippet"]["description"]);
$stt->bindValue(':post_title', $api_content[0]["items"][$j]["snippet"]["title"]);
$tag_count = count($video_api_content[0]["items"][0]["snippet"]["tags"]);
$tags = null;
for ($tc = 0; $tc < $tag_count; $tc++) {
$tags .= $video_api_content[0]["items"][0]["snippet"]["tags"][$tc].",";
} // for $tc
$stt->bindValue(':movie_tags', $tags);
$stt->bindValue(':post_status', 'draft');
$stt->bindValue(':post_modified', date("Y/m/d"));
$stt->bindValue(':post_modified_gmt', date("Y/m/d"));
$stt->bindValue(':movie_id', $api_content[0]["items"][$j]["id"]["videoId"]);
$stt->execute();
//カテゴリの付与
$post_id = $db->lastInsertId('ID');
$stt1 = $db->prepare("SELECT * FROM wp**_terms WHERE name = '{$value}' ");
$stt1->execute();
$row1 = $stt1->fetch();
$stt2 = $db->prepare("SELECT * FROM wp**_term_taxonomy WHERE term_id = '{$row1['term_id']}' ");
$stt2->execute();
$row2 = $stt2->fetch();
$stt3 = $db->prepare("INSERT IGNORE INTO wp**_term_relationships ( object_id, term_taxonomy_id ) VALUES ( :object_id, :term_taxonomy_id )");
$stt3->bindValue(':object_id', $post_id);
$stt3->bindValue(':term_taxonomy_id', $row2['term_taxonomy_id']);
$stt3->execute();
} // for $j
}
?>
113-127行目を追記しています、細かくみていきましょう。
記事とカテゴリを関連付けるためには、「wp**_posts > ID」と「wp**_term_taxonomy > term_taxonomy_id」が必要です。これらを取得するにはどうすればいいか逆算する感じで辿っていきます。
追記直前の111行目で動画データをwp**_postsに挿入し「wp**_posts > ID」が自動で付与されています。この新しく追加された記事レコードのIDを調べるには、直近に追加されたレコードの値を返す lastInsertId();というPDOメソッドがあります。こいつを使ってIDを調べているのが114行目です。
そして該当カテゴリの「term_taxonomy_id」を調べるためには「term_id」が必要なので、116-118行目でカテゴリレコードが入っている「wp**_terms」テーブルから検索キーワードと一致するレコードの「term_id」を取得しています。
120-122行目で取得した「term_id」と一致するレコードを「wp**_term_taxonomy」から探す事で、ようやく「term_taxonomy_id」までたどり着きました。
後は124-127行目で記事レコードのIDとカテゴリのIDをセットにして「wp**_term_relationships」にレコードを挿入して、ようやくカテゴリの自動付与プログラムの完成です。処理実行後、ワードプレスの投稿から記事を確認しカテゴリが付いていれば成功です。こんな感じになっているハズです。
これで動画まとめサイトの自動化がさらに進みました。次は動画情報内のキーワードから動画の内容を判別し、付与するタグを変えるプログラムを書いてみたいと思います。こっちの仕組みの方が汎用的に使用できるかもしれません。
記事レコードの生成と同時にタグを付与する
仕組み的には上記のカテゴリの付与と同じで、事前に必要なタグをワードプレスのエディタから作成しておいて下さい。ちなみにカテゴリもタグもプログラムで自動付与した後でも、記事エディタから普通に手作業で修正する事もできます。
ではカテゴリと同じ様にサンプルソースコードに肉付けする形で、タグを自動付与するプログラムを書いてみたいと思います。
キーワードを検出してタグを自動で判定する
タグ自動付与の具体的なイメージは例えば「可愛い」というタグを作っておいて、タイトル・動画説明・動画タグに可愛いを連想させるキーワード(可愛い・かわいい・カワイイ・キュート・プリティーなど)が含まれていた場合、該当の記事と「可愛い」タグの紐づけるレコードを「wp**_term_relationships」に挿入するようにしたいです。
動画内容を判断する文章は「wp**_posts」テーブルの「post_content」「post_title」「movie_tags」に格納してあるので、この3つのカラムの中身をif関数とpreg_match関数を使ってキーワードの有無を判定すれば、タグの振り分けが出来そうです。
今回は犬にちなんで「おもしろいぬ」「かしこいぬ」「かわいいぬ」「すごいぬ」「ねむいぬ」の5種類のタグを動画内容に応じて付けようと思います。検索するキーワードと振り分けるタグをまとめるとこんな感じにしてみました。
動画データに含まれるキーワード | 振り分けるタグ |
---|---|
笑,面白,おもしろ,楽しい, | おもしろいぬ |
しつけ,頭いい,芸,かしこい,賢い, | かしこいぬ |
可愛い,かわいい,カワイイ,キュート,プリティ,癒し,和む, | かわいいぬ |
超,すごい,凄い, | すごいぬ |
眠,寝,ねむい, | ねむいぬ |
後はどの程度タグが付くか実際にプログラムを動かして調整していきます。思ったようにタグが付かなければ、検索するキーワードや付与するタグを増やしてみましょう。この辺はトライ&エラーを繰り返して精度を高めて行くのが良いと思います。
タグを外部から付与するサンプルソースコード
では上記の仕様でプログラムをサンプルソースコードに追記して行きます。こんな感じで書いてみました。
<?php
include(dirname(__FILE__) . '/functions.php');
try {
$db = new PDO('mysql:host=localhost;dbname=DB名;charset=utf8', 'DBのユーザー名', 'DBのパスワード');
} catch(PDOException $e) {
die('エラーメッセージ:'.$e->getMessage());
}
$stt = $db->prepare("INSERT IGNORE INTO wp**_posts ( post_author, post_date, post_date_gmt, post_content, post_title, movie_tags, post_status, post_modified, post_modified_gmt, movie_id ) VALUES ( :post_author, :post_date, :post_date_gmt, :post_content, :post_title, :movie_tags, :post_status, :post_modified, :post_modified_gmt, :movie_id )");
$dog_category = array('アメリカンコッカースパニエル', 'コーギー', 'ゴールデンレトリバー');
foreach($dog_category as $value){
$option = array(
//apiキー
'key' => 'APIキー',
//必須パラメータ:id or snippet
'part' => 'snippet',
//フィルタ:最大1個まで
'forContentOwner' => null,
'forMine' => null,
'relatedToVideoId' => null,
//省略可能なパラメータ
'channelId' => null,
'channelType' => null,
'eventType' => null,
'maxResults' => '1',
'onBehalfOfContentOwner' => null,
'order' => 'relevance',
'pageToken' => null,
'publishedAfter' => null,
'publishedBefore' => null,
'q' => $value,
'regionCode' => 'JP',
'safeSearch' => null,
'topicId' => null,
'type' => 'video',
'videoCaption' => null,
'videoCategoryId' => '15',
'videoDefinition' => null,
'videoDimension' => null,
'videoDuration' => null,
'videoEmbeddable' => null,
'videoLicense' => null,
'videoSyndicated' => null,
'videoType' => null
);
$search_url = "https://www.googleapis.com/youtube/v3/search?".http_build_query($option, '&');
$decode_search_url = array(htmlspecialchars_decode( $search_url ));
$api_content = youtube_api_get($decode_search_url);
//print_r($api_content);
$video_count = $api_content[0]["pageInfo"]["resultsPerPage"];
for ($j = 0; $j < $video_count; $j++) {
$video_option = array(
//apiキー
'key' => 'APIキー',
//フィルタ
'chart' => null,
'id' => $api_content[0]["items"][$j]["id"]["videoId"],
'myRating' => null,
//任意のフィルタ
'maxResults' => null,
'onBehalfOfContentOwner' => null,
'nextPageToken' => null,
'regionCode' => null,
'videoCategoryId' => null,
//指定可能のレスポンス:
'part' => 'snippet,contentDetails,statistics,player,topicDetails,recordingDetails'
);
$video_url = "https://www.googleapis.com/youtube/v3/videos?".http_build_query($video_option, 'a', '&');
$decode_video_url = array(htmlspecialchars_decode( $video_url ));
$video_api_content = youtube_api_get($decode_video_url);
//print_r($video_api_content);
$stt->bindValue(':post_author', 1);
$stt->bindValue(':post_date', date("Y/m/d"));
$stt->bindValue(':post_date_gmt', date("Y/m/d"));
$stt->bindValue(':post_content', $video_api_content[0]["items"][0]["snippet"]["description"]);
$stt->bindValue(':post_title', $api_content[0]["items"][$j]["snippet"]["title"]);
$tag_count = count($video_api_content[0]["items"][0]["snippet"]["tags"]);
$tags = null;
for ($tc = 0; $tc < $tag_count; $tc++) {
$tags .= $video_api_content[0]["items"][0]["snippet"]["tags"][$tc].",";
} // for $tc
$stt->bindValue(':movie_tags', $tags);
$stt->bindValue(':post_status', 'draft');
$stt->bindValue(':post_modified', date("Y/m/d"));
$stt->bindValue(':post_modified_gmt', date("Y/m/d"));
$stt->bindValue(':movie_id', $api_content[0]["items"][$j]["id"]["videoId"]);
$stt->execute();
//カテゴリの付与
$post_id = $db->lastInsertId('ID');
$stt1 = $db->prepare("SELECT * FROM wp**_terms WHERE name = '{$value}' ");
$stt1->execute();
$row1 = $stt1->fetch();
$stt2 = $db->prepare("SELECT * FROM wp**_term_taxonomy WHERE term_id = '{$row1['term_id']}' ");
$stt2->execute();
$row2 = $stt2->fetch();
$stt3 = $db->prepare("INSERT IGNORE INTO wp**_term_relationships ( object_id, term_taxonomy_id ) VALUES ( :object_id, :term_taxonomy_id )");
$stt3->bindValue(':object_id', $post_id);
$stt3->bindValue(':term_taxonomy_id', $row2['term_taxonomy_id']);
$stt3->execute();
//タグの付与
unset($post_tags);
$post_tags = array();
if(preg_match('/(笑)|(面白)|(おもしろ)|(楽しい)/', $video_api_content[0]["items"][0]["snippet"]["description"])){array_push($post_tags, "おもしろいぬ");}
if(preg_match('/(しつけ)|(頭いい)|(芸)|(かしこい)|(賢い)/', $video_api_content[0]["items"][0]["snippet"]["description"])){array_push($post_tags, "かしこいぬ");}
if(preg_match('/(可愛い)|(かわいい)|(カワイイ)|(キュート)|(プリティ)|(癒し)|(和む)/', $video_api_content[0]["items"][0]["snippet"]["description"])){array_push($post_tags, "かわいいぬ");}
if(preg_match('/(超)|(凄い)|(すごい)/', $video_api_content[0]["items"][0]["snippet"]["description"])){array_push($post_tags, "すごいぬ");}
if(preg_match('/(眠)|(寝)|(ねむい)/', $video_api_content[0]["items"][0]["snippet"]["description"])){array_push($post_tags, "ねむいぬ");}
if(preg_match('/(笑)|(面白)|(おもしろ)|(楽しい)/', $api_content[0]["items"][$j]["snippet"]["title"])){array_push($post_tags, "おもしろいぬ");}
if(preg_match('/(しつけ)|(頭いい)|(芸)|(かしこい)|(賢い)/', $api_content[0]["items"][$j]["snippet"]["title"])){array_push($post_tags, "かしこいぬ");}
if(preg_match('/(可愛い)|(かわいい)|(カワイイ)|(キュート)|(プリティ)|(癒し)|(和む)/', $api_content[0]["items"][$j]["snippet"]["title"])){array_push($post_tags, "かわいいぬ");}
if(preg_match('/(超)|(凄い)|(すごい)/', $api_content[0]["items"][$j]["snippet"]["title"])){array_push($post_tags, "すごいぬ");}
if(preg_match('/(眠)|(寝)|(ねむい)/', $api_content[0]["items"][$j]["snippet"]["title"])){array_push($post_tags, "ねむいぬ");}
if(preg_match('/(笑)|(面白)|(おもしろ)|(楽しい)/', $tags)){array_push($post_tags, "おもしろいぬ");}
if(preg_match('/(しつけ)|(頭いい)|(芸)|(かしこい)|(賢い)/', $tags)){array_push($post_tags, "かしこいぬ");}
if(preg_match('/(可愛い)|(かわいい)|(カワイイ)|(キュート)|(プリティ)|(癒し)|(和む)/', $tags)){array_push($post_tags, "かわいいぬ");}
if(preg_match('/(超)|(凄い)|(すごい)/', $tags)){array_push($post_tags, "すごいぬ");}
if(preg_match('/(眠)|(寝)|(ねむい)/', $tags)){array_push($post_tags, "ねむいぬ");}
$unique_post_tags = array_values(array_unique($post_tags));
$tags_count = count($unique_post_tags);
for ($j = 0; $j < $tags_count; $j++) {
$stt4 = $db->prepare("SELECT * FROM wp**_terms WHERE name = '{$unique_post_tags[$j]}' ");
$stt4->execute();
$row4 = $stt4->fetch();
$stt5 = $db->prepare("SELECT * FROM wp**_term_taxonomy WHERE term_id = '{$row4['term_id']}' ");
$stt5->execute();
$row5 = $stt5->fetch();
$stt6 = $db->prepare("INSERT IGNORE INTO wp**_term_relationships ( object_id, term_taxonomy_id ) VALUES ( :object_id, :term_taxonomy_id )");
$stt6->bindValue(':object_id', $post_id);
$stt6->bindValue(':term_taxonomy_id', $row5['term_taxonomy_id']);
$stt6->execute();
}
} // for $j
}
?>
タグを自動付与するために129-167行目を追加しました。追記の内容をまとめるとこんな感じです。
- 133-137行目:動画説明に指定のキーワードが含まれていた場合は対応するタグを$post_tagsに追加
- 139-143行目:タイトルに指定のキーワードが含まれていた場合は対応するタグを$post_tagsに追加
- 145-149行目:動画タグに指定のキーワードが含まれていた場合は対応するタグを$post_tagsに追加
- 151行目:$post_tags内の重複した値を削除
- 152-169行目:タグの数だけ紐づけデータの挿入処理を繰り返す
このプログラムを実行した後にワードプレスの投稿一覧を確認し、動画データから作成した記事にタグが付いていれば成功です。
ずいぶん長くなってしまいましたが、これでyoutube APIから取得した動画データを記事化した際に、カテゴリとタグも自動で付与する事ができました。上の方でも書きましたが、後は実際にいくつか記事データを作成して、タグが付与される頻度から検索キーワードやタグの量を調整してみて下さい。
まとめ&次回予告
カテゴリとタグの自動付与完成しましたね。どの仕組みでやっても100%の精度は不可能だと思うので、やっぱり最終的にはカテゴリやタグが間違って付与されていないか人間の目でチェックする必要があると思います。でもそのチェックは投稿された記事をさらりと眺めればできる事なので、カテゴリとタグを自動で付けてくれるメリットと比べれば十分に釣り合うコストだと思ってます。
次回は記事のアイキャッチ設定も自動化してみようと思います。普通に手作業でやるならYouTubeから動画サムネイルをダウンロードし、ワードプレスにアップロードしてから投稿エディタで記事のアイキャッチに設定、といった手順になると思いますが、今回カテゴリとタグを自動で付けたように、動画サムネイルを記事のアイキャッチに自動で設定するプログラムを書いてみたいと思います。
それでは次回もお楽しみに(‘ω’)ノ
分からない事や依頼があれば受け付けてますんでお問い合わせからご連絡下さいm(_ _)m
youtubeの動画を自動で投稿するwpプラグイン完成!
当サイトで解説しているyoutube動画まとめサイト作成のノウハウを元に、誰でも簡単に動画まとめサイトが作れるwpプラグインを作りました、使用感は下記の動画をご覧ください!
こんな感じで簡単な設定だけで記事が自動投稿される動画まとめサイトを作れます、youtunerの詳しい解説は下記の記事で解説しています。
個人開発プログラマーを応援するメンバーシップを始めました('ω')ノ
質問・要望・共同作業など、みんなのやりたい事をスマイルがお手伝いします。立ち上げたばかりでよく分かってないので、とりあえず何でもありやってみます。
コメント