Importieren einer großen Anzahl von benutzerdefinierten Beiträgen in WordPress aus einer CSV-Datei

Lesezeit: 8 Minuten

Benutzer-Avatar
zoranc

Ich habe ein Problem mit meinem Skript, das Einträge aus einer CSV-Datei importiert und sie als benutzerdefinierte WordPress-Posts einfügt (jede Zeile ist ein einzelner Post) … Anfangs habe ich die Importfunktion in einer eigenen Klasse eingerichtet und das hat kaum funktioniert … nach dem, was ich gesammelt habe, war das Problem, dass die globalen Variablen nicht zwischengespeichert werden und jedes Mal, wenn ich die Instanz aufrufe, mehr Speicher verbraucht wird, bis der Prozess keinen Speicher mehr hat und abstürzt … also habe ich die Klasse und den Satz entfernt Importieren Sie die Importfunktion wie im folgenden Code beschrieben.

Mit diesem Setup habe ich den Punkt erreicht, an dem es mit bis zu 17.000 Posts gut funktioniert, aber wenn ich versuche, mehr Posts als das zu importieren, bricht es einfach ohne Fehler ab (keine Fehler werden in meinem PHP-Fehlerprotokoll oder der WordPress debug.log-Dateien)

Das Skript fügt erfolgreich 17.000 Posts ein und druckt die Echoinformationen bis zu dem Punkt, an dem es vorzeitig bei “XXX verbleibende Elemente” stoppt, und es beendet das Laden der Seite, wobei an diesem Punkt nichts mehr ausgegeben wird … es schafft es nie bis zum Finale echo "Done!"; Aussage…

Dies geschieht sowohl in der Localhost-Entwicklungsumgebung als auch auf einem gehosteten Entwicklungsserver. Ich habe die Speicherauslastung im Auge behalten, und sie überschreitet auf meinem lokalen Host nie 60 % (beginnt bei ~50 %), und ich sehe keinen schrittweisen Speicheranstieg, der auf einen Speicherverlust hindeutet…

Ich habe auch versucht, ini_set(‘memory_limit’, ’64M’) zu verwenden; und set_time_limit(0);

Nach dem, was ich über einige andere ähnliche Fragen zu diesem Thema gelesen habe,

  • für SQL sollten 20k Einträge keine große Sache sein
  • WordPress sollte das auch können, wenn der Server leistungsfähig genug ist

Welche Art von Optimierungen/Verbesserungen kann ich am folgenden Code vornehmen, damit dieses Skript in dieser Größenordnung funktioniert?

oder vielleicht die eingebaute WordPress-Funktionalität überspringen und alles mit LOAD DATA INFILE verarbeiten, wie von fancypants hier erwähnt

Ich würde die Daten lieber über die bereitgestellte WordPress-Funktionalität verarbeiten.

Die CSV-Datei ist ~ 1 MB …

der Code:


diese Funktionen befinden sich in einer eigenen Datei – import.php

function fileupload_process() {
  ini_set('memory_limit', '64M');
  set_time_limit(0);
  $uploadfiles = $_FILES['uploadfiles'];
  if (is_array($uploadfiles)) {
    foreach ($uploadfiles['name'] as $key => $value) {
      // look only for uploaded files
      if ($uploadfiles['error'][$key] == 0) {
        $filetmp = $uploadfiles['tmp_name'][$key];
        if (($handle = fopen($filetmp, "r")) !== FALSE) {
          $flag = true;
          $songs = explode("\n",file_get_contents($filetmp));
          $count = count( $songs );
          unset($songs);
          echo "Total item count: " . $count . "<BR />";
          // typical entry: If You Have To Ask,Red Hot Chili Peppers,0:03:37, Rock & Alternative,1991,on
          // using a generous 1000 length - will lowering this actually impact performance in terms of memory allocation?
          while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
            // Skip the first entry in the csv containing colmn info
            if($flag) {
                      $flag = false; 
              echo "<BR />"; 
              $count--; 
              continue; 
            }
            // insert the current post and relevant info into the database
            $currently_processed = process_custom_post($data, $count);
            $count--;
          }
          echo "Done!";
          fclose($handle);
        }
        unlink($filetmp); // delete the temp csv file
      }
    }
  }
} // END: file_upload_process()
function process_custom_post($song, $count) {
  $track =  (array_key_exists(0, $song) && $song[0] != "" ?  $song[0] : 'N/A');
  $artist = (array_key_exists(1, $song) && $song[1] != ""  ?  $song[1] : 'N/A');
  $length = (array_key_exists(2, $song) && $song[2] != ""  ?  $song[2] : 'N/A');
  $genre = (array_key_exists(3, $song) && $song[3] != ""  ?  $song[3] : 'N/A');
  $year = (array_key_exists(4, $song) && $song[4] != ""  ?  $song[4] : 'N/A');
  $month = (array_key_exists(5, $song) && $song[5] != ""  ?  $song[5] : 'N/A');
  $playlist = (array_key_exists(6, $song) && $song[6] != ""  ?  $song[6] : '');
  $custom_post = array();
  $custom_post['post_type'] = 'songs';
  $custom_post['post_status'] = 'publish';
  $custom_post['post_title'] = $track;
  echo "Importing " . $artist  . " - " . $track . " <i> (" . $count ." items remaining)...</i><BR />";
  $post_id = wp_insert_post( $custom_post );
  $updated = update_post_meta($post_id, 'artist_name', $artist);
  $updated = update_post_meta($post_id, 'song_length', $length);
  $updated = update_post_meta($post_id, 'song_genre', $genre);
  $updated = update_post_meta($post_id, 'song_year', $year);
  $updated = update_post_meta($post_id, 'song_month', $month);
  $updated = update_post_meta($post_id, 'sample_playlist', $playlist);
  return true;
} // END: process_custom_post()
function import_page () {
//HTML for the import page + the file upload form
  if (isset($_POST['uploadfile'])) {
    fileupload_process();
       }
}

Die import.php ist in einem WordPress-Plugin außerhalb der Klasse des Plugins enthalten

dh hier sind die relevanten Informationen darüber, wie ich das Skript auf der Importseite bekomme:

define( 'MY_PLUGIN_ROOT' , dirname(__FILE__) );
include_once( MY_PLUGIN_ROOT . 'import.php');
class my_plugin () {
 function __construct() {
  add_action( 'init', array( &$this, 'admin_menu_init' ) );
 }
 function admin_menu_init() {
   if(is_admin()) {
     //Add the necessary pages for the plugin
     add_action('admin_menu', array(&$this, 'add_menu_items'));
   }
 }
 function add_menu_items() {
  add_submenu_page( 'edit.php?post_type=songs', 'Import Songs', 'Import Songs',  'manage_options', 'import-songs', 'import_page' );
 }
}

Alle Gedanken, Kommentare oder Empfehlungen würden sehr geschätzt.

  • Das einzige, was ich empfehlen kann, ist, Ihre Zählung in der While-Schleife zu erhalten, anstatt die gesamte Datei zu lesen und sie dann zu explodieren, um die Zählung zu erhalten. Außerdem scheint es, als würden Sie den Zähler nur verwenden, um ihn für die menschliche Lesbarkeit auszugeben. Immer wenn ich große Importe mache, echoe ich nur am Ende, vielleicht ein paar Zeilen, die Ausgabe von 20.000 Zeilen auf dem Bildschirm könnte ein weiteres Problem sein.

    – Bryan

    29. September 2013 um 19:29 Uhr

  • Ich habe alle Echo-Anweisungen entfernt, mit Ausnahme des abschließenden Echos ‘Fertig!’; Ich habe die Zähldatei-Explosion weggelassen und sie innerhalb der Schleife implementiert … es importiert immer noch nicht alle Posts, stoppt bei ~ 17K … Allerdings bekomme ich jetzt einen 500 Internal Server Error, obwohl die Server-Logfiles meines Providers absolut fehlerfrei sind?? Ich werde versuchen, dies auf localhost zu tun, da ich mehr Übersicht zur Verfügung habe, und hoffentlich werde ich sehen, ob ich das Problem aufspüren kann, das dies verursacht ….

    – zoranc

    29. September 2013 um 22:51 Uhr

  • Kein Glück, localhost beendet sich ohne Fehler wie zuvor … trotzdem danke für die Vorschläge

    – zoranc

    30. September 2013 um 1:56 Uhr

  • Nehmen Sie Originalbeiträge aus der lokalen/entfernten Datenbank, oder ist die CSV-Datei Ihre einzige Quelle für Beitragsdaten? Wenn Sie Zugriff auf db haben, geben Sie einfach Tabellen in SQL-Dateien aus

    – Gwillie

    30. September 2013 um 4:38 Uhr

  • Ich frage mich, ob es kein Problem mit der Datei um Zeile 17k gibt?

    – Bryan

    1. Oktober 2013 um 6:32 Uhr

Benutzer-Avatar
zoranc

Also habe ich nach einer langen Profiling-Sitzung mit XDEBUG, phpmyadmin und Freunden endlich eingegrenzt, was der Engpass war: die MySQL-Abfragen

ich aktiviert habe

define('SAVEQUERIES', true);

um die Abfragen genauer zu untersuchen, und ich gebe das Abfrage-Array in meine Datei debug.log aus

global $wpdb;
error_log( print_r( $wpdb->queries, true ) );

beim Untersuchen jedes INSERT-Aufrufs an die Datenbank

  1. wp_insert_post()

    [0] => EINFÜGEN IN wp_posts (post_author,post_date,post_date_gmt,post_content,post_content_filtered,post_title,post_excerpt,post_status,post_type,comment_status,ping_status,post_password,post_name,to_ping,pinged,post_modified,post_modified_gmt,post_parent,menu_order,guid) WERTE (…)

    [1] => 0,10682702064514

  2. update_post_meta()

    [0] => EINFÜGEN IN wp_postmeta (post_id,meta_key,meta_value) WERTE (…)

    [1] => 0,10227680206299

Ich habe festgestellt, dass jeder dieser Aufrufe der Datenbank auf meinem localhost-Server durchschnittlich ~ 0,1 s kostet

Aber da ich 6 benutzerdefinierte Felder pro Eintrag aktualisiere, funktioniert es

6 cf Aufrufe einfügen/Eintrag * ~0,1 s/cf Aufruf einfügen * 20 000 Einträge gesamt * 1 min/60 s = 200 min

~2,5 Stunden, um allein die benutzerdefinierten Felder für 20.000 Beiträge zu verarbeiten

Da nun alle benutzerdefinierten Felder in dieselbe Tabelle, wp_post_meta, eingefügt werden, habe ich versucht, alle update_post_meta-Aufrufe in einem Aufruf zu kombinieren und somit die Leistung in Bezug auf die Ausführungszeit dieses Importskripts drastisch zu verbessern.

Ich habe die update_post_meta-Funktion im wp-Kern durchgesehen und festgestellt, dass es keine native Option zum Übergeben eines Arrays anstelle eines einzelnen cf-Schlüssels und -Werts gibt die Zeilen von:

`INSERT INTO `wp_postmeta` (`post_id`,`meta_key`,`meta_value`) 
  VALUES ( $post_id,  'artist_name' , $artist) 
         ( $post_id,  'song_length' , $length )
         ( $post_id,  'song_genre' , $genre ) ...` 

und so weiter für alle 6 benutzerdefinierten Felder in meinem Fall, die alle in einem einzigen gerollt sind $wpdb->query(); .

Als ich meine Funktion process_custom_post () optimierte, um dies widerzuspiegeln, kam ich zu folgendem Ergebnis:

function process_custom_post($song) {
  global $wpdb;

  // Prepare and insert the custom post
  $track =  (array_key_exists(0, $song) && $song[0] != "" ?  $song[0] : 'N/A');
  $custom_post = array();
  $custom_post['post_type'] = 'songs';
  $custom_post['post_status'] = 'publish';
  $custom_post['post_title'] = $track;
  $post_id = wp_insert_post( $custom_post );

  // Prepare and insert the custom post meta
  $meta_keys = array();
  $meta_keys['artist_name'] = (array_key_exists(1, $song) && $song[1] != ""  ?  $song[1] : 'N/A');
  $meta_keys['song_length'] = (array_key_exists(2, $song) && $song[2] != ""  ?  $song[2] : 'N/A');
  $meta_keys['song_genre'] = (array_key_exists(3, $song) && $song[3] != ""  ?  $song[3] : 'N/A');
  $meta_keys['song_year'] = (array_key_exists(4, $song) && $song[4] != ""  ?  $song[4] : 'N/A');
  $meta_keys['song_month'] = (array_key_exists(5, $song) && $song[5] != ""  ?  $song[5] : 'N/A');
  $meta_keys['sample_playlist'] = (array_key_exists(6, $song) && $song[6] != ""  ?  $song[6] : '');
  $custom_fields = array();
  $place_holders = array();
  $query_string = "INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value) VALUES ";
  foreach($meta_keys as $key => $value) {
     array_push($custom_fields, $post_id, $key, $value);
     $place_holders[] = "('%d', '%s', '%s')";
  }
  $query_string .= implode(', ', $place_holders);
  $wpdb->query( $wpdb->prepare("$query_string ", $custom_fields));
  return true;
}

und Bratsche! Anstelle von 7 INSERT-Aufrufen an die Datenbank pro benutzerdefiniertem Beitrag habe ich am Ende 2, die einen gewaltigen Unterschied machen, wenn ich versuche, mehrere benutzerdefinierte Felder zu verarbeiten, während ich über eine große Anzahl von Beiträgen iteriere – in meinem Fall werden jetzt 20.000 Einträge innerhalb von 15 vollständig verarbeitet -20min (im Vergleich zu einer Situation, in der ich nach ~17.000 Posts und ein paar Stunden Bearbeitungszeit einen Abbruch erleben würde) …

Das Wichtigste, was ich aus dieser ganzen Erfahrung gelernt habe,

Denken Sie an Ihre Datenbankaufrufe – sie können sich schnell summieren!

1055990cookie-checkImportieren einer großen Anzahl von benutzerdefinierten Beiträgen in WordPress aus einer CSV-Datei

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy