array_map( fn (string $sUrl) => $oPool->as($sUrl) ->withHeaders([ 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language' => 'zh-CN,zh;q=0.9,en;q=0.8', ]) ->withOptions([ 'curl' => [ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, ], ]) ->connectTimeout(10) ->timeout(30) ->retry(3, 1000) ->get($sUrl), self::ARRAY_TARGET_URLS )); } catch (\Throwable $oException) { TeleSlave::warn("ShadowRocketCrawl:并发请求执行异常:" . $oException->getMessage()); return []; } $aAllAccountList = []; foreach (self::ARRAY_TARGET_URLS as $sUrl) { $oResponse = $aResponses[$sUrl] ?? null; if ($oResponse instanceof \Throwable) { TeleSlave::warn("ShadowRocketCrawl:请求网络异常:" . $sUrl . ",错误: " . $oResponse->getMessage()); continue; } if (!$oResponse || $oResponse->failed()) { $iStatusCode = $oResponse ? $oResponse->status() : 0; TeleSlave::warn("ShadowRocketCrawl:请求页面失败:" . $sUrl . ",状态码: " . $iStatusCode); continue; } $sPageContent = $oResponse->body(); if (empty(trim($sPageContent))) { TeleSlave::warn("ShadowRocketCrawl:请求页面内容为空:" . $sUrl); continue; } $aParsedAccounts = self::parseHtmlToAccounts($sPageContent); $aAllAccountList = array_merge($aAllAccountList, $aParsedAccounts); } $arr = self::deduplicateAccounts($aAllAccountList); $json = json_encode($arr); $sTodayDate = date('Y-m-d'); $oShadowRocketRecord = ShadowRocketIds::whereDate('created_at', $sTodayDate)->first() ?? new ShadowRocketIds(); if (empty($json)) { $json = "[]"; } $oShadowRocketRecord->arr = $json; $oShadowRocketRecord->save(); echo "\n ok"; } /** * 按邮箱对爬取的账号进行去重 * * @param array $aAccountList * @return array */ private static function deduplicateAccounts(array $aAccountList): array { $aUniqueAccounts = []; foreach ($aAccountList as $aAccount) { $sEmail = $aAccount['sEmail'] ?? ''; if (!empty($sEmail) && !isset($aUniqueAccounts[$sEmail])) { $aUniqueAccounts[$sEmail] = $aAccount; } } return array_values($aUniqueAccounts); } // 好像因为这里成为0 /** * 解析 HTML 并提取账号列表(包含地区与检测时间) * * @param string $sHtmlContent * @return array */ private static function parseHtmlToAccounts(string $sHtmlContent): array { $aAccountList = []; $oCrawler = new Crawler($sHtmlContent); $oCrawler->filter('.posts-item')->each(function (Crawler $oNode) use (&$aAccountList) { $sEmail = self::extractEmailFromNode($oNode); $sPassword = self::extractPasswordFromNode($oNode); $sRegion = self::extractRegionFromNode($oNode); $sCheckTime = self::extractCheckTimeFromNode($oNode); if (!empty($sEmail) && !empty($sPassword)) { $aAccountList[] = [ 'sEmail' => $sEmail, 'sPassword' => $sPassword, 'sRegion' => $sRegion, 'sCheckTime' => $sCheckTime, ]; } }); return $aAccountList; } /** * 从单个卡片节点中精准提取完整 Email * * @param Crawler $oNode * @return string */ private static function extractEmailFromNode(Crawler $oNode): string { $oFullLinkNode = $oNode->filter('.item-tags a.external'); if ($oFullLinkNode->count() > 0) { $sHref = $oFullLinkNode->attr('href') ?? ''; if (str_contains($sHref, '#')) { $aParts = explode('#', $sHref); $sDecoded = self::decodeCloudflareEmail($aParts[1] ?? ''); if (self::isValidEmail($sDecoded)) { return $sDecoded; } } } $oTagEmailNode = $oNode->filter('.item-tags [data-cfemail]'); if ($oTagEmailNode->count() > 0) { $sHex = $oTagEmailNode->attr('data-cfemail') ?? ''; $sDecoded = self::decodeCloudflareEmail($sHex); if (self::isValidEmail($sDecoded)) { return $sDecoded; } } $oAllCfNodes = $oNode->filter('[data-cfemail]'); foreach ($oAllCfNodes as $oDomElement) { $sHex = $oDomElement->getAttribute('data-cfemail'); $sDecoded = self::decodeCloudflareEmail($sHex); if (self::isValidEmail($sDecoded)) { return $sDecoded; } } return ''; } /** * 从单个卡片节点中提取密码 * * @param Crawler $oNode * @return string */ private static function extractPasswordFromNode(Crawler $oNode): string { $oBtnNodes = $oNode->filter('button[copy]'); foreach ($oBtnNodes as $oDomElement) { $sCopyVal = trim($oDomElement->getAttribute('copy')); if (!empty($sCopyVal)) { return $sCopyVal; } } return ''; } /** * 从单个卡片节点中提取账号地区(如:美区、日本、中国大陆) * * @param Crawler $oNode * @return string */ private static function extractRegionFromNode(Crawler $oNode): string { $oSpans = $oNode->filter('h5 span'); if ($oSpans->count() >= 2) { $sRawText = trim($oSpans->eq(1)->text('')); return trim(str_replace(['【', '】'], '', $sRawText)); } return ''; } /** * 从单个卡片节点中提取检测时间 * * @param Crawler $oNode * @return string */ private static function extractCheckTimeFromNode(Crawler $oNode): string { $oTimeNode = $oNode->filter('.text-muted'); if ($oTimeNode->count() > 0) { $sRawText = trim($oTimeNode->text('')); return trim(str_replace('检测时间:', '', $sRawText)); } return ''; } /** * 解密 Cloudflare XOR 加密邮箱 * * @param string $sEncodedHex * @return string */ private static function decodeCloudflareEmail(string $sEncodedHex): string { if (empty($sEncodedHex) || strlen($sEncodedHex) < 4 || !ctype_xdigit($sEncodedHex)) { return ''; } $iKey = hexdec(substr($sEncodedHex, 0, 2)); $sDecodedEmail = ''; $iLength = strlen($sEncodedHex); for ($iIndex = 2; $iIndex < $iLength; $iIndex += 2) { $sDecodedEmail .= chr(hexdec(substr($sEncodedHex, $iIndex, 2)) ^ $iKey); } return trim($sDecodedEmail); } /** * 校验邮箱合法性 * * @param string $sEmail * @return bool */ private static function isValidEmail(string $sEmail): bool { return !empty($sEmail) && filter_var($sEmail, FILTER_VALIDATE_EMAIL) !== false; } }