主流博客文章数据分析系统+springboot+vue3实现
前言
大家好,今天给大家带来了一个博客文章分析系统,支持目前各大主流博客(CSDN,博客园,知乎,思否,掘金), 系统可以分析文章的内容, 评论,点赞,访问。具体内容请见下面视频。
解决了登录,环境异常等问题。这是一个完整的商用系统,不是demo,是定制开发的线上版本。
视频演示
https://githubs.xyz/show/71f17896-1112-4246-b66c-453a4c309960.mp4
图片演示




源码文件我已经整理清楚了,移步获取:
githubs点xyz/product/366
系统功能描述
1. 博客配置管理
- 支持多个博客平台的数据爬取(博客园、思否、CSDN、知乎、掘金)
- 爬取文章标题,内容,评论,点赞量,访问量
2. 爬虫任务管理
- 支持创建和管理博客爬取任务
- 实时显示任务执行状态和进度
- 提供任务日志查看功能
3. 数据分析功能
- 展示文章访问量、点赞量、评论量等数据
- 支持分析被封博客预警
- 支持和上一次分析对比评论上升量,点赞上升量,访问上升量
4. 系统配置
- 支持WebDriver配置管理
- 提供元素选择器字典配置
使用说明
- 首先在【博客设置】中配置需要爬取的博客信息
- 在【驱动配置】中确保WebDriver配置正确
- 通过【任务列表】创建新的爬取任务
- 在【任务结果】中查看数据分析结果
部署搭建
将文件解压到D盘的根目录:

按顺序启动1,2,3,4 bat 文件就可以了, 启动 “0.rundb.bat” , 如果启动失败,可能是3306端口被占用了,自行解决。

启动 “1.run_server.bat” , 启动会启动一个浏览器,不要动它。

启动 “2.run_font.bat” , 这个启动会等待一段时间,然后会自动关闭。
启动 “3.run_font2.bat” :

访问:
http://localhost:4000/
技术实现
源码我已经整理清楚了,移步获取:
gitee( 典 ) C 〇 M/qiqi915/java01.git
1. 动态配置元素定位
后端使用的是java版本的selenium ,元素的定位信息都是存储到t_blog_element_settings表里面的:

这样的好处就是,页面更新后,只需要在后台修改下元素定位就可以了,不用改代码。
2. 优秀的隐藏机制
selenium 运行时, 很容易被识别出来,我使用了如下代码来隐藏,下面是构造驱动的部分代码:
// 添加更多的浏览器指纹 Map<String, Object> prefs = new HashMap<>(); prefs.put("profile.default_content_settings.images", 2); prefs.put("profile.managed_default_content_settings.javascript", 1); prefs.put("credentials_enable_service", false); prefs.put("profile.password_manager_enabled", false); options.setExperimentalOption("prefs", prefs); // 用来解决 登录智能 验证 Map<String, Object> parameters = new HashMap<>(); parameters.put("source", "Object.defineProperty(navigator, \"webdriver\", {get: () => undefined})"); driver.executeCdpCommand("Page.addScriptToEvaluateOnNewDocument", parameters);
尤其是遇到知乎时,一打开就是环境异常!!!!
我也是搞了好久,java还是解决不了这个问题,最终用一个脚本解决,脚本使用python写的,然后用java调用exe执行。
下面是部分代码:
Process process = null; try { List<String> command = new ArrayList<>(); command.add(exePath.toFile().getAbsolutePath()); TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "开始执行脚本:" + exePath.toFile().getAbsolutePath()); command.add(env.get("in")); TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "脚本参数:" + env.get("in")); ProcessBuilder processBuilder = new ProcessBuilder(command); processBuilder.redirectErrorStream(true); // 合并错误流到标准输出流 process = processBuilder.start(); // 创建单独的线程来读取进程输出 final Process process1 = process; Thread outputThread = new Thread(() -> { System.out.println("知乎脚本输出线程开始: "); try (BufferedReader reader = new BufferedReader(new InputStreamReader(process1.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println("知乎脚本输出: " + line); } } catch (IOException e) { e.printStackTrace(); } // System.out.println("==================输出流线程退出========================"); }); outputThread.start(); TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "脚本执行中,请耐心等待..."); // 等待进程完成,设置超时时间 if (!process.waitFor(5, TimeUnit.HOURS)) { throw new InterruptedException("进程执行超时(" + 5 + "小时)"); } // 检查进程退出码 int exitCode = process.exitValue(); if (exitCode != 0) { System.out.println("脚本执行退出码错误," + exitCode); setError("脚本执行退出码错误," + exitCode); TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "脚本内部异常,exitCode:" + exitCode); return; } } catch (Exception e) { e.printStackTrace(); setError("脚本执行异常"); TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "脚本执行异常, msg:" + e.getMessage()); return; } finally { if (process != null && process.isAlive()) { process.destroy(); // 确保进程被终止 try { // 等待一段时间让进程正常结束 if (!process.waitFor(5, TimeUnit.SECONDS)) { process.destroyForcibly(); // 强制终止 } } catch (InterruptedException e) { process.destroyForcibly(); // 强制终止 } } }
3. 驱动支持配置
我设计了一个驱动设置表,可以支持用户自定义浏览器版本,不过一般用默认的就可以了。
CREATE TABLE `t_driver_settings` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', `chrome_driver_path` varchar(2000) COLLATE utf8mb4_bin NOT NULL COMMENT '驱动路径', `chrome_exe_path` varchar(2000) COLLATE utf8mb4_bin NOT NULL COMMENT '浏览器EXE路径', `headless` int(11) DEFAULT '0' COMMENT '是否显示浏览器,0-显示 1-不显示', `driver_count` int(11) DEFAULT '0' COMMENT '驱动数量', `user_id` int(11) DEFAULT NULL COMMENT '用户id', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='驱动设置表';

4. 文章分析系统
文章不仅仅可以显示文章数据列表,还可以自动进行分析,将被封的文章和评论上升量等分析出来:

实现就是通过对比上一次的任务结果数据进行计算。
5. 优秀的异常处理
总所周知,页面元素会出现偶尔的查找错误,比如登录,我这里进行了重试和 每一段代码都是异常try,保证了流程的正常进行,和数据提取。
比如下面部分代码:
@Override public boolean crawlLogin() { boolean success = false; final String loginUrl = settings.getLoginUrl(); String userName = settings.getUserName(); String pwd = settings.getPassword(); int retryCount = 0; while (retryCount < 10) { try { retryCount++; TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "开始第" + retryCount + "次登录"); if (!driver.get(loginUrl, (msg) -> setProgress("博客园登录页访问错误,url:" + loginUrl + ",进行重试"))) { TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "页面访问错误,url:" + loginUrl); continue; } setProgress("博客园访问登录页面成功,url:" + loginUrl); // 判断是否已经登录 driver.sleep(1); WebElement loginOut = driver.findElement(By.id("loginOut"), null); if(loginOut != null){ //已经登录过 ,直接返回 success = true ; TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "已经登录"); return success ; } // 输入用户名和密码 WebElement usernameInput = driver.findElement(finder.findBy(1), msg -> setProgress("错误:" + finder.findAlias(1))); if (usernameInput == null) { TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "元素查找错误,名称:" + finder.findAlias(1)); continue; } WebElement passwordInput = driver.findElement(finder.findBy(2), msg -> setProgress("错误:" + finder.findAlias(2))); if (passwordInput == null) { TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "元素查找错误,名称:" + finder.findAlias(2)); continue; } usernameInput.clear(); passwordInput.clear(); usernameInput.sendKeys(userName); passwordInput.sendKeys(pwd); TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "输入用户名:" + userName); // 点击登录按钮 WebElement loginButton = driver.findElement(finder.findBy(3), msg -> setProgress("错误:" + finder.findAlias(3))); if (loginButton == null) { TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "元素查找错误,名称:" + finder.findAlias(3)); continue; } try{ loginButton.click(); }catch(Exception e){ e.printStackTrace(); TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "元素点击错误,名称:" + finder.findAlias(3)); continue; } // 等待登录完成,检查是否存在登录成功的标志(比如用户头像) // 出现智能验证 //*[@id="rectMask"] try{ WebElement ver = driver.findElement(finder.findBy(4), msg -> setProgress("错误:" + finder.findAlias(4))); if (passwordInput != null) { ver.click(); getCrawlerListener().onProgress("点智能验证"); TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "点击智能验证成功"); } }catch (Exception e){ e.printStackTrace(); TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "点击智能验证错误, msg:" + e.getMessage()); } // https://www.cnblogs.com/ WebElement user = driver.findElement(finder.findBy(5), msg -> setProgress("错误:" + finder.findAlias(5))); if (user == null) { TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "主页找不到头像,需要重新登录"); continue; } success = true; getCrawlerListener().onProgress("登录成功"); break; } catch (Exception e) { e.printStackTrace(); } } if(!success){ setError("登录失败"); TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "登录失败"); }else{ TaskLogService.add(task.getTaskId(), "[" + alias() + "] " + "登录成功"); } return success; }
结尾语
有问题联系小编。或者注意文章内容,能看到 响应的 你想要的 源代码信 息。

浙公网安备 33010602011771号