master
lion 9 hours ago
parent 38fbcb9a3d
commit 6ba8d2ab51

@ -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('/<em>\s*([^<]+?)\s*<\/em>/u', $html, $titleMatch)) {
// 电院等:<div class="tit"><p>姓名</p><span>教授</span></div>
if (preg_match('#<div class="tit"[^>]*>\s*<p>[^<]+</p>\s*<span>\s*([^<]+?)\s*</span>#su', $html, $match)) {
$title = CrawlAuthorParser::cleanText($match[1]);
if ($this->looksLikeAcademicTitle((string) $title)) {
return $title;
}
}
// 材料学院等:<div class="people-name"><p>姓名</p><em>教授</em></div>
if (preg_match('#<(?:div|p)[^>]*class="[^"]*people-name[^"]*"[^>]*>.*?<em>\s*([^<]+?)\s*</em>#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('#<div class="tit"[^>]*>.*?<span>([^<]+)</span>#su', $html, $match)) {
return CrawlAuthorParser::cleanText($match[1]);
if (preg_match_all('/<em>\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('#<div class="tit"[^>]*>.*?<span>\s*([^<]+?)\s*</span>#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);

@ -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'
<a href="/" title="电气首页" class="name "><em>电气首页</em></a>
<h2><a href="/" title="首页">电气首页</a></h2>
<div class="tit">
<p>王承民</p>
<span>教授</span>
</div>
<div class="js-info">
<p>办公电话021-12345678</p>
<p>电子邮件wcm@sjtu.edu.cn</p>
</div>
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;

Loading…
Cancel
Save