SEO tööde automatiseerimine WP-CLI abil
Kui sul on WordPressis piltide ALT-tekstid ja failinimed korrastamata, siis siin on üks lihtne viis, kuidas see WP-CLI abil kiiresti korda teha.

Tavaliselt käib WordPressi veebilehe haldamine järgmiselt: avad brauseri, logid sisse, klikid vajaliku lehe lahti, teed muudatuse ja vajutad "Uuenda". See on täiesti okei, kui sul on vaja lisada uusi lehti, postitusi või aeg-ajalt infot värskendada.
Aga kujuta ette olukorda, et sul on vaja sama mustri järgi ära muuta sadu või tuhandeid lehti või meediafaile. Siis läheb see klikkimine kiiresti väga ajamahukaks. Kui plaanid lehte otsingumootoritele arusaadavamaks teha, võid tihti just sellise probleemi otsa sattuda. Olgu selleks suures koguses linkide muutmist, piltide nimetuste uuendamine või lehe pealkirjade ja kirjelduste lisamine.
Näiteks, kui sul on 100 pilti ja tahad igale lisada ALT-teksti. Kui ühe pildi avamine, teksti lisamine ja uuendamine võtaks (optimistlikult) umbes ühe minuti, siis on see kokku 100 minutit ehk umbes kaks tundi manuaalset tööd. Päriselus kipub see sageli pikemaks venima.
Selle töö kiirendamiseks tuleb mängu käsurida. Kui sa saad "ava-muuda-uuenda" protsessi teha automaatselt, võidad sa palju aega. WordPressil on selleks lausa eraldi tööriist, WP-CLI. WP-CLI abil saad uuendada pluginaid, luua kasutajaid, muuta sisu, puhastada andmebaasi ja palju muud.
WP-CLI kasutusvõimalusi on väga palju ja seda saab suurepäraselt ära kasutada ka SEO töid tehes, seda nii esmase SEO auditi koostamisel kui ka sisu muutmisel. Kui korraga on vaja muuta palju sisu, on lihtsam käivitada käsk, mis teeb soovitud uuendused automaatselt.
Otsingumootoritele optimeerimine koosneb paljudest erinevatest tegevustest ja sinna kuulub ka piltide optimeerimine. Pildid ei tohiks lehte aeglaseks teha ja failinimed võiksid olla arusaadavate nimetustega. Pilt nimega “IMG_6819.jpg”, millel puudub ALT-tekst, ei ütle otsingumootorile mis pildiga on tegemist. ALT-teksti on puudumisel jääb puudulikuks ka veebilehe ligipääsetavuse osa.
Kui aga failinimi on näiteks “moller-oomega-3-kalamaksaoli-250ml.jpg” ja ALT-tekst “Möller Oomega 3 kalamaksaõli pudel, 250 ml”, siis on pildi sisu oluliselt arusaadavam nii otsingumootorile kui ka ekraanilugerit kasutavale kasutajale.
Väike detail? Jah. Aga neid detaile on veebilehe otsingumootoritele arusaadavamaks tegemisel palju ning neist moodustub lõpuks suurem pilt.
Kuidas saada kiire ülevaade, millistel piltidel on puudu ALT-tekst
Kujuta ette WordPressi veebilehte, mille meediakaustas on üle 3000 pildi, ja sul on vaja kiiresti teada, kui paljudel piltidel puudub ALT-tekst. Ükshaaval läbi pole mõistlik neid läbi klikkida, kuid WP-CLI saab selle tööga suurepäraselt hakkama ning väljastab sulle CSV-faili, mille saad Excelis avada. Täpselt sellisel kujul, nagu sul ülevaateks ja hilisemateks muudatusteks vaja on.
Kasutame selleks järgnevat käsklust:
wp --quiet --skip-plugins --skip-themes eval '
$fh = fopen("php://output", "w");
fwrite($fh, "\xEF\xBB\xBF");
$delimiter = ",";
fputcsv($fh, [
"attachment_id",
"attachment_slug",
"mime_type",
"image_title",
"alt_text",
"caption",
"description",
"attached_file",
"guid",
"parent_id",
"parent_type",
"parent_title",
"parent_slug",
"new_file_name",
"new_alt_text",
"new_image_title"
], $delimiter);
$paged = 1;
$per_page = 500;
do {
$q = new WP_Query([
"post_type" => "attachment",
"post_mime_type" => "image",
"post_status" => "inherit",
"posts_per_page" => $per_page,
"paged" => $paged,
"fields" => "ids",
"orderby" => "ID",
"order" => "ASC",
]);
foreach ($q->posts as $id) {
$a = get_post($id);
if (!$a) continue;
$alt = (string) get_post_meta($id, "_wp_attachment_image_alt", true);
$attached = (string) get_post_meta($id, "_wp_attached_file", true);
$current_file_name = "";
if ($attached !== "") {
$current_file_name = basename($attached);
} elseif (!empty($a->guid)) {
$path = parse_url($a->guid, PHP_URL_PATH);
$current_file_name = $path ? basename($path) : "";
}
$parent_id = (int) $a->post_parent;
$p = $parent_id ? get_post($parent_id) : null;
fputcsv($fh, [
$id,
(string) $a->post_name,
(string) $a->post_mime_type,
(string) $a->post_title,
$alt,
(string) $a->post_excerpt,
(string) $a->post_content,
$attached,
(string) $a->guid,
$parent_id,
$p ? (string) $p->post_type : "",
$p ? (string) $p->post_title : "",
$p ? (string) $p->post_name : "",
$current_file_name,
$alt,
(string) $a->post_title
], $delimiter);
}
$paged++;
} while ($paged <= (int) $q->max_num_pages);
' > images-to-edit.csv 2>/dev/nullTegemist on pika koodijupiga, kuid lihtsustatult käib see läbi kogu WordPressi veebilehe meediakausta ja koostab CSV-tabeli, kuhu saad tulevikus lisada uued pildinimed ja ALT-tekstid. Vajadusel saad loodud tabeli anda ka mõnele AI-tööriistale, mis täidab/uuendab seda sinu antud juhendi järgi.
Natuke täpsemalt käsklusest
Esiteks ütleme WordPressile:
- Ära lae pluginaid ja teemasid
- Pärast seda hakatakse kirjutama CSV-faili sisu.
- Seejärel otsitakse pildid ning iga pildi kohta lisatakse info, mida küsisime
Lisame tabelisse ka lisaveerud "new_file_name", "new_alt_text" ja "new_image_title". See on koht, kuhu lisame uued väärtused ilma lehte brauseris klõpsimata. Lõpuks on sul olemas suur tabel nimega "images-to-edit.csv", mille saad serverist alla laadida.
Loodud tabel näeb välja umbes selline:

Uute lisatud väärtustega tabel näeb välja midagi sellist:

Kui oled failis kõik soovitud muudatused ära teinud, salvestad muudetud tabeli uue nimega "edited-images.csv" ja laed fail tagasi serverisse.
Kui fail on serverisse tõstetud, lisad serverisse ka kaks abifaili, mis tõstavad muudatused automaatselt sisse. Need kaks faili on rename-media.php ja search-replace-media-urls.sh.
Enne muudatuste tegemist on mõistlik teha varukoopia nii andmebaasist kui ka veebilehest, juhuks kui midagi peaks nihu minema.
Andmebaasi varukoopia saad teha WP-CLI abil, käivitades käsu:
wp db exportJa kogu lehe koopia näiteks nii:
tar -czf media-rename-before-$(date +%F).tar.gz .Kui oled varukoopiad teinud, lae need enda arvutisse ja kustuta serverist. Varukoopiad sisaldavad ligipääsu sinu veebilehele ning need ei tohiks olla kellelegi peale sinu kättesaadavad.
Nüüd tõstame serverisse piltide muutmise abifailid:
See kood loeb uuendatud CSV-faili, leiab üles vana pildi, nimetab pildi ümber, lisab ALT-teksti ja annab WordPressile teada, et pilt on nüüd uue nimega.
<?php
if ( ! defined( 'WP_CLI' ) ) {
fwrite( STDERR, "This script must be run with WP-CLI.\n" );
exit( 1 );
}
global $wpdb;
function cli_get_script_args() {
$argv = $_SERVER['argv'] ?? [];
$base = array_map( 'basename', $argv );
$idx = array_search( basename( __FILE__ ), $base, true );
$args = ( $idx !== false ) ? array_slice( $argv, $idx + 1 ) : [];
$args = array_values( array_filter( $args, fn($a) => $a !== '--' ) );
return $args;
}
$args = cli_get_script_args();
if ( empty( $args ) ) {
WP_CLI::error( "Usage: wp eval-file rename-media.php -- edited-images.csv [--dry-run] [--delete-old-thumbs]" );
}
$csv_path = $args[0];
$dry_run = in_array( '--dry-run', $args, true );
$del_old = in_array( '--delete-old-thumbs', $args, true );
if ( ! file_exists( $csv_path ) ) {
WP_CLI::error( "CSV not found: {$csv_path}" );
}
$uploads = wp_upload_dir();
$basedir = rtrim( $uploads['basedir'], '/' );
$fh = fopen( $csv_path, 'r' );
if ( ! $fh ) {
WP_CLI::error( "Failed to open CSV: {$csv_path}" );
}
$header = fgetcsv( $fh );
if ( ! $header ) {
WP_CLI::error( "CSV appears empty." );
}
$header = array_map( 'trim', $header );
if ( isset( $header[0] ) ) {
$header[0] = preg_replace( "/^\xEF\xBB\xBF/", "", $header[0] );
}
$required = [ 'attached_file', 'guid', 'new_file_name', 'new_alt_text', 'new_image_title' ];
foreach ( $required as $col ) {
if ( ! in_array( $col, $header, true ) ) {
WP_CLI::error( "Missing required column: {$col}" );
}
}
$ix = array_flip( $header );
$processed = 0;
$renamed = 0;
$missing = 0;
$skipped = 0;
$failed = 0;
while ( ( $row = fgetcsv( $fh ) ) !== false ) {
$processed++;
$old_file = trim( (string) ( $row[ $ix['attached_file'] ] ?? '' ) );
$old_guid = trim( (string) ( $row[ $ix['guid'] ] ?? '' ) );
$new_file = trim( (string) ( $row[ $ix['new_file_name'] ] ?? '' ) );
$new_alt = trim( (string) ( $row[ $ix['new_alt_text'] ] ?? '' ) );
$new_title = trim( (string) ( $row[ $ix['new_image_title'] ] ?? '' ) );
if ( $old_file === '' || $new_file === '' ) {
WP_CLI::warning( "Row {$processed}: missing old/new file name, skipping." );
$skipped++;
continue;
}
$new_file = basename( $new_file );
$att_id = null;
if ( $old_guid !== '' ) {
$att_id = $wpdb->get_var( $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_type='attachment' AND guid=%s LIMIT 1",
$old_guid
) );
}
if ( ! $att_id ) {
$like = '%' . $wpdb->esc_like( '/' . $old_file );
$att_id = $wpdb->get_var( $wpdb->prepare(
"SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='_wp_attached_file' AND meta_value LIKE %s ORDER BY post_id DESC LIMIT 1",
$like
) );
if ( ! $att_id ) {
$att_id = $wpdb->get_var( $wpdb->prepare(
"SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='_wp_attached_file' AND meta_value=%s LIMIT 1",
$old_file
) );
}
}
if ( ! $att_id ) {
WP_CLI::warning( "Not found: {$old_file}" . ( $old_guid ? " ({$old_guid})" : '' ) );
$missing++;
continue;
}
$old_rel = get_post_meta( $att_id, '_wp_attached_file', true );
if ( ! $old_rel && $old_guid ) {
$path = parse_url( $old_guid, PHP_URL_PATH );
$old_rel = ltrim( (string) $path, '/' );
}
if ( ! $old_rel ) {
WP_CLI::warning( "[{$att_id}] Unable to determine old file path for {$old_file}, skipping." );
$skipped++;
continue;
}
$dir_rel = dirname( $old_rel );
if ( $dir_rel === '.' ) { $dir_rel = ''; }
$new_rel = $dir_rel ? rtrim( $dir_rel, '/' ) . '/' . $new_file : $new_file;
$old_full = $basedir . '/' . ltrim( $old_rel, '/' );
$new_full = $basedir . '/' . ltrim( $new_rel, '/' );
$using_abspath = false;
if ( ! file_exists( $old_full ) ) {
$alt_old = rtrim( ABSPATH, '/' ) . '/' . ltrim( $old_rel, '/' );
if ( file_exists( $alt_old ) ) {
$old_full = $alt_old;
$new_full = rtrim( ABSPATH, '/' ) . '/' . ltrim( $new_rel, '/' );
$using_abspath = true;
}
}
if ( ! file_exists( $old_full ) ) {
WP_CLI::warning( "[{$att_id}] File not found on disk: {$old_full}" );
$failed++;
continue;
}
$old_meta = wp_get_attachment_metadata( $att_id );
$thumbs_to_delete = [];
if ( $del_old && is_array( $old_meta ) && ! empty( $old_meta['sizes'] ) && is_array( $old_meta['sizes'] ) ) {
$old_dir = dirname( $old_full );
foreach ( $old_meta['sizes'] as $info ) {
if ( ! empty( $info['file'] ) ) {
$thumbs_to_delete[] = $old_dir . '/' . $info['file'];
}
}
}
WP_CLI::log( sprintf( "[%d] %s -> %s%s", $att_id, $old_rel, $new_rel, $dry_run ? " (dry-run)" : "" ) );
if ( ! $dry_run ) {
$new_dir = dirname( $new_full );
if ( ! is_dir( $new_dir ) ) {
wp_mkdir_p( $new_dir );
}
if ( file_exists( $new_full ) ) {
WP_CLI::warning( "[{$att_id}] Target already exists, skipping rename: {$new_full}" );
$skipped++;
continue;
}
if ( ! @rename( $old_full, $new_full ) ) {
WP_CLI::warning( "[{$att_id}] Rename failed: {$old_full}" );
$failed++;
continue;
}
if ( $del_old && ! empty( $thumbs_to_delete ) ) {
foreach ( $thumbs_to_delete as $p ) {
if ( is_file( $p ) ) { @unlink( $p ); }
}
}
update_post_meta( $att_id, '_wp_attached_file', $new_rel );
if ( $new_alt !== '' ) {
update_post_meta( $att_id, '_wp_attachment_image_alt', $new_alt );
}
$update = [ 'ID' => $att_id ];
if ( $new_title !== '' ) {
$update['post_title'] = $new_title;
}
$slug_source = $new_title !== '' ? $new_title : pathinfo( $new_file, PATHINFO_FILENAME );
$update['post_name'] = sanitize_title( $slug_source );
if ( $old_guid ) {
$guid_dir = rtrim( dirname( $old_guid ), '/' );
$update['guid'] = $guid_dir . '/' . $new_file;
}
wp_update_post( $update );
$mime = get_post_mime_type( $att_id );
if ( is_string( $mime ) && strpos( $mime, 'image/' ) === 0 && $mime !== 'image/svg+xml' ) {
$meta = wp_generate_attachment_metadata( $att_id, $new_full );
if ( ! is_wp_error( $meta ) && ! empty( $meta ) ) {
wp_update_attachment_metadata( $att_id, $meta );
} else {
WP_CLI::warning( "[{$att_id}] Metadata regeneration failed." );
}
}
}
$renamed++;
}
fclose( $fh );
WP_CLI::success( "Done. Processed={$processed}, renamed={$renamed}, missing={$missing}, skipped={$skipped}, failed={$failed}" );
WP_CLI::log( "" );
WP_CLI::log( "Next step: update hard-coded URLs in content/meta if any." );Ja lisaks:
See on abiskript on selleks, et uus pildinimi asendataks ka siis, kui pildi link või nimi on kuskile teksti sisse kirjutatud.
#!/usr/bin/env bash
set -euo pipefail
CSV="${1:-edited-images.csv}"
DRYRUN=0
if [[ "${2:-}" == "--dry-run" ]]; then
DRYRUN=1
fi
if ! command -v wp >/dev/null 2>&1; then
echo "wp command not found in PATH"
exit 1
fi
if [[ ! -f "$CSV" ]]; then
echo "CSV not found: $CSV"
exit 1
fi
php -r '
$csv = $argv[1];
$fh = fopen($csv, "r") or die("CSV open failed\n");
$h = fgetcsv($fh);
if (!$h) die("CSV appears empty\n");
$h = array_map("trim", $h);
if (isset($h[0])) $h[0] = preg_replace("/^\xEF\xBB\xBF/", "", $h[0]);
$ix = array_flip($h);
if (!isset($ix["guid"], $ix["new_file_name"])) die("CSV must contain columns: guid,new_file_name\n");
while (($r = fgetcsv($fh)) !== false) {
$old = trim((string)($r[$ix["guid"]] ?? ""));
$newf = trim((string)($r[$ix["new_file_name"]] ?? ""));
if ($old === "" || $newf === "") continue;
$newf = basename($newf);
$new = preg_replace("~[^/]+$~", $newf, $old);
echo $old . "\t" . $new . "\n";
}
' "$CSV" | while IFS=$'\t' read -r old_guid new_guid; do
if [[ -z "$old_guid" || -z "$new_guid" ]]; then
continue
fi
echo "Replacing: $old_guid -> $new_guid"
if [[ $DRYRUN -eq 1 ]]; then
wp search-replace "$old_guid" "$new_guid" --all-tables --precise --dry-run --report-changed-only
else
wp search-replace "$old_guid" "$new_guid" --all-tables --precise --report-changed-only
fi
doneKui failid on serverisse tõstetud, siis külastad lisatud faili:
sinudomeen.com/rename-media.php
Ja piltide muutmise kood käivitub ning mõne aja pärast on kõikide piltide soovitud nimed ja kirjeldused sisendi järgimuudetud. Oleme nüüd suhteliselt kiiresti korrastanud kogu veebilehe meediakausta. Tehes seda WP-CLI abil oluliselt kiiremini kui haldusliideses manuaalselt toimetades.
Kokkuvõte
See on ainult üks näide, kuidas WP-CLI saab SEO töödel enda kasuks tööle panna. Kui sul on vaja korraga korrastada sadu pilte, linke või metaandmeid, siis WP-CLI aitab eemaldada manuaalse töö. Tehes selle ära korra, saad sama lähenemist ka järgnevate veebilehtede puhul kasutada. Käsurida pole vaja karta, sest väga paljud asjad saab läbi selle oluliselt kiiremini ära teha.
Lisa lugemist:
Kuidas soovitab Google pilte nimetada. Vaata siit
W3C WAI juhend ALT-tekstide lisamiseks. Vaata siit
Loe rohkem WP-CLI kohta ametlikult kodulehelt. Vaata siit