谷歌搜索优化

This commit is contained in:
menft 2025-10-24 17:05:49 +08:00
parent 17c620d530
commit ec367725a3

View File

@ -22,10 +22,11 @@ import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
/** /**
* Google排名查询工具类基于speculationrules解析 * Google排名查询工具类基于speculationrules解析
* * <p>
* 本工具类采用全新的解析方案 * 本工具类采用全新的解析方案
* 1. 不再解析复杂的DOM结构 * 1. 不再解析复杂的DOM结构
* 2. 直接提取Google返回的<script type="speculationrules">中的URL列表 * 2. 直接提取Google返回的<script type="speculationrules">中的URL列表
@ -63,6 +64,9 @@ public class GoogleRankUtil {
private static final ThreadLocal<WebDriver> TL_DRIVER = new ThreadLocal<>(); private static final ThreadLocal<WebDriver> TL_DRIVER = new ThreadLocal<>();
private static final AtomicLong LAST_NAV_AT = new AtomicLong(0L); private static final AtomicLong LAST_NAV_AT = new AtomicLong(0L);
// 关键修复全局锁防止定时任务并发执行导致的会话冲突
private static final ReentrantLock GLOBAL_LOCK = new ReentrantLock();
// ==================== 配置方法 ==================== // ==================== 配置方法 ====================
public static void setHeadless(boolean headless) { public static void setHeadless(boolean headless) {
@ -275,7 +279,7 @@ public class GoogleRankUtil {
/** /**
* 核心方法从speculationrules脚本中解析排名 * 核心方法从speculationrules脚本中解析排名
* * <p>
* 解析逻辑 * 解析逻辑
* 1. 查找<script type="speculationrules">标签 * 1. 查找<script type="speculationrules">标签
* 2. 提取JSON内容 * 2. 提取JSON内容
@ -578,6 +582,31 @@ public class GoogleRankUtil {
options.addArguments("--no-sandbox"); options.addArguments("--no-sandbox");
options.addArguments("--disable-dev-shm-usage"); options.addArguments("--disable-dev-shm-usage");
// 关键修复服务器环境强制使用唯一的临时目录彻底避免冲突
try {
String tempDir = System.getProperty("java.io.tmpdir");
String uniqueDataDir = tempDir + "/chrome-data-" +
System.currentTimeMillis() + "-" +
Thread.currentThread().getId() + "-" +
(int)(Math.random() * 100000);
java.io.File dataDir = new java.io.File(uniqueDataDir);
if (!dataDir.exists()) {
dataDir.mkdirs();
}
options.addArguments("--user-data-dir=" + uniqueDataDir);
log.info("✅ 使用唯一临时目录: {}", uniqueDataDir);
} catch (Exception e) {
log.warn("创建临时目录失败: {}", e.getMessage());
}
// 额外修复添加更多服务器友好参数
options.addArguments("--disable-gpu");
options.addArguments("--disable-software-rasterizer");
options.addArguments("--disable-setuid-sandbox");
options.addArguments("--remote-debugging-port=0");
// 是否保留浏览器窗口 // 是否保留浏览器窗口
options.setExperimentalOption("detach", KEEP_BROWSER_OPEN); options.setExperimentalOption("detach", KEEP_BROWSER_OPEN);
@ -593,6 +622,70 @@ public class GoogleRankUtil {
return driver; return driver;
} }
/**
* 检测是否为服务器环境Linux 且无显示
*/
private static boolean isServerEnvironment() {
String osName = System.getProperty("os.name").toLowerCase();
boolean isLinux = osName.contains("linux");
// 检查是否有 DISPLAY 环境变量Linux 图形界面
String display = System.getenv("DISPLAY");
boolean hasDisplay = (display != null && !display.isEmpty());
// Linux 且无显示 = 服务器环境
return isLinux && !hasDisplay;
}
/**
* 强制清理残留的 Chrome ChromeDriver 进程
*/
private static void forceKillChromeProcesses() {
try {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("linux") || osName.contains("mac")) {
// Linux/Mac: 使用 pkill 命令
try {
Process p1 = Runtime.getRuntime().exec(new String[]{"pkill", "-9", "chrome"});
p1.waitFor();
log.debug("已执行 pkill chrome");
} catch (Exception e) {
log.debug("pkill chrome 失败: {}", e.getMessage());
}
try {
Process p2 = Runtime.getRuntime().exec(new String[]{"pkill", "-9", "chromedriver"});
p2.waitFor();
log.debug("已执行 pkill chromedriver");
} catch (Exception e) {
log.debug("pkill chromedriver 失败: {}", e.getMessage());
}
} else if (osName.contains("win")) {
// Windows: 使用 taskkill 命令
try {
Process p1 = Runtime.getRuntime().exec("taskkill /F /IM chrome.exe");
p1.waitFor();
log.debug("已执行 taskkill chrome.exe");
} catch (Exception e) {
log.debug("taskkill chrome.exe 失败: {}", e.getMessage());
}
try {
Process p2 = Runtime.getRuntime().exec("taskkill /F /IM chromedriver.exe");
p2.waitFor();
log.debug("已执行 taskkill chromedriver.exe");
} catch (Exception e) {
log.debug("taskkill chromedriver.exe 失败: {}", e.getMessage());
}
}
log.info("✅ 已尝试清理残留的 Chrome 进程");
} catch (Exception e) {
log.warn("清理 Chrome 进程失败: {}", e.getMessage());
}
}
/** /**
* 格式化排名输出 * 格式化排名输出
*/ */
@ -604,48 +697,74 @@ public class GoogleRankUtil {
} }
/** /**
* 查询谷歌关键词排名主体方法 * 查询谷歌关键词排名主体方法定时任务专用版本
* @author vinjor-M *
* @date 11:17 2025/10/24 * 关键修复
* @param searchText TODO * 1. 添加全局锁防止定时任务并发执行
* @param site TODO * 2. 自动检测服务器环境强制使用 headless 模式
* @return int * 3. 禁用会话复用每次独立执行
* 4. 强制清理 Chrome 进程
*
* @param searchText 搜索关键词
* @param site 目标网站
* @return 排名>0表示找到-1表示未找到-2表示验证码0表示失败
* @author menft
* @date 2025/10/24
**/ **/
public static int getGoogleRankMain(String searchText, String site) { public static int getGoogleRankMain(String searchText, String site) {
// 关键使用全局锁确保同一时间只有一个任务执行
GLOBAL_LOCK.lock();
log.info("🔒 获取全局锁成功,开始执行 Google 排名查询任务");
try {
int rank = -1; int rank = -1;
// 配置参数
GoogleRankUtil.setHeadless(false); // Mac环境建议 false // 关键检测是否为服务器环境Linux/无显示环境
GoogleRankUtil.setSessionReuse(true); // 复用浏览器会话 boolean isServer = isServerEnvironment();
log.info("环境检测isServer={}, OS={}", isServer, System.getProperty("os.name"));
// 配置参数服务器环境专用
GoogleRankUtil.setHeadless(isServer); // 服务器强制 headless
GoogleRankUtil.setSessionReuse(false); // 关键禁用会话复用
GoogleRankUtil.setGlobalRequestInterval(8000, 5000); // 8s + 抖动 0~5s GoogleRankUtil.setGlobalRequestInterval(8000, 5000); // 8s + 抖动 0~5s
GoogleRankUtil.setRetryPolicy(3, 10000); // 重试3次递增退避 GoogleRankUtil.setRetryPolicy(3, 10000); // 重试3次递增退避
GoogleRankUtil.setKeepBrowserOpen(false); // 结束后关闭 GoogleRankUtil.setKeepBrowserOpen(false); // 关键强制关闭浏览器
// 新增设置Google地理位置 log.info("配置完成headless={}, sessionReuse={}, region={}",
// 常见代码US=美国, CN=中国, ZA=南非, EG=埃及, NG=尼日利亚GH=加纳 HEADLESS, SESSION_REUSE, GOOGLE_REGION);
GoogleRankUtil.setGoogleRegion("GH"); // 设置为加纳
// 测试关键词 // 执行查询
List<String> keywords = new ArrayList<>(); List<String> keywords = new ArrayList<>();
keywords.add(searchText); keywords.add(searchText);
// System.out.println("========== GoogleRankUtil 测试 ==========");
// System.out.println("解析方案speculationrules稳定、准确");
// System.out.println("目标站点: " + site);
// System.out.println("地理位置: " + (GoogleRankUtil.getGoogleRegion() != null ? GoogleRankUtil.getGoogleRegion() : "默认"));
// System.out.println("关键词数量: " + keywords.size());
// System.out.println("=========================================\n");
for (String kw : keywords) { for (String kw : keywords) {
// System.out.println("[" + (i + 1) + "/" + keywords.size() + "] 正在采集关键词: " + kw); log.info("正在查询关键词:{}, 目标站点:{}", kw, site);
// long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
rank = getGoogleRank(kw, site); rank = getGoogleRank(kw, site);
// long elapsed = System.currentTimeMillis() - startTime;
// System.out.println("关键词[" + kw + "] 排名: " + formatRank(rank)); long elapsed = System.currentTimeMillis() - startTime;
// System.out.println("耗时: " + elapsed + "ms"); log.info("查询完成:关键词[{}] 排名={}, 耗时={}ms", kw, formatRank(rank), elapsed);
// System.out.println("-----------------------------------\n");
} }
// System.out.println("========== 采集完成 ==========");
// 清理 // 关键强制清理 WebDriver
GoogleRankUtil.shutdownDriver(); GoogleRankUtil.shutdownDriver();
// 额外保险强制清理残留的 Chrome 进程
if (isServer) {
forceKillChromeProcesses();
}
return rank; return rank;
} catch (Exception e) {
log.error("Google 排名查询异常", e);
return 0;
} finally {
// 关键释放全局锁
GLOBAL_LOCK.unlock();
log.info("🔓 释放全局锁");
}
} }
// ==================== 测试方法 ==================== // ==================== 测试方法 ====================