ぼっちサーファーのブログ

一人海外サーフトリップの記録と雑記

【WordPress】投稿に追加済みの画像にalt属性[代替テキスト]を一括で挿入する方法

2018/6/6〜25までバリ島一人サーフトリップの記録

リアルタイム更新の記事はこちら

f:id:Apps:20180517173109j:plain

画像にはalt属性を付けたほうが多少なりともSEO的に有利になる。。。らしい。

Googleのクローラーさんは画像の内容まではわからないらしいので、alt属性値に「画像の説明」を書いたほうが良いとのこと。

<img src="sample.gif" alt="ここね">


AIが発展してクローラーさんが画像の中身自体を認識できるようになったら重要度は下がると思うけどね。
たしかに現状では書いたほうがいい気がする。






WordPressのalt属性を効率よく設定するプラグイン

WordPressではメディアの[代替テキスト]に入力するとalt属性に反映される。
f:id:Apps:20180516113936p:plain
でも、いちいちメディアを開いて入力するのは面倒。


ググると[代替テキスト]を効率よく入力できる「Media Library Alt Fields」というWordPressプラグインあった。
でも更新が止まってる。


同じよなプラグインで「Quick Alt Editor」ってのもある。

ja.wordpress.org



メディアをリスト表示にすると[代替テキスト]フィールドが表示されるようになる。
f:id:Apps:20180516111228p:plain

テキストエリアから出た時点で自動保存されるので「Media Library Alt Fields」より便利っぽい。




でもこれ「メディアにalt属性を入れられる」だけなんだよね。
まぁそういうプラグインなんだけど。




やりたいのは「投稿に挿入済のimgタグにalt属性を追加したい」なんだよね。
かなり探したけどそんなプラグインはないみたい。


挿入済の画像にalt属性を追加するにはどうするか

メディアにalt属性を追加しても記事には反映されない(困った

画像がショートコード的な感じで挿入されているなら動的に出力されるんだろうけどね。


WordPressの仕様だと挿入された画像は「HTMLをベタ打ち」したのと同じもの。
挿入された時点で内部のデータベースとの関連性は切れてしまっている。


手打ちでやろうにも既に画像は800枚を超えている。
メディアの[代替テキスト]を手動で設定するのは仕方ないとしても、挿入済のタグに「手打ちでalt="〇〇〇〇〇〇〇〇"を追加」は辛すぎる。


なんとか自動で反映させる方法を考えてみる。



【下調べ】画像URLとalt値は何処に保存されているのか

WordpPressのデータはデータベース(MySQL)に保存されている。
データベースの中身を直接見るのは大変なので「phpMyAdmin」で中身を見てみる。

phpMyAdminはMySQLサーバーをブラウザで管理できるデータベース接続クライアントツール。
今使ってるMIXHOSTでもcPanelからphpMyAdminを使えるようだ。
f:id:Apps:20180516133648p:plain

MIXHOST以外でもWordPressをコントロールパネルからインストール出来るようなレンタルサーバならphpMyAdminが使えるようになってるんじゃないだろうか?


alt属性値のWordPress内での扱い

画像(メディア)は単一のテーブルに保存されてるのかと思いきやPosts(投稿)テーブルだった(驚


「posts」テーブルの「meta_key」フィールドの値が「image/〇〇〇」になっている。
〇〇〇はjpegとかpngとか。
ファイル名は「guid」フィールドに保存されている。

でもalt属性の値は「posts」テーブルには保存されていない


alt属性の値は「postmeta」テーブルの「meta_value」フィールドに保存されている。
「meta_key」フィールドは 「_wp_attachment_image_alt」
f:id:Apps:20180516135540p:plain




「画像のURL」と「alt属性値」のデータを抜き出す

欲しいデータは「画像のURL」と「alt属性の値」
テーブルをまたいでいるので内部結合(INNER JOIN)でデータを抜き出す。
postsとpostmetaはidでリレーション。

「post_mime_type」に「image」を含み、かつ「meta_key」が「_wp_attachment_image_alt」のレコードのみを抽出する。


こんな感じのクエリになった。
SQLとか超久しぶりで8割ぐらい忘れてるな。
〇〇はデータベースのプレフィックスね。

SELECT 〇〇_posts.guid,〇〇_postmeta.meta_value FROM 〇〇_postmeta INNER JOIN 〇〇_posts
ON 〇〇_postmeta.post_id = 〇〇_posts.ID 
AND 〇〇_posts.post_mime_type LIKE '%image%' 
AND 〇〇_postmeta.meta_key = "_wp_attachment_image_alt";

ちなみに投稿記事を取得するのはこんな感じ。

SELECT post_content,ID FROM 〇〇_posts
WHERE `post_status` LIKE 'publish'
AND `post_type` LIKE 'post' ORDER BY `ID` ASC;

記事中にalt属性値を反映する

ここまでできればあとは記事中のimgタグのsrcとalt属性値のリストを照らし合わせてalt属性を追加するだけ。

いろいろ方法あると思うけどRubyで書いた。

require 'rubygems'
require 'nokogiri'
require 'uri'
require 'mysql'


###ここを環境に合わせて変更する###

#WordPressのドメイン
domain   = "https://www.〇〇.comとか"


#下記はWordPressのconfig.phpにも書かれてるはず
hostname = "〇〇〇.〇〇〇.〇〇〇.〇〇〇 とかサーバのIPアドレス"
username = "データベースにアクセス可能なユーザー名"
password = "パスワード"
dbname   = "データベース名"
prefix   = "プレフィックス"

##############################


#WordPressのDBに接続
client= Mysql.connect(hostname, username, password, dbname)

#alt属性値とファイル名を取得するクエリ
query = "SELECT #{prefix}_posts.guid,#{prefix}_postmeta.meta_value FROM #{prefix}_postmeta INNER JOIN #{prefix}_posts
ON #{prefix}_postmeta.post_id = #{prefix}_posts.ID
AND #{prefix}_posts.post_mime_type LIKE '%image%'
AND #{prefix}_postmeta.meta_key = '_wp_attachment_image_alt';"


#alt値の配列
alt_array = Hash.new
client.query(query).each do |guid, meta_value|
  alt_array[guid] = meta_value
end


#公開済みの記事を取得
query = "SELECT post_content,ID FROM `#{prefix}_posts`
          WHERE `post_status` LIKE 'publish'
          AND `post_type` LIKE 'post' ORDER BY `ID` ASC;"

#postをループ
client.query(query).each do |post_content, id|
  modified = false
  
  #Nokogiriでパースすると「&nbsp;」が消えるのと「&」がエンコードされるので文字に変えておく
  doc = Nokogiri::HTML::DocumentFragment.parse(post_content.gsub("&nbsp;", "|nbsp|").gsub("&", "toampersanduu"))
  
  #imgタグをループ
  doc.css('img').each{|imgtag|
    src =  imgtag.attr('src')
    
    #サーバー内のファイルのみ処理
    if src.include?(domain) == false
      break
    end

    #サイズ違いの画像ファイルにもalt属性を設定する
    alt_array.each{|set|
      #このヘンもう少しなんとかならんか
      tmp = set[0].split('.')
      url = tmp[0...tmp.length-1].join('.').gsub('.','\.')
      extension = '\.'+tmp[-1]
      regex =/#{url}.*#{extension}/
      src.match(regex){|ret|
        #alt属性値反映
        imgtag["alt"] = set[1]
        modified = true
      }
    }
  }

  #もとに戻す
  post_content = doc.to_html.gsub("|nbsp|", "&nbsp;").gsub("toampersanduu", "&")

  #記事更新クエリ
  query = "UPDATE `#{prefix}_posts` SET `post_content` = '#{post_content}' WHERE `#{prefix}_posts`.`ID` = #{id};"
  
  #記事更新
  if modified == true
    client.query(query)
  end

end


とりあえず自分の環境ではうまく動いた。
MySQLの中身を直接さわるのでバックアップ必須。


PHPで書けばWordPress内から実行できたかもなぁ。。。
またそのうちPHPで書いてみよう。


なんだかんだで丸1日以上かかったわ。
きっと本職のプログラマーさんならすぐなんだろうな。