💡

WordPressで自動タグ付けのバッチを実装する

2024/04/29に公開

WordPressのタグ付けは、数や人が増えると運用が難しくなるため、バッチによる自動タグ付けを検討する。

「Simple Tags(旧TaxoPress)」などプラグインによる自動タグ付けは便利だが、数が増えるとメモリ的に処理できなくなる。

ここでは

  • カウント50以上のタグで足切りすることでタグ数を絞る
  • WordPressをコマンドラインから実行できるWP-CLIでバッチ処理する

タグがcountで取得できない問題

WordPress標準のget_termsでは、count件数を抽出条件にできないため、件数が多いときは無駄が多い。

function get_relevant_tags() {
    $tags = get_terms(array(
        'taxonomy'   => 'post_tag',
        'orderby'    => 'count',
        'order'      => 'DESC'
    ));

    $relevant_tags = [];
    foreach ($tags as $tag) {
        if ($tag->count < 50) {
            break;
        }
        $relevant_tags[$tag->term_id] = $tag->name;
    }

    // id=>name な連想配列
    return $relevant_tags;
}

WordPressの自動タグ付け実装

  • get_relevant_tags(): SQLを使用して、カウントが50以上のタグのみを取得する
  • tag_post(): postタイトルか本文にタグ名を含んでいればタグ付けする
  • log_tagging_info(): タグ付けの情報をログファイルに記録する

タグ取得を直接SQLにした版。

<?php
/**
 * カウントが50以上のタグを取得する
 * @return array id=>nameな連想配列
 */
function get_relevant_tags() {
    global $wpdb;
    $query = "
    SELECT t.term_id, t.name
    FROM {$wpdb->terms} AS t
    JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id
    WHERE tt.taxonomy = 'post_tag' AND tt.count >= 50";

    $results = $wpdb->get_results($query, OBJECT);
    $tags = [];
    foreach ($results as $result) {
        $tags[$result->term_id] = $result->name;
    }

    echo "Number of tags retrieved: " . count($tags) . "\n";
    return $tags;
}

/**
 * 投稿にタグを付ける関数
 * @param WP_Post $post 投稿オブジェクト
 * @param array $relevant_tags id=>nameな連想配列
 * @return array 追加されたタグのリスト
 */
function tag_post($post, $relevant_tags) {
    $tags_added = [];
    $excluded_tags = ['NEWS', 'データ'];  // 除外するタグ名のリスト

    foreach ($relevant_tags as $tag_id => $tag_name) {
        if (in_array($tag_name, $excluded_tags)) {
            continue;
        }

        if (strpos($post->post_title, $tag_name) !== false || strpos($post->post_content, $tag_name) !== false) {
            wp_set_post_terms($post->ID, array($tag_id), 'post_tag', true);
            $tags_added[] = $tag_name;
        }
    }

    return $tags_added;
}

/**
 * タグ付け情報をログに記録する関数
 * @param int $post_id 投稿ID
 * @param string $post_title 投稿タイトル
 * @param array $tags_added 追加されたタグのリスト
 */
function log_tagging_info($post_id, $post_title, $tags_added) {
    $log_message = sprintf(
        "Post ID: %d, Title: \"%s\", Tags added: %s\n",
        $post_id,
        $post_title,
        implode(', ', $tags_added)
    );
    echo $log_message . "\n";
    file_put_contents('/path/to/your/log/file.log', $log_message, FILE_APPEND);
}

/**
 * 主処理ループ
 */
function process_posts() {
    $relevant_tags = get_relevant_tags();
    $offset = 0;
    $batch_size = 50;

    while (true) {
        $args = [
            'posts_per_page' => $batch_size,
            'offset'         => $offset,
            'post_type'      => ['post', 'page', 'your_custom_post_type'],  // 無指定ならpostのみ取得される
            'post_status'    => 'publish',
            'orderby'        => 'ID',
            'order'          => 'DESC'
        ];
        $query = new WP_Query($args);

        echo "Number of posts retrieved: " . $query->post_count . "\n";

        if (!$query->have_posts()) {
            echo "No more posts to process.\n";
            break;
        }

        foreach ($query->posts as $post) {
            $tags_added = tag_post($post, $relevant_tags);
            if (!empty($tags_added)) {
                log_tagging_info($post->ID, $post->post_title, $tags_added);
            }
        }

        $offset += $batch_size;
        wp_reset_postdata();
    }

    echo "All matching posts have been tagged.\n";
}

// 処理の実行
process_posts();
?>

実装のポイント

  • タグとしてカスタムタクソノミーを使用している場合、get_relevant_tags()のtt.taxonomy = 'post_tag'部分や、tag_post()のwp_set_post_terms()のpost_tag部分を変更する必要がある。
  • カスタムポストタイプを使用している場合、process_posts()で、post_typeを配列で指定する必要がある。デフォルトではpostのみ取得される。
  • 表記揺れ対応や小文字同一視などタグのヒット率を高める実装をしてもよいが、処理コストは上がる。
  • excluded_tags[]で除外タグを配列指定する。無関係なタグ付けを避けるため。

Discussion