Serinin ilk yazısında gurmezin.com’u bir yapay zekâ ajanıyla (Claude Code) yönetmek için kurduğumuz altyapıyı anlatmış, sıradaki adımda içerik otomasyonunun ilk gerçek örneklerini paylaşacağımızı söylemiştik. Bu yazıda o örneği anlatıyoruz. Bir yazının bilgisayardaki bir Markdown dosyasından çıkıp sitede belirli bir saatte yayınlanmasına kadar geçtiği yolu adım adım gösteriyoruz. Bu yolun bir kez nasıl tıkandığını ve ardından hangi kuralı koyduğumuzu da saklamadan yazıyoruz.
Yayın Düzeni: Pazartesi ve Perşembe, Saat 10:00
gurmezin.com’da haftada iki yazı yayınlanıyor: Pazartesi ve Perşembe, Türkiye saatiyle 10:00’da. Bu yazıların çoğu Python eğitim yazıları, arada yapay zekâ gündemine dair özetler ve bu seri gibi çalışma notları da yer alıyor. Ekim başından bu yana yayınlanan yazılar, bu yazıda anlatacağımız hattan geçti.
Hattın her aşamasını tek tabloda görelim:
| Aşama | Ne üretiliyor? | Araç |
|---|---|---|
| 1. Hazırlık | Yazı klasörü ve örnek kod | Klasör düzeni, Python |
| 2. Yazım | yazi.md |
Markdown |
| 3. Görsel | 1200×900 öne çıkan görsel | Python + Pillow |
| 4. Dönüşüm | Gutenberg blokları | md2wp.py (pandoc + BeautifulSoup) |
| 5. Yükleme | Zamanlanmış yazı | wp_yukle.py (WP REST API) |
| 6. Paylaşım | Sosyal medya metinleri | medya.md |
1. Her Yazının Bir Klasörü Var
Her yazı, tarihle başlayan kendi klasöründe hazırlanıyor. Örneğin şu an okuduğunuz yazının klasörü yazilar/2026-10-12-claude-otomasyon-yayin-hatti/. Klasörde yazının Markdown metni, görseli üreten betik, görselin kendisi, yükleme bilgileri ve sosyal medya metinleri birlikte duruyor. Bir yazı hakkında bilinmesi gereken her şey tek bir yerde bulunuyor.
Python yazılarında bu klasörde bir de main.py bulunuyor. Yazıdaki her kod bloğunun çıktısı bu dosya çalıştırılarak alınıyor, yani çıktıları tahmin etmiyoruz. Örneğin dataclasses yazısındaki bellek ölçümü bu şekilde yapıldı. Yazı yayınlandıktan sonra kodun temiz bir kopyası GitHub’daki practical-python-examples deposuna sıradaki numarayla ekleniyor.
2. Markdown’dan Gutenberg Bloklarına
Yazılar Markdown ile yazılıyor, çünkü düz metin hem insanın hem yapay zekânın rahatça okuyup düzenleyebildiği bir biçim. WordPress’in blok düzenleyicisi (Gutenberg) ise içeriği bloklar halinde saklıyor. Markdown’dan üretilen HTML’i doğrudan gönderirsek yazı düzenleyicide tek bir “Klasik” blok olarak görünüyor ve sonradan bir paragrafı düzeltmek zorlaşıyor.
Bu yüzden md2wp.py adında küçük bir dönüştürücü yazdık. Önce pandoc, Markdown’ı HTML’e çeviriyor. Ardından BeautifulSoup, en üst seviyedeki her HTML öğesini karşılık gelen Gutenberg bloğuna sarıyor:
if el.name == "p":
bloklar.append(blok("paragraph", str(el)))
elif el.name in ("h2", "h3", "h4"):
seviye = "" if el.name == "h2" else '{"level":%s}' % el.name[1]
bloklar.append(blok("heading", str(el), seviye))
elif el.name == "table":
bloklar.append(blok("table", f'<figure class="wp-block-table">{el}</figure>'))
Dönüştürücü birkaç küçük işi daha üstleniyor. Markdown’daki ilk başlığı atıyor, çünkü yazının başlığı WordPress’e ayrı bir alan olarak gönderiliyor. GURMEZINMOREMARKER satırını “devamını oku” bloğuna çeviriyor. Kod bloklarındaki [ karakterini [ olarak yazıyor. Bunun nedeni şu: WordPress köşeli parantez içindeki bazı ifadeleri kısa kod (shortcode) sanıp yorumlamaya çalışabiliyor.
Bu dönüştürücünün de gözden kaçan bir hatası vardı. Kod bloklarında <code> etiketine programlama dilini belirten iki nitelik ekliyordu:
<!-- md2wp.py'nin ürettiği -->
<pre class="wp-block-code"><code class="language-python" lang="python">...</code></pre>
<!-- WordPress'in kod bloğunun beklediği -->
<pre class="wp-block-code"><code>...</code></pre>
Ziyaretçilerin gördüğü sayfada hiçbir sorun yoktu, bu yüzden hata birkaç yazı boyunca fark edilmedi. Ancak blok düzenleyici, açılan her bloğu kendi kurallarıyla yeniden oluşturup kayıtlı haliyle karşılaştırıyor. Fazladan bir nitelik bile bulduğunda bloğu “Bu blok beklenmedik ya da geçersiz içerik içeriyor” uyarısıyla işaretliyor. Site sahibi bir yazıyı düzenlemek için açtığında bütün kod bloklarında bu uyarıyla karşılaştı.
Düzeltme iki adımda yapıldı. Önce dönüştürücüden bu iki nitelik çıkarıldı. Sitede kod renklendirme eklentisi olmadığı için zaten bir işe yaramıyorlardı. Ardından REST API ile etkilenen dört yazı tarandı ve 30 kod bloğu düzeltildi. Değişiklikten önce her yazının ham içeriği yedeklendi. Buradan çıkardığımız ders şu: Otomatik üretilen içeriği yalnızca sitede değil, düzenleyicide de en az bir kez açıp kontrol etmek gerekiyor.
3. Yükleme: Tek Komut, Tek Bilgi Dosyası
Yazının WordPress’te ihtiyaç duyduğu her bilgi klasördeki yazi.json dosyasında toplanıyor:
{
"baslik": "Python'da dataclasses: Sınıf Yazmanın Kısa Yolu",
"slug": "pythonda-dataclasses-sinif-yazmanin-kisa-yolu",
"tarih": "2026-10-08T10:03:00",
"markdown": "yazi.md",
"gorsel": "python-dataclasses.jpg",
"gorsel_alt": "...",
"ozet": "...",
"kategoriler": ["kod", "programlama", "python", "Türkçe"],
"etiketler": ["python", "dataclasses", "..."]
}
wp_yukle.py bu dosyayı okuyup işi sırayla yapıyor:
- Aynı adreste (slug) bir yazı olup olmadığını kontrol ediyor. Varsa durup uyarı veriyor. Böylece aynı yazı yanlışlıkla iki kez yüklenmiyor.
- Markdown’ı
md2wp.pyile bloklara çeviriyor. - Görseli medya kütüphanesine yüklüyor. Alt metin, altyazı ve açıklamayı da ekliyor.
- Kategori ve etiketleri adıyla arıyor, bulamadığını oluşturuyor.
- Yazıyı
future(zamanlanmış) durumunda, belirtilen tarih ve saatle oluşturuyor.
Betik yalnızca Python’un standart kütüphanesini (urllib) kullanıyor. Kimlik bilgileri proje dosyalarında değil, ortam değişkenlerinde duruyor. İlk yazıda anlattığımız gibi, kullanılan WordPress hesabı Editor rolünde: içerik yönetebiliyor ama eklentilere, temaya ve site ayarlarına dokunamıyor.
4. “Önce Taslak” Kuralından “Zamanlanmış” Akışa
İlk yazıda otomasyonla oluşturulan yazıların önce taslak olarak kaydedileceğini, yayına alma kararının ise her zaman insana ait olacağını yazmıştık. Pratikte akışımız biraz değişti, bunu da açıkça belirtelim.
Yazılar artık taslak olarak değil, doğrudan zamanlanmış olarak yükleniyor. Zamanlanmış bir yazı, yayın saatine kadar ziyaretçilere görünmüyor. Site sahibi bu süre içinde yazıyı okuyup değiştirebiliyor ya da yeniden taslağa alabiliyor. Konu seçimi ve yayın takvimi kararı da yine site sahibinde. Değişen tek şey, onay verilmezse yazının olduğu yerde beklemesi yerine zamanında yayınlanması. Haftada iki yazılık bir düzende bu, taslakların unutulmasını önlüyor.
wp_yukle.py hâlâ --taslak seçeneğini destekliyor. Yayından önce mutlaka elden geçmesi gereken yazılar bu seçenekle taslak olarak yüklenebiliyor.
5. Görsel ve Sosyal Medya Metinleri
Öne çıkan görseller hazır bir görsel üretme servisinden değil, her yazı için yazılan kısa bir Pillow betiğinden çıkıyor. Görselin boyutu 1200×900. Üzerinde yazının başlığı ve konusunu özetleyen bir unsur yer alıyor. Örneğin dataclasses yazısında bu unsur, 13 satırlık klasik bir sınıfla 5 satırlık dataclass karşılığının yan yana gösterilmesiydi. Betik yazı klasöründe kaldığı için görselde bir düzeltme gerekirse görsel birkaç saniyede yeniden üretilebiliyor.
Bu yöntemin küçük ama öğretici bir tuzağı var: her yazı tipi her karakteri içermiyor. Dataclasses görselinde kullandığımız “→” oku, seçtiğimiz yazı tipinde bulunmadığı için ilk denemede boş bir kare olarak çıktı. Görseli yayından önce mutlaka gözle kontrol etmemizin nedeni bu.
Her klasörde bir de medya.md dosyası var. Bu dosyada LinkedIn, X ve Facebook için ayrı ayrı yazılmış paylaşım metinleri ile görselin alt metni ve açıklaması bulunuyor. Yazı yayınlandığında gerçek bağlantı bu metinlere ekleniyor ve paylaşım hazır hale geliyor.
6. Hat Bir Kez Tıkandı: 8 Ekim Perşembe
Bu kadar araç olunca sistemin kendi kendine işlediğini düşünmek kolay. Ama her aracın çalışması için birinin onu çalıştırması gerekiyor.
8 Ekim Perşembe sabahı saat 09:58’de, birkaç dakika sonra yayınlanması gereken yazının hiç hazırlanmadığını fark ettik. Sitede zamanlanmış bir yazı yoktu. Araçlar hazır olduğu için konu seçimi, örnek kodun yazılıp çalıştırılması, görsel ve yükleme hızla tamamlanabildi. Ancak yazı 10:00 yerine 10:03’te yayına girdi ve site sahibinin yazıyı yayından önce okuma fırsatı olmadı.
Sorun araçlarda değil, takvimdeydi. Yazılar ancak biri “hazırlayalım” dediğinde hazırlanıyordu, bu yüzden yayın günü sabahına kadar hiçbir şey olmayabiliyordu. Bu olaydan sonra şu kuralı koyduk:
Sitede her zaman en az bir zamanlanmış yazı bulunur. Bir yazı yayına girdiğinde sıradaki yazı zaten zamanlanmış olmalıdır.
Kuralı uygulamak kolay, çünkü sıradaki yazıların listesi REST API ile tek bir istekle görülebiliyor:
curl -s -u "$WP_USER:$WP_APP_PASSWORD" \
"$WP_URL/wp-json/wp/v2/posts?status=future&_fields=id,date,title"
Bu istek boş bir liste ([]) döndürüyorsa, kuyruk boşalmış demektir. Şu an okuduğunuz yazı da bu kural konduktan sonra, yayın gününden dört gün önce hazırlanıp zamanlanan ilk yazı.
7. Ajan Neyi Hatırlıyor?
Bir yapay zekâ ajanıyla çalışırken akla gelen sorulardan biri şu: Her yeni oturumda kuralları baştan anlatmak gerekiyor mu? Claude Code, proje için kalıcı bir hafıza dosyası tutabiliyor. gurmezin.com’da bu hafızada şu tür bilgiler yer alıyor:
- Yazıların her zaman 10:00’da zamanlanacağı (bir yazıyı 09:00’a zamanladığımızda gelen düzeltmeden sonra eklendi)
- Her yazı klasörüne
medya.mdekleneceği - Yazıların hangi klasörde hazırlanacağı ve GitHub kopyasının nereye gideceği
- Yükleme araçlarının nerede olduğu ve nasıl çalıştırılacağı
- Az önce anlattığımız “en az bir zamanlanmış yazı” kuralı
Bu bilgilerin çoğu bir hatadan ya da düzeltmeden sonra eklendi. Hafıza bu sayede kendiliğinden bir kural listesine dönüşüyor.
Sırada Ne Var?
İlk yazıda ilk somut iyileştirme olarak görsellerin WebP biçimine geçirilmesini göstermiştik. Bu iş henüz yapılmadı. Yeni yazıların görselleri de hâlâ JPG olarak yükleniyor. Sıradaki adımlar şunlar:
- WebP dönüşümü: Önce yeni görseller, sonra mevcut medya kütüphanesi.
- Kuyruk kontrolünün otomatikleşmesi: Zamanlanmış yazı kalmadığında uyarı veren düzenli bir kontrol.
- GitHub deposunun bakımı: Eski projelerin README dosyalarında bulduğumuz biçim hatalarının düzeltilmesi.
Her yazıda olduğu gibi bu adımları da gerçekten yapıldıkça, yaşandığı gibi paylaşacağız.
Ahmet Aksoy & Claude



