TL;DR
WordPress REST API + Application Passwords ile R’dan içerik çekin. httr2 ile API çağrıları, tidyverse ile veri manipülasyonu yapın. Draft, publish, pending içerikleri listeleyin ve analiz edin.
| Bileşen | Araç/Yöntem |
|---|---|
| API İstemcisi | httr2::request() |
| Kimlik Doğrulama | Application Passwords |
| Veri İşleme | tidyverse (tibble, dplyr) |
| Endpoint | /wp-json/wp/v2/posts |
| Max Per Page | 100 (pagination gerekli) |
WordPress sitelerinde çoklu yazarlar, AI destekli içerik üretimi ve editoryal süreçler nedeniyle farklı durumlarda bekleyen çok sayıda içerik birikebiliyor. Taslaklar, zamanlanmış içerikler, yetim sayfalar… Bunları düzenli takip etmek, içerik stratejisinin sağlığı için kritik.
Bu yazıda R ile WordPress içeriklerinizi nasıl listeleyeceğinizi ve temel analizler yapacağınızı anlatıyorum.
İleri seviye analiz ve interaktif dashboard için R Shiny Content Intelligence Dashboard yazısına göz atın.
Gereksinimler
# Paket kurulumu
install.packages(c("httr2", "tidyverse", "jsonlite"))
library(httr2)
library(jsonlite)
library(tidyverse)
WordPress REST API Bağlantısı
WordPress 4.7+ sürümlerinde REST API varsayılan olarak aktif. Kimlik doğrulama için Application Passwords kullanacağız (WordPress 5.6+).
Application Password Oluşturma
- WordPress Admin → Users → Profile
- “Application Passwords” bölümüne gidin
- Yeni bir isim verin (örn: “R Script”)
- “Add New Application Password” tıklayın
- Oluşan şifreyi kopyalayın (bir kez gösterilir)
Application Password’ü güvenli saklayın. .Renviron dosyasında environment variable olarak tutmanızı öneririm.
Yapılandırma
# .Renviron dosyasına ekleyin:
# WP_USER=kullaniciadi
# WP_APP_PASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx
# WP_SITE_URL=https://siteniz.com
# R'da kullanım
wp_config <- list(
base_url = paste0(Sys.getenv("WP_SITE_URL"), "/wp-json/wp/v2/"),
user = Sys.getenv("WP_USER"),
app_password = Sys.getenv("WP_APP_PASSWORD")
)
İçerikleri Çekme
#' WordPress postlarını çek
#' @param status Post durumu: publish, draft, pending, private, future
#' @param per_page Sayfa başına içerik (max 100)
#' @param page Sayfa numarası
get_wp_posts <- function(status = "publish", per_page = 100, page = 1) {
response <- request(wp_config$base_url) |>
req_url_path_append("posts") |>
req_url_query(
status = status,
per_page = per_page,
page = page,
`_fields` = "id,title,excerpt,date,modified,status,categories,tags"
) |>
req_auth_basic(wp_config$user, wp_config$app_password) |>
req_perform()
# Response'u tibble'a çevir
content <- resp_body_json(response)
tibble(
id = map_int(content, "id"),
title = map_chr(content, ~.x$title$rendered),
excerpt = map_chr(content, ~.x$excerpt$rendered),
date = map_chr(content, "date"),
modified = map_chr(content, "modified"),
status = map_chr(content, "status"),
categories = map(content, "categories"),
tags = map(content, "tags")
)
}
Taslakları Listeleme
# Draft'ları çek
drafts <- get_wp_posts(status = "draft")
# Başlıkları görüntüle
drafts |>
select(id, title, modified) |>
arrange(desc(modified)) |>
print(n = 20)
Çıktı:
# A tibble: 15 × 3
id title modified
<int> <chr> <chr>
1 892 R Shiny ile Dashboard Geliştirme 2025-01-10T14:30:00
2 887 WordPress API Best Practices 2025-01-08T09:15:00
3 845 Semantic SEO Stratejileri 2024-12-22T16:45:00
...
Tüm Durumları Çekme
#' Tüm içerikleri çek (pagination ile)
get_all_posts <- function(status = "publish") {
all_posts <- tibble()
page <- 1
total_pages <- Inf
while (page <= total_pages) {
response <- request(wp_config$base_url) |>
req_url_path_append("posts") |>
req_url_query(
status = status,
per_page = 100,
page = page,
`_fields` = "id,title,excerpt,date,modified,status,categories,tags"
) |>
req_auth_basic(wp_config$user, wp_config$app_password) |>
req_perform()
if (page == 1) {
total_pages <- as.integer(resp_header(response, "X-WP-TotalPages"))
}
content <- resp_body_json(response)
posts <- tibble(
id = map_int(content, "id"),
title = map_chr(content, ~.x$title$rendered),
excerpt = map_chr(content, ~.x$excerpt$rendered),
date = map_chr(content, "date"),
modified = map_chr(content, "modified"),
status = map_chr(content, "status"),
categories = map(content, "categories"),
tags = map(content, "tags")
)
all_posts <- bind_rows(all_posts, posts)
page <- page + 1
Sys.sleep(0.5)
}
all_posts
}
# Tüm durumları birleştir
all_content <- bind_rows(
get_all_posts("publish") |> mutate(status = "publish"),
get_all_posts("draft") |> mutate(status = "draft"),
get_all_posts("pending") |> mutate(status = "pending")
)
# Özet
all_content |>
count(status) |>
arrange(desc(n))
Temel Analizler
Category Dağılımı
# Category'leri çek
get_categories <- function() {
response <- request(wp_config$base_url) |>
req_url_path_append("categories") |>
req_url_query(per_page = 100) |>
req_perform()
content <- resp_body_json(response)
tibble(
id = map_int(content, "id"),
name = map_chr(content, "name"),
count = map_int(content, "count")
)
}
categories <- get_categories()
# En çok içerik olan kategoriler
categories |>
arrange(desc(count)) |>
head(10)
İçerik Yaşı Analizi
all_content |>
mutate(
date = as.Date(date),
age_days = as.numeric(Sys.Date() - date),
age_group = case_when(
age_days < 30 ~ "Son 30 gün",
age_days < 90 ~ "1-3 ay",
age_days < 365 ~ "3-12 ay",
TRUE ~ "1 yıl+"
)
) |>
count(status, age_group) |>
pivot_wider(names_from = status, values_from = n, values_fill = 0)
Eski Draft’ları Bulma
# 90 günden eski draft'lar
stale_drafts <- drafts |>
mutate(
modified_date = as.Date(modified),
days_stale = as.numeric(Sys.Date() - modified_date)
) |>
filter(days_stale > 90) |>
arrange(desc(days_stale)) |>
select(id, title, days_stale)
cat("90+ gün önce güncellenen draft sayısı:", nrow(stale_drafts), "\n")
print(stale_drafts)
Sonraki Adımlar
Bu temel yapı ile WordPress içeriklerinizi R’dan yönetebilirsiniz. Daha ileri analizler için:
- Semantic analiz: Embedding’lerle içerik benzerliği
- Graph görselleştirme: İç link ilişkileri
- Gap analizi: Eksik içerik tespiti
- SEO alignment: Hedef keyword coverage
Bu konuları interaktif bir dashboard’da birleştiren R Shiny Content Intelligence Dashboard yazısına göz atın.
Eski Yöntem: XML-RPC (Deprecated)
XML-RPC güvenlik riskleri nedeniyle önerilmiyor. Birçok hosting sağlayıcısı varsayılan olarak devre dışı bırakıyor. REST API kullanın.
Eski RWordPress paketi XML-RPC kullanıyordu:
# ❌ ESKİ YÖNTEM - Kullanmayın
# library("RWordPress")
# options(WordPressLogin = c(user = "pass"),
# WordPressURL = "http://site.com/xmlrpc.php")
# getPosts()
Sık Sorulan Sorular (FAQ)
WordPress REST API R’dan nasıl kullanılır?
httr2 paketi ile req_auth_basic() fonksiyonu kullanarak Application Passwords ile kimlik doğrulama yapılır. wp-json/wp/v2/posts endpoint’ine GET isteği gönderilir.
request(base_url) |>
req_url_path_append("posts") |>
req_auth_basic(user, app_password) |>
req_perform()
WordPress Application Password nedir?
WordPress 5.6+ ile gelen, REST API için güvenli kimlik doğrulama yöntemi. Admin → Users → Profile → Application Passwords bölümünden oluşturulur. Bir kez gösterilen şifreyi .Renviron dosyasında saklayın.
R ile WordPress draft’ları nasıl listelenir?
status = "draft" parametresi ile:
drafts <- get_wp_posts(status = "draft")
100’den fazla içerik için get_all_posts() fonksiyonu pagination yapar.
XML-RPC neden önerilmiyor?
XML-RPC güvenlik riskleri nedeniyle birçok hosting sağlayıcısı tarafından varsayılan olarak kapatılıyor. REST API daha güvenli, daha hızlı ve daha esnek bir alternatif.
WordPress içerik yaşı analizi nasıl yapılır?
R’da mutate() ve case_when() ile tarih farkı hesaplanır:
all_content |>
mutate(
age_days = as.numeric(Sys.Date() - as.Date(date)),
age_group = case_when(
age_days < 30 ~ "Son 30 gün",
age_days < 90 ~ "1-3 ay",
TRUE ~ "3 ay+"
)
)
Özet: Temel Çıkarımlar
- httr2 + tidyverse kombinasyonu modern WordPress API entegrasyonu sağlar
- Application Passwords güvenli kimlik doğrulama için zorunlu
- status parametresi ile draft/publish/pending/future filtreleme yapılır
- Pagination ile 100’den fazla içerik çekilebilir (
X-WP-TotalPagesheader) - XML-RPC deprecated - güvenlik nedeniyle REST API tercih edin
*[API]: Application Programming Interface *[REST]: Representational State Transfer