自建摸鱼网站([box.hnmybsbp.com)),各大网站热搜一览,上班和摸鱼很配哦!

简介: 自建摸鱼网站([box.hnmybsbp.com)),各大网站热搜一览,上班和摸鱼很配哦!

一、前言

大家好呀,我是summo,最近发生了些事情(被裁员了,在找工作中)导致断更了,非常抱歉。刚被裁的时候还是有些难受,而且我还有房贷要还,有些压力,不过休息了一段时间,心态也平复了一些,打算一边找工作一边写文,如果有和我一样经历的同学,大家共勉!

《花100块做个摸鱼小网站! 》这个系列的前六篇已经大概把整体的流程写完了,从这篇起我会补充一些细节和组件,让我们的小网站更加丰富一些。这一篇呢我会介绍如何将用户的访问记录留下来,看着自己做的网站被别人访问是一件很有意思和很有成就感的事情。

对应的组件也就是我用红框标出来的那个,如下图:

解释下PV和UV的意思,如下:

PV:页面访问量,即PageView,用户每次对网站的访问均被记录,用户对同一页面的多次访问,访问量累计。
UV:独立访问用户数:即UniqueVisitor,访问网站的一台电脑客户端为一个访客。

二、用户身份标识

用于表明一个用户身份最好的做法是做登录注册,但是一旦加了这样的逻辑,就会有很多麻烦的问题要处理,比如如何做人机验证啦、接口防刷啦,等等,这些问题不处理的话网站很容易被攻击。像我们这样的小网站,我觉得这个功能没必要,我们只需要知道有多少人访问过我们的网站就可以了。

对于这样的需求,最简单的做法是根据用户的访问IP作为标识,然后根据IP解析一下地域信息,这样就已经很不错了。而目前最常用的IP解析工具就是:ip2region,如何使用这个组件我之前写过一篇文章进行介绍了,文章链接:SpringBoot整合ip2region实现使用ip监控用户访问城市。

核心代码是这段:

package com.example.springbootip.util;

import org.apache.commons.io.FileUtils;
import org.lionsoul.ip2region.xdb.Searcher;

import java.io.File;
import java.text.MessageFormat;
import java.util.Objects;

public class AddressUtil {

/**
 * 当前记录地址的本地DB
 */
private static final String TEMP_FILE_DIR = "/home/admin/app/";

/**
 * 根据IP地址查询登录来源
 *
 * @param ip
 * @return
 */
public static String getCityInfo(String ip) {
    try {
        // 获取当前记录地址位置的文件
        String dbPath = Objects.requireNonNull(AddressUtil.class.getResource("/ip2region/ip2region.xdb")).getPath();
        File file = new File(dbPath);
        //如果当前文件不存在,则从缓存中复制一份
        if (!file.exists()) {
            dbPath =    TEMP_FILE_DIR + "ip.db";
            System.out.println(MessageFormat.format("当前目录为:[{0}]", dbPath));
            file = new File(dbPath);
            FileUtils.copyInputStreamToFile(Objects.requireNonNull(AddressUtil.class.getClassLoader().getResourceAsStream("classpath:ip2region/ip2region.xdb")), file);
        }
        //创建查询对象
        Searcher searcher = Searcher.newWithFileOnly(dbPath);
        //开始查询
        return searcher.searchByStr(ip);
    } catch (Exception e) {
        e.printStackTrace();
    }
    //默认返回空字符串
    return "";
}

public static void main(String[] args) {
    System.out.println(getCityInfo("1.2.3.4"));
}

}

三、功能实现

为了解耦逻辑,我使用了一个注解:@VisitLog,只要将该注解放在IndexController的index方法上即可。同时为了统计用户的访问数据,我们需要设计一张访问记录表将数据存下来,并设计一个小组件用来展示这些数据,具体流程如下文。

  1. 后端部分
    (1)访问记录表设计
    建表语句

-- summo-sbmy.t_sbmy_visit_log definition

CREATE TABLE t_sbmy_visit_log (
id bigint(20) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT '物理主键',
device_type varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '设备类型,手机还是电脑',
ip varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '访问',
address varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'IP地址',
time int DEFAULT NULL COMMENT '耗时',
method varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '调用方法',
params text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT '参数',
gmt_create datetime DEFAULT NULL COMMENT '创建时间',
gmt_modified datetime DEFAULT NULL COMMENT '更新时间',
creator_id bigint DEFAULT NULL COMMENT '创建人',
modifier_id bigint DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (id) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=oDEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

DO、Mapper、Repository等文件

还记得我在第三篇介绍的那个DO生成插件吗,在config.properties改下表名和DO名,双击mybatis-generator:generate就可以生成对应的DO、Mapper、xml了。

(2)VisitLog注解
VisitLog.java

package com.summo.sbmy.aspect;

/**

  • 访问标识注解
    */
    public @interface VisitLog {
    }

VisitLogAspect.java

package com.summo.sbmy.aspect.visit;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.summo.sbmy.common.util.AddressUtil;
import com.summo.sbmy.common.util.HttpContextUtil;
import com.summo.sbmy.common.util.IpUtil;
import com.summo.sbmy.dao.entity.SbmyVisitLogDO;
import com.summo.sbmy.dao.repository.SbmyVisitLogRepository;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import static com.summo.sbmy.common.util.DeviceUtil.isFromMobile;

@Slf4j
@Aspect
@Component
public class VisitLogAspect {

@Autowired
private SbmyVisitLogRepository sbmyVisitLogRepository;

@Pointcut("@annotation(com.summo.sbmy.aspect.visit.Log)")
public void pointcut() {
    // do nothing
}

@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    //获取request
    HttpServletRequest request = HttpContextUtil.getHttpServletRequest();
    // 请求的类名
    MethodSignature signature = (MethodSignature)joinPoint.getSignature();
    Method method = signature.getMethod();
    String className = joinPoint.getTarget().getClass().getName();
    // 请求的方法名
    String methodName = signature.getName();
    String ip = IpUtil.getIpAddr(request);
    String address = AddressUtil.getAddress(ip);
    SbmyVisitLogDO sbmyVisitLogDO = SbmyVisitLogDO.builder().deviceType(isFromMobile(request) ? "手机" : "电脑").method(
        className + "." + methodName + "()").ip(ip).address(AddressUtil.getAddress(address)).build();
    // 请求的方法参数值
    Object[] args = joinPoint.getArgs();
    // 请求的方法参数名称
    LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
    String[] paramNames = u.getParameterNames(method);
    if (args != null && paramNames != null) {
        // 创建 key-value 映射用于生成 JSON 字符串
        Map<String, Object> paramMap = new LinkedHashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            if (args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse) {
                continue;
            }
            paramMap.put(paramNames[i], args[i]);
        }
        // 使用 Fastjson 将参数映射转换为 JSON 字符串
        String paramsJson = JSON.toJSONString(paramMap);
        sbmyVisitLogDO.setParams(paramsJson);
    }
    long beginTime = System.currentTimeMillis();
    Object proceed = joinPoint.proceed();
    long end = System.currentTimeMillis();
    sbmyVisitLogDO.setTime((int)(end - beginTime));
    sbmyVisitLogRepository.save(sbmyVisitLogDO);
    return proceed;
}

/**
 * 参数构造器¬
 *
 * @param params
 * @param args
 * @param paramNames
 * @return
 * @throws JsonProcessingException
 */
private StringBuilder handleParams(StringBuilder params, Object[] args, List paramNames)
    throws JsonProcessingException {
    for (int i = 0; i < args.length; i++) {
        if (args[i] instanceof Map) {
            Set set = ((Map)args[i]).keySet();
            List<Object> list = new ArrayList<>();
            List<Object> paramList = new ArrayList<>();
            for (Object key : set) {
                list.add(((Map)args[i]).get(key));
                paramList.add(key);
            }
            return handleParams(params, list.toArray(), paramList);
        } else {
            if (args[i] instanceof Serializable) {
                Class<?> aClass = args[i].getClass();
                try {
                    aClass.getDeclaredMethod("toString", new Class[] {null});
                    // 如果不抛出 NoSuchMethodException 异常则存在 toString 方法 ,安全的 writeValueAsString ,否则 走 Object的
                    // toString方法
                    params.append(" ").append(paramNames.get(i)).append(": ").append(
                        JSONObject.toJSONString(args[i]));
                } catch (NoSuchMethodException e) {
                    params.append(" ").append(paramNames.get(i)).append(": ").append(
                        JSONObject.toJSONString(args[i].toString()));
                }
            } else if (args[i] instanceof MultipartFile) {
                MultipartFile file = (MultipartFile)args[i];
                params.append(" ").append(paramNames.get(i)).append(": ").append(file.getName());
            } else {
                params.append(" ").append(paramNames.get(i)).append(": ").append(args[i]);
            }
        }
    }
    return params;
}

}

这里使用到了一些工具类,代码我已经上传到仓库了,大家直接down下来就行。

(3)注解使用

在IndexController.java中加入该注解即可

package com.summo.sbmy.web.controller;

import com.summo.sbmy.aspect.visit.VisitLog;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {

@GetMapping("/")
@VisitLog
public String index(){
    return "index";
}

}

  1. 前端部分
    (1)新建VisitorLog组件

代码如下:






{ { statsData.todayPv }}

今日 PV





{ { statsData.todayUv }}

今日 UV



{ { statsData.allPv }}

总 PV



{ { statsData.allUv }}

总 UV










(2)组件使用

在App.vue组件中引入VisitorLog组件,顺便将布局重新分一下,代码如下:
















咱们做出来的效果就是这样的,如下:

这个小组件做起来还是简单的,主要就是监控了别人的访问IP,然后通过IP反解析出所属地域,最后将其存到数据库中。

[box.sclycx.com)
[box.517piao.com)
[box.chinaweiyuan.com)
[box.dtjxzx.com)
[box.1d-cn.com)

番外:搜狗热搜爬虫

  1. 爬虫方案评估

搜狗的热搜接口返回的一串JSON格式数据,这就很简单了,省的我们去解析dom,访问链接是:https://go.ie.sogou.com/hot_ranks

  1. 网页解析代码

SougouHotSearchJob.java

package com.summo.sbmy.job.sougou;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.summo.sbmy.common.model.dto.HotSearchDetailDTO;
import com.summo.sbmy.dao.entity.SbmyHotSearchDO;
import com.summo.sbmy.service.SbmyHotSearchService;
import com.summo.sbmy.service.convert.HotSearchConvert;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;

import static com.summo.sbmy.common.cache.SbmyHotSearchCache.CACHE_MAP;
import static com.summo.sbmy.common.enums.HotSearchEnum.DOUYIN;
import static com.summo.sbmy.common.enums.HotSearchEnum.SOUGOU;

/**

  • @author summo
  • @version SougouHotSearchJob.java, 1.0.0
  • @description 搜狗热搜Java爬虫代码
  • @date 2024年08月09
    */
    @Component
    @Slf4j
    public class SougouHotSearchJob {

    @Autowired
    private SbmyHotSearchService sbmyHotSearchService;

    @XxlJob("sougouHotSearchJob")
    public ReturnT hotSearch(String param) throws IOException {

     log.info("搜狗热搜爬虫任务开始");
     try {
         //查询搜狗热搜数据
         OkHttpClient client = new OkHttpClient().newBuilder().build();
         Request request = new Request.Builder().url("https://go.ie.sogou.com/hot_ranks").method("GET", null)
                 .build();
         Response response = client.newCall(request).execute();
         JSONObject jsonObject = JSONObject.parseObject(response.body().string());
         JSONArray array = jsonObject.getJSONArray("data");
         List<SbmyHotSearchDO> sbmyHotSearchDOList = Lists.newArrayList();
         for (int i = 0, len = array.size(); i < len; i++) {
             //获取知乎热搜信息
             JSONObject object = (JSONObject)array.get(i);
             //构建热搜信息榜
             SbmyHotSearchDO sbmyHotSearchDO = SbmyHotSearchDO.builder().hotSearchResource(SOUGOU.getCode()).build();
             //设置知乎三方ID
             sbmyHotSearchDO.setHotSearchId(object.getString("id"));
             //设置文章标题
             sbmyHotSearchDO.setHotSearchTitle(object.getJSONObject("attributes").getString("title"));
             //设置文章连接
             sbmyHotSearchDO.setHotSearchUrl(
                     "https://www.sogou.com/web?ie=utf8&query=" + sbmyHotSearchDO.getHotSearchTitle());
             //设置热搜热度
             sbmyHotSearchDO.setHotSearchHeat(object.getJSONObject("attributes").getString("num"));
             //按顺序排名
             sbmyHotSearchDO.setHotSearchOrder(i + 1);
             sbmyHotSearchDOList.add(sbmyHotSearchDO);
         }
         if (CollectionUtils.isEmpty(sbmyHotSearchDOList)) {
             return ReturnT.SUCCESS;
         }
         //数据加到缓存中
         CACHE_MAP.put(SOUGOU.getCode(), HotSearchDetailDTO.builder()
             //热搜数据
             .hotSearchDTOList(
                 sbmyHotSearchDOList.stream().map(HotSearchConvert::toDTOWhenQuery).collect(Collectors.toList()))
             //更新时间
             .updateTime(Calendar.getInstance().getTime()).build());
         //数据持久化
         sbmyHotSearchService.saveCache2DB(sbmyHotSearchDOList);
         log.info("搜狗热搜爬虫任务结束");
     } catch (IOException e) {
         log.error("获取搜狗数据异常", e);
     }
     return ReturnT.SUCCESS;
    

    }
    }

相关文章
|
24天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
16天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
20天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2577 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
18天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
3天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
2天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
163 2
|
20天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1576 16
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
22天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
973 14
|
3天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
219 2
|
17天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
734 9