From 6ba8d2ab51c88654b5de3974ae6948acd0cbde99 Mon Sep 17 00:00:00 2001 From: lion <120344285@qq.com> Date: Wed, 29 Jul 2026 14:17:25 +0800 Subject: [PATCH] =?UTF-8?q?=E7=88=AC=E8=99=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Crawl/Adapters/FacultyListHtmlAdapter.php | 50 ++++++++++++++++--- tests/Unit/FacultyListHtmlAdapterTest.php | 39 +++++++++++++++ 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/app/Services/Crawl/Adapters/FacultyListHtmlAdapter.php b/app/Services/Crawl/Adapters/FacultyListHtmlAdapter.php index 9bab8bf..cf2abb8 100644 --- a/app/Services/Crawl/Adapters/FacultyListHtmlAdapter.php +++ b/app/Services/Crawl/Adapters/FacultyListHtmlAdapter.php @@ -1958,7 +1958,7 @@ class FacultyListHtmlAdapter implements CrawlerAdapterInterface $lead = is_array($item->extra['lead_author'] ?? null) ? $item->extra['lead_author'] : []; $changed = false; - if (empty($lead['academic_title'])) { + if (empty($lead['academic_title']) || ! $this->looksLikeAcademicTitle((string) $lead['academic_title'])) { $title = $this->extractAcademicTitleFromProfileHtml($html); if ($title !== null && $title !== '') { $lead['academic_title'] = $title; @@ -2055,27 +2055,65 @@ class FacultyListHtmlAdapter implements CrawlerAdapterInterface protected function extractAcademicTitleFromProfileHtml(string $html): ?string { - if (preg_match('/\s*([^<]+?)\s*<\/em>/u', $html, $titleMatch)) { + // 电院等:

姓名

教授
+ if (preg_match('#
]*>\s*

[^<]+

\s*\s*([^<]+?)\s*#su', $html, $match)) { + $title = CrawlAuthorParser::cleanText($match[1]); + if ($this->looksLikeAcademicTitle((string) $title)) { + return $title; + } + } + + // 材料学院等:

姓名

教授
+ if (preg_match('#<(?:div|p)[^>]*class="[^"]*people-name[^"]*"[^>]*>.*?\s*([^<]+?)\s*#su', $html, $titleMatch)) { $title = CrawlAuthorParser::cleanText($titleMatch[1]); - if ($title !== null && $title !== '') { + if ($this->looksLikeAcademicTitle((string) $title)) { return $title; } } foreach (['职称', '职务'] as $label) { $title = $this->parseLabeledField($html, $label); - if ($title !== null && $title !== '') { + if ($this->looksLikeAcademicTitle((string) $title)) { return $title; } } - if (preg_match('#
]*>.*?([^<]+)#su', $html, $match)) { - return CrawlAuthorParser::cleanText($match[1]); + if (preg_match_all('/\s*([^<]+?)\s*<\/em>/u', $html, $ems)) { + foreach ($ems[1] as $candidate) { + $title = CrawlAuthorParser::cleanText($candidate); + if ($this->looksLikeAcademicTitle((string) $title)) { + return $title; + } + } + } + + if (preg_match('#
]*>.*?\s*([^<]+?)\s*#su', $html, $match)) { + $title = CrawlAuthorParser::cleanText($match[1]); + if ($this->looksLikeAcademicTitle((string) $title)) { + return $title; + } } return null; } + protected function looksLikeAcademicTitle(?string $title): bool + { + $title = trim((string) $title); + if ($title === '' || mb_strlen($title) > 30) { + return false; + } + + if (preg_match('/首页|导航|菜单|返回|更多|搜索|登录|关于/u', $title)) { + return false; + } + + return (bool) preg_match( + '/教授|副教授|讲师|助教|研究员|副研究员|助理研究员|工程师|院士|博导|导师|专家|学者|长聘|准聘|特聘|兼职|访问|青年|副高|正高|中级|初级/u', + $title, + ); + } + protected function extractPhoneFromProfileHtml(string $html): ?string { $scoped = $this->profileContentHtml($html); diff --git a/tests/Unit/FacultyListHtmlAdapterTest.php b/tests/Unit/FacultyListHtmlAdapterTest.php index 8905c53..8a36d6e 100644 --- a/tests/Unit/FacultyListHtmlAdapterTest.php +++ b/tests/Unit/FacultyListHtmlAdapterTest.php @@ -267,6 +267,45 @@ HTML; $this->assertSame('aiqian@sjtu.edu.cn', $emailMethod->invoke($adapter, $html)); } + public function test_apply_profile_metadata_from_see_tit_span_not_nav_em(): void + { + $html = <<<'HTML' +电气首页 +

电气首页

+
+

王承民

+ 教授 +
+
+

办公电话:021-12345678

+

电子邮件:wcm@sjtu.edu.cn

+
+HTML; + + $adapter = new FacultyListHtmlAdapter; + $method = new \ReflectionMethod($adapter, 'applyProfileMetadataToItem'); + $method->setAccessible(true); + $item = $method->invoke( + $adapter, + new \App\Services\Crawl\CrawlItemDto( + externalId: 'faculty:see-wcm', + title: '王承民', + canonicalUrl: 'https://see.sjtu.edu.cn/jiaoshiml/wangchengmin.html', + extra: [ + 'lead_author' => [ + 'name' => '王承民', + // 模拟上次误抓导航文案,详情页应覆盖 + 'academic_title' => '电气首页', + ], + ], + ), + $html, + ); + + $this->assertSame('教授', $item->extra['academic_title']); + $this->assertSame('教授', $item->extra['lead_author']['academic_title']); + } + public function test_apply_profile_metadata_from_cs_and_frontier_pages(): void { $adapter = new FacultyListHtmlAdapter;