首页 / 中文教程 / 教程

单次 AJAX 调用从 API 导入自定义文章:特色图 + 自定义字段

本教程将演示如何通过抓取一个公开 API,把一批电影(含图片和自定义字段)导入到自定义文章类型中。

Ray Chan·2026-08-12·约 5 分钟

本教程将演示如何通过抓取一个公开 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

填充 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 把一批电影导入到了自定义文章类型里!

需要帮忙?

用 Bricks 建站?我接客户项目。

从快速营销站到完整的 Bricks 建站,再到从 Elementor 迁移——我都做过。本站每篇教程都来自真实项目经验。告诉我你的需求,一个工作日内回复。

  • Bricks 建站与改版
  • Elementor / Divi → Bricks 迁移
  • Bricks → Astro / headless 性能升级
  • 速度优化,PageSpeed 95+ 目标