За последние 24 часа нас посетил 22861 программист и 1264 робота. Сейчас ищут 763 программиста ...

Перебор данных из внешнего JSON: как правильно проверить получение?

Тема в разделе "PHP для новичков", создана пользователем Moor, 10 мар 2020.

Метки:
  1. Moor

    Moor Новичок

    С нами с:
    1 мар 2020
    Сообщения:
    23
    Симпатии:
    0
    Адрес:
    Latvia
    Привет!
    Я очень начинающий, в PHP у меня есть небольшие практические навыки, но в теории пока только разбираюсь. Это был дисклеймер :) Буду очень рад конструктивной критике и помощи.

    Пишу для собственных нужд небольшой инструмент для работы с REST API. Получаю из источника данные, например, о товарах. Они разбиты постранично, объем не сказать чтоб мелкий — при 100 товарах на страницу ответ на запрос превышает 5 Мб. Источник данных иногда тормозит, и потому подключение может отваливаться. Задача — вытянуть сначала существующие товары и записать их в MongoDB.

    Первый скрипт выглядел так:
    PHP:
    1. $products = $woocommerce->get('products?per_page=20'); // Получаем первую страницу
    2.  
    3. $lastResponse = $woocommerce->http->getResponse(); // Нужно получить количество страниц из хэдера
    4. $headers = $lastResponse->getHeaders();
    5. $totalPages = $headers['X-WP-TotalPages']; // Получаем количество страниц
    6.  
    7. $productsCollection = $database->selectCollection('products'); // Указываем, куда записывать в MongoDB
    8.  
    9. for ($n=1; $n<($totalPages + 1); $n++) { // Перебираем страницы
    10.    
    11.     $products_page = $woocommerce->get('products?per_page=20&page=' . $n); // Текущая страница
    12.     echo '<b>Страница ' . $n . ' из ' . $totalPages . '</b><br>'; // Вывод номера страницы
    13.    
    14.     foreach ($products_page as $product) {
    15.         $product_id = $product->id; // Получаем ID товара
    16.         $productdata = $woocommerce->get('products/' . $product_id); // Получаем товар, отсюда можно начинать отправлять в базу
    17.         //$productsCollection->insertOne($productdata); // Закомментировано, пока просто проверим, дойдет ли до конца
    18.         echo 'Товар ' . $product_id . ' отправлен в базу. <br>'; // Получилось
    19.     }
    20. }
    21.  
    22. echo 'Готово!';
    Страниц в ответе 56, на каждой по 20 товаров. Сколько я ни пытался, сколько ни отключал таймауты и ни увеличивал max_execution_time, я так ни разу и не увидел надписи «Готово!». Скрипт не доходит до конца, отваливаясь, насколько я могу понять, именно на получении очередного товара (всегда разные id).

    Так как я ни черта не знаю о проверках подключения, я в поисках решения не нашел ничего лучше, чем сделать циклы проверок переменных. Получилось вот так:
    PHP:
    1. prodStart:
    2. $products = $woocommerce->get('products?per_page=20'); // Получаем первую страницу
    3.  
    4. if ($products) {
    5.  
    6.     $lastResponse = $woocommerce->http->getResponse(); // Нужно получить количество страниц из хэдера
    7.     $headers = $lastResponse->getHeaders();
    8.     $totalPages = $headers['X-WP-TotalPages']; // Получаем количество страниц
    9.  
    10.     $productsCollection = $database->selectCollection('products'); // Указываем, куда записывать в MongoDB
    11.  
    12.     for ($n=1; $n<($totalPages + 1); $n++) { // Перебираем страницы
    13.        
    14.         prodPageLoop:
    15.         $products_page = $woocommerce->get('products?per_page=20&page=' . $n); // Текущая страница
    16.         if ($products_page) {
    17.            
    18.             echo '<b>Страница ' . $n . ' из ' . $totalPages . '</b><br>'; // Вывод номера страницы
    19.            
    20.             foreach ($products_page as $product) {
    21.                
    22.                 prodCycleLoop:
    23.                 if ($product) {
    24.                     $product_id = $product->id; // Получаем ID товара
    25.  
    26.                     prodSingleLoop:
    27.                     $productdata = $woocommerce->get('products/' . $product_id); // Получаем товар, отсюда можно начинать отправлять в базу
    28.                    
    29.                     if ($productdata) {
    30.                         //$productsCollection->insertOne($productdata); // Закомментировано, пока просто проверим, дойдет ли до конца
    31.                         echo 'Товар ' . $product_id . ' отправлен в базу. <br>';
    32.                     }
    33.                     else {
    34.                         goto prodSingleLoop;
    35.                     }
    36.                 }
    37.                 else {
    38.                     goto prodCycleLoop;
    39.                 }
    40.             }
    41.         }
    42.         else {
    43.             goto prodPageLoop;
    44.         }
    45.     }
    46.  
    47. }
    48. else {
    49.     goto prodStart;
    50. }
    51.  
    52. echo 'Готово!';
    Теоретически этот код должен долбить запрос, пока не получит ответ и не обработает его. Но даже такой код отваливается на каком-нибудь товаре. Последний раз он исполнился до 11 товара на 43 странице. Это, конечно, успех :) Но задача не выполнена.

    Скажите, есть ли какой-то более эффективный способ решения?
     
  2. ADSoft

    ADSoft Старожил

    С нами с:
    12 мар 2007
    Сообщения:
    3.823
    Симпатии:
    736
    Адрес:
    Татарстан
    goto ?????
     
  3. Moor

    Moor Новичок

    С нами с:
    1 мар 2020
    Сообщения:
    23
    Симпатии:
    0
    Адрес:
    Latvia
    Оператор goto есть в PHP. Помню такое дело еще из Бейсика :) Подскажете, как правильнее — будет очень клёво.
     
  4. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Если будетelse, будет бесконечный цикл. Очень мило :) А с чего вы решили вообще, что вам может прийти сюда что-то ещё?
    --- Добавлено ---
    Есть. Точнее, появился в 5.3. Раза два использовал за свою карьеру шестилетнюю. В вашем случае, не понятно, что вы пытаетесь этим достичь.

    Не надо выполнять длительные задачи по запросу из браузера. Их надо выполнять в консольных скриптах либо использовать очереди.
     
    #4 mkramer, 10 мар 2020
    Последнее редактирование: 11 мар 2020
  5. Moor

    Moor Новичок

    С нами с:
    1 мар 2020
    Сообщения:
    23
    Симпатии:
    0
    Адрес:
    Latvia
    @mkramer Я могу ошибаться, но смысл в том, чтобы циклически запрашивать данные, пока они не будут получены. То есть это проверка на наличие данных. Сейчас я уже вместо if($productdata) поставил if(!empty($productdata)). Но ситуацию это никак не изменило. Отваливается на рандомных местах. Ориентировочный объем данных одной страницы 2-3 МБ
     
  6. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Ну, во-первых, такие вещи делаются циклами while или do-while, а не с помощью goto. Goto в современном программировании стараются избегать. Во-вторых, покажите фрагменты json-ов, которые возвращаются.
     
  7. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.072
    Симпатии:
    1.237
    Адрес:
    там-сям
    Мои goto остались в бейсике. :) Ни разу не использовал его в работе!
    @Moor проверь работу скрипта на малом наборе данных, лучше из консоли, как @mkramer подсказывает.
     
  8. Valick

    Valick Активный пользователь

    С нами с:
    12 авг 2018
    Сообщения:
    1.911
    Симпатии:
    328
    На Бейсике goto я использовал очень часто, когда начал изучать Паскаль, то преподаватель сказал, что goto признак дурного тона, после недолговременного "разрыва шаблона" я понял красоту жизни без goto и я уже не использовал его нигде и никогда. Признаться честно я далеко не сразу узнал, что он вообще есть в РНР, узнал лишь для того что бы забыть.
     
  9. miketomlin

    miketomlin Старожил

    С нами с:
    9 авг 2016
    Сообщения:
    3.792
    Симпатии:
    650
    Спасибо, что напомнили. А то я прям жить без него не могу :)
     
  10. Moor

    Moor Новичок

    С нами с:
    1 мар 2020
    Сообщения:
    23
    Симпатии:
    0
    Адрес:
    Latvia
    @artoodetoo, @Valick, @mkramer, спасибо, уроки некоторые для себя вынес, читаю мануалы.
    В сообщение форума не влезает даже JSON одного товара :) По ссылке 1 товар, не самый объемный https://codeshare.io/Gb1mBj
     
  11. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    А что по общему запросу возвращается? Почему надо перезапрашивать по одному товару?
     
  12. Moor

    Moor Новичок

    С нами с:
    1 мар 2020
    Сообщения:
    23
    Симпатии:
    0
    Адрес:
    Latvia
    В корень зрите :) Я перепроверил — общего запроса хватает для товаров. Просто перед этим делал заказы, там надо было в каждый заходить. Я уже упростил, и заработало как надо. Правда, пришлось пошаманить, потому что массиве есть ключи, которые содердат точку, Монго их не любит, и скрипт падал. В общем, вот что получилось:
    PHP:
    1. $products = $woocommerce->get('products'); // первая страница
    2.  
    3. $a = 0; // счетчик — посмотрим, совпадет ли количество
    4. $find = 'amazonS3_cache'; // дрянь, из-за которой добавление в базу не работает
    5.  
    6. $lastResponse = $woocommerce->http->getResponse();
    7. $headers = $lastResponse->getHeaders();
    8. $totalPages = $headers['X-WP-TotalPages']; // получаем количество страниц
    9.  
    10. $productsCollection = $database->selectCollection('products'); // отправляем в эту коллекцию
    11.  
    12. for ($n=1; $n<($totalPages + 1); $n++) { // перебираем страницы, пока не кончатся
    13.    
    14.     $products_page = $woocommerce->get('products?page='.$n); // добавляем номер страницы
    15.     echo '<b>Страница ' . $n . ' из ' . $totalPages . '</b><br>'; //////////////
    16.  
    17.     foreach ($products_page as $product) { // перебираем товары на каждой странице
    18.         $product_id = $product->id;
    19.        
    20.         foreach($product->meta_data as $struct) { // ищем дрянь
    21.             if ($find == $struct->key) {
    22.                 unset($struct->value); // очищаем ключи дряни
    23.             }
    24.         }
    25.         $productsCollection->insertOne($product);
    26.  
    27.         echo 'Товар ' . $product_id . ' отправлен в базу.<br>'; //////////////
    28.         $a++; // плюс 1 к счетчику
    29.     }
    30. }
    31. echo '<h1>' . $a . ' товаров добавлено!</h1>';
    Буду очень признателен за любые советы и комментарии по делу.