Понадобилось мне тут загрузить большое количество постов в Wordpress. Вручную это делать не захотелось и у меня оставалось 2 варианта.

  1. Использовать старый XMLRPC
  2. Использовать новый WP REST API

Интереснее было пощупать второй вариант. Клиента для REST API я решил написать на Python 3.

Итак, первым делом выясняем что для того чтобы отправлять POST-запросы нам необходимо авторизоваться в Wordpress и для этого нам нужно поставить один из сторонних (!) плагинов. Это конечно забавно, ну да ладно. Я выбрал плагин WP REST API - OAuth 1.0a Server который дает возможность создавать в профиле пользователя токен для запросов к API.

Затем я взял Python и нагуглил вот этот модуль https://github.com/derwentx/wp-api-python

В документации к нему почему-то для авторизации надо указывать и связку логин-пароль и название ключа с секретным токеном.

Разбираться честно говоря было некогда поэтому я прописал и логин/пароль и ключи.

В моей задаче это был некий парсинг данных и их дальнейший постинг. Переделывать пример мне лень, поэтому публикую его как есть =)

Тут еще необходимо доустановить BeautifulSoup для парсинга HTML. Сам код с парсингом я убрал и добавил только тестовые данные внутри цикла.

import time
import requests
import os
import json
from bs4 import BeautifulSoup
from wordpress import API
from urllib.parse import urlparse

def main():
    wpapi = API(
        url="http://example.com",
        api="wp-json",
        version='wp/v2',
        wp_user="admin",
        wp_pass="password",
        oauth1a_3leg=True,
        consumer_key = "key",
        consumer_secret = "secret",
        callback = 'http://example.com',
    )
    url = 'http://example.com/wp-json/wp/v2/news'

    with open('index.html', "r") as html_data:
        soup = BeautifulSoup(html_data, 'html.parser')
        section = soup.select_one("section")

        items = []
        for child in section.children:
            # test data begin
            link = 'https://ya.ru'
            image_link = 'https://example.com/1.jpg'
            date = '10.10.2017'
            date = time.strftime('%Y-%m-%dT%H:%M:%SZ', date)
            company_name = 'Google'
            title = 'Super'
            desc = '<p>Superlongdescription la la la</p>'
            # test data end

            items.append({
                'status': 'publish',
                'author': 0,
                'date': date,
                'title': title,
                'excerpt': desc,
                'company_name': company_name,
                'company_link': link,
                'featured_media': image_link
            })

        for item in items[::-1]:
            item['featured_media'] = upload_media_from_url_to_wp(item['featured_media'], wpapi)
            print(item)
            wpapi.post(url, item)
            print('=========')

def upload_media_from_url_to_wp(image_link, wpapi):
    r = requests.get(image_link)
    if r.status_code is not 200:
        return False

    data = r.content
    a = urlparse(image_link)
    image_file_name = os.path.basename(a.path)
    _, extension = os.path.splitext(image_file_name)
    headers = {
        'cache-control': 'no-cache',
        'content-disposition': 'attachment; filename=%s' % image_file_name,
        'content-type': 'image/%s' % extension
    }
    endpoint = "/media"
    r = wpapi.post(endpoint, data, headers=headers)
    if r.status_code is not 201:
        return False
    result = json.loads(r.text)
    return result['id']

if __name__ == "__main__":
    main()

Тут в коде все просто - сперва в цикле парсим некую страницу и формируем список items.

Затем его разворачиваем в обратную сторону и пробегаемся по нему в цикле публикуя посты. В моем случае картинки есть для каждого поста поэтому я убрал лишние проверки. Для загрузки изображений вызывается функция upload_media_from_url_to_wp в которой сперва скачивается изображение со стороннего сервера и потом заливается к нам по API. В ответ прилетает id аттача.

Очень важный параметр - url = 'http://example.com/wp-json/wp/v2/news'

В моем случае это означает что мы постим записи в кастомный тип news.

Для обычных постов было бы url = 'http://example.com/wp-json/wp/v2/posts'

Также можно заметить 2 странных параметра - company_name и company_link.

Это кастомные поля которые я создал для кастомного типа news при помощи плагина Advanced Custom Fields.

Долго ломал голову как из заполнять через API так как заполняться они упорно не хотели.

Пришлось на стороне Wordpress в functions.php у темы добавить пару обработчиков:

function news_get_post_meta($object, $field_name, $request)
{
    return get_post_meta($object['id'], $field_name, true);
}
function news_update_post_meta($value, $object, $field_name)
{
    return update_post_meta($object->ID, $field_name, $value);
}
add_action('rest_api_init', function() {
    register_rest_field(
        'news',
        'company_name',
        array(
            'get_callback' => 'news_get_post_meta',
            'update_callback' => 'news_update_post_meta',
            'schema' => null
        )
    );
    register_rest_field(
        'news',
        'company_link',
        array(
            'get_callback' => 'news_get_post_meta',
            'update_callback' => 'news_update_post_meta',
            'schema' => null
        )
    );
});

Получается что мы зарегистрировали 2 поля в REST API и в коллбеках в ним указали 2 функции для получения данных и их сохранения. После данных манипуляций все заработало как надо.

В итоге получилось опубликовать кастомные типы новостей с кастомными полями и привязанной картинкой через API.

Предыдущая запись Следующая запись