Импорт постов через Wordpress REST API.
Понадобилось мне тут загрузить большое количество постов в Wordpress. Вручную это делать не захотелось и у меня оставалось 2 варианта.
Интереснее было пощупать второй вариант. Клиента для 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.