Разберём ещё несколько практических задач из области разработки PHP парсеров, связанных с импортом, сортировкой и экспортом CSV данных. Часто требуется представить результаты парсинга в виде CSV файла, далее такой файл можно импортировать в базу данных сайта. Средства импорта CSV данных часто поддерживаются стандартными средствами различных CMS.
CSV (Comma-Separated Values) — текстовый формат, предназначенный для представления табличных данных. Из названия следует, что данные разделены запятыми, но могут использоваться и другие разделители, например, точка с запятой (DSV формат).
CSV файлы можно открывать разными текстовыми редакторами, а также программой EXCEL. Но бывают проблемы с кодировкой. Например, EXCEL плохо переваривает UTF-8 без BOM. Под EXCEL нужно данные просто в UTF-8 сохранять.
В тестовой задаче нам нужно отсортировать ряд CSV файлов по столбцу email. Файлы содержат списки организаций с различными контактными данными, при этом не у всех фирм есть электронная почта. Так, чтобы облегчить работу со списками удобно скриптом отсортировать списки и наверх поставить компании с известными почтовыми ящиками.
Скрипт index.php и каталог с входными данными следует разместитьть в папке csv-sorter.
Обработанные файлы будут записываться в директорию output.
Импорт и экспорт CSV данных на PHP
Листинг файла index.php
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Сортировка CSV файлов по заполненности полей</title> </head> <body> <h1>Сортировка CSV файлов</h1> <!--p>Файлы на выходе программы преобразуются в кодировку UTF-8.</p--> <?php /** * Функция преобразования строки в CSV формате в массив * * @param string $filename, int $file_size, string $delimiter * * @return string */ function csv2array($filename='', $file_size, $delimiter=';') { ini_set('auto_detect_line_endings', true); if(!file_exists($filename) || !is_readable($filename)) { return false; } $header = null; $data = array(); if(($handle = fopen($filename, 'r')) !== false) { while(($row = fgetcsv($handle, $file_size, $delimiter)) !== false) { if(!$header) { if($row[0] != 'sep=') { $header = $row; } } else { if (count($header) > count($row)) { $difference = count($header) - count($row); for ($i = 1; $i <= $difference; $i++) { $row[count($row) + 1] = ''; } } } if($row[0] != 'sep=') { $data[] = $row; } } fclose($handle); } return $data; } /** * Функция сортировки массива по 1-му полю или N полей * * @param string|array $keys, string $order [ASC|DESC] * * @return int */ function sort_arr_ncol($keys, $order = 'ASC') { $order = ($order == 'DESC') ? -1 : 1; if(is_array($keys)) { //если сортировка по нескольким полям return function($a, $b) use ($keys, $order) { foreach($keys as $k) { if($a[$k] != $b[$k]) { return $order * (($a[$k] < $b[$k]) ? 1 : -1); } } return 0; }; } else { //если сортировка по одному полю return function($a, $b) use ($keys, $order) { if ($a[$keys] == $b[$keys]) { return 0; } return $order * (($a[$keys] < $b[$keys]) ? 1 : -1); }; } } /** * Функция преобразования массива в строку в CSV формате * * @param @param string $input, int $file_size, string $delimiter, string $enclosure * * @return string */ function str_putcsv($input, $file_size, $delimiter = ';', $enclosure = '"') { // Open a memory "file" for read/write... $fp = fopen('php://temp', 'r+'); // ... write the $input array to the "file" using fputcsv()... fputcsv($fp, $input, $delimiter, $enclosure); // ... rewind the "file" so we can read what we just wrote... rewind($fp); // ... read the entire line into a variable... $data = stream_get_contents($fp); // ... close the "file"... fclose($fp); // ... and return the $data to the caller, with the trailing newline from fgets() removed. return $data; //rtrim($data, "\r"); } // Параметры сортировщика $data_dir_name = "input"; // Каталог с исходными файлами $res_dir_name = "output"; // Каталог с отсортированными файлами $key_list_str = 'email'; // Название столбца для сортировки (с нуля) if(!empty($_REQUEST['action']) && $_REQUEST['action'] = 'run') { if(!isset($_REQUEST['charset'])) { $charset = 'no'; } else { $charset = $_REQUEST['charset']; } $k = 0; $er = 0; $error_file_names = array(); $all_file_count = 0; $entries = scandir($data_dir_name); foreach($entries as $entry) { if(mb_strpos($entry, '.csv') !== false) { // Обрабатываем только CSV файлы $filepath_in = $data_dir_name . "/" . $entry; $file_size = filesize($filepath_in); $csv_data_arr = csv2array($filepath_in, $file_size); $header_arr[0] = array_shift($csv_data_arr); $key_list = array_keys($header_arr[0], $key_list_str); usort($csv_data_arr, sort_arr_ncol($key_list, 'ASC')); $csv_data_arr = array_merge($header_arr, $csv_data_arr); // Формируем строку для CSV файла $res_csv_file = ''; foreach($csv_data_arr as $key_row => $csv_data_arr_row) { $res_csv_file .= str_putcsv($csv_data_arr_row, $file_size); } if($charset == 'yes') { $res_csv_file = iconv("WINDOWS-1251", "UTF-8", $res_csv_file); if($res_csv_file == false) { $res_csv_file = iconv("WINDOWS-1251", "UTF-8//IGNORE", $res_csv_file); $error_file_names[] = $entry; } } $filepath_out = $res_dir_name . "/" . $entry; file_put_contents($filepath_out, $res_csv_file) ? $k++ : $er++; $all_file_count++; } } echo "<div id=\"stat_block\">Обработано файлов: <span>" . $k . "</span> из <span>" . $all_file_count . "</span>. Ошибок: <span>" . $er . "</span></div>"; echo "<div id=\"error_files\"><p>" . implode("</p><p>", $error_file_names) . "</p></div>"; } ?> <div><a href="/csv-sorter/">Перезагрузка</a> :: <a href="/csv-sorter/?action=run">Запуск</a>...</div> </body> </html>
Алгоритм работы следующий. Считываются файлы из заданного каталога. Далее данные преобразуются из CSV формата в PHP массив — это по сути импорт CSV.
Функция csv2array($filepath_in, $file_size) считывает CSV данные из файла и преобразует их в массив с помощью стандартной функции PHP fgetcsv().
Далее массив сортируется по заданному полю с помощью функции sort_arr_ncol(). Полезная штука в разных задачах для сортировки многомерных массивов по столбцам.
Далее обработанные данные экспортируются обратно в файл. Вначале преобразуем PHP массив в строку в формате CSV при помощи функции str_putcsv() и записываем результат в файл.
Список компаний до сортировки:
Пример файла после сортировки по столбцу email:
Теперь мы с вами умеем сохранять результаты парсинга не только в JSON формате, но и в CSV. А уже с CSV могут работать много разных программ и движков сайтов. Например, часто с помощью CSV файлов экспортируются и импортируются товары в интернет-магазинах.
Это работает, отлично! Но, как это запустить из консоли?
/usr/bin/php /home/public_html/csv-sorter/?action=run
Не получается, с амерсандом вместо знака вопроса, тоже…
Для запуска из консоли этот скрипт не приспособлен. Вам интерфейс получается совсем не нужен. Самый простой вариант дописать в начало скрипта .
Либо разобраться с передачей переменных в консоли и сделать замену переменных в скрипте:
/usr/bin/php/home/public_html/csv-sorter/ run no
То в скрипте нужно определить переменные
$params[‘action’] = $argv[1]; // =run
$paras[‘charset’] = $argv[2]; // =no
Массив $params[] будет вместо $_REQUEST[] в данном случае.
Здравствуйте.
А, возможно ли спарсить с удаленного сервера (по FTP) нужные для преобразования CSV/XML файлы, чтобы в итоге получить на выходе один CSV?
В PHP есть функция для скачивания файлов по FTP ftp_get().