{"id":22178050,"url":"https://github.com/fi1a/crawler","last_synced_at":"2026-04-29T10:01:22.301Z","repository":{"id":65497793,"uuid":"580222923","full_name":"fi1a/crawler","owner":"fi1a","description":"PHP crawler","archived":false,"fork":false,"pushed_at":"2023-02-22T02:43:24.000Z","size":365,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-26T19:48:25.098Z","etag":null,"topics":["crawler","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fi1a.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-20T02:42:23.000Z","updated_at":"2023-02-06T04:03:33.000Z","dependencies_parsed_at":"2024-12-02T08:45:24.525Z","dependency_job_id":null,"html_url":"https://github.com/fi1a/crawler","commit_stats":{"total_commits":61,"total_committers":1,"mean_commits":61.0,"dds":0.0,"last_synced_commit":"fb859a2111fd644739b027583f70abd6adbdccb9"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fi1a/crawler","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fi1a%2Fcrawler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fi1a%2Fcrawler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fi1a%2Fcrawler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fi1a%2Fcrawler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fi1a","download_url":"https://codeload.github.com/fi1a/crawler/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fi1a%2Fcrawler/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32420356,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T06:29:02.080Z","status":"ssl_error","status_checked_at":"2026-04-29T06:29:00.631Z","response_time":110,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crawler","php"],"created_at":"2024-12-02T08:34:33.993Z","updated_at":"2026-04-29T10:01:22.285Z","avatar_url":"https://github.com/fi1a.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PHP crawler (парсинг сайта)\n\n[![Latest Version][badge-release]][packagist]\n[![Software License][badge-license]][license]\n[![PHP Version][badge-php]][php]\n![Coverage Status][badge-coverage]\n[![Total Downloads][badge-downloads]][downloads]\n[![Support mail][badge-mail]][mail]\n\nЭтот пакет предоставляет API для обхода ссылок и скачивания файлов (парсинга сайта). С помощью данного пакета вы можете забирать\nлюбую информацию со стороннего сайта. Есть возможность создать собственные обработчики, которые позволят\nкастомизировать логику парсинга страницы, подготовки и сохранения.\n\n## Установка\n\nУстановить этот пакет можно как зависимость, используя Composer.\n\n``` bash\ncomposer require fi1a/crawler\n```\n\n## Шаги процесса парсинга\n\nПроцесс парсинга разделен на три шага:\n\n1. загрузка;\n1. процесс;\n1. запись.\n\nНа шаге \"загрузка\" осуществляется обход страниц или файлов и их загрузка. Парсинг новых ссылок  из\nзагруженной страницы осуществляется классом реализующим интерфейс `Fi1a\\Crawler\\UriParsers\\UriParserInterface`.\nОпределение загружать адрес или нет осуществляется классом реализующим интерфейс\n`Fi1a\\Crawler\\Restrictions\\RestrictionInterface`.\n\nШаг \"процесс\" идет следующим за шагом \"загрузка\". На данном шаге осуществляется преобразование адресов\nклассом реализующим интерфейс `Fi1a\\Crawler\\UriTransformers\\UriTransformerInterface`.\n\nПоследним шагом идет шаг \"запись\". Перед записью осуществляется подготовка контента с помощью класса реализующего\nинтерфейс `Fi1a\\Crawler\\PrepareItems\\PrepareItemInterface` (класс `Fi1a\\Crawler\\PrepareItems\\PrepareHtmlItem`\nзаменяет старые ссылки на новые). Запись осуществляется с помощью `Fi1a\\Crawler\\Writers\\WriterInterface`.\n\nМетодом `run` класса `Fi1a\\Crawler\\Crawler` запускается все три шага последовательно, но можно запускать шаги поочередно\nметодами `download` (шаг загрузки), `process` (шаг процесса) и `write` (шаг записи).\n\n## Примеры\n\nНиже представлены наиболее часто встречающиеся задачи для обхода веб-сайтов.\n\n### Создание копии сайта\n\nС помощью представленного кода можно создать копию веб-сайта `https://some-domain.ru`\nв указанной директории `__DIR__ . '/local-site'`.\n\n```php\nuse Fi1a\\Crawler\\Config;\nuse Fi1a\\Crawler\\ConfigInterface;\nuse Fi1a\\Crawler\\Crawler;\nuse Fi1a\\Crawler\\ItemStorages\\ItemStorage;\nuse Fi1a\\Crawler\\ItemStorages\\StorageAdapters\\LocalFilesystemAdapter;\nuse Fi1a\\Crawler\\Writers\\FileWriter;\n\n$config = new Config();\n\n$config-\u003esetVerbose(ConfigInterface::VERBOSE_DEBUG)\n    -\u003esetSizeLimit('5Mb')\n    -\u003eaddStartUri('https://some-domain.ru');\n\n$crawler = new Crawler($config, new ItemStorage(new LocalFilesystemAdapter(__DIR__ . '/runtime/storage')));\n\n$crawler-\u003esetWriter(new FileWriter(__DIR__ . '/local-site'));\n\n$crawler-\u003erun();\n```\n\nДля начала нужно создать объект конфигурации `Config` и передать его в конструктор класса `Crawler` вместе с \nобъектом класса `ItemStorage`. Затем установить объект `FileWriter` с помощью метода `setWriter` класса `Crawler`\nреализующего логику сохранения элементов (страниц, файлов) в локальную файловую систему.\n\nЗапуск парсинга сайта осуществляется с помощью метода `run` класса `Crawler`.\n\n### Парсинг новостей\n\nКласс парсера ссылок новостей. Находит на странице списка и возвращает ссылки относящиеся к\nдетальной новости и к списку.\n\n```php\nnamespace Foo\\UriParsers;\n\nuse Fi1a\\Console\\IO\\ConsoleOutputInterface;\nuse Fi1a\\Crawler\\ItemInterface;\nuse Fi1a\\Crawler\\UriCollection;\nuse Fi1a\\Crawler\\UriCollectionInterface;\nuse Fi1a\\Crawler\\UriParsers\\UriParserInterface;\nuse Fi1a\\Http\\Uri;\nuse Fi1a\\Log\\LoggerInterface;\nuse Fi1a\\SimpleQuery\\SimpleQuery;\nuse InvalidArgumentException;\n\n/**\n * Парсер ссылок новостей\n */\nclass NewsUriParser implements UriParserInterface\n{\n    /**\n     * @inheritDoc\n     */\n    public function parse(\n        ItemInterface $item,\n        ConsoleOutputInterface $output,\n        LoggerInterface $logger\n    ): UriCollectionInterface {\n        $collection = new UriCollection();\n\n        if (\n            !$item-\u003eisAllow()\n            || $item-\u003egetItemUri()-\u003ehost() !== 'news-domain.ru'\n            || $item-\u003egetItemUri()-\u003epath() !== '/news/'\n        ) {\n            return $collection;\n        }\n\n        $sq = new SimpleQuery((string) $item-\u003egetBody());\n\n        // выбираем ссылки ведущие на детальную новости и ссылки постраничной навигации\n        $nodes = $sq('#news .header, #news .pm_s, #news .pm_n');\n        /** @var \\DOMElement $node */\n        foreach ($nodes as $node) {\n            $value = $sq($node)-\u003eattr('href');\n            if (!is_string($value) || !$value) {\n                continue;\n            }\n            try {\n                $uri = new Uri($value);\n            } catch (InvalidArgumentException $exception) {\n                continue;\n            }\n\n            $collection[] = $uri;\n        }\n\n        return $collection;\n    }\n}\n```\n\nКласс преобразования ссылок. Преобразует ссылки новостей из формата источника в новый формат\n(https://news-domain.ru/news/news-code-1.html =\u003e /news/news-code-1/).\n\n```php\nnamespace Foo\\UriTransformers;\n\nuse Fi1a\\Console\\IO\\ConsoleOutputInterface;\nuse Fi1a\\Crawler\\ItemInterface;\nuse Fi1a\\Crawler\\UriTransformers\\UriTransformerInterface;\nuse Fi1a\\Http\\UriInterface;\nuse Fi1a\\Log\\LoggerInterface;\n\n/**\n * Преобразует uri новостей из внешних адресов в новые\n */\nclass NewsUriTransformer implements UriTransformerInterface\n{\n    /**\n     * @inheritDoc\n     */\n    public function transform(\n        ItemInterface $item,\n        ConsoleOutputInterface $output,\n        LoggerInterface $logger\n    ): UriInterface {\n        if (!$item-\u003eisAllow()) {\n            return $item-\u003egetItemUri();\n        }\n\n        $isNewsPage = preg_match(\n            '#https://news-domain.ru/news/(.+)\\.html#mui',\n            $item-\u003egetItemUri()-\u003euri(),\n                $matches\n        ) \u003e 0;\n\n        if (!$isNewsPage) {\n            $output-\u003ewriteln('    \u003ccolor=blue\u003e- Не является ссылкой на новость\u003c/\u003e');\n\n            return $item-\u003egetItemUri();\n        }\n\n        // Преобразуем ссылки на новости в новый формат\n        $object = $item-\u003egetItemUri()\n            -\u003ewithHost('')\n            -\u003ewithPort(null)\n            -\u003ewithPath('/news/' . $matches[1] . '/');\n\n        return $object;\n    }\n}\n```\n\nКласс подготавливает HTML новости удаляя \"хлебные крошки\" и блоки не относящиеся к контенту новости.\n\n```php\nnamespace Foo\\PrepareItems;\n\nuse Fi1a\\Console\\IO\\ConsoleOutputInterface;\nuse Fi1a\\Crawler\\ItemCollectionInterface;\nuse Fi1a\\Crawler\\ItemInterface;\nuse Fi1a\\Crawler\\PrepareItems\\PrepareHtmlItem;\nuse Fi1a\\Log\\LoggerInterface;\nuse Fi1a\\SimpleQuery\\SimpleQuery;\n\n/**\n * Подготавливает HTML новости\n */\nclass NewsPrepareItem extends PrepareHtmlItem\n{\n    /**\n     * @inheritDoc\n     */\n    public function prepare(\n        ItemInterface $item,\n        ItemCollectionInterface $items,\n        ConsoleOutputInterface $output,\n        LoggerInterface $logger\n    ) {\n        $isNewsPage = preg_match(\n            '#https://news-domain.ru/news/(.+)\\.html#mui',\n            $item-\u003egetItemUri()-\u003euri()\n        ) \u003e 0;\n\n        if (!$isNewsPage) {\n            return false;\n        }\n\n        $sq = new SimpleQuery((string) $item-\u003egetBody(), 'UTF-8');\n\n        $news = $sq('#news');\n        // Удаляем лишние элементы, остается только новость с заголовком и контентом\n        $news('.share, .breadcrumbs, p:last-child')-\u003eremove();\n\n        // Заменяем ссылки на новые ссылки новостей\n        $this-\u003ereplace('a', 'href', $news, $item, $items);\n\n        return $news-\u003ehtml();\n    }\n}\n```\n\nКласс добавляющий/обновляющий новости на сайте 1С-Битрикс записывая их в ИБ.\n\n```php\nnamespace Foo\\Writers;\n\nuse ErrorException;\nuse Fi1a\\Console\\IO\\ConsoleOutputInterface;\nuse Fi1a\\Crawler\\ItemInterface;\nuse Fi1a\\Crawler\\Writers\\WriterInterface;\nuse Fi1a\\Log\\LoggerInterface;\nuse Fi1a\\SimpleQuery\\SimpleQuery;\nuse Bitrix\\Main\\Loader;\nuse Bitrix\\Iblock\\IblockTable;\n\n/**\n * Записывает результат в ИБ 1С-Битрикса\n */\nclass NewsWriter implements WriterInterface\n{\n    /**\n     * @var int\n     */\n    protected $newsIblockId;\n\n    public function __construct()\n    {\n        Loader::includeModule('iblock');\n\n        $iblock = IblockTable::query()\n            -\u003esetSelect(['ID',])\n            -\u003ewhere('CODE', '=', 'furniture_news_s1')\n            -\u003eexec()\n            -\u003efetch();\n\n        if (!$iblock) {\n            throw new ErrorException('Инфоблок новостей не найден');\n        }\n\n        $this-\u003enewsIblockId = (int) $iblock['ID'];\n    }\n\n    /**\n     * @inheritDoc\n     */\n    public function write(ItemInterface $item, ConsoleOutputInterface $output, LoggerInterface $logger): bool\n    {\n        $isNewsPage = preg_match(\n                '#https://news-domain.ru/news/(.+)\\.html#mui',\n                $item-\u003egetItemUri()-\u003euri(),\n                $matches\n            ) \u003e 0;\n\n        if (!$isNewsPage) {\n            $output-\u003ewriteln('    \u003ccolor=blue\u003e- Не является страницей новости\u003c/\u003e');\n\n            return false;\n        }\n\n        $sq = new SimpleQuery((string) $item-\u003egetPrepareBody(), 'UTF-8');\n\n        $name = $sq('h1')-\u003ehtml();\n        $sq('h1')-\u003eremove();\n        $code = $matches[1];\n        $detailText = $sq('body')-\u003ehtml();\n        $previewText = \\TruncateText(strip_tags($detailText), 50);\n\n        $fields = [\n            'IBLOCK_ID' =\u003e $this-\u003enewsIblockId,\n            'NAME' =\u003e $name,\n            'CODE' =\u003e $code,\n            'DETAIL_TEXT' =\u003e $detailText,\n            'DETAIL_TEXT_TYPE' =\u003e 'html',\n            'PREVIEW_TEXT' =\u003e $previewText,\n            'ACTIVE' =\u003e 'Y',\n        ];\n\n        $news = \\CIBlockElement::GetList([], [\n            '=IBLOCK_ID' =\u003e $this-\u003enewsIblockId,\n            '=CODE' =\u003e $code,\n        ], false, false, ['ID'])-\u003eFetch();\n\n        $instance = new \\CIBlockElement();\n\n        if ($news) {\n            $result = $instance-\u003eUpdate($news['ID'], $fields);\n            if ($result === false) {\n                $output-\u003ewriteln('    \u003cerror\u003eНе удалось обновить новость: {{}}\u003c/\u003e', [$instance-\u003eLAST_ERROR]);\n            }\n\n            return $result;\n        }\n\n        $newsId = (int) $instance-\u003eAdd($fields);\n\n        if (!$newsId) {\n            $output-\u003ewriteln('    \u003cerror\u003eНе удалось создать новость: {{}}\u003c/\u003e', [$instance-\u003eLAST_ERROR]);\n\n            return false;\n        }\n\n        return true;\n    }\n}\n```\n\nСоздается объект конфигурации `Config` со значениями:\n- уровень подробности вывода;\n- время жизни элементов в хранилище (0 - без ограничения);\n- ограничение на загружаемый файл (5Mb для всех типов файлов);\n- добавляется точка входа, с которой начинается обход (https://news-domain.ru/news/ - список новостей)\n\nУстанавливаем классы определяющие поведение:\n- метод `setUriParser` устанавливает парсер uri для обхода (в зависимости от типа контента);\n- метод `setUriTransformer` устанавливает класс преобразователь адресов из внешних во внутренние;\n- метод `setPrepareItem` устанавливает класс подготавливающий контент (удаляет лишние теги не относящиеся к новости);\n- метод `setWriter` устанавливает класс записывающий результат обхода (записывает новость в ИБ 1С-Битрикса).\n\nМетодом `loadFromStorage` класса `Fi1a\\Crawler\\Crawler` загружаем из хранилища обработанные элементы с последнего запуска и\nдля страниц списка новостей, отмечаем повторную обработку с целью найти новые добавленные новости.\n\nЗапускаем парсинг новостей методом `run` класса `Fi1a\\Crawler\\Crawler`.\n\n```php\nuse Fi1a\\Crawler\\Config;\nuse Fi1a\\Crawler\\ConfigInterface;\nuse Fi1a\\Crawler\\Crawler;\nuse Fi1a\\Crawler\\ItemInterface;\nuse Fi1a\\Crawler\\ItemStorages\\ItemStorage;\nuse Fi1a\\Crawler\\ItemStorages\\StorageAdapters\\LocalFilesystemAdapter;\nuse Fi1a\\Http\\Mime;\nuse Foo\\PrepareItems\\NewsPrepareItem;\nuse Foo\\UriParsers\\NewsUriParser;\nuse Foo\\UriTransformers\\NewsUriTransformer;\nuse Foo\\Writers\\NewsWriter;\n\n$config = new Config();\n\n$config-\u003esetVerbose(ConfigInterface::VERBOSE_DEBUG)\n    -\u003esetLifetime(0)\n    -\u003esetSizeLimit('5Mb')\n    -\u003eaddStartUri('https://news-domain.ru/news/');\n\n$crawler = new Crawler($config, new ItemStorage(new LocalFilesystemAdapter(__DIR__ . '/runtime/storage')));\n\n$crawler-\u003esetUriParser(new NewsUriParser(), Mime::HTML)\n    -\u003esetUriTransformer(new NewsUriTransformer())\n    -\u003esetPrepareItem(new NewsPrepareItem())\n    -\u003esetWriter(new NewsWriter(), Mime::HTML);\n\n$crawler-\u003eloadFromStorage();\n\n// При повторном запуске страницы списка помечаем на повторную обработку для добавления новых новостей\nforeach ($crawler-\u003egetItems() as $item) {\n    assert($item instanceof ItemInterface);\n    if (\n        !$item-\u003eisAllow()\n        || $item-\u003egetItemUri()-\u003ehost() !== 'news-domain.ru'\n        || $item-\u003egetItemUri()-\u003epath() !== '/news/'\n    ) {\n        continue;\n    }\n\n    $item-\u003esetDownloadStatus(null);\n    $item-\u003esetProcessStatus(null);\n    $item-\u003esetWriteStatus(null);\n}\n\n$crawler-\u003erun();\n```\n\n## Основные классы пакета:\n\nНиже представлены основные классы пакета. С помощью одних можно настроить поведение парсера,\nа с помощью других расширить его.\n\n- `Fi1a\\Crawler\\Crawler` - основной класс пакета;\n- `Fi1a\\Crawler\\Config` - конфигурация парсинга;\n- `Fi1a\\Crawler\\UriCollection` - коллекция адресов.\n- `Fi1a\\Crawler\\Item` - элемент обхода;\n- `Fi1a\\Crawler\\ItemCollection` - коллекция элементов обхода.\n- `Fi1a\\Crawler\\ItemStorages\\ItemStorage` - реализует хранилище элементов парсинга;\n  - `Fi1a\\Crawler\\ItemStorages\\StorageAdapters\\LocalFilesystemAdapter` - адаптер для хранения в локальной файловой системе;\n  - `Fi1a\\Crawler\\ItemStorages\\StorageAdapters\\FilesystemAdapter` - адаптер для хранения в файловой системе;\n- Прокси\n  - `Fi1a\\Crawler\\Proxy\\Proxy` - прокси для запроса;\n  - `Fi1a\\Crawler\\Proxy\\ProxyCollection` - коллекция прокси;\n  - `Fi1a\\Crawler\\Proxy\\ProxyStorage` - реализует хранилище для прокси;\n    - `Fi1a\\Crawler\\Proxy\\StorageAdapters\\LocalFilesystemAdapter` - адаптер для хранения в локальной файловой системе;\n    - `Fi1a\\Crawler\\Proxy\\StorageAdapters\\FilesystemAdapter` - адаптер для хранения в файловой системе;\n  - Подбор подходящих прокси для запроса\n    - `Fi1a\\Crawler\\Proxy\\Selections\\FilterByAttempts` - фильтрация прокси по числу ошибок соединения;\n    - `Fi1a\\Crawler\\Proxy\\Selections\\Limit` - ограничение на кол-во подобранных прокси;\n    - `Fi1a\\Crawler\\Proxy\\Selections\\OnlyActive` - фильтрация прокси по активности;\n    - `Fi1a\\Crawler\\Proxy\\Selections\\SortedByTime` - отсортированные по времени использования;\n- Классы расширяющие операции\n  - Ограничение обхода uri\n    - `Fi1a\\Crawler\\Restrictions\\NotAllowRestriction` - запрет на обход для всех uri;\n    - `Fi1a\\Crawler\\Restrictions\\UriRestriction` - ограничение по домену и пути;\n  - Шаг загрузки\n    - `Fi1a\\Crawler\\UriParsers\\HtmlUriParser` - парсит html и возвращает uri для обхода;\n  - Шаг преобразования uri\n    - `Fi1a\\Crawler\\UriTransformers\\SiteUriTransformer` - преобразует uri из внешних адресов в локальные;\n  - Шаг записи\n    - `Fi1a\\Crawler\\PrepareItem\\PrepareHtmlItem` - подготавливает HTML элемент (заменяет ссылки страницы на новые);\n    - `Fi1a\\Crawler\\Writers\\FileWriter` - записывает результат обхода в файл;\n\n## Объект настроек\n\n- `startUri` - точка входа, с которой начинается обход;\n  - `addStartUri(string $startUri)` - добавить точку входа;\n  - `getStartUri(): array` - возвращает добавленные точки входа;\n- `httpClientConfig` - объект настроек http-клиента ([подробнее об объекте настроек](https://github.com/fi1a/http-client#объект-настроек));\n  - `setHttpClientConfig(Fi1a\\HttpClient\\ConfigInterface $config)` - установить объект настроек http-клиента;\n  - `getHttpClientConfig(): Fi1a\\HttpClient\\ConfigInterface` - возвращает объект настроек http-клиента;\n- `httpClientHandler` (\"Fi1a\\HttpClient\\Handlers\\StreamHandler\") - обработчик запросов (возможные значения: \"Fi1a\\HttpClient\\Handlers\\StreamHandler\", \"Fi1a\\HttpClient\\Handlers\\CurlHandler\")\n  - `setHttpClientHandler(string $handler)` - установить обработчик запросов;\n  - `getHttpClientHandler(): string` - вернуть обработчик запросов;\n- `verbose` (ConfigInterface::VERBOSE_NORMAL) - уровень подробности вывода (возможные значения: ConfigInterface::VERBOSE_NONE, ConfigInterface::VERBOSE_NORMAL,\nConfigInterface::VERBOSE_HIGHT, ConfigInterface::VERBOSE_HIGHTEST, ConfigInterface::VERBOSE_DEBUG);\n  - `setVerbose(int $verbose)` - установить уровень подробности вывода;\n  - `getVerbose(): int` - вернуть уровень подробности вывода;\n- `logChannel` (\"crawler\") - канал логирования;\n  - `setLogChannel(string $logChannel)` - установить канал логирования;\n  - `getLogChannel(): string` - вернуть канал логирования;\n- `saveAfterQuantity` (10) - параметр, определяющий через какое новое кол-во элементов сохранять элементы в хранилище;\n  - `setSaveAfterQuantity(int $quantity)` - установить параметр, определяющий через какое новое кол-во элементов сохранять элементы в хранилище;\n  - `getSaveAfterQuantity(): int` - возвращает параметр, определяющий через какое новое кол-во элементов сохранять элементы в хранилище;\n- `lifeTime` (24 * 60 * 60) - время жизни элементов в хранилище;\n  - `setLifetime(int $lifeTime)` - установить время жизни элементов в хранилище;\n  - `getLifetime(): int` - вернуть время жизни элементов в хранилище;\n- `delay` ([0, 0]) - пауза между запросами;\n  - `setDelay($delay)` - установить паузу между запросами (возможные значения: int|array\u003carray-key, int\u003e);\n  - `getDelay(): array` - вернуть паузу между запросами;\n- `sizeLimits` - ограничение на загружаемый файл по типу контента;\n  - `setSizeLimit($sizeLimit, ?string $mime = null)` - установить ограничение на загружаемый файл по типу контента;\n  - `getSizeLimits(): array` - возвращает ограничения на загружаемые файлы по типу контента;\n- `retry` (3) - кол-во попыток запросов к адресу при http ошибки;\n  - `setRetry(int $retry)` - установить кол-во попыток запросов к адресу при http ошибки;\n  - `getRetry(): int` - вернуть кол-во попыток запросов к адресу при http ошибки.\n\nПример:\n\n- установить уровень подробности вывода на самый наивысший уровень ConfigInterface::VERBOSE_DEBUG;\n- ограничение на все загружаемые файлы в 5Mb и на файл типа jpeg в 1Mb;\n- пауза между запросами случайным образом от 3 до 10 секунд;\n- добавить точку входа `https://some-domain.ru`.\n\n```php\nuse Fi1a\\Crawler\\Config;\nuse Fi1a\\Crawler\\ConfigInterface;\n\n$config = new Config();\n\n$config-\u003esetVerbose(ConfigInterface::VERBOSE_DEBUG)\n    -\u003esetSizeLimit('5Mb')\n    -\u003esetSizeLimit('1Mb', 'image/jpeg')\n    -\u003esetDelay([3, 10])\n    -\u003eaddStartUri('https://some-domain.ru');\n```\n\n## Ограничение обхода\n\nДля ограничения обхода парсером используется класс реализующий интерфейс `Fi1a\\Crawler\\Restrictions\\RestrictionInterface`,\nдобавленный методом `addRestriction` класса `Fi1a\\Crawler\\Crawler`.\n\nВ пакете имеются два класса для реализации ограничения:\n\n- `Fi1a\\Crawler\\Restrictions\\NotAllowRestriction` - запрет на обход;\n- `Fi1a\\Crawler\\Restrictions\\UriRestriction` - ограничение по домену и пути;\n\nПример ограничения обхода папкой news домена some-domain.ru:\n\n```php\nuse Fi1a\\Crawler\\Restrictions\\UriRestriction;\n\n$crawler-\u003eaddRestriction(new UriRestriction('https://some-domain.ru/news/'));\n```\n\nЕсли не были заданы ограничения при начале шага загрузки, они `Fi1a\\Crawler\\Restrictions\\UriRestriction`\nдобавляются автоматически на основе точек входа заданных методом `addStartUri` объекта конфигурации.\n\n## Элемент обхода\n\nПри парсинге объект класса `Fi1a\\Crawler\\Item` используется как конечная точка адреса содержащая в себе всю\nнеобходимую информацию для парсинга.\n\nМетоды класса:\n\n| Метод                                           | Описание                                                    |\n|-------------------------------------------------|-------------------------------------------------------------|\n| getItemUri(): UriInterface                      | Возвращает uri                                              |\n| setStatusCode(?int $statusCode)                 | Устанавливает код статуса ответа                            |\n| getStatusCode(): ?int                           | Возвращает код статуса ответа                               |\n| setReasonPhrase(?string $reasonPhrase)          | Устанавливает текст причины ассоциированный с кодом статуса |\n| getReasonPhrase(): ?string                      | Возвращает текст причины ассоциированный с кодом статуса    |\n| setDownloadStatus(?bool $status)                | Запрос выполнен успешно или нет                             |\n| getDownloadStatus(): ?bool                      | Запрос выполнен успешно или нет                             |\n| setProcessStatus(?bool $status)                 | Обработка выполнена успешно или нет                         |\n| getProcessStatus(): ?bool                       | Обработка выполнена успешно или нет                         |\n| setWriteStatus(?bool $status)                   | Запись выполнена успешно или нет                            |\n| getWriteStatus(): ?bool                         | Запись выполнена успешно или нет                            |\n| setAllow(bool $allow)                           | Разрешено к обработке или нет                               |\n| isAllow(): bool                                 | Разрешено к обработке или нет                               |\n| setBody(string $body)                           | Установить тело ответа                                      |\n| getBody(): ?string                              | Вернуть тело ответа                                         |\n| setPrepareBody($body)                           | Установить подготовленное тело ответа                       |\n| getPrepareBody()                                | Вернуть подготовленное тело ответа                          |\n| free()                                          | Очищает тело запроса                                        |\n| reset()                                         | Сбрасывает состояние                                        |\n| setContentType(?string $contentType)            | Установить тип контента                                     |\n| getContentType(): ?string                       | Вернуть тип контента                                        |\n| setNewItemUri(UriInterface $newItemUri)         | Установить новый uri                                        |\n| getNewItemUri(): ?UriInterface                  | Вернуть новый uri                                           |\n| expiresAt(?DateTime $dateTime)                  | Истечет в переданное время                                  |\n| expiresAfter(?int $lifetime)                    | Истекает через переданное время                             |\n| getExpire(): ?DateTime                          | Возвращает когда закончится срок жизни                      |\n| isExpired(): bool                               | Срок жизни истек                                            |\n| getAbsoluteUri(UriInterface $uri): UriInterface | Возвращает абсолютный путь относительно элемента            |\n| isImage(): bool                                 | Является ли изображением                                    |\n| isFile(): bool                                  | Является ли \"файлом\"                                        |\n| isPage(): bool                                  | Является ли \"страницей\"                                     |\n| isCss(): bool                                   | Является ли Css файлом                                      |\n| isJs(): bool                                    | Является ли Js файлом                                       |\n| toArray(): array                                | В массив                                                    |\n| static fromArray(array $fields)                 | Из массива                                                  |\n\nПолучить коллекцию элементов обхода можно методом `getItems` класса `Fi1a\\Crawler\\Crawler`.\n\n## Геттеры коллекций элементов обхода\n\nПосле выполнения парсинга или загрузки элементов из хранилища с помощью метода `loadFromStorage`\nкласса `Fi1a\\Crawler\\Crawler` становится доступна коллекция элементов `Fi1a\\Crawler\\ItemCollectionInterface`, которую\nможно получить методом `getItems` класса `Fi1a\\Crawler\\Crawler`.\n\nУ данной коллекции есть вспомогательные методы, позволяющие отфильтровать элементы коллекции\nпо какому либо признаку:\n\n- `getDownloaded` - возвращает успешно загруженные элементы;\n- `getProcessed` - возвращает успешно обработанные элементы;\n- `getWrited` - возвращает успешно записанные элементы;\n- `getImages` - возвращает все элементы изображений;\n- `getFiles` - возвращает все элементы файлов;\n- `getPages` - возвращает все элементы страниц;\n- `getCss` - возвращает все элементы css файлов;\n- `getJs` - возвращает все элементы js файлов.\n\nПример выведет все ссылки на загруженные изображения:\n\n```php\n$crawler-\u003eloadFromStorage();\n$collection = $crawler-\u003egetItems();\n\nforeach ($collection-\u003egetDownloaded()-\u003egetImages() as $item) {\n    echo $item-\u003egetItemUri()-\u003euri() . PHP_EOL;\n}\n```\n\n## Использование прокси при запросах\n\nПри парсинге сайтов часто требуется использовать прокси. Данный пакет имеет вспомогательные классы для работы с\nпрокси при запросах.\n\nПри работе для записи или чтения информации о прокси используется класс `Fi1a\\Crawler\\Proxy\\ProxyStorage`. Данный класс\nреализует хранилище прокси. Каким образом будет осуществляться хранение определяется адаптером передаваемым\nпервым аргументов в конструктор (адаптер `Fi1a\\Crawler\\Proxy\\StorageAdapters\\LocalFilesystemAdapter` осуществляет\nхранение прокси в json-файле).\n\nДоступны два типа прокси: http и socks5 прокси. Следующий код добавляет прокси в хранилище:\n\n```php\nuse Fi1a\\Crawler\\Proxy\\Proxy;\nuse Fi1a\\Crawler\\Proxy\\ProxyStorage;\nuse Fi1a\\Crawler\\Proxy\\StorageAdapters\\LocalFilesystemAdapter;\n\n$proxyStorage = new ProxyStorage(new LocalFilesystemAdapter(__DIR__ . '/runtime'));\n\n$httpProxy = Proxy::factory([\n    'type' =\u003e 'http',\n    'host' =\u003e '127.0.0.1',\n    'port' =\u003e 50100,\n    'userName' =\u003e 'username',\n    'password' =\u003e 'password',\n]);\n$proxyStorage-\u003esave($httpProxy);\n\n$httpProxy = Proxy::factory([\n    'type' =\u003e 'socks5',\n    'host' =\u003e '127.0.0.1',\n    'port' =\u003e 50101,\n    'userName' =\u003e 'username',\n    'password' =\u003e 'password',\n]);\n$proxyStorage-\u003esave($httpProxy);\n```\n\nПри следующем запуске парсера данные прокси будут загружены из хранилища и использованы для запросов.\n\nПодбор подходящих прокси осуществляется с помощью классов `Fi1a\\Crawler\\Proxy\\Selections\\ProxySelectionInterface`:\n\n- `Fi1a\\Crawler\\Proxy\\Selections\\FilterByAttempts` - фильтрация прокси по числу ошибок соединения;\n- `Fi1a\\Crawler\\Proxy\\Selections\\Limit` - ограничение на кол-во подобранных прокси;\n- `Fi1a\\Crawler\\Proxy\\Selections\\OnlyActive` - фильтрация прокси по активности;\n- `Fi1a\\Crawler\\Proxy\\Selections\\SortedByTime` - отсортированные по времени использования;\n\nСледующий код выберет только активные прокси (`OnlyActive`), отфильтрует по числу ошибок (`FilterByAttempts`),\nотсортирует по времени использования (`SortedByTime`) и вернет одну прокси (`Limit`) для использования в запросе:\n\n```php\n$crawler-\u003esetProxySelection(new Limit(new SortedByTime(new FilterByAttempts(new OnlyActive(), 3)), 1));\n```\n\nПример парсинга сайта с использованием сохраненных прокси в хранилище:\n\n```php\nuse Fi1a\\Crawler\\Config;\nuse Fi1a\\Crawler\\ConfigInterface;\nuse Fi1a\\Crawler\\Crawler;\nuse Fi1a\\Crawler\\ItemStorages\\ItemStorage;\nuse Fi1a\\Crawler\\ItemStorages\\StorageAdapters\\LocalFilesystemAdapter;\nuse Fi1a\\Crawler\\Proxy\\ProxyStorage;\nuse Fi1a\\Crawler\\Proxy\\Selections\\FilterByAttempts;\nuse Fi1a\\Crawler\\Proxy\\Selections\\Limit;\nuse Fi1a\\Crawler\\Proxy\\Selections\\OnlyActive;\nuse Fi1a\\Crawler\\Proxy\\Selections\\SortedByTime;\nuse Fi1a\\Crawler\\Proxy\\StorageAdapters\\LocalFilesystemAdapter as ProxyStorageLocalFilesystemAdapter;\nuse Fi1a\\Crawler\\Writers\\FileWriter;\n\n$config = new Config();\n\n$config-\u003esetVerbose(ConfigInterface::VERBOSE_DEBUG)\n    -\u003esetSizeLimit('5Mb')\n    -\u003eaddStartUri('https://some-domain.ru');\n\n$crawler = new Crawler(\n    $config,\n    new ItemStorage(new LocalFilesystemAdapter(__DIR__ . '/runtime/storage')),\n    new ProxyStorage(new ProxyStorageLocalFilesystemAdapter(__DIR__ . '/runtime'))\n);\n\n$crawler-\u003esetProxySelection(new Limit(new SortedByTime(new FilterByAttempts(new OnlyActive(), 3)), 1))\n    -\u003esetWriter(new FileWriter(__DIR__ . '/local-site'));\n\n$crawler-\u003erun();\n```\n\n[badge-release]: https://img.shields.io/packagist/v/fi1a/crawler?label=release\n[badge-license]: https://img.shields.io/github/license/fi1a/crawler?style=flat-square\n[badge-php]: https://img.shields.io/packagist/php-v/fi1a/crawler?style=flat-square\n[badge-coverage]: https://img.shields.io/badge/coverage-100%25-green\n[badge-downloads]: https://img.shields.io/packagist/dt/fi1a/crawler.svg?style=flat-square\u0026colorB=mediumvioletred\n[badge-mail]: https://img.shields.io/badge/mail-support%40fi1a.ru-brightgreen\n\n[packagist]: https://packagist.org/packages/fi1a/crawler\n[license]: https://github.com/fi1a/crawler/blob/master/LICENSE\n[php]: https://php.net\n[downloads]: https://packagist.org/packages/fi1a/crawler\n[mail]: mailto:support@fi1a.ru","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffi1a%2Fcrawler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffi1a%2Fcrawler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffi1a%2Fcrawler/lists"}