getContent(); $this->saveContent($contents); } /** * Читает все HTML-файлы из tmp и возвращает массив: * [ * ['file' => '/absolute/path/to/file.html', 'html' => '...'], * ... * ] */ public function getResult(): array { $tmpDir = self::TMP_DIR; if (! is_dir($tmpDir)) { return []; } $files = glob($tmpDir . '/*.html') ?: []; $results = []; foreach ($files as $file) { $html = file_get_contents($file); if ($html === false) { error_log("[Encar] Не удалось прочитать файл: $file"); continue; } $results[] = [ 'file' => $file, 'html' => $html, ]; } return $results; } /** * Удаляет все HTML-файлы из tmp. * Метод готов, но пока не вызывается из Parser. */ public function clearData(): void { $tmpDir = self::TMP_DIR; if (! is_dir($tmpDir)) { return; } $files = glob($tmpDir . '/*.html') ?: []; foreach ($files as $file) { if (! unlink($file)) { error_log("[Encar] Не удалось удалить файл: $file"); } } } // ------------------------------------------------------------------------- // Private // ------------------------------------------------------------------------- /** * Список ссылок для парсинга */ private function getLinks(): array { return [ 'https://fem.encar.com/cars/detail/41664398?pageid=dc_carsearch&listAdvType=pic&carid=41664398&view_type=checked&wtClick_korList=015&advClickPosition=kor_pic_p1_g1', 'https://fem.encar.com/cars/detail/41939059?pageid=dc_carsearch&listAdvType=pic&carid=41939059&view_type=checked&wtClick_korList=015&advClickPosition=kor_pic_p1_g2', ]; } /** * Загружает HTML-контент по всем ссылкам. * Возвращает массив: [ ['url' => ..., 'html' => ...], ... ] */ private function getContent(): array { $links = $this->getLinks(); $results = []; foreach ($links as $index => $url) { $html = $this->fetchUrl($url); if ($html !== false) { $body = $this->extractBody($html); if ($body !== null) { $results[] = [ 'url' => $url, 'html' => $body, ]; } else { error_log("[Encar] Не удалось извлечь : $url"); } } else { error_log("[Encar] Не удалось загрузить: $url"); } // Пауза между запросами (кроме последнего) if ($index < count($links) - 1) { sleep(self::REQUEST_INTERVAL); } } return $results; } /** * Извлекает содержимое тега из HTML-строки. * Возвращает null если тег не найден. */ private function extractBody(string $html): ?string { $dom = new \DOMDocument(); // Подавляем ошибки парсинга (битый HTML, корейские символы и т.д.) libxml_use_internal_errors(true); // UTF-8 hint — иначе DOMDocument может неверно определить кодировку $dom->loadHTML('' . $html, LIBXML_NOWARNING | LIBXML_NOERROR); libxml_clear_errors(); $body = $dom->getElementsByTagName('body')->item(0); if ($body === null) { return null; } // Сериализуем innerHTML тега (без самих тегов ) $innerHTML = ''; foreach ($body->childNodes as $child) { $innerHTML .= $dom->saveHTML($child); } return trim($innerHTML); } /** * Выполняет HTTP GET-запрос с кастомным User-Agent */ private function fetchUrl(string $url): string | false { $response = Http::withHeaders([ 'User-Agent' => self::USER_AGENT, 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language' => 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7', 'Connection' => 'keep-alive', ]) ->timeout(30) ->withOptions(['allow_redirects' => true]) ->get($url); if ($response->failed()) { return false; } return $response->body(); } /** * Сохраняет полученный HTML в файлы в папке tmp */ private function saveContent(array $contents): void { if (! is_dir(self::TMP_DIR)) { mkdir(self::TMP_DIR, 0755, true); } foreach ($contents as $item) { $carId = $this->extractCarId($item['url']); $filename = self::TMP_DIR . '/' . $carId . '_' . time() . '.html'; $written = file_put_contents($filename, $item['html']); if ($written === false) { error_log("[Encar] Не удалось сохранить файл: $filename"); } else { echo "[Encar] Сохранено: $filename" . PHP_EOL; } } } /** * Извлекает carid из URL, либо возвращает md5-хэш URL */ private function extractCarId(string $url): string { if (preg_match('/carid=(\d+)/', $url, $matches)) { return $matches[1]; } return md5($url); } }