|
|
|
|
@ -1995,8 +1995,11 @@ class FacultyListHtmlAdapter implements CrawlerAdapterInterface
|
|
|
|
|
|
|
|
|
|
$existingDirections = $item->extra['research_direction_names'] ?? ($lead['research_direction_names'] ?? null);
|
|
|
|
|
if (! is_array($existingDirections) || $existingDirections === []) {
|
|
|
|
|
$directionText = $this->extractResearchDirectionTextFromProfileHtml($html);
|
|
|
|
|
$directionNames = $this->parseResearchDirectionNames($directionText ?? '');
|
|
|
|
|
$directionNames = $this->extractResearchDirectionNamesFromProfileHtml($html);
|
|
|
|
|
if ($directionNames === []) {
|
|
|
|
|
$directionText = $this->extractResearchDirectionTextFromProfileHtml($html);
|
|
|
|
|
$directionNames = $this->parseResearchDirectionNames($directionText ?? '');
|
|
|
|
|
}
|
|
|
|
|
if ($directionNames !== []) {
|
|
|
|
|
$lead['research_direction_names'] = $directionNames;
|
|
|
|
|
$changed = true;
|
|
|
|
|
@ -2174,7 +2177,8 @@ class FacultyListHtmlAdapter implements CrawlerAdapterInterface
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($parts !== []) {
|
|
|
|
|
return implode('、', $parts);
|
|
|
|
|
// 用换行拼接,避免顿号字符类拆分破坏多字节编码
|
|
|
|
|
return implode("\n", $parts);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -2193,7 +2197,7 @@ class FacultyListHtmlAdapter implements CrawlerAdapterInterface
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($numberedParts !== []) {
|
|
|
|
|
return implode('、', $numberedParts);
|
|
|
|
|
return implode("\n", $numberedParts);
|
|
|
|
|
}
|
|
|
|
|
if ($parts !== []) {
|
|
|
|
|
return implode("\n", $parts);
|
|
|
|
|
@ -2209,44 +2213,103 @@ class FacultyListHtmlAdapter implements CrawlerAdapterInterface
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 优先按列表项整段提取研究方向,避免再按顿号拆分时破坏 UTF-8。
|
|
|
|
|
*
|
|
|
|
|
* @return list<string>
|
|
|
|
|
*/
|
|
|
|
|
protected function extractResearchDirectionNamesFromProfileHtml(string $html): array
|
|
|
|
|
{
|
|
|
|
|
if (! preg_match('#研究方向\s*</div>.*?panel-body[^>]*>\s*<ul>(.*?)</ul>#su', $html, $match)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! preg_match_all('#<li[^>]*>(.*?)</li>#su', (string) $match[1], $lis)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$names = [];
|
|
|
|
|
foreach ($lis[1] as $li) {
|
|
|
|
|
$text = CrawlAuthorParser::cleanText($this->htmlToPlain($li));
|
|
|
|
|
if ($text === null || $text === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$text = preg_replace('/^研究方向[一二三四五六七八九十\d]+[::]\s*/u', '', $text) ?? $text;
|
|
|
|
|
$text = trim($text);
|
|
|
|
|
if ($text === '' || mb_strlen($text) > 80 || preg_match('/^(已形成|主要|长期从事)/u', $text)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$names[] = $text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return array_values(array_unique($names));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return list<string>
|
|
|
|
|
*/
|
|
|
|
|
protected function parseResearchDirectionNames(string $direction): array
|
|
|
|
|
{
|
|
|
|
|
$direction = trim($direction);
|
|
|
|
|
$direction = CrawlAuthorParser::ensureUtf8(trim($direction));
|
|
|
|
|
if ($direction === '') {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (preg_match_all('/\d+[\.、.]\s*([^0-9\n]{2,80}?)(?=\s*\d+[\.、.]|[;;]|$)/u', $direction, $numbered)) {
|
|
|
|
|
// 换行分隔的条目优先整段保留
|
|
|
|
|
if (str_contains($direction, "\n")) {
|
|
|
|
|
$lines = preg_split('/\R+/u', $direction) ?: [];
|
|
|
|
|
$names = [];
|
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
|
foreach ($this->parseResearchDirectionNames(trim($line)) as $name) {
|
|
|
|
|
$names[] = $name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return array_values(array_unique(array_filter(
|
|
|
|
|
$names,
|
|
|
|
|
fn (string $part) => $part !== '' && mb_check_encoding($part, 'UTF-8') && mb_strlen($part) <= 80,
|
|
|
|
|
)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (preg_match_all('/(?:^|[\n;;])\s*\d+(?:\.|、|.)\s*(.+?)(?=(?:[\n;;]\s*\d+(?:\.|、|.))|$)/us', $direction, $numbered)) {
|
|
|
|
|
$parts = array_values(array_unique(array_filter(array_map(
|
|
|
|
|
fn (string $part) => trim($part, " \t\n\r\0\x0B;;,,。"),
|
|
|
|
|
function (string $part): string {
|
|
|
|
|
$part = CrawlAuthorParser::ensureUtf8(trim($part));
|
|
|
|
|
|
|
|
|
|
return preg_replace('/^[\s;,.;,。]+|[\s;,.;,。]+$/u', '', $part) ?? $part;
|
|
|
|
|
},
|
|
|
|
|
$numbered[1],
|
|
|
|
|
), fn (string $part) => $part !== '' && mb_strlen($part) <= 80)));
|
|
|
|
|
), fn (string $part) => $part !== '' && mb_check_encoding($part, 'UTF-8') && mb_strlen($part) <= 80)));
|
|
|
|
|
if (count($parts) >= 2) {
|
|
|
|
|
return $parts;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$parts = preg_split('/[、,,;;\/\|\r\n]+/u', $direction) ?: [];
|
|
|
|
|
// 禁用多字节字符类,改用交替,避免 PCRE 按字节误切 UTF-8
|
|
|
|
|
$parts = preg_split('/(?:、|,|,|;|;|\/|\||\r|\n)+/u', $direction) ?: [];
|
|
|
|
|
|
|
|
|
|
return array_values(array_unique(array_filter(array_map(
|
|
|
|
|
function (string $part): string {
|
|
|
|
|
$part = trim($part);
|
|
|
|
|
$part = preg_replace('/^\d+[\.、.]\s*/u', '', $part) ?? $part;
|
|
|
|
|
$part = CrawlAuthorParser::ensureUtf8(trim($part));
|
|
|
|
|
$part = preg_replace('/^\d+(?:\.|、|.)\s*/u', '', $part) ?? $part;
|
|
|
|
|
$part = preg_replace('/^研究方向[一二三四五六七八九十\d]+[::]\s*/u', '', $part) ?? $part;
|
|
|
|
|
// 勿用含多字节字符的 trim 掩码(按字节裁剪会破坏 UTF-8)
|
|
|
|
|
$part = preg_replace('/^[\s;,.;,。]+|[\s;,.;,。]+$/u', '', $part) ?? $part;
|
|
|
|
|
|
|
|
|
|
return trim($part, " \t\n\r\0\x0B;;,,。.");
|
|
|
|
|
return $part;
|
|
|
|
|
},
|
|
|
|
|
$parts,
|
|
|
|
|
), fn (string $part) => $part !== '' && mb_strlen($part) <= 80)));
|
|
|
|
|
), fn (string $part) => $part !== ''
|
|
|
|
|
&& mb_check_encoding($part, 'UTF-8')
|
|
|
|
|
&& mb_strlen($part) <= 80
|
|
|
|
|
&& ! preg_match('/^(已形成|主要|长期从事)/u', $part))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function normalizePhone(string $phone): string
|
|
|
|
|
{
|
|
|
|
|
$phone = html_entity_decode($phone, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
|
|
|
|
$phone = trim(preg_replace('/\s+/u', ' ', $phone) ?? '');
|
|
|
|
|
$phone = trim($phone, " \t\n\r\0\x0B;;,,");
|
|
|
|
|
$phone = preg_replace('/^[\s;,;,]+|[\s;,;,]+$/u', '', $phone) ?? $phone;
|
|
|
|
|
|
|
|
|
|
return $phone;
|
|
|
|
|
}
|
|
|
|
|
|