top of page

Programatik SEO ile İçerik Genişletme: Python ve Velo Kullanım Kılavuzu

SEO dünyasında "içerik kraldır" sözünü sıkça duyarsınız. Bu söz kesinlikle doğrudur, ancak hangi içerik, hangi yazım tarzı ve hangi anahtar kelimeler için içerik üretileceği konusunda net bir yol haritası sunmaz. Anahtar kelime araştırmaları arama talebini gösterebilir, fakat içeriğin gerçekten performans gösterip göstermeyeceğini ancak yayınlandıktan sonra anlayabilirsiniz.


Bu yazıda, programatik içerik üretimini kullanarak hızlıca bir e-ticaret sitesi nasıl oluşturulacağını göreceğiz. Yazılımın büyük avantajı, devasa maliyetler olmadan işleri ölçeklendirebilmenizdir. Bir içerik çerçevesi oluşturarak, bir ürünün trafik çekme potansiyelini test edebilir ve prototip geliştirebilirsiniz.


İçerik Kapsamı


Bu ileri düzey bir SEO stratejisi olacak ve programlama adımlarını içerecek. Veri manipülasyonu için Python, API geliştirmek için ise JavaScript tabanlı Wix Velo kullanacağız. Bu rehberi işaretlemenizi öneririm çünkü projenin bölümleri birçok farklı web projesine adapte edilebilir.


İşleyeceğimiz temel beceriler şunlardır:


  • Chrome'un Snippets özelliğiyle script yazımı

  • Puppeteer ile web scraping

  • Karışık veri dosyalarını temizleme

  • Programatik ürün açıklamaları oluşturma

  • Ürün görsellerini dinamik olarak renklendirme

  • Wix Velo ile API geliştirme


Veri mühendisliğine aşina olanlar için bu klasik bir ETL sürecidir: Ürün verilerini çıkaracak (extract), kullanabileceğimiz bir formata dönüştürecek (transform) ve Velo ile Wix sitemize yükleyeceğiz (load).



Programatik SEO E-ticaret Sitesi İçin Gerekenler


Bir e-ticaret sitesi başlatmak için çok şeye ihtiyacınız yoktur: Bir içerik yönetim sistemi (CMS), bir marka ve bir ürün yeterlidir.


İçerik Yönetim Sistemi (CMS)


Bu demo için Wix'i e-ticaret CMS'i olarak kullanacağız. Wix'in güçlü Velo platformu sayesinde, JavaScript IDE ortamında NodeJS çalıştırarak hem frontend hem de backend ile etkileşime girebilir ve programatik olarak içerik ekleyebilirsiniz.


Marka


Projemiz için Candle Crafty adında butik el yapımı mum mağazası oluşturacağız. Henüz hangi mumları üretmemiz gerektiğini bilmiyoruz. Anahtar kelime araştırması yardımcı oldu, ancak hangi kokuların veya renklerin popüler olacağı konusunda yeterli yönlendirme sağlamadı. Bunun yerine, ürünümüzün birçok varyasyonunu programatik olarak oluşturacağız ve arama motorlarının müşterileri doğru ürünlere yönlendirmesine güveneceğiz.


Ürün


Ürün kaynağı (tedarikçi) olarak candlescience.com'dan kokular ve renkler kullanacağız. Bu site, yeni mum işletmelerine yardımcı olmak için geniş bir koku yelpazesi, isimlendirme fikirleri ve renk önerileri sunuyor.


Chrome ile Ürün Kazıma (Scraping)


Tüm Ürün Sayfalarını Toplama


Ürün çıkarımı için mevcut tüm kokuları keşfetmemiz gerekiyor. Neyse ki Candle Science'ın ürün listeleme sayfası eksiksiz bir URL listesi içeriyor. Bir sayfadaki tüm <a> etiketlerini kazımanın pek çok yolu vardır. En hızlı prototipleme yollarından biri doğrudan tarayıcıda script çalıştırmaktır.


Chrome'da: Sağ tıklayın > "İncele" > "Sources" sekmesi > "New Snippet" düğmesi. Buradan sayfada JavaScript çalıştırabilirsiniz. İşte scriptimiz:


Google Chrome'un İnceleme özelliği içinde JavaScript çalıştırmak

let x = document.getElementsByClassName('products')[0]
let links = x.getElementsByTagName("a");
let rows = ['Links'];
for (link of links) {
    rows.push(link.href)
}

let csvContent = "data:text/csv;charset=utf-8," 
    + rows.join("\n");

var encodedUri = encodeURI(csvContent);
window.open(encodedUri);

Bu script, "products" sınıfına sahip ilk elementi alarak başlar. Sonra bu sınıf içindeki tüm linkleri toplar. 3-5. satırlar linkleri bir diziye koyar, 6. satırdan sonrası ise bu diziyi CSV formatına aktarır. Artık tüm ürünlerin olduğu bir CSV dosyamız var!


Puppeteer ile Ürün Sayfalarını Kazıma


Bu bölümde Python kullanacağım. Normalde web kazıma için Python'un requests kütüphanesini kullanırım, ancak tedarikçi web sitesinin Nuxt.js ile oluşturulduğunu ve ürün açıklamalarının sunucu tarafında render edilmediğini fark ettim.


Bu sorunu Pyppeteer ile çözebiliriz. Google'ın headless Chrome ürünü için bir sarmalayıcıdır ve JavaScript siteleri render etmenizi sağlar.


Sayfaya baktığımızda, ürünlerimizi oluşturmak için yararlı olacak bir dizi özellik çıkarabiliriz:


  • Ürün Başlığı

  • Ürün Açıklaması

  • Üst Notalar

  • Orta Notalar

  • Alt Notalar

  • Karışım Fikirleri

  • Renk Fikirleri


İlk olarak, extract.py adında bir dosya oluşturalım. Dosyanın başında gerekli kütüphaneleri içe aktarıyoruz. CSV okuma ve yazma için Pandas, HTML ayrıştırma için BeautifulSoup, web tarayıcısını başlatmak için asyncio ve Pyppeteer.

import pandas as pd
from bs4 import BeautifulSoup
import asyncio
from pyppeteer import launch

Web tarayıcımız için bir user agent'a ihtiyacımız var. Chrome'un user agent'ını kullanabilir veya kendinizi başka şekillerde tanıtabilirsiniz.

headers = {
    'User-Agent': 'CandleCrafty 1.0',
}

Ardından, ilgilendiğimiz metin özelliklerini içeren CSS sınıflarını aramak için bir parse fonksiyonu yazalım. Bu fonksiyon HTML içeriğini alır, "soup" haline getirir ve başlıkları, koku notalarını veya ürün açıklamalarını yakalamamıza izin verir. Sonuçları CSV'ye kolayca ekleyebilmek için liste formatında döndürüyoruz.

def parse(content):
    soup = BeautifulSoup(content, 'html.parser')
 
    # Başlık
    title = soup.find(class_="product-headline").text.strip()
    
    # Notaları Al
    notes = soup.find(class_="fragrance-notes")
    txt = notes.findAll('span')
    res = []
    [res.append(spans.getText().strip()) for spans in txt if spans.getText().strip() not in res]
    res = [i for i in res if i]
    
    try:
        top_notes = res[1]
    except:
        top_notes = ''
    try:
        mid_notes = res[3]
    except:
        mid_notes = ''
    try:
        base_notes = res[5]
    except:
        base_notes = ''
    notes_fallback = res
 
    # Ürün Açıklamasını Al
    txt = soup.find(class_="text")
    description_p = txt.text.split('\n')
    blend_ideas = ''
    brand_ideas = ''
    color_ideas = ''
    note = ''
    complete_list = ''
    paragraphs = ''
    
    for p in description_p:
        if ':' in p and 'blend' in p.lower():
            blend_ideas = p
        elif ':' in p and 'brand' in p.lower():
            brand_ideas = p
        elif ':' in p and 'color' in p.lower():
            color_ideas = p
        elif ':' in p and 'note' in p.lower():
            note = p
        elif 'complete list' in p.lower():
            complete_list = p
        else:
            paragraphs = paragraphs + '\n' + p
            
    return [title, top_notes, mid_notes, base_notes, notes_fallback, blend_ideas, brand_ideas, color_ideas, note, complete_list, paragraphs]

Son olarak ana fonksiyonumuzu çalıştırıyoruz. Hedef URL'lerimizin CSV dosyasını okur ve bunları başlatılmış bir tarayıcı kullanarak döngüye alır. Sonuçlar kaydedilir.

async def main():
    df = pd.read_csv("download.csv")
    urls = list(df['Links'])
    browser = await launch()
    page = await browser.newPage()
    data = []
    
    for url in urls:
        await page.goto(url)
        content = await page.content()
        data.append(parse(content))
        df = pd.DataFrame(data, columns=['Title', 'Top notes', 'Mid notes', 'Base notes', 'Notes Fallback', 'Blend ideas', 'Brand ideas', 'Color ideas', 'Note', 'Complete list', 'Paragraphs'])
        df.to_csv('save.csv')
        
    await browser.close()
 
asyncio.get_event_loop().run_until_complete(main())

İşte kazıma scriptimiz tamamlandı! İşte ilk veri satırımız (kısaltılmış hali):

Başlık

Üst Notalar

Orta Notalar

Alt Notalar

Karışım Fikirleri

Marka Fikirleri

Renk Fikirleri

Paragraflar

Alpine Balsam

Bergamot, Şampanya

Sedir, Balsam

Paçuli, Yosun, Ardıç, Çam

Saffron Cedarwood, Fireside

Twinkling Balsam, White Balsam, Noble Fir and Cedar

Doğal, Koyu Yeşil

Soğuk bir kış gecesini hayal edin...

Veri Temizleme


Bu en eğlenceli konu olmayabilir, ancak veri veya web projelerinin büyük bir parçasıdır. Web'den soyut verileri alıp düzenli hale getirmek son derece faydalı bir beceridir.


Karışık Metin Temizleme


Verilerimiz biraz karışık başladı. Karışım fikirleri, Marka fikirleri ve Renk fikirleri sütunlarını, ekstra metni kaldırarak ve sadece gerçekten yararlı olan virgülle ayrılmış değerleri döndürerek temizleyebiliriz. Her birinin iki nokta üst üste işaretiyle ayrılmış değerleri var, bu yüzden iki nokta üst üsteden sonraki tüm metni alabiliriz.

def clean_pretext(data):
    try:
        return data.split(':')[1].strip()
    except:
        return data
 
clean_pretext(row['Blend ideas'])

Ayrıca paragraflarla ilgili birkaç sorunu temizleyebiliriz:


  • İçerikten önce veya sonra boşluklar

  • Çok satırlı metin

  • "’" gibi görünebilecek bozuk kodlama

s = row['Paragraphs'].strip()
s = ' '.join(s.splitlines())
s = "".join([x if ord(x) < 128 else '' for x in s])

Renk Metnini Hex Kodlarına Dönüştürme


Bazı durumlarda metni veriye dönüştürmemiz gerekir. Renk fikirlerini hex kodlarına eşleştirelim. Python'da Colour adında harika bir kütüphane var, metin açıklamamız için doğru rengi bulmamıza yardımcı olacak.

from colour import Color
 
def get_color(data):
    hex = '#ffffff'
    if isinstance(data, str):
        arr = re.findall(r"[\w']+", data)
        for item in arr:
            try:
                hex = Color(item).hex
            except:
                pass
    return hex
 
get_color(row['Color ideas'])

Her Şeyi Bir Araya Getirme


Bu script her satırda döngü yapar, verileri temizler ve renkler oluşturur. Ardından iki dosya kaydeder: iyileştirilmiş verilerle yeni bir CSV ve her açıklamanın tek satırda olduğu yeni bir .txt dosyası.

import pandas as pd
import re
from colour import Color
 
df = pd.read_csv("../extract/data.csv")
df['hexcodes'] = '#ffffff'
df['color_literal'] = 'white'
txt = ''
 
def clean_pretext(data):
    try:
        return data.split(':')[1].strip()
    except:
        return data
 
def get_color(data):
    hex = '#ffffff'
    color_literal = 'white'
    if isinstance(data, str):
        arr = re.findall(r"[\w']+", data)
        for item in arr:
            try:
                color_literal = Color(item)
                hex = color_literal.hex
            except:
                pass
    return {"color": color_literal, "hex": hex}
 
for i, row in df.iterrows():
    # Ön metni kaldır
    df.at[i,'Blend ideas'] = clean_pretext(row['Blend ideas'])
    df.at[i,'Brand ideas'] = clean_pretext(row['Brand ideas'])
    df.at[i,'Color ideas'] = clean_pretext(row['Color ideas'])
 
    # Hex kodları oluştur
    color_data = get_color(row['Color ideas'])
    df.at[i,'hexcodes'] = color_data['hex']
    df.at[i,'color_literal'] = color_data['color']
 
    # Paragrafı temizle
    s = row['Paragraphs'].strip()
    s = ' '.join(s.splitlines())
    s = "".join([x if ord(x) < 128 else '' for x in s])
    df.at[i,'Paragraphs'] = s
 
    # Temizlenmiş paragrafı metin dosyasına ekle
    txt = txt + '\n' + s
 
# Yeni CSV'yi dışa aktar
df.to_csv("cleaned.csv")
 
# Yeni içerik metin dosyasını dışa aktar
with open('content.txt', 'w') as f:
    f.write(txt)

Güçlü Programatik Ürün Açıklaması Nasıl Yazılır


Otomatik oluşturulan içeriğin manuel işleme yol açabileceğini hatırlayabilirsiniz. Google özellikle "otomatik eş anlamlı kullanım veya gizleme teknikleri kullanılarak oluşturulan metin"i vurguladı. Ayrıca Yardımcı İçerik güncellemesine de dikkat etmeliyiz. Açıklamaların yardımcı ve ürünleri tanımlamada kullanışlı olduğundan emin olmalıyız.


Bunun diğer yüzü, Google'ın yinelenen ürün açıklamalarını nasıl ele aldığıdır: Yinelenen içeriğe sahip tüm sayfalar arasından en alakalı siteyi seçer. Bu, yeni bir mağazanın tedarikçiden gelen varsayılan metinle rekabet edemeyeceği anlamına gelir.


Bununla başa çıkmak için Google ile yarı yolda buluşuyoruz: Açıklamanın gerçek insanlara yardımcı olduğundan emin olmak istiyor. Google özellikle "yayınlamadan önce insan incelemesi veya düzenlemesi"ni vurguluyor. Temel olarak, üretirseniz, okunabilir olduğundan emin olun.


Bu bizi metin oluşturma için iki stratejimize götürüyor:


  • Makine öğrenimi ile oluşturulan metin

  • Ad lib tarzı metin oluşturma


Makine öğrenimi içerik yazımı için inanılmaz derecede faydalı olabilir, ancak bir bilim kadar da bir sanattır. Bu demo amacıyla, makine öğrenimi metin oluşturma sonuçları çok kötüydü ve çok fazla düzenleme çalışması gerektiriyordu. Google'ın istemediği türden bir içerikti.


İkinci strateji ile, okuyucuya yardımcı olan ve ürünü detaylandıran yeterince benzersiz açıklamalar yapabiliriz, bu yüzden bunu kullanacağız.


"Boşluk Doldurma" Stratejisi


Bu strateji Ad Lib tarzı oyunlara çok benzer. Temel bir şablon sağlıyoruz, biraz çeşitlilik ekliyoruz ve ardından bir açıklama elde ediyoruz.


İyi bir ürün açıklaması birkaç şey yapabilir:


  • Tüketicinin ürünü kullanırken kendini hayal etmesini sağlar

  • Ürünün faydalarını açıklar

  • Duyusal kelimeler kullanır

  • Sosyal kanıt sağlar

  • Sayılar kullanır


Bununla birlikte, yardımcı metin dizileri önceden oluşturabiliriz.

verb = ['süzülen', 'yayılan', 'kayan', 'dönen']
noun = ['ikram', 'zevk', 'keyif']
adj = ['büyüleyici', 'keyifli']
feeling = ['mutlu', 'neşeli', 'memnun', 'huzurlu', 'coşkulu']
craftsmanship = ['özenle hazırlanmış', 'el yapımı', 'elle dökülmüş', 'tasarım', 'mimari', 'özel', 'seçkin']
benefits = ['Zeninizi bulun', 'Mutlu yerinizi tasarlayın', 'Misafirlerinizi büyüleyin', 'Rahatlayın ve gelişin']
socialproof = ['En popüler kokularımızdan biri, ', 'Favorilerden biri, ', 'Sürekli harika tepkiler alan, ']
cta = ['Hemen sipariş verin!', 'Bugün sipariş edin!', 'Mum kulübüne katılın ve şimdi sipariş verin!', 'İdeal kokununuzu bugün sipariş edin!', 'Bu kokuyu bugün evinize götürün!']
numbers = ['Bu 240 gramlık mumlar ortalama 60 saat dayanır.', '60 saatlik mumlarımız evinizi defalarca dolduracak.', 'Bu uzun ömürlü mumlar 60 saate kadar koku sağlayacak.']
product = ['soya mumu', 'soya kokulu mum', 'tamamen doğal mum', 'koku']

Açıklama için mum detaylarından bazılarını kullanmak istiyoruz. Çekmek için oldukça fazla seçeneğimiz var:


  • Renk

  • Başlık

  • Tedarikçi açıklamasının parçaları

  • Koku notaları


CSV'nin her satırında döngü yapacağız ve bu detayları çekerek bazı rastgeleleştirilmiş cümleler oluşturacağız.

def getNotes(data):
    try:
        return re.findall(r"[\w']+", data)
    except:
        return []
 
for i, row in df.iterrows():
    color = row['color_literal']
    # Tedarikçiden ilk cümleyi ödünç al
    sentence = tokenize.sent_tokenize(row['Paragraphs'])[0]
    title = row['Title']
    # Tüm koku notalarımızı al
    top_notes = getNotes(row['Top notes'])
    mid_notes = getNotes(row['Mid notes'])
    base_notes = getNotes(row['Base notes'])
    # Bunları tek bir dizide birleştir
    all_notes = [*top_notes, *mid_notes, *base_notes]
    # Virgülle ayrılmış metne biçimlendir. Son notadan önce 've' ekle.
    notes_txt = ''
    for x in all_notes[:-1]:
        notes_txt = notes_txt + x + ', '
    notes_txt = notes_txt + 've ' + all_notes[-1]

Ardından, bu değişkenleri alıp bir cümle listesine birleştirebiliriz.

    # Bazı cümleler oluştur
    sentences = [
        f'{title}, evinizde {random.choice(noun)} olması kesin {random.choice(craftsmanship)} bir {random.choice(product)}tir.',
        f'{random.choice(socialproof)} bu {color} {random.choice(product)} sizi {random.choice(feeling)} hissettirecek.',
        f'{random.choice(benefits)} {title} - {notes_txt} notalarıyla {random.choice(craftsmanship)}.',
        sentence
    ]

Bu cümleleri rastgele bir sıraya karıştırarak başka bir rastgeleleştirme katmanı ekleyebiliriz. Bunları herhangi bir sırada mantıklı olacak şekilde yazdık.

random.shuffle(sentences)

Son cümle bir harekete geçirici mesaj olmalı. Bunu ekleyelim.

sentences.append(random.choice(cta))

Son olarak, cümle dizisini nihai bir açıklamada birleştirebiliriz.

description = ' '.join(sentences)

Her Şeyi Bir Araya Getirme


İşte script bir araya getirildiğinde nasıl görünüyor:

import pandas as pd
from nltk import tokenize
import random
import re
 
df = pd.read_csv("../../transform/cleaned.csv")
df['adlib'] = df['Paragraphs']
 
verb = ['süzülen', 'yayılan', 'kayan', 'dönen']
noun = ['ikram', 'zevk', 'keyif']
adj = ['büyüleyici', 'keyifli']
feeling = ['mutlu', 'neşeli', 'memnun', 'huzurlu', 'coşkulu']
craftsmanship = ['özenle hazırlanmış', 'el yapımı', 'elle dökülmüş', 'tasarım', 'mimari', 'özel', 'seçkin']
benefits = ['Zeninizi bulun', 'Mutlu yerinizi tasarlayın', 'Misafirlerinizi büyüleyin', 'Rahatlayın ve gelişin']
socialproof = ['En popüler kokularımızdan biri, ', 'Favorilerden biri, ', 'Sürekli harika tepkiler alan, ']
cta = ['Hemen sipariş verin!', 'Bugün sipariş edin!', 'Mum kulübüne katılın ve şimdi sipariş verin!', 'İdeal kokununuzu bugün sipariş edin!', 'Bu kokuyu bugün evinize götürün!']
numbers = ['Bu 240 gramlık mumlar ortalama 60 saat dayanır.', '60 saatlik mumlarımız evinizi defalarca dolduracak.', 'Bu uzun ömürlü mumlar 60 saate kadar koku sağlayacak.']
product = ['soya mumu', 'soya kokulu mum', 'tamamen doğal mum', 'koku']
 
def getNotes(data):
    try:
        return re.findall(r"[\w']+", data)
    except:
        return []
 
for i, row in df.iterrows():
    color = row['color_literal']
    sentence = tokenize.sent_tokenize(row['Paragraphs'])[0]
    title = row['Title']
    top_notes = getNotes(row['Top notes'])
    mid_notes = getNotes(row['Mid notes'])
    base_notes = getNotes(row['Base notes'])
    all_notes = [*top_notes, *mid_notes, *base_notes]
    notes_txt = ''
    for x in all_notes[:-1]:
        notes_txt = notes_txt + x + ', '
    notes_txt = notes_txt + 've ' + all_notes[-1]
 
    sentences = [
        f'{title}, evinizde {random.choice(noun)} olması kesin {random.choice(craftsmanship)} bir {random.choice(product)}tir.',
        f'{random.choice(socialproof)} bu {color} {random.choice(product)} sizi {random.choice(feeling)} hissettirecek.',
        f'{random.choice(benefits)} {title} - {notes_txt} notalarıyla {random.choice(craftsmanship)}.',
        sentence
    ]
 
    random.shuffle(sentences)
    sentences.append(random.choice(cta))
 
    description = ' '.join(sentences)
    df.at[i,'adlib'] = description
 
df.to_csv('adlib.csv')

Ürün Görsellerini Dinamik Olarak Oluşturma


Temel verilerle, görseller oluşturmaya bakabiliriz. Şimdi Google'ın Imagen veya OpenAI'nin DALL·E 2 gibi inanılmaz makine öğrenimi görsel oluşturma araçları mevcut, ancak bu kadar gösterişli olmamıza gerek yok.


Başlangıçta çizgi sanatı ve Ölçeklenebilir Vektör Grafikleri (SVG'ler) kullanarak görselleri programatik olarak oluşturmayı denedim. İyi bir strateji çünkü HTML'ye çok benziyor ve hatta CSS kullanabiliyorsunuz. Görüntünün tüm öğeleri programlanabilir.


Ancak kalite yeterli değildi. Belki daha iyi bir tasarımcıysanız bu uygulanabilir bir stratejidir, bu yüzden hala bahsetmeye değer.


Bunun yerine, düzenli bitmap piksel görüntülerini kullanmayı deneyelim. Buradaki fikir iki PNG katmanına sahip olmak olacak. Biri statik arka plan olacak temel katmanımız olacak. Diğer görseli ise çeşitli RGB ayarlarıyla renklendireceğiz. Böylece görsellerimizi dinamik olarak renklendirebiliriz.


Soldaki temel görsel kendi başına oldukça güzel görünüyor. Mum rengini değiştirmek veya kavanozun üzerine metin eklemek için alan var. Sağdaki katmanı renklendirip/tonlayıp, temel katmanın üzerine bindirmek için Python Imaging Library (PIL) kullanabiliriz.

Bu demo için yeni bir Python dosyası başlatalım ve şu içe aktarımlarla başlayalım:

from PIL import Image, ImageOps, ImageDraw, ImageFont

PNG'lerimiz ve bazı fontlar gibi ön hazırlık varlıklarını yüklememiz gerekiyor. Aynı fontu iki kez ekledim, biri 28 piksel, diğeri 10 piksel boyutunda.

foreground = Image.open("image/Candle01.png")
background = Image.open("image/Candle02.png")
fnt = ImageFont.truetype("Shrikhand-Regular.ttf", 28)
fnt_sml = ImageFont.truetype("Shrikhand-Regular.ttf", 10)

Ardından, tint_image adında sihirli bir fonksiyon tanımlamamız gerekiyor. Görseli alacak, opaklığını kopyalayacak, gri tonlamalı yapacak, biraz renk ekleyecek ve sonra opaklığı geri koyacak. Sonuç renklendirilmiş bir görsel! Bunu ön planda kullanarak renkli mumumuzu dinamik olarak oluşturacağız. Demomuz için turuncu yapalım.

def tint_image(src, color="#FFFFFF"):
    src.load()
    r, g, b, alpha = src.split()
    gray = ImageOps.grayscale(src)
    result = ImageOps.colorize(gray, (0, 0, 0, 0), color)
    result.putalpha(alpha)
    return result
 
color = "orange"
foreground = tint_image(foreground, color)

Devam edip renklendirilmiş ön planı arka plana birleştirebiliriz.

background.paste(foreground, (0, 0), foreground)

Bir sonraki adım olarak biraz metin ekleyebiliriz. Burada ismi "Kayısı Bahçesi" olarak yazıyorum. Metin sağa 330 piksel ve aşağı 400 piksel ortalanmış (doğru konumu bulmak için sadece deneme yanılmaydı).


Beyaz alanı doldurmak için küçük metni kullanıyoruz.

d = ImageDraw.Draw(background)
d.multiline_text((330, 400), "Kayısı\nBahçesi", font=fnt, fill=(100, 100, 100, 10), align='center', spacing=28, anchor="mm")
d.multiline_text((330, 650), "El yapımı soya mumu", font=fnt_sml, fill=(80, 80, 80, 10), align='center', spacing=14, anchor="mm")
background.show()

Son olarak, tam görseli PNG olarak kaydediyoruz.

background.save("save.png")

Sonuç çizgi çizimden radikal şekilde daha iyi ve çok öngörülebilir bir sonuç oluşturuyor.


Ürünleri Yükleme


Şimdiye kadar yazdığımız tüm scriptleri kullanarak, yeni ürün bilgilerimizi Velo'ya bağlayabiliriz.


API Endpoint Oluşturma


Velo, bir Wix web sitesinin hem ön ucu hem de arka ucu ile doğrudan etkileşime izin veren son derece güçlü bir platformdur. Velo, bir dizi CMS özelliği üzerinde ince ayarlı kontrol sunar. Bugün sadece birkaç Velo fonksiyonuyla backend'imizin işlevselliğini genişletiyoruz. Bu rehber, Velo'ya ve gelecekte nelerin inşa edilebileceğine dair sadece ilk adımdır.


Wix Editörünün üst kısmında, Geliştirici modunu etkinleştirdiğinizden emin olun.


Wix Editöründeki Velo Geliştirici Modu menüsü

Ardından, Genel & Backend bölümüne girmek isteyeceksiniz. Backend altında, Site API'sını Göster'i seçebilirsiniz, bu otomatik olarak http-functions.js adlı bir dosya oluşturacaktır.


Wix editörünün genel ve arka uç bölümlerinde site API'sini nasıl kullanıma sunarsınız?

Bu, Wix web sitenizi bir API olarak açar ve özel fonksiyonlar veya servisler endpoint'ler olarak yazmanızı sağlar. NodeJS ile bir backend gibi düşünün, ancak Wix'e doğrudan entegrasyonlarla.


Bunlara karşı GET veya POST istekleri yapabilir ve tüm Velo araçlarına erişebilirsiniz. Fonksiyonların amacını tanımlamak için sadece get_funcName() veya post_funcName() ile başlamaları gerekir.


Scriptimizle birlikte, ihtiyacımız olan Velo kütüphaneleri şunlardır: Wix Media Backend, Wix Stores Backend ve Wix HTTP Functions'ı içe aktarın.

import { mediaManager } from 'wix-media-backend';
import wixStoresBackend from 'wix-stores-backend';
import { ok, notFound, serverError } from 'wix-http-functions';

wix-media-backend'den mediaManager kütüphanesi, yüklediğimiz ürün görsellerini manipüle etmemizi sağlar. wix-store-backend ürünleri yükleyeceğimiz yerdir. Son olarak, wix-http-functions API yanıtlarımızı oluşturmamıza izin verir.


Velo endpoint'imizde atmamız gereken adımlar şunlardır:


  1. Ürün ve görsel verisi içeren bir payload ile POST isteği kabul edin.

  2. Ürün verilerimizle bir ürün oluşturun.

  3. Yerel PC'mizden Wix sitemize bir görsel yükleyin.

  4. Bu görseli daha önce tekrarlanan ürüne uygulayın.


Başlamak için yeni bir endpoint oluşturalım:

export async function post_echo(request) {
    let response = {
        "headers": {
            "Content-Type": "application/json"
        }
    }
    response.body = 'Bu endpoint çalışıyor!'
    return ok(response)
}

Dosyayı kaydettikten sonra, bu örnek fonksiyona https://{kullanici-adi}.wixsite.com/{magaza-adi}/_functions-dev/echo adresinden erişilebilir. POST isteğiyle vurmak mesajı geri göndermelidir!


Ardından, bir ürün oluşturmak için biraz kod yazalım. Fonksiyonumuzun asenkron çalışmasını sağlamak için yeni bir promise oluşturuyoruz. Modern JavaScript hayatı kolaylaştırıyor. Ardından, daha önce içe aktardığımız wixStoresBackend'i kullanıyoruz ve içeri geçtiğimiz ürün verileriyle createProduct'ı çağırıyoruz. Sonra Velo'dan ürün kimliği veya yeni oluşturulan hakkında ekstra detaylar gibi bilgiler alıyoruz. Bu adım için ihtiyacımız olan tek şey bu.

function createProduct(product) {
    return new Promise(resolve => {
        wixStoresBackend.createProduct(product).then(res => {
            resolve(res)
        }).catch(err => {console.error(err)})
    })
}

Bir sonraki adım, yerel PC'mizden Wix sitesine bir görsel yüklemektir. Base64 ile kodlanmış bir görsele, bir klasör adına, görsel dosya adına ve bir mimetype'a (png gibi) ihtiyacımız olacak.


Base64 kodlu görsel bir buffer'a dönüştürülür ve Wix'e akışla gönderilir. Sonra sadece Wix mediaManager'a ihtiyaç duyduğu tüm bilgileri veriyoruz ve bizim için görseli yükleyecek!

function uploadImage(image_base64, image_folder, image_filename, image_mimetype) {
    return new Promise(resolve => {
        let buf = Buffer.from(image_base64, 'base64')
        mediaManager.upload(
            image_folder,
            buf,
            image_filename, {
                "mediaOptions": {
                    "mimeType": image_mimetype,
                    "mediaType": "image"
                },
                "metadataOptions": {
                    "isPrivate": false,
                    "isVisitorUpload": false,
                }
            }
        ).then(res => {
            mediaManager.getDownloadUrl(res.fileUrl).then(url => {
                resolve(url)
            })
        });
    })
}

Son adım için, her şeyi bir araya getiren bir fonksiyona ihtiyacımız var. Şöyle görünüyor:


  • Post isteğinden verileri al.

  • Görseli yükle.

  • Ürünü oluştur.

  • Yüklemeden geri aldığımız ürün kimliğini kullanarak görsel URL'sini ürüne eşle.

  • Kar et!

export async function post_upload(request) {
    let response = {
        "headers": {
            "Content-Type": "application/json"
        }
    }
    let body = await request.body.text()
    let data = JSON.parse(body)
    let img_url = await uploadImage(data.image.base64, data.image.folder, data.image.filename, data.image.mimetype)
    let product = await createProduct(data.product)
    response.body = product.productPageUrl
    wixStoresBackend.addProductMedia(product._id, [{'url':img_url}])
    return ok(response)
}

Özetle, işte tüm kod bir araya getirilmiş hali:

import { mediaManager } from 'wix-media-backend';
import wixStoresBackend from 'wix-stores-backend';
import { ok, notFound, serverError } from 'wix-http-functions';

export async function post_upload(request) {
    let response = {
        "headers": {
            "Content-Type": "application/json"
        }
    }
    let body = await request.body.text()
    let data = JSON.parse(body)
    let img_url = await uploadImage(data.image.base64, data.image.folder, data.image.filename, data.image.mimetype)
    let product = await createProduct(data.product)
    response.body = product.productPageUrl
    wixStoresBackend.addProductMedia(product._id, [{'url':img_url}])
    return ok(response)
}

// Görsel Yükle
// Bir ürüne atanabilecek bir URL döndürür
function uploadImage(image_base64, image_folder, image_filename, image_mimetype) {
    return new Promise(resolve => {
        let buf = Buffer.from(image_base64, 'base64')
        mediaManager.upload(
            image_folder,
            buf,
            image_filename, {
                "mediaOptions": {
                    "mimeType": image_mimetype,
                    "mediaType": "image"
                },
                "metadataOptions": {
                    "isPrivate": false,
                    "isVisitorUpload": false,
                }
            }
        ).then(res => {
            mediaManager.getDownloadUrl(res.fileUrl).then(url => {
                resolve(url)
            })
        });
    })
}

function createProduct(product) {
    return new Promise(resolve => {
        wixStoresBackend.createProduct(product).then(res => {
            resolve(res)
        }).catch(err => {console.error(err)})
    })
}

API Endpoint'ini Kullanma


Artık verilerimizi gönderecek bir yerimiz olduğuna göre, bazı ürünler oluşturalım!

İlk olarak, "load.py" adında yeni bir Python scripti oluşturun.


Bir ürünü oluşturan tüm bilgileri oluşturmak için birkaç adımımız olacak:


  • Görsel için bir dosya adı

  • Ürün için bir SKU

  • Yüksek tıklama oranına sahip bir meta açıklama

  • Arama için optimize edilmiş zengin bir başlık

  • Görseli açmak ve base64'e dönüştürmek için bir şey


İşte bunların hepsi nasıl görünüyor:

def get_filename(name):
    return name.lower().replace(" ", "_") + '.png'
 
def get_sku(name):
    return name.lower().replace(" ", "_") + '_g'
 
def get_metadescription(color, name):
    year = date.today().year
    return f'{year} sürümü {color} soya mumu - elle dökülmüş ve elle hazırlanmış. CandleCrafty\'nin {name} yaşam alanları ve ambiyans için en iyi mumları. Hemen satın alın!'
 
def get_image(name):
    filename = f'image/saves/save-{name}.png'
    with open(filename, "rb") as f:
        im_bytes = f.read()       
    im_b64 = base64.b64encode(im_bytes).decode("utf8")
    return im_b64
 
def get_seotitle(row):
    name = row['Title']
    notes = (row['Top notes'].split(', ') + row['Mid notes'].split(', ') + row['Base notes'].split(', '))
    note = random.choice(notes)
    color = row['color_literal']
    return f'{name} - {note} kokulu {color} soya mumu'

Ardından, ürün verilerimizin CSV'sindeki her satır için ürün ve görsel nesnesini oluşturmamız gerekiyor. İlk olarak, CSV'yi pandas'ta açıyoruz ve üzerinde döngü yapıyoruz. O satırdan isim ve renk gibi ihtiyaç duyacağımız bazı yaygın şeyleri alabiliriz.


Sonra, son bir kalite kontrolü olarak ürünün adında "Üretimi Durduruldu" olup olmadığını kontrol ediyorum. Bundan sonra, fonksiyonlarımızı veya değişkenlerimizi büyük veri nesnesindeki ilgili alanlara eşliyoruz. Bu, tüm rehberin oluşturduğu sihirli sos budur.

df = pd.read_csv('data.csv')
for i, row in df.iterrows():
    name = row['Title']
    color = row['color_literal']
    if 'Discontinued' not in name:
        print(name)
        data = {
            'image': {
                'base64': get_image(name),
                'folder': 'programmatic',
                'filename': get_filename(name),
                'mimetype': 'image/png'
            },
            'product': {
                'name': name,
                'description': row['adlib'],
                'price': 20,
                'sku': get_sku(name),
                'visible': True,
                'productType': 'physical',
                'product_weight': 1,
                'product_ribbon': '',
                "seoData": {
                    "tags": [{
                        "type": "title",
                        "children": get_seotitle(row),
                        "custom": False,
                        "disabled": False
                    },
                    {
                        "type": "meta",
                        "props": {
                            "name": "description",
                            "content": get_metadescription(color, name)
                        },
                        "custom": False,
                        "disabled": False
                    }]
                }
            }
        }
        upload(data)

Kod bölümündeki son satır henüz sahip olmadığımız bir upload() fonksiyonudur. Şimdi bundan geçelim.


Ürün verilerini alın ve Wix API'sinin okuyabileceği JSON'a dönüştürün. Ardından payload'u gönderin.

def upload(data):
    url = 'https://kullaniciadi.wixsite.com/candle-crafty/_functions-dev/upload'
    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
  
    payload = json.dumps(data)
    response = requests.post(url, data=payload, headers=headers)
    try:
        data = response.json()    
        print(data)               
    except requests.exceptions.RequestException:
        print(response.text)

Otomatik Mağazayı Çalıştırın


Yıldızlar hizalanırsa ve tüm kodumuz çalışırsa, dinamik olarak oluşturulmuş ürünlerle dolu tamamen işlevsel bir mağaza vitrine sahip olacağız. Ara sıra sayfayı yenileyin ve ürünlerin göründüğünü izleyin. Artık A/B testi yapabileceğiniz, optimize edebileceğiniz ve en çok satan varyasyonları arayabileceğiniz sayısız ürünün keyfini çıkarın.


Görselin sol tarafında Python kodunun ekran görüntüsü, sağ tarafında ise Wix kontrol panelindeki ürün listeleri yer almaktadır.

İçerik Genişletme Neden İşe Yarar


Bu stratejinin tamamı geniş bir ağ atma fikrine dayanmaktadır: SEO zaten huni tepesi pazarlamasıdır. SEO hunisinin en tepesinde ise anahtar kelimeler bulunur. Birçok yeni, çeşitli ve anahtar kelime odaklı sayfa oluşturarak, gösterimleri artırıyoruz. Ardından optimizasyon stratejileri aracılığıyla tıklama oranını artırabiliriz.


Bununla birlikte, içerik genişletmenin gerçek sırrı veri toplamaktır. Veriniz yoksa, bilinçli kararlar verme gücüne sahip olamazsınız. Geniş bir anahtar kelime ilgili sayfa yelpazesi oluşturarak, yinelemeye başlayabilirsiniz. Yüksek etkili alanlarda inşa edin ve düşük etkili sayfaları azaltın veya yönlendirin. Unutmayın, içerik kraldır, ancak tüm krallıklar müreffeh değildir.


Velo ile İhtiyaçlarınıza Göre Özelleştirin


Velo güçlü bir CMS IDE'sidir - bu makale onunla başarabileceğiniz şeylerin sadece başlangıcıdır. Herkesin ne kadar çok şeyin başarılabileceğini görmek için API Genel Bakış'ı gözden geçirmesini tavsiye ederim.


Çoğu içerik yönetim sistemine özellik eklemek hackçi hissettirir ve yazılımın her an bozulabileceğini merak ettirir. Velo ile entegrasyon bunun tam tersiydi. Wix web sitesinin sahip olduğum herhangi bir özel isteğe uyacak şekilde özelleştirmemi istiyormuş gibi hissettirdi. CMS'inizden daha fazlasına ihtiyaç duyduysanız, Wix'i ve Velo'yu etkinleştirmeyi düşünürdüm. Kodsuz, düşük kodlu ve tam kodlu siteler arasındaki en iyi spektrumdur.


Sonuç ve Önemli Çıkarımlar


Programatik içerik üretimi, doğru uygulandığında muazzam ölçekleme fırsatları sunar. Bu rehberde öğrendiklerinizin özeti:


Temel Adımlar:

  1. Veri kaynağını belirleyin ve içeriği kazıyın

  2. Verileri temizleyin ve yapılandırın

  3. Programatik açıklamalar oluşturun

  4. Görselleri dinamik olarak üretin

  5. API ile CMS'e entegre edin


Kritik Başarı Faktörleri:

  • Kalite Kontrolü: Google'ın otomatik içerik kurallarına uyun

  • Özgünlük: Her sayfa benzersiz değer sunmalı

  • Test ve Optimizasyon: A/B testleri yapın, veri toplayın

  • Ölçeklenebilirlik: Küçük başlayın, başarılı olanları genişletin


Dikkat Edilmesi Gerekenler:

  • İnce içerikten kaçının

  • Anahtar kelime doldurma yapmayın

  • Kullanıcı deneyimini önceliklendirin

  • Düzenli olarak performans izleyin


Programatik SEO, teknik bilgi ve yaratıcılığın birleşimidir. Bu rehberdeki araçlar ve stratejiler, kendi ölçeklenebilir içerik sistemlerinizi oluşturmanız için sağlam bir temel sağlar. Unutmayın: Amaç sadece içerik üretmek değil, kullanıcılara değer katan, arama motorları tarafından ödüllendirilen içerik üretmektir.

bottom of page