本教程将演示如何通过抓取一个公开 API,把一批电影(含图片和自定义字段)导入到自定义文章类型中。
目录
引言
首先说明,本教程是 Cédric Bontems 最近在群里分享的那个视频的后续——他在视频里详细讲解了如何从 API 抓取数据重建 Netflix 首页。本教程采用不同的思路达到同样目的:把数据导入 WordPress,而不是每次页面加载时都调用 API。
为什么要这么做?好处不少:
- 用这个方法,你只需要抓取一次 API。数据导入完成后甚至可以完全删掉这段代码,因为所有内容都已存在 WordPress 数据表里。
- 数据存进 WordPress 后,你可以不用写任何自定义 PHP 代码就能操作它——在 Bricks 构建器里创建查询循环、嵌套滑块、筛选器、单篇页面、分类页面等等。
- 用 WordPress 表里的数据比每次调 API 快得多,内容和 WP 查询都可以缓存。
- 内容展示不依赖外部源:就算 API 某天挂了,你也不受影响。
本教程还大量参考了 WPCasts 的视频,建议去他的频道学习更多高级 WordPress 技巧。
获取 API 访问权限
本教程使用 Cédric 分享的同一个 API:https://www.themoviedb.org/settings/api。他在视频里讲过怎么注册账号、拿到访问权限,这里就不赘述了。记得把 API key 定义在 functions.php 里:
define ('TMDB_API_KEY', '<YOUR_API_KEY>' );
获取 API 响应
我们沿用我在 Bricks 中抓取 API 入门里讲的完全相同的逻辑,没读过的话建议先看那篇。先创建主函数:
function bl_get_movies_from_api(){
$moviedb_api = 'https://api.themoviedb.org/3/movie/top_rated?api_key=' . TMDB_API_KEY . '&language=en-US';
$response = wp_safe_remote_get($moviedb_api);
// Stop the function if the response returns an error
if ( is_wp_error( $response ) ) {
return false;
}
// Get the body part
$response_body = wp_remote_retrieve_body( $response );
// Decode the JSON object into a PHP array
$response_body_decoded = json_decode( $response_body, true );
return $response_body_decoded['results'];
}
此时我们拿到了一个包含每部电影数据的 PHP 数组,输出大致长这样:
array(20) {
[0]=>
array(14) {
["adult"]=>
bool(false)
["backdrop_path"]=>
string(32) "/rl7Jw8PjhSIjArOlDNv0JQPL1ZV.jpg"
["genre_ids"]=>
array(2) {
[0]=>
int(10749)
[1]=>
int(18)
}
["id"]=>
int(851644)
["original_language"]=>
string(2) "ko"
["original_title"]=>
string(15) "20 Century Girl"
["overview"]=>
string(377) "Yeon-du asks her best friend Bora to collect all the information she can about Baek Hyun-jin while she is away in the U.S. for heart surgery. Bora decides to get close to Baek's best friend, Pung Woon-ho first. However, Bora's clumsy plan unfolds in an unexpected direction. In 1999, a year before the new century, Bora, who turns seventeen, falls into the fever of first love."
["popularity"]=>
float(314.92899999999997)
["poster_path"]=>
string(32) "/od22ftNnyag0TTxcnJhlsu3aLoU.jpg"
["release_date"]=>
string(10) "2022-10-06"
["title"]=>
string(17) "20th Century Girl"
["video"]=>
bool(false)
["vote_average"]=>
float(8.8000000000000007)
["vote_count"]=>
int(248)
}
[1]=> etc....
本教程会用到这些字段:
['backdrop_path']:取图片链接,作为文章的特色图['title']:作为文章标题['id']:拼进文章 slug,保证文章唯一['overview']:作为文章摘要['release_date']、['vote_average']、['vote_count']:用来填充 ACF 自定义字段
创建自定义文章
现在我们已经有了包含全部电影信息的大数组。接下来要遍历数组、为每部电影创建自定义文章,不过先写一个处理文章 slug 的自定义函数:
function bl_slugify($text){
// remove unwanted characters
$text = preg_replace('~[^-\w]+~', '', $text);
// trim
$text = trim($text, '-');
// remove duplicate -
$text = preg_replace('~-+~', '-', $text);
// lowercase
$text = strtolower($text);
if (empty($text)) {
return 'n-a';
}
return $text;
}
这个函数负责替换特殊字符、用 - 分隔每个单词、把文本转成小写,没什么复杂的。
接下来遍历数组,向 CPT movie 创建自定义文章:
// Loop in each movie
foreach($response_body_decoded['results'] as $movie){
// Slug
$movie_slug = bl_slugify( $movie['title'] . '-' . $movie['id'] );
// Existing movie inside the CPT
$existing_movie = get_page_by_path( $movie_slug, 'OBJECT', 'movie' );
// Check if the movie post has already been created
if( $existing_movie === null ){
// Create a new custom post
$inserted_movie = wp_insert_post( [
'post_name' => $movie_slug,
'post_title' => $movie['title'],
'post_excerpt' => $movie['overview'],
'post_type' => 'movie',
'post_status' => 'publish'
] );
// Continue to the next loop iteration if the post was not correctly created
if( is_wp_error( $inserted_movie ) || $inserted_movie === 0 ) {
continue;
}
}
}
几点说明:
- 生成 slug 时把电影标题和ID拼接起来,保证 slug 唯一。
- 创建文章前先用 get_page_by_path() 检查 slug 是否已存在于自定义文章类型 movie 中——不存在(这正是我们想要的)时返回
null,然后才创建新文章——这样函数跑多次也不会重复建文章。 - 用 WordPress 核心函数 wp_insert_post() 创建自定义文章,这个函数允许传数组参数来设置文章名(slug)、标题等。
- 文章创建后检查是否处理正确:如果函数检测到错误,或返回的文章 ID 为 0,就跳过本次循环迭代,处理下一部电影。
设置特色图
从 API 下载图片、放进媒体库、设为文章特色图:
// Set Featured Image
$url = 'https://image.tmdb.org/t/p/original' . $movie['backdrop_path'];
$img = media_sideload_image( $url, $inserted_movie);//download image to wpsite from url
$img = explode("'",$img)[1];// extract http.... from <img src'http...'>
$attId = attachment_url_to_postid($img);//get id of downloaded image
set_post_thumbnail( $inserted_movie, $attId );//set the given image as featured image for the post
- 先用 API 返回的
['backdrop_path']拼出图片 URL。 - 然后用 media_sideload_image() 把图片下载到服务器。
- 从 img 标签里提取出 URL,再用 attachment_url_to_postid() 找到附件 ID——此时图片已进入媒体库。
- 最后用 set_post_thumbnail() 把图片设为文章特色图。
填充 ACF 字段
先在 ACF 里创建字段:
记得在「显示选项」(Screen Options)里勾选 字段键(Field Keys) 复选框。
现在把自定义字段和 API 值对应起来:箭头左边是 ACF 字段键,右边是 API 返回的数组值:
// Map ACF keys with the API values
$acf_fields = array(
'field_6375ea77d3491' => 'release_date',
'field_6375ea8fd3492' => 'vote_average',
'field_6375ea9fd3493' => 'vote_count',
);
最后用 API 值更新每个字段:
// Update each ACF value
foreach( $acf_fields as $key => $name ) {
update_field( $key, $movie[$name], $inserted_movie );
}
坚持住,快结束了。
启用 AJAX
思路是:通过一次 AJAX 调用运行我们的函数,一次性填充所有自定义文章。要启用 AJAX 调用并把函数当作回调,需要用到 wp_ajax 钩子:
add_action( 'wp_ajax_nopriv_get_movies_from_api', 'get_movies_from_api' );
add_action( 'wp_ajax_get_movies_from_api', 'get_movies_from_api' );
完整代码
好了,所有代码都齐了:
// Movies API
add_action( 'wp_ajax_nopriv_get_movies_from_api', 'get_movies_from_api' );
add_action( 'wp_ajax_get_movies_from_api', 'get_movies_from_api' );
function bl_get_movies_from_api(){
define ('TMDB_API_KEY', '<YOUR_API_KEY>' );
$moviedb_api = 'https://api.themoviedb.org/3/movie/top_rated?api_key=' . TMDB_API_KEY . '&language=en-US';
$response = wp_safe_remote_get($moviedb_api);
// Stop the function if the response returns an error
if ( is_wp_error( $response ) ) {
return false;
}
// Get the body part
$response_body = wp_remote_retrieve_body( $response );
// Decode the JSON object into a PHP array
$response_body_decoded = json_decode( $response_body, true );
// Loop in each movie
foreach($response_body_decoded['results'] as $movie){
// Slug
$movie_slug = bl_slugify( $movie['title'] . '-' . $movie['id'] );
// Existing movie inside the CPT
$existing_movie = get_page_by_path( $movie_slug, 'OBJECT', 'movies' );
// Check if the movie post has already been created
if( $existing_movie === null ){
// Create a new custom post
$inserted_movie = wp_insert_post( [
'post_name' => $movie_slug,
'post_title' => $movie['title'],
'post_excerpt' => $movie['overview'],
'post_type' => 'movie',
'post_status' => 'publish'
] );
// Continue to the next loop iteration if the post was not correctly created
if( is_wp_error( $inserted_movie ) || $inserted_movie === 0 ) {
continue;
}
// Set Featured Image
$url = 'https://image.tmdb.org/t/p/original' . $movie['backdrop_path'];
$img = media_sideload_image( $url, $inserted_movie);//download image to wpsite from url
$img = explode("'",$img)[1];// extract http.... from <img src'http...'>
$attId = attachment_url_to_postid($img);//get id of downloaded image
set_post_thumbnail( $inserted_movie, $attId );//set the given image as featured image for the post
// Map ACF keys with the API values
$acf_fields = array(
'field_6375ea77d3491' => 'release_date',
'field_6375ea8fd3492' => 'vote_average',
'field_6375ea9fd3493' => 'vote_count',
);
// Update each ACF value
foreach( $acf_fields as $key => $name ) {
update_field( $key, $movie[$name], $inserted_movie );
}
}
}
}
function bl_slugify($text){
// remove unwanted characters
$text = preg_replace('~[^-\w]+~', '', $text);
// trim
$text = trim($text, '-');
// remove duplicate -
$text = preg_replace('~-+~', '-', $text);
// lowercase
$text = strtolower($text);
if (empty($text)) {
return 'n-a';
}
return $text;
}
运行 AJAX 调用
简单的一步!进入 /wp-admin/,粘贴下面的 URL:your-domain.com/wp-admin/admin-ajax.php?action=bl_get_movies_from_api(把 your-domain 换成你自己的域名)。这会通过 admin-ajax.php 方式执行我们的 AJAX 调用。
然后等着就行。函数在后台运行,从 API 导入所有数据。
函数跑完后会返回 0,这完全正常。
来看看我们的自定义文章类型 movie!
所有电影都导入成功了!再看看文章数据:
所有内容都正确填充并保存。
恭喜,你刚刚用公开 API 把一批电影导入到了自定义文章类型里!