loadHTML('
'.str_replace(ArticleRecommendations::SLOT, '', $html).'
', LIBXML_NONET); $root = $dom->getElementById('aid-root'); if (!$root) return null; $xpath = new DOMXPath($dom); $copies = $xpath->query('.//*[@data-clipboard-text]', $root); if (!$copies || $copies->length % 2) return null; // Empty historical updates are valid, but plaintext account rows must not disappear. if (!$copies->length && (preg_match('/账号[::]|密码[::]/u', $root->textContent) || !$xpath->query('.//*[self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6][starts-with(normalize-space(.), "免费苹果")]', $root)->length)) return null; $result = ['accounts' => [], 'regions' => [], 'warnings' => [], 'intro' => '', 'outro' => '']; $containers = []; for ($i = 0; $i < $copies->length; $i += 2) { $account = $copies->item($i); $password = $copies->item($i + 1); $container = $account->parentNode; while ($container && $container !== $root && $container->parentNode !== $root) $container = $container->parentNode; // Unknown nesting, labels or additional copy controls retain the legacy rendering. if (!$container instanceof DOMElement || $container->parentNode !== $root || $container->tagName !== 'p') return null; $fields = $xpath->query('.//*[@data-clipboard-text]', $container); if ($fields->length !== 2 || !$fields->item(0)->isSameNode($account) || !$fields->item(1)->isSameNode($password)) return null; $text = self::text($container->textContent); if (!preg_match('/^账号[::]\s*.+[,,]\s*密码[::]\s*.+[,,]\s*地区[::]\s*(.*?)[,,]\s*检测时间[::]\s*(.*?)\s*$/u', $text, $match)) return null; $username = $account->getAttribute('data-clipboard-text'); $secret = $password->getAttribute('data-clipboard-text'); if ($username === '' || $secret === '') return null; $region = trim($match[1]); if ($region === '') $region = '未注明'; $result['accounts'][] = ['account' => $username, 'password' => $secret, 'region' => $region, 'checked_at' => trim($match[2])]; $result['regions'][$region] = ($result['regions'][$region] ?? 0) + 1; $containers[] = $container; } $seenAccount = false; foreach (iterator_to_array($root->childNodes) as $node) { $isAccount = false; foreach ($containers as $container) if ($node->isSameNode($container)) { $isAccount = true; break; } if ($isAccount) { $seenAccount = true; continue; } $text = self::text($node->textContent); if ($text === '') continue; if ($node instanceof DOMElement && preg_match('/^免费苹果(?:账号|ID)/u', $text) && preg_match('/^h[1-6]$/', $node->tagName)) continue; if (str_contains($text, '必须从App Store登录') && str_contains($text, 'iCloud')) { $warning = preg_replace('/^[\x{26A0}\x{FE0F}\s]+/u', '', $text); if (!in_array($warning, $result['warnings'], true)) $result['warnings'][] = $warning; continue; } $result[$seenAccount ? 'outro' : 'intro'] .= $dom->saveHTML($node); } return $result; } catch (\Throwable) { return null; } finally { libxml_clear_errors(); libxml_use_internal_errors($before); } } private static function text(string $text): string { return trim(preg_replace('/\s+/u', ' ', $text)); } }