From 0d895688c3d4d6f26eff9abd50c653048049aaaf Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Thu, 5 Mar 2026 15:47:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=B3=E9=94=AE=E8=AF=8D=E6=8A=93=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/controller/UsedCarController.php | 21 +++ backend/app/service/UsedCarCrawlService.php | 136 +++++++++++++++++++ backend/config/route.php | 1 + 3 files changed, 158 insertions(+) diff --git a/backend/app/controller/UsedCarController.php b/backend/app/controller/UsedCarController.php index a9978b7..9f0af2e 100644 --- a/backend/app/controller/UsedCarController.php +++ b/backend/app/controller/UsedCarController.php @@ -187,6 +187,27 @@ class UsedCarController extends BaseController } } + /** + * 关键词搜索懂车帝车型数据 + */ + public function crawlKeyword(Request $request): Response + { + $this->requireAuth($request); + $keyword = $request->post('keyword') ?: $request->get('keyword'); + + if (empty($keyword)) { + return $this->error('关键词不能为空'); + } + + try { + $crawlService = new \app\Service\UsedCarCrawlService(); + $data = $crawlService->searchByKeyword($keyword); + return $this->success($data); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + /** * 生成PDF */ diff --git a/backend/app/service/UsedCarCrawlService.php b/backend/app/service/UsedCarCrawlService.php index 6e0ef31..a285f5d 100644 --- a/backend/app/service/UsedCarCrawlService.php +++ b/backend/app/service/UsedCarCrawlService.php @@ -521,4 +521,140 @@ class UsedCarCrawlService return $result; } + + /** + * 通过关键词搜索懂车帝车系信息 + * + * @param string $keyword 搜索关键词(如 "奥迪A4") + * @return array 品牌和车系信息 + * @throws \Exception + */ + public function searchByKeyword(string $keyword): array + { + // 访问懂车帝搜索页面获取第一个结果的链接 + $searchUrl = "https://www.dongchedi.com/search?keyword=" . urlencode($keyword); + + try { + $html = $this->fetchHtml($searchUrl); + $ql = QueryList::html($html); + + // 查找"查看全部"按钮的链接 + $href = ''; + $ql->find('a.fold-btn')->each(function ($item) use (&$href) { + $itemHref = $item->attr('href'); + if ($itemHref) { + $href = $itemHref; + } + }); + + if (empty($href)) { + throw new \Exception('未找到相关车系,请使用更精确的关键词'); + } + + // 提取车系ID + $seriesId = $this->extractSeriesId($href); + if (!$seriesId) { + throw new \Exception('无法解析车系ID'); + } + + // 获取品牌和车系信息 + return $this->getBrandSeries($seriesId); + } catch (\Exception $e) { + throw new \Exception('搜索失败: ' . $e->getMessage()); + } + } + + /** + * 获取品牌和车系名称 + * + * @param string $seriesId 车系ID + * @return array 包含品牌和车系信息的数组 + * @throws \Exception + */ + public function getBrandSeries(string $seriesId): array + { + $url = "https://www.dongchedi.com/auto/series/" . $seriesId . "?anchorId=carModels"; + + try { + $html = $this->fetchHtml($url); + $ql = QueryList::html($html); + + // 从面包屑导航中提取品牌和车系 + $breadcrumbs = []; + $ql->find('div.breadcrumb_wrap__2GLeU span')->each(function ($item) use (&$breadcrumbs) { + $breadcrumbs[] = trim($item->text()); + }); + + $brand = $breadcrumbs[2] ?? ''; + $series = $breadcrumbs[3] ?? ''; + + // 获取车型列表 + $models = $this->getSeriesModels($seriesId); + + return [ + 'brand' => $brand, + 'series' => $series, + 'series_id' => $seriesId, + 'models' => $models, + ]; + } catch (\Exception $e) { + throw new \Exception('获取车系信息失败: ' . $e->getMessage()); + } + } + + /** + * 获取车系下的车型列表 + * + * @param string $seriesId 车系ID + * @return array 车型列表 + */ + public function getSeriesModels(string $seriesId): array + { + $url = "https://www.dongchedi.com/auto/series/" . $seriesId . "?anchorId=carModels"; + $models = []; + + try { + $html = $this->fetchHtml($url); + $ql = QueryList::html($html); + + // 解析车型列表 + $ql->find('div.carModelsPager__EiwGC a.model-item__1r8j')->each(function ($item) use (&$models) { + $modelName = trim($item->text()); + $modelUrl = $item->attr('href'); + + if ($modelName) { + $models[] = [ + 'name' => $modelName, + 'url' => $modelUrl ? 'https://www.dongchedi.com' . $modelUrl : '', + ]; + } + }); + + return $models; + } catch (\Exception) { + // 车型解析失败,返回空数组 + return []; + } + } + + /** + * 从懂车帝URL中提取车系ID + * + * @param string $url 懂车帝URL + * @return string|null 车系ID + */ + private function extractSeriesId(string $url): ?string + { + // 匹配 /auto/series/x123/ 格式 + if (preg_match('/\/auto\/series\/(\d+)/', $url, $matches)) { + return $matches[1]; + } + + // 匹配 /auto/series/x123/params/ 格式 + if (preg_match('/\/auto\/series\/(\d+)/', $url, $matches)) { + return $matches[1]; + } + + return null; + } } diff --git a/backend/config/route.php b/backend/config/route.php index 23d26bb..644ae6b 100644 --- a/backend/config/route.php +++ b/backend/config/route.php @@ -72,6 +72,7 @@ Route::group('/api/v1/usedcar', function () { Route::post('/status', [app\controller\UsedCarController::class, 'updateStatus']); Route::post('/crawl/url', [app\controller\UsedCarController::class, 'crawlByUrl']); Route::post('/crawl/mobile', [app\controller\UsedCarController::class, 'crawlMobileUrl']); + Route::post('/crawl/keyword', [app\controller\UsedCarController::class, 'crawlKeyword']); Route::post('/pdf/generate', [app\controller\UsedCarController::class, 'generatePdf']); Route::get('/pdf/download', [app\controller\UsedCarController::class, 'downloadPdf']); Route::get('/pdf/url', [app\controller\UsedCarController::class, 'getPdfUrl']);