スクレイピング+wp media import で他サイトから画像を移設してみたよ

WP-CLI Advent Calendar 2014』24日目担当の @tecking です。早いものでこのカレンダーも今日と明日で終わりですね。

今日は、前回紹介した「wp media」系コマンドの続編。管理画面を介さずに画像などのメディアファイルをアップロードできる wp media import の紹介です。当初、今日のテーマを「デザイナーのための wp scaffold 入門」で予定していたのですが急遽変更しました。すみません。ちょうど今取り組んでる案件で wp media import を使う場面があったので「易きに流れて」しまいました……

wp media import の基本的な使い方

公式マニュアルからの引用になりますが

wp media import foo.jpg --post_id=123 --title='bar' --featured_image

といった用法で使います。

  • foo.jpg … アップしたいメディアファイル名 ※必須
  • ––post_id …… メディアファイルに紐づけたい記事ID
  • ––title …… メディアファイルに付記するタイトル文字列
  • ––featured_image …… このオプションを付けた場合、アイキャッチ画像として扱われる

ほかにもいくつかオプションを設定できるので詳しくはマニュアルをご参照ください。

ところで、マニュアルの「EXAMPLES」欄を見ますとこんな用例が書いてあります。

# Import an image from the web
wp media import http://s.wordpress.org/style/images/wp-header-logo.png --title='The WordPress logo' --alt="Semantic personal publishing"

そう、wp media import はローカルのメディアファイルだけでなくウェブ上のリソースも参照してくれるんです。

ということで、「静的HTMLサイトからWordPressサイトに移設しつつリニューアル」という絶賛施工中の案件に応用してみることにしました。既存サイトにある画像(写真)ファイルを wp media import でまるっとWordPress側にインポートしようという企画です。

既存サイトの画像ファイルを残したまま、テーマファイルや記事内で直リンする手もありますが、今回の案件では一つの画像に対しサイズの異なる複数のファイルをつくる必要があるためWordPress側に取り込んで管理することにしました。

《関連記事》
ひっくり返るくらい超絶便利な wp media regenerate

定番ライブラリ『PHP Simple HTML DOM Parser』を導入する

簡単に、既設サイトの概要を書きます。

  • charset は Shift_JIS
  • テーブルレイアウト
  • 画像ファイルへのパスは相対URLで記述
  • 画像はCSSクラス foo がふられた table 要素内にあるJPEGファイル

なかなか手強い相手です…… ともあれ、画像ファイルのパスを取れないことには話が始まりません。

そこでここからは、スクレイピングの定番ライブラリ『PHP Simple HTML DOM Parser』に登場いただくことにします。

ウェブスクレイピング(Web scraping)とは、ウェブサイトから情報を抽出するコンピュータソフトウェア技術のこと。ウェブ・クローラー(Web crawler) あるいはウェブ・スパイダー(Web spider)とも呼ばれる。
Wikipedia「ウェブスクレイピング」の項より

ということで、ライブラリを使って既存サイトのHTMLファイルを順番にまるっと読み込みながら、目指すデータ(今回は画像ファイルへのパス)を抽出していきます。

スクレイピング+メディアファイルインポートに使うPHPスクリプトを media-import.php として、WordPressのインストールディレクトリに配置します(そうしないと wp media import が効かないので)。

『PHP Simple HTML DOM Parser』はPHPスクリプトとして配布されているので、公式サイトからダウンロードしたのち

<?php
require_once '/path/to/simple_html_dom.php';

と media-import.php のアタマに書きます。これで media-import.php 側から既存サイトへスクレイピングする準備ができました。

HTMLファイルのリストをつくり、画像ファイルのパスや alt 属性などを取得する

今回は前もって、同ライブラリを使って画像のあるHTMLファイルのURL一覧(link.txt としました)をつくっておきました。

http://example.com/foo/bar/baz_01.html
http://example.com/foo/bar/baz_02.html
http://example.com/foo/bar/baz_03.html 

といったかたちで1行1URL(改行コード LF)で並んでいます(注:なぜか、上記のサンプルは行間があいて表示されてますね……)

baz_01.html, baz_02.html, baz_03.html……の親ページ(アーカイブページのようなもの)をスクレイピングして a 要素の href 属性だけを取り出しました。つくりかたの詳細は省きますが、基本的には後述のコードを応用すればいけるのではないかとおもいます(丸投げ失敬)。


[2014.12.25追記]
画像のあるHTMLファイルのリストを生成するコード例を記事の最後に追加しました。


URL一覧ができたところで、それぞれのURLを配列に格納していきます。

$file = file_get_contents( '/path/to/link.txt' );
$urls = explode( "\n", $file );

配列とくれば foreach で回すしかないですな。

foreach ( $urls as $url ) {
	if ( !empty( $url ) ) extract_img_url( $url ); // extract_img_url はユーザー定義関数(後述)
}

foreach で一つ一つのHTMLファイルを読み込みつつ、extract_img_url() 関数で画像URLを抜きだしていきます。

function extract_img_url( $url ) {
	preg_match( '/^.*\//', $url, $dir ); // HTMLファイルのあるディレクトリを絶対URLで取得
	$content = mb_convert_encoding( file_get_contents( $url ), 'UTF-8', 'SJIS' ); // 引数 $url で渡されたファイルを文字列に読み込み Shift_JIS → UTF-8 変換

	$html = str_get_html( $content ); // 変換後の文字列をもとにオブジェクト $html を生成(str_get_html は『PHP Simple HTML DOM Parser』の関数)

	foreach ( $html->find( '.foo img[src$=".jpg"]') as $element ) { // CSSクラス foo 内にある img 要素を抽出
		$img_src = $dir[0] . $element->src; // 画像ファイルの位置を絶対URLに置換
		$img_alt = $element->alt; // 画像ファイルの alt 属性を抽出
		if ( !empty( $element->title ) ) {
			$img_title = $element->title; // 画像ファイルの title 属性を抽出
		}
		else {
			$img_title = $img_alt; 
		}

		import_media( $img_src, $img_alt, $img_title );  // import_media はユーザー定義関数(後述)
	}

	$html->clear(); // メモリ不足を回避するため処理が終わるごとに『PHP Simple HTML DOM Parser』の clear メソッド呼び出し
	return;
}

wp media import で一気にインポート

ここまで来たらあと一息。import_media() 関数を定義しておしまいです。

function import_media( $img_src, $img_alt, $img_title ) {
	exec( "wp media import $img_src --alt='$img_alt' --title='$img_title'" ); // 引数をもとに wp media import でWordPress側に画像取り込み
	echo 'imported media file - ' . $img_src . "\n"; // 進行状況把握のためメッセージ出力
	return;
}

というわけで、長い道のりでしたが

php media-import.php

とすることで、スクレイピング→インポートが自動的にできるようになりました。ところどころ無駄なコードを書いている箇所があるかもしれませんし、前提となるURL一覧のつくりかたはブーンとぶんなげて終わってしまいました。

お気づきの箇所などありましたらご指摘いただければありがたいです。なお、スクレイピングのご利用は計画的に

謝辞

『WP-CLI』は使い始めて1年ちょっととはいえもっぱら

  • WordPress導入
  • プラグインの追加・有効化・無効化・削除
  • コアファイルやプラグインのアップデート

に使うケースが大半なので、レンタルサーバへの導入など『WP-CLI Advent Calendar 2014』でも比較的ハードル低めのネタを中心に書いたつもりです。参加メンバーにのせられて毎週水曜日の日直を担当したわけですが、お役に立てましたでしょうか。私の記事をきっかけに「黒い画面でWordPress使ってみようかなー」という方が一人でも増えたらうれしいです。

と誘ってくださった @tekapo さんありがとうございました!

ではでは、ハッピーなWP-CLIライフを! 『WP-CLI Advent Calendar 2014』最終日の日直(12月25日)は @wokamoto さんです。


[2014.12.25追記]
画像のあるHTMLファイルのリストを生成するコード例を記事の最後に追加しました。

各ページへのリンク先URLは、親ページ(アーカイブページのようなもの)内の要素を .foo .bar a と絞り込むことで抽出できそうなので media-import.php の前半で記述した

$file = file_get_contents( '/path/to/link.txt' );
$urls = explode( "\n", $file );
foreach ( $urls as $url ) {
	if ( !empty( $url ) ) extract_img_url( $url ); // extract_img_url はユーザー定義関数(後述)
}

のコードを

$target = 'http://example.com/foo/bar/index.html'; // 親ページのURLを指定
$content = mb_convert_encoding( file_get_contents( $target ), 'UTF-8', 'SJIS' ); // 親ページのファイルを文字列に読み込み Shift_JIS → UTF-8 変換
$html = str_get_html( $content ); // 変換後の文字列をもとにオブジェクト $html を生成
foreach ( $html->find( '.foo .bar a') as $element ) {// .foo .bar 内にある a 要素を抽出
	preg_match( '/^.*\//', $target, $dir ); // リンク先ファイルのあるディレクトリを取得
	$url = $dir[0] . $element->href;  // リンク先の位置を絶対URLに置換
	scraping_img_url( $url ); // スクレイピング処理
}

と書き換えて、前もってリンク先一覧のテキストファイルをつくらず画像ファイルをインポートできるようにしました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください