IMUZA.com

Xserver<<WordPress(ConoHa)<<はてなブログ

ホーム / Wordpress / WordPress:jQueryなしのAjaxでウェブスクレイピング結果を表示する

WordPress:jQueryなしのAjaxでウェブスクレイピング結果を表示する

2022/09/27 Wordpress

Heroku の無料プランが11月28日で終了することを受けて、その代替となる機能を WordPress 内で完結するよう実装しました。あらためて WordPress の Ajax を理解することが出来ました。

  • Ajax でウェブスクレイピング結果を表示する
  • WordPress の Ajax
    • wp_localize_script
    • Fetch による Ajax スクリプト
    • サーバー側 wp_ajax アクションフック
  • 結果をグローバル変数に保存している理由

Ajax でウェブスクレイピング結果を表示する

関連記事は、

  • php正規表現 vs pythonスクレイピング
  • phpからpythonを呼び出し結果を取得
  • Filmarks の自分の投稿へ飛ぶ疑似ソーシャルボタン
  • Node.js でスクレイピングしてクライアントに返す

目的としていることはやや特殊ですが、Ajax やウェブスクレイピングの考え方自体は応用のきくものですので参考にしていただけることもあるかと思います。やろうとしていることは、私自身が運用している映画のレビューサイトと、同時にレビューを投稿している Filmarks のマイページの記事をリンクさせようということです。

  • そんなには褒めないよ。映画評
  • Filmarks

もともとは、このサイトも含めすべてはてなブログで運用していましたので Javascript 以外に機能追加の方法がなく、Heroku に Node.js サーバーを立ち上げていました。たとえば、この Filmarks ボタンをクリックするとその映画のレビューが Filmarks にあればそのマイページに飛ぶということです。

それを今回 WordPress に移行しましたのですべて PHP で完了させました。

WordPress の Ajax

WordPress には Ajax で通信するための機能が /wp-admin/admin-ajax.php として用意されています。ですので、ブラウザからこの admin-ajax.php にリクエストを送ればいいのですが、そのためにはブラウザにここに送ってくださいよということを知らせる必要があります。

wp_localize_script

一般的に WordPress では、wp_enqueue_scripts アクションフックを使ってスクリプトを読み込みます。その際に同時に PHP からブラウザに変数を渡す方法が用意されています。wp_localize_script です。

  • wp_localize_script

基本的な構文は、

wp_localize_script( 'スクリプトのハンドル名', 'データを格納する変数名', '配列データ' );

で、今回の場合は、

function sample_enqueue_scripts_styles() {
	if(is_single()){ // この条件分岐は今回の内容のため
		wp_enqueue_script( 'mi-filmarks', get_stylesheet_directory_uri() . '/lib/js/get-filmarks.min.js', '', '', true );
		wp_localize_script( 'mi-filmarks', 'wp_ajax', array(
			'ajax_url' => admin_url( 'admin-ajax.php' ),
			'nonce' => wp_create_nonce( 'filmarks-mypage' ),
		) );
	}
}
add_action( 'wp_enqueue_scripts', 'sample_enqueue_scripts_styles' );

としています。

wp_localize_script に wp_enqueue_script を使って読み込むスクリプトのハンドル名(mi-filmarks)、変数(wp_ajax)とデータ(配列)を与えておきます。そうしますと HTML に次のコードが出力されます。URL はローカルの開発環境になっています。

<script type="text/javascript" id="mi-filmarks-js-extra">
/* <![CDATA[ */
var wp_ajax = {"ajax_url":"http:\/\/localhost:8000\/wp-admin\/admin-ajax.php","nonce":"bf000952a7"};
/* ]]> */
</script>

Fetch による Ajax スクリプト

ブラウザからサーバーへリクエストを送る Ajax スクリプトには fetch を使います。

  • Fetch の使用

基本的な構文は、

fetch(URL, {オプション})
.then(response => response.json()) // response.text(), response.blob()
.then(data => console.log(data))
.catch(error => console.log(error))

で、今回の場合は、映画タイトルと記事 ID が必要になりますのでそれらをオプションとしてサーバーに送ります。サーバー側の php では Filmarks にレビューがあればその URL を、なければ ‘no review’ のテキストを返しますので一旦グローバル変数に保存するようにしています。このあたりは特殊なケースですので後述します。

window.imz = window.imz || {}; // 特殊,グローバル変数を使用するため

const params = new URLSearchParams();
const matches = /postid-(\d+)/.exec(document.body.classList); // bodyのクラス名から記事IDを取得

params.append('action', 'get_filmarks'); // 送ったデータを処理するWordPressのアクションフック名
params.append('nonce', wp_ajax.nonce );
params.append('title', document.title);
params.append('postid', matches[1]);
const opt = {
	method: 'post',
	body: params
}

fetch(wp_ajax.ajax_url, opt)
	.then(response => response.text())
	.then(text => {
		imz.filmarks = text; // FilmarksのURLやエラーテキストをグローバル変数に保存する
	})
	.catch(error => {
		imz.filmarks = 'error';
	});

サーバーから返されたデータ filmarks をグローバル変数に保存している理由は後述します。いずれにしても Ajax でなにを取得してどう処理するかはそもそもの目的ですので text であれ、json であれ、画像などのバイナリデータであれ、必要な処理方法をとるということになります。

サーバー側 wp_ajax アクションフック

ブラウザが admin-ajax.php あてに送ったデータは wp_ajax_[action名] や wp_ajax_nopriv_[action名] のアクションフックで受け取り処理します。前者はログインユーザー用、後者はログインしていない一般ユーザー用ですので、処理作業によってはログインユーザーのみにすることが出来ます。

一般的な構文は次のとおりです

function get_filmarks_func(){
	echo '成功';
	die;
}
add_action('wp_ajax_get_filmarks', 'get_filmarks_func');
add_action('wp_ajax_nopriv_get_filmarks', 'get_filmarks_func');

die で処理を終了しておかないと admin-ajax.php が「0」を返しますので要注意です。

で、今回のケースはかなり特殊ですが一応コードを載せておきます。ウェブスクレイピングに正規表現を使っているのは、python よりも速かったからです。

  • php正規表現 vs pythonスクレイピング
function get_filmarks_func(){
	// nonceが一致しない場合は'fatal error'を返す
	if(!check_ajax_referer( 'filmarks-mypage', 'nonce', false )){
		echo 'fatal error';
		die;
	}

	preg_match('/^.*?「(.+?)」.*$/', $_POST['title'], $matches); // 特殊,記事タイトルの中から映画タイトルを取り出している
	$title = $matches[1];
	$postid = $_POST['postid'];
	// カスタムフィールドにURLが保存されていれば返して終了
	$meta_value = get_post_meta($postid, 'filmarks', true);
	if ($meta_value !== ''){
		echo $meta_value;
		die;
	}else{
		$meta_value = 'no review';
	}
	// 以下はFilmarksのマイページをスクレイピングして該当レビューURLを探している
	$url = 'https://filmarks.com/users/ausnichts';
	$mypage = file_get_contents($url);
	preg_match('/"c-pagination__last".*href="\/users\/ausnichts\?page=(\d+)/s', $mypage, $matches);
	$page = $matches[1];
	
	for($i=1; $i<=$page; $i++){
		$nowurl = $url . '?page=' . $i;
		$nowpage = file_get_contents($nowurl);
		$pattern = '/<a href="(\/movies\/\d+\?mark_id=\d+)">' . $title . '/s';
		if(preg_match($pattern, $nowpage, $matches)){
			$meta_value = 'https://filmarks.com' . str_replace('?mark_id=', '/reviews/', $matches[1]);
			break;
		}
	}
	// $meta_valueには、レビューがあればURL,なければ'no review'が入っている
	add_post_meta($postid, 'filmarks', $meta_value, true);
	echo $meta_value;
	die;
}
add_action('wp_ajax_get_filmarks', 'get_filmarks_func');
add_action('wp_ajax_nopriv_get_filmarks', 'get_filmarks_func');

結果をグローバル変数に保存している理由

ということで、ブラウザに目的の Filmarks のレビュー URL が渡りグローバル変数に保存されるわけですが、この後なにをやろうとしているかを整理しますと、映画レビューサイトはそれぞれの映画について記事タイトルに映画タイトルを入れて1記事ずつ書いています。その記事の下に SNS のシェアボタンと一緒に Filmarks のボタンをおいて Filmarks サイトに自分のレビューがあればそのページに飛ぶようにしているということです。

ただ問題は、

  • Filmarks にレビュー記事があるかどうかわからない
  • レビュー記事がある場合には別ウィンドウ(タブ)を立ち上げる必要がある
  • レビュー記事がない場合にはダイアログを表示する必要がある

ということになりますので、一旦結果をグローバル変数に保存し、ボタンがクリックされた場合に、その値を読んで、値が URL であれば別ウィンドウで Filmarks を表示し、値が ‘no review’ であればダイアログを表示してレビューがない旨を表示するよう Javascript で書いているということです。

また、クリックされたときにまだグローバル変数が存在しないケースがありますので、その場合はローディング画像を出し setInterval() を使ってグローバル変数を読み込めるまで待つようにしています。

というかなり特殊なことですのでコードは省略です。

以上です。

JavaScript & Ajax プロが教える“本当の使い方”

JavaScript & Ajax プロが教える“本当の使い方”

MdN編集部
3,080円(06/01 04:12時点)
Amazon
山形県産 和梨 3kg(秀品/6~8玉入り)

山形県産 和梨 3kg(秀品/6~8玉入り)

Amazon
php正規表現 vs pythonスクレイピング
WordPress:スマホ実機でAjaxが働かない…ミスです
Twitter
Facebook
ブックマーク
LINEで送る

最初のサイドバー

最新記事

2023/05/25

WordPress:リビジョン削除、回数制限

2023/05/10

WordPress:裏技的サイトリニューアル

2023/04/28

XserverへのSSH接続がエラーになってしまった

2023/04/16

正規表現の最短一致でミスる

2023/04/4

WordPress:公開中サイトをサブディレクトリでリニューアルし公開する

最新記事を一覧で見る

よく読まれている記事

よく読まれている記事を一覧で見る

カテゴリー

  • はてなブログ215
  • WebTips109
  • javascript98
  • Joomla!88
  • Wordpress70
  • Windows68
  • CSS63
  • Joomla!更新53
  • Linux49
  • はてなテーマ45
  • Google34
  • Plamo33
  • はてなプラグイン25
  • php23
  • Node.js18
  • Ubuntu16
  • SASS16
  • laravel415
  • Chrome11
  • cms-style10
  • iPhone9
  • Git入門6
  • ConoHa WING6
  • genesis6
  • Python5
  • Android5
  • PC全般4
  • Facebook4
  • スマートフォン4
  • Xserver3
  • Firefox3
  • 静的サイトジェネレーター3
  • SSD3
  • Docker3
  • Blankslate3
  • Twitter2
  • Mactype2
  • GitHub2
  • youtube1
  • はてなブクマ1
  • rails入門1
  • 映画1

Footer

My Web Sites

  • @半径とことこ60分
  • そんなには褒めないよ。映画評
  • IMUZA.com
  • GitHub

Related Sites

  • WordPress公式
  • WordPress関数リファレンス
  • PHPマニュアル

Contact Us

  • お問い合わせフォーム
  • Twitter
  • Facebook
  • Feedly

Copyright © 2023 · IMUZA.com