From 2ab0fbde9ecfe3d315b0e783ea70d292b183a269 Mon Sep 17 00:00:00 2001 From: zycha Date: Fri, 10 Jul 2026 14:41:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=83=AD=E9=97=A8=E6=8E=A8?= =?UTF-8?q?=E8=8D=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + tasks/dailyTrending.js | 148 ++++++++++++++++++++++++++++++++--------- 2 files changed, 119 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 79a0624..760a2e0 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ dist-ssr *.njsproj *.sln *.sw? +*.exe diff --git a/tasks/dailyTrending.js b/tasks/dailyTrending.js index 5b985b2..31ac57e 100644 --- a/tasks/dailyTrending.js +++ b/tasks/dailyTrending.js @@ -21,12 +21,10 @@ const { const { runDebugSteps } = require(path.join(__dirname, '..', 'utils', 'debugSteps')); const PROJECT_CARD_SELECTORS = [ - '.hot-selection .recommend-card', - '.hot-selection-list a.recommend-card', - 'a.recommend-card', - 'a[href*="source_module=home_hot_selection"]', - 'a[href*="source_module=home_recommend"]', - 'a[href*="/"][href*="?source_module="]', + '.dynamics-grid a.home-repo-card__link', + '.dynamic-card-project a.home-repo-card__link', + 'a.home-repo-card__link[href*="source_module=dynamics"]', + 'a[href*="source_module=dynamics"]', ]; /** @@ -63,7 +61,7 @@ async function dailyTrending(page, options = {}) { }, { name: '点击去完成并打开热门页', - page: (state) => state.page, + page: (state) => state.trendingPage || state.page, run: async (state) => { state.trendingPage = await openNewPage( state.page.context(), @@ -79,42 +77,120 @@ async function dailyTrending(page, options = {}) { name: '查找热门项目卡片', page: (state) => state.trendingPage, run: async (state) => { - state.cards = await findProjectCards(state.trendingPage); + const targetPage = state.trendingPage; + + if (!targetPage || targetPage.isClosed()) { + throw new Error('热门页面不存在或已经关闭'); + } + + state.currentPage = targetPage; + + logger.info(`查找热门项目卡片,当前页面: ${targetPage.url()}`); + + // 等待热门项目区域出现 + await targetPage + .locator('.dynamics-grid') + .waitFor({ + state: 'visible', + timeout: 15000, + }); + + state.cards = await findProjectCards(targetPage); + + if (state.cards.length === 0) { + throw new Error('未找到热门项目卡片'); + } + + logger.info(`找到热门项目卡片: ${state.cards.length} 个`); }, inspect: async (state) => ({ - hotSelectionCount: await state.trendingPage.locator('.hot-selection').count(), - recommendCardCount: await state.trendingPage.locator('a.recommend-card').count(), - sourceModuleLinkCount: await state.trendingPage.locator('a[href*="source_module"]').count(), - allLinkCount: await state.trendingPage.locator('a[href]').count(), + currentUrl: state.trendingPage?.url(), + dynamicsGridCount: await state.trendingPage + .locator('.dynamics-grid') + .count(), + dynamicCardCount: await state.trendingPage + .locator('.dynamics-grid .dynamic-card') + .count(), + repoCardCount: await state.trendingPage + .locator('.dynamics-grid .home-repo-card') + .count(), + repoLinkCount: await state.trendingPage + .locator('.dynamics-grid a.home-repo-card__link') + .count(), + dynamicsSourceLinkCount: await state.trendingPage + .locator('a[href*="source_module=dynamics"]') + .count(), }), }, { name: '打开第一个热门项目详情页', page: (state) => state.trendingPage, run: async (state) => { - state.detailPage = await openLinkPage( - state.trendingPage.context(), - state.cards[0], - { label: '热门项目详情页' } - ); - state.currentPage = state.detailPage; + const firstCard = state.cards[0]; + + if (!firstCard) { + throw new Error('没有找到可点击的热门项目卡片'); + } + + await firstCard.scrollIntoViewIfNeeded(); + + const href = await firstCard.getAttribute('href'); + logger.info(`准备点击热门项目: ${href}`); + + const context = state.trendingPage.context(); + + const [detailPage] = await Promise.all([ + context.waitForEvent('page', { + timeout: 15000, + }), + firstCard.click({ + timeout: 15000, + }), + ]); + + state.detailPage = detailPage; + state.currentPage = detailPage; + + await waitPageReady(detailPage); + + logger.info(`热门项目详情页已打开: ${detailPage.url()}`); }, inspect: async (state) => ({ cardCount: state.cards.length, + firstCardHref: state.cards[0] + ? await state.cards[0].getAttribute('href') + : null, }), }, { - name: '等待热门项目详情页加载', + name: '停留热门项目详情页', page: (state) => state.detailPage, run: async (state) => { + if (!state.detailPage || state.detailPage.isClosed()) { + throw new Error('热门项目详情页不存在或已关闭'); + } + await waitPageReady(state.detailPage); - logger.info('热门项目详情页已打开'); + + // 给页面曝光统计、埋点和任务接口留出时间 + await state.detailPage.waitForTimeout(10000); + + logger.info( + `已在热门项目详情页停留10秒: ${state.detailPage.url()}` + ); }, + inspect: async (state) => ({ + detailPageUrl: state.detailPage?.url(), + detailPageTitle: state.detailPage + ? await state.detailPage.title().catch(() => '') + : '', + }), }, ]; try { await runDebugSteps('查看热门', steps, ctx, options); + await ctx.page.waitForTimeout(5000); } finally { await safeClose(ctx.detailPage); await safeClose(ctx.trendingPage); @@ -122,20 +198,32 @@ async function dailyTrending(page, options = {}) { } async function findProjectCards(page) { - const cards = await waitForElements(page, PROJECT_CARD_SELECTORS, { - interval: 1000, - maxRetries: 15, - minCount: 1, - }); + logger.info(`findProjectCards 实际页面: ${page.url()}`); + for (const selector of PROJECT_CARD_SELECTORS) { + logger.info(`尝试选择器: "${selector}"`); + const locator = page.locator(selector); + try { + await locator.first().waitFor({ + state: 'visible', + timeout: 5000, + }); + const count = await locator.count(); + if (count > 0) { + logger.info(`选择器 "${selector}" 找到 ${count} 个热门项目链接`); - if (cards.length > 0) { - logger.info(`热门项目候选数量: ${cards.length}`); - return cards; + return Array.from( + { length: count }, + (_, index) => locator.nth(index) + ); + } + } catch (_) { + logger.info(`选择器 "${selector}" 未找到元素`); + } } - const info = await getPageInfo(page); throw new Error( - `未找到热门项目卡片,页面结构可能已变化。页面标题: ${info.title}, 页面URL: ${info.url}` + `未找到热门项目卡片,页面结构可能已变化。` + + `页面标题: ${info.title}, 页面URL: ${info.url}` ); }