December 20, 2019

Парсер картинок на php

При написании этого скрипта я ставил перед собой цель обойтись одним файлом без сторонних зависимостей. Итак, приступим.

У меня патологическая тяга к сохранению картинок, с изучением же кодинга архив изображений стал пополняться чуть ли не в геометрической прогрессии. Парсить я решил двач, так как там удобное API.


Использовать скрипт мы будем так: php parser.php ссылка директория, причем на Windows нужно добавить путь к php.exe в переменную %PATH%.
Для начала нужно обработать аргументы, которых у нас будет три - первый для вывода имени файла (я без понятия, как вы собираетесь назвать свой скрипт), второй для ссылки и третий, необязательный, для директории сохранения картинок. Заодно выведем краткий хелп:

if (isset($argv[1])) {
    $url = $argv[1];
} else {
    print_r("Usage: php {$argv[0]} url output_directory(optional)\n");
    exit("\n\tExit with status: wrong script usage");
}

if (isset($argv[2])) {
    $imagesDirectory = $argv[2];
} else {
    $imagesDirectory = 'images';
}

Обратите внимание на двойные кавычки и фигурные скобки вокруг первого аргумента.

Далее объявим массив для ссылок и заменим окончание ссылки с .html на .json, чтобы взаимодействовать с API:

$imageLinks = [];

$request = preg_split('~(html)$~', $url, null, PREG_SPLIT_NO_EMPTY);
$result = json_decode(file_get_contents($request[0] . 'json'), true);
Документация по json_decode и прочим функциям находится на сайте php.net, доступна в том числе и на русском языке.

Теперь нужно пройтись по полученному json'у и записать в массив все ссылки на картинки:

$threads = $result['threads'][0];
foreach ($threads as $thread) {
    foreach ($thread as $posts) {
        foreach ($posts as $key => $value) {
            if ($key == 'files') {
                foreach ($value as $path) {
                    $imageLinks[] = 'https://2ch.hk' . $path['path'];
                }
            }
        }
    }
}

После этого можно приступать к функции сохранения картинок, она будет принимать на вход 2 аргумента - массив ссылок и директорию вывода. Приступим к написанию функционала:

function saveImages($imageLinks, $imagesDirectory) {
$extensionPattern = '~(https:\/\/2ch.hk\/[a-z]+\/[a-z]+\/[0-9]+\/[0-9]+)~';
$imageNamePattern = '~(https://2ch.hk/)[a-z]+(/[a-z]+/[0-9]+/)~';
foreach ($imageLinks as $imageLink) {
    $imageName = preg_split($imageNamePattern, $imageLink, null, PREG_SPLIT_NO_EMPTY);
    $extension = preg_split($extensionPattern, $imageLink, null, PREG_SPLIT_NO_EMPTY);
    $imageName = $imageName[0];
    $extension = $extension[0];
    if ($extension == '.webm' || $extension == '.mp4' || $extension == '.gif') {
        print_r($imageName . " will not be saved!\n");
        continue;
    } else {
        file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . $imagesDirectory . DIRECTORY_SEPARATOR . $imageName, file_get_contents($imageLink));
        print_r($imageName . " was saved successfully\n");
        }
    }
}

Для начала объявим 2 регулярных выражения для расширения и имени картинки (чтобы не генерировать рандомное имя каждый раз). Переприсвоение переменных - необходимость, вызванная особенностью preg_split.
Затем просто проходимся циклом по массиву ссылок, сохраняя картинки и отсекая видео и гифки. В конце сохраняем всё в указанную или дефолтную директорию, притом не важно, вызываете вы скрипт на Linux или на Windows, сохранение будет произведено корректно (Win10 1903 / Ubuntu 16.04 полёт нормальный).

Ну и в конце делаем проверку наличия директории, если она не была передана в качестве аргумента, сохраняем в дефолтную, которая называется 'images' и будет создана там же, где вы вызвали скрипт.

if (file_exists($imagesDirectory)) {
saveImages($imageLinks, $imagesDirectory);
    print_r("\nJob Done!\n");
} else {
    mkdir($imagesDirectory);
    saveImages($imageLinks, $imagesDirectory);
    print_r("\nJob Done!\n");
}

Полный исходный код (вывод в консоль можно убрать, я его использую для отображения прогресса):

<?php

if (isset($argv[1])) {
    $url = $argv[1];
} else {
    print_r("Usage: php {$argv[0]} url output_directory(optional)\n");
    exit("\n\tExit with status: wrong script usage");
}

if (isset($argv[2])) {
    $imagesDirectory = $argv[2];
} else {
    $imagesDirectory = 'images';
}

$imageLinks = [];

$request = preg_split('~(html)$~', $url, null, PREG_SPLIT_NO_EMPTY);
$result = json_decode(file_get_contents($request[0] . 'json'), true);

$threads = $result['threads'][0];
foreach ($threads as $thread) {
    foreach ($thread as $posts) {
        foreach ($posts as $key => $value) {
            if ($key == 'files') {
                foreach ($value as $path) {
                    $imageLinks[] = 'https://2ch.hk' . $path['path'];
                }
            }
        }
    }
}

function saveImages($imageLinks, $imagesDirectory) {
    $extensionPattern = '~(https:\/\/2ch.hk\/[a-z]+\/[a-z]+\/[0-9]+\/[0-9]+)~';
    $imageNamePattern = '~(https://2ch.hk/)[a-z]+(/[a-z]+/[0-9]+/)~';
    foreach ($imageLinks as $imageLink) {
        $imageName = preg_split($imageNamePattern, $imageLink, null, PREG_SPLIT_NO_EMPTY);
        $extension = preg_split($extensionPattern, $imageLink, null, PREG_SPLIT_NO_EMPTY);
        $imageName = $imageName[0];
        $extension = $extension[0];
        if ($extension == '.webm' || $extension == '.mp4' || $extension == '.gif') {
            print_r($imageName . " will not be saved!\n");
            continue;
        } else {
            file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . $imagesDirectory . DIRECTORY_SEPARATOR . $imageName, file_get_contents($imageLink));
            print_r($imageName . " was saved successfully\n");        
        }
    }
}

if (file_exists($imagesDirectory)) {
    saveImages($imageLinks, $imagesDirectory);
    print_r("\nJob Done!\n");
} else {
    mkdir($imagesDirectory);
    saveImages($imageLinks, $imagesDirectory);
    print_r("\nJob Done!\n");
}
small_shell с любовью для @CodingCommunity