整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          簡單實用node腳本!通過定時任務和2個fetch請求實現網站自動簽到

          一個網站每天簽到可以獲取流量,之前每天都是自己打開網頁登錄然后手動點簽到,但是如果連續7天沒簽到之前獲取的所有流量都會清空。類似的需求會有很多,很多網站、應用也都會提供簽到獲取積分、金幣、能量、飼料...其實用node腳本來實現每天自動簽到很簡單。

          想到的實現方案

          • 用無頭瀏覽器 Puppeteer、Playwright 之類的庫模擬dom操作,這個稍微復雜點,涉及到比較離譜的圖形驗證還需要接入第三方驗證碼識別
          • 抓包應用的網絡請求,直接通過分析登錄、簽到接口,發起網絡請求實現簽到

          下方示例采用了第二種接口簽到方式,一般我們只需要先分析登錄接口,拿到登錄態,如果是直接返回的 token 那就更簡單了,也有的網站會自動通過 cookie 設置登錄態,拿到登錄態我們再帶上登錄態去請求簽到接口就可以了。

          實現步驟

          1、分析登錄接口,拿到登錄態參數

          輸入賬號密碼登錄后,發現登錄接口 Response 里并沒有數據返回,然后看 Headers 里的 Response Headers 里的 Set-Cookies 就是登陸成功服務端自動設置的登錄態信息:

          添加圖片注釋,不超過 140 字(可選)

          這一步只需要帶著賬號密碼參數去請求登錄接口,然后再解析出 Set-Cookies 里我們需要的參數就行了,發起請求不需要用第三方庫,17.5.0版本后 node 里也可以直接使用 fetch 發送請求了,可以直接在瀏覽器控制臺 network 里選中接口右鍵 Copy - Copy as Node.js fetch 復制,示例代碼如下

          /**
           * 獲取登錄態 cookies
           * @returns
           */
          const getCookie = async function() {
              const res = await fetch("https://cafe123.cn/auth/login", {
                  "headers": {
                      "accept": "application/json, text/javascript, */*; q=0.01",
                      "accept-language": "zh-CN,zh;q=0.9",
                      "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
                      "sec-ch-ua": "\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\"",
                      "sec-ch-ua-mobile": "?0",
                      "sec-ch-ua-platform": "\"Windows\"",
                      "sec-fetch-dest": "empty",
                      "sec-fetch-mode": "cors",
                      "sec-fetch-site": "same-origin",
                      "x-requested-with": "XMLHttpRequest",
                      "Referer": "https://cafe123.cn/auth/login",
                      "Referrer-Policy": "strict-origin-when-cross-origin"
                  },
                  "body": "email=123456%40qq.com&passwd=123456&code=&remember_me=week",
                  "method": "POST"
              })
          
              if (res.status !== 200) {
                  console.log('登錄失敗:', new Date(), res)
                  return
              }
          
              // res.json().then(r => {
              //   console.log(r)
              // })
              const headers = res.headers
              cookies = []
              for (const element of headers) {
                  if (element[0] === 'set-cookie') {
                      const val = element[1].split(';')[0]
                      cookies.push(val)
                  }
              }
              checkin()
          }

          拿到響應結果后,通過 res.headers 可以拿到響應頭里的所有參數,然后可以用 for of 遍歷下取出我們需要的參數就行了。

          發起簽到請求

          先按照上面登錄接口類似的方法分析簽到接口的構成,這一步主要是請求頭里面的 cookie 參數,直接用上一步拿到的響應頭里的 Set-Cookies 里的登錄信息按 key=value 用分號分隔拼接好就行了。

          /**
           * 簽到
           */
          const checkin = async function() {
              if (!cookies) {
                  getCookie()
                  return
              }
          
              const res = await fetch("https://cafe123.cn/user/checkin", {
                  "headers": {
                      "accept": "application/json, text/javascript, */*; q=0.01",
                      "accept-language": "zh-CN,zh;q=0.9",
                      "sec-ch-ua": "\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\"",
                      "sec-ch-ua-mobile": "?0",
                      "sec-ch-ua-platform": "\"Windows\"",
                      "sec-fetch-dest": "empty",
                      "sec-fetch-mode": "cors",
                      "sec-fetch-site": "same-origin",
                      "x-requested-with": "XMLHttpRequest",
                      "cookie": cookies.join(';'),
                      "Referer": "https://cafe123.cn/user",
                      "Referrer-Policy": "strict-origin-when-cross-origin"
                  },
                  "body": null,
                  "method": "POST"
              })
              if (res.status !== 200) {
                  console.log('簽到失敗:', new Date().toLocaleString(), res)
                  cookies = null
                  getCookie()
              } else {
                  console.log('簽到成功:', new Date().toLocaleString(), res)
                  res.json().then(r => {
                      console.log(r)
                  })
              }
          }

          3、發起簽到請求

          這一步需要借助定時任務 node-cron 庫,注意 node-cron 表達式總共6位,首位的秒是可以省略的,所以也可以5位:

          /**
           * 定時任務
           */
          cron.schedule("30 2 8 * * *", function() {
              // node-cron 表達式總共6位,首位的秒可以省略,所以也可以5位
              // 每秒執行一次 * * * * * *
              // 每天12點30分執行一次  30 12 * * *
              // 每隔5秒執行一次  */5 * * * * *
              // 每天早上8點到晚上6點之間每個正點執行任務一次  0 0 8-18 * * *,等同于  0 0 8,9,10,11,12,13,14,15,16,17,18 * * *
              console.log("觸發定時任務:", new Date().toLocaleString())
              checkin()
          })

          注意問題

          如果在自己本地電腦上運行,windows 進入睡眠狀態時,定時任務是不會執行的,即使用 pm2 啟動服務也不會執行的,只能設置讓電腦從不睡眠了,或者有服務器的部署在自己的服務器上跑。

          還有如果想要在每天隨機一個時間執行內執行,用 cron 表達式是不行的,定時任務開啟后只能在某個時間點執行或者間隔多長時間執行,想到的方案是可以開啟兩個定時任務,第一個都是每天同一時間觸發,最好是每天0點0分0秒,然后在這個定時任務里再去隨機獲取一個時間,去開啟另一個定時任務去執行,這個我還沒實驗,或者你有更好的方案可以分享下喲!

          完整的示例項目源碼地址:https://github.com/cafehaus/check-in,直接把里面的接口地址、參數這些換一下就行了。

          篇文章講到HTML炫酷的主流框架,今天小明就介紹一個HTML5功能的實現代碼

          Introduce(介紹)

          用戶簽到的H5例子(css+jquery,無圖片),由于網上找的的用戶簽到例子都不好,要不就是好多圖片組成的,要不就大量冗余代碼,所以特意做了個簽到界面(移動端)。

          User sign sample page for mobile using h5 which only use css + jquery + html.

          一些關鍵的地方

          這個功能的編寫思路是,先構建日期和簽到相關數據,然后從服務端獲取數據,并對原有數據進行更改,最后進行渲染。

          這樣子很好的擺脫了邏輯比較凌亂的問題,并且可以直接將這些數據用 vue.js 來掛載(本文沒有這樣做)。

          生成日期數據

          //生成日期數據
           function buildData() {
           var da = {
           dates: [],//日期數據,從1號開始
           current: '',//當前日期
           monthFirst: 1,//獲取當月的1日等于星期幾
           month: 0,//當前月份
           days: 30,//當前月份共有多少天
           day: 0,//今天幾號了
           isSigned: false,//今天是否已經簽到
           signLastDays:3,//連續簽到日子
           signToday: function () {
           this.isSigned = true;
           this.dates[this.day].isSigned = true;
           },
           };
           var ds = [];
           //初始化日期數據
           var dt = new Date();
           da.current = dt.ToString('yyyy年M月d日');
           da.monthFirst = new Date(dt.getFullYear(), dt.getMonth(), 1).getDay();
           da.month = dt.getMonth() + 1;
           da.days = new Date(dt.getFullYear(), parseInt(da.month), 0).getDate();//獲取當前月的天數
           da.day = dt.getDate();
           for (var i = 1; i < da.days + 1; i++) {
           var o = {
           isSigned: false,//是否簽到了
           num: i,//日期
           isToday: i == da.day,//是否今天
           isPass: i < da.day,//時間已過去
           };
           ds[i] = o;
           }
           da.dates = ds;
           return da;
           }
          

          有了數據之后,就可以將數據轉換為界面了

          //渲染數據
           function renderData(da) {
           var signDays = document.getElementById('spSignDays');
           signDays.innerText = da.signLastDays;
           var root = document.getElementById("signTable");
           root.innerHTML = '';
           var tr, td;
           var st = da.monthFirst;
           var dates = da.dates;
           var rowcount = 0;
           //最多6行
           for (var i = 0; i < 42; i++) {
           if (i % 7 == 0) {
           //如果沒有日期了,中斷
           if (i > (st + da.days))
           break;
           tr = document.createElement('tr');
           tr.className = 'darkcolor trb';
           root.appendChild(tr);
           rowcount++;
           }
           //前面或后面的空白
           if (i < st || !dates[i - st + 1]) {
           td = document.createElement('td');
           td.innerHTML = '<div class="sign-blank"><span></span></div>';
           tr.appendChild(td);
           continue;
           }
           //填充數字部分
           var d = dates[i - st + 1];
           td = document.createElement('td');
           var tdcss = '';
           if (d.isToday)
           tdcss = 'sign-today';
           else if (d.isPass)
           tdcss = 'sign-pass';
           else
           tdcss = 'sign-future';
           if (d.isSigned) {
           tdcss = 'sign-signed ' + tdcss;
           td.innerHTML = '<div class="' + tdcss + '"><span>' + d.num + '</span><svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="sign-pin svg-triangle "><polygon points="0,0 35,0 0,35" /></svg></div>';
           } else {
           tdcss = 'sign-unsign ' + tdcss;
           td.innerHTML = '<div class="' + tdcss + '"><span>' + d.num + '</span></div>';
           }
           tr.appendChild(td);
           }
           //計算是否需要添加最后一行
           if ((st + da.days + 1) / 7 > rowcount)
           root.appendChild(tr);
           }
           //構建日期數據
           var da = buildData();
           //渲染
           renderData(da);
          

          以上就是本篇文章的全內容了

          學習從來不是一個人的事情,要有個相互監督的伙伴,想要學習或交流前端問題的小伙伴可以私信回復小明“學習” 獲取資料,一起學習!

          到功能

          1、數據庫中要有相應的表,并創建相應的實體類,復寫相關方法

          2、在相關的jsp頁面添加兩個jsp按鈕,用于簽到與簽退,并添加id屬性

          簽到簽退

          <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
          <%
          String path = request.getContextPath();
          String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
          %>



          <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
          <html xmlns="http://www.w3.org/1999/xhtml">
          <head>
          <base href="<%=basePath%>" />
          <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
          <title>無標題文檔</title>
          <link href="css/style.css" rel="stylesheet" type="text/css" />
          <script type="text/javascript" src="js/jquery.js"></script>
          <script type="text/javascript">
          //給按鈕signin綁定單擊事件,實現簽到功能
          $(function(){
          $("#signin").click(function(){
          //alert("ok");
          //發送ajax請求,完成簽到.并通過回調函數顯示結果
          $.ajax({
          url:"duty?method=signin",
          type:"post",
          dataType:"text",
          success:function(data){
          //顯示簽到結果
          if(data==0){
          $("#result").html("簽到失敗");
          }else if (data==1) {
          $("#result").html("簽到成功");
          }else {
          $("#result").html("已經簽到,無需重復簽到");
          }
          }
          });
          });

          //給按鈕signout綁定單擊事件,實現簽退
          $("#signout").click(function(){
          //alert("ok?");
          $.ajax({
          url:"duty?method=signout",
          type:"post",
          dataType:"text",
          success:function(result){
          $("#result").html(result);
          }
          });
          });

          });



          </script>
          </head>

          <body>

          <div class="place">
          <span>位置:</span>
          <ul class="placeul">
          <li><a href="#">考勤管理</a></li>
          <li><a href="#">簽到簽退</a></li>
          </ul>
          </div>

          <div class="formbody">

          <div class="formtitle"><span>基本信息</span></div>

          <ul class="forminfo">
          <li><label> </label><input name="signinTime" type="button" class="btn" id="signin" value="簽到"/> 每天簽到一次,不可重復簽到</li>
          <li><label> </label></li>
          <li><label> </label></li>
          <li><label> </label><input name="signoutTime" type="button" class="btn" id="signout" value="簽退"/>可重復簽退,以最后一次簽退為準</li>
          </ul>

          </div>
          <div id="result"></div>

          </body>

          </html>

          3、在servlet中編寫簽到相關方法

          servlet方法

          //考勤簽到操作
          public void signin(HttpServletRequest request,
          HttpServletResponse response) throws ServletException, IOException {

          //獲取session作用域中的emp對象,emp存放的是被考勤人信息,自行創建
          Employee emp= (Employee) request.getSession().getAttribute("emp");
          String empId=emp.getEmpId();
          //調用業務層完成簽到操作
          DutyService ds=new DutyServiceImpl();
          int n=ds.signin(empId);
          //0失敗 1成功 2已經簽到

          //無需頁面跳轉,直接響應結果(通常與ajax連用)
          response.getWriter().println(n);
          }
          4、在service實現類編寫簽到的邏輯方法

          業務實現類

          截圖勾線有誤,其實進行在dao層進行了兩個操作 1、查詢是否簽到的操作 2、保存簽到信息的操作

          @Override
           public int signin(String empId) {
            //判斷用戶是否已經簽到
            Date now=new Date();//yyyyMMdd——>hhmmss
            java.sql.Date today=new java.sql.Date(now.getTime());//hhmmss
            boolean flag= dd.find(empId,today);//1、查詢是否簽到的操作
            
            //對返回的flag進行處理,true:已經簽到,false沒有簽到,進行簽到
            int n=0;
            if (flag) {
             return 2;
            } else {
             Duty duty=new Duty();
             //duty.setDtId(dtId);序列自增
             duty.setEmpId(empId);
             duty.setDtDate(today);
             DateFormat sdf = new SimpleDateFormat("hh:mm:ss");
             String signinTime= sdf.format(now);
             duty.setSigninTime(signinTime);
             
             //完成簽到
             n=dd.save(duty);//2、保存簽到信息的操作
             return n;//0失敗 1成功
            }
           }
          

          5、dao層實現類

          dao層方法

          @Override
          public boolean find(String empId, Date today) {
          //jdbc查詢操作
          //創建jdbc變量
          Connection conn=null;
          PreparedStatement ps=null;
          ResultSet rs=null;
          //創建變量
          boolean flag=false;
          //默認情況為查不到
          try {
          //創建連接
          conn=DBUtil.getConnection();
          //創建SQL語句
          String sql="select * from duty where empid=? and dtdate=?";
          //創建SQL命令
          ps=conn.prepareStatement(sql);
          //給占位符賦值
          ps.setString(1, empId);
          ps.setDate(2, today);
          //執行sql語句
          rs=ps.executeQuery();
          //遍歷
          if (rs.next()) {
          flag=true;
          //簽到成功
          }

          } catch (Exception e) {
          e.printStackTrace();
          }finally{
          //關閉變量
          DBUtil.closeAll(rs, ps, conn);
          }
          return flag;


          }
          @Override
          public int save(Duty duty) {
          //添加操作
          //創建sql語句
          String sql="insert into duty values(seq_duty.nextval,?,?,?,null)";
          //創建params變量集合
          Object[] params={duty.getEmpId(),duty.getDtDate(),duty.getSigninTime()};

          return DBUtil.executeUpdate(sql, params);
          6、在原來的jsp頁面中,編寫ajax請求,處理從servlet傳來的數據

          jsp頁面添加方法

          <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
          <%
          String path = request.getContextPath();
          String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
          %>



          <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
          <html xmlns="http://www.w3.org/1999/xhtml">
          <head>
          <base href="<%=basePath%>" />
          <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
          <title>無標題文檔</title>
          <link href="css/style.css" rel="stylesheet" type="text/css" />
          <script type="text/javascript" src="js/jquery.js"></script>
          <script type="text/javascript">
          //給按鈕signin綁定單擊事件,實現簽到功能
          $(function(){
          $("#signin").click(function(){
          //alert("ok");
          //發送ajax請求,完成簽到.并通過回調函數顯示結果
          $.ajax({
          url:"duty?method=signin",
          type:"post",
          dataType:"text",
          success:function(data){
          //顯示簽到結果
          if(data==0){
          $("#result").html("簽到失敗");
          }else if (data==1) {
          $("#result").html("簽到成功");
          }else {
          $("#result").html("已經簽到,無需重復簽到");
          }
          }
          });
          });

          //給按鈕signout綁定單擊事件,實現簽退
          $("#signout").click(function(){
          //alert("ok?");
          $.ajax({
          url:"duty?method=signout",
          type:"post",
          dataType:"text",
          success:function(result){
          $("#result").html(result);
          }
          });
          });

          });



          </script>
          </head>

          <body>

          <div class="place">
          <span>位置:</span>
          <ul class="placeul">
          <li><a href="#">考勤管理</a></li>
          <li><a href="#">簽到簽退</a></li>
          </ul>
          </div>

          <div class="formbody">

          <div class="formtitle"><span>基本信息</span></div>

          <ul class="forminfo">
          <li><label> </label><input name="signinTime" type="button" class="btn" id="signin" value="簽到"/> 每天簽到一次,不可重復簽到</li>
          <li><label> </label></li>
          <li><label> </label></li>
          <li><label> </label><input name="signoutTime" type="button" class="btn" id="signout" value="簽退"/>可重復簽退,以最后一次簽退為準</li>
          </ul>

          </div>
          <div id="result"></div>

          </body>

          </html>

          簽退功能

          1、在servlet層編寫簽退相關方法(提前處理,ajax直接顯示結果)

          //考勤簽退操作
          public void signout(HttpServletRequest request,
          HttpServletResponse response) throws ServletException, IOException {

          //獲取session作用域中的emp對象
          Employee emp= (Employee) request.getSession().getAttribute("emp");
          String empId=emp.getEmpId();
          //調用業務層完成簽到操作
          DutyService ds=new DutyServiceImpl();
          int n=ds.signout(empId);
          //0失敗 1成功 2沒有簽到

          //無需頁面跳轉,直接響應結果(通常與ajax連用)
          response.setContentType("text/html; charset =utf-8");
          PrintWriter out= response.getWriter();
          if (n==1) {
          out.println("簽退成功");
          } else if (n==0) {
          out.println("簽退失敗");
          } else {
          out.println("尚未簽退");
          }
          }
          2、在service的實現類編寫簽退的邏輯方法

          //考勤簽退操作
          public void signout(HttpServletRequest request,
          HttpServletResponse response) throws ServletException, IOException {

          //獲取session作用域中的emp對象
          Employee emp= (Employee) request.getSession().getAttribute("emp");
          String empId=emp.getEmpId();
          //調用業務層完成簽到操作
          DutyService ds=new DutyServiceImpl();
          int n=ds.signout(empId);
          //0失敗 1成功 2沒有簽到

          //無需頁面跳轉,直接響應結果(通常與ajax連用)
          response.setContentType("text/html; charset =utf-8");
          PrintWriter out= response.getWriter();
          if (n==1) {
          out.println("簽退成功");
          } else if (n==0) {
          out.println("簽退失敗");
          } else {
          out.println("尚未簽退");
          }
          }
          3、dao層調用的方法與簽到一致,無需添加 4、在原來的jsp頁面中,編寫ajax請求,處理從servlet傳來的數據(result為簽到簽退按鈕下的一個div的id用來顯示考勤結果)

          jsp頁面中的添加的方法/函數

          //給按鈕signout綁定單擊事件,實現簽退
            $("#signout").click(function(){
             //alert("ok?");
             $.ajax({
              url:"duty?method=signout",
              type:"post",
              dataType:"text",
              success:function(result){
               $("#result").html(result);
              }
             });
            });
          

          注:采取mvc架構模式

          總結:

          簽到實現 1、點擊簽到按鈕,跳轉到簽到的servlet,調用相關的方法 2、dao層首先去數據庫查看用戶是否簽到,如果簽到則返回true,如果沒簽到則返回false,并執行保存簽到信息的方法。返回簽到的結果0失敗,1成功,2已簽到,并將數據返回到servlet 3、servlet將數據直接響應給前臺頁面,jsp頁面通過Ajax獲取信息,更根據相應的值顯示相應的提示語。

          簽退實現 1、點擊簽退按鈕,跳轉到簽退的servlet,調用相關的方法 2、dao層首先去數據庫查看用戶是否簽退,如果簽到則返回true,如果沒簽到則返回false,并執行保存簽退信息的方法。返回簽退的結果0失敗,1成功,2已簽到,并將數據返回到servlet 3、servlet將數據直接響應給前臺頁面,jsp頁面通過Ajax獲取信息,更根據相應的值顯示相應的提示語。


          項目地址(手動復制下面地址到瀏覽器即可): shimo.im/docs/IaCeHAzWu80UoG45


          主站蜘蛛池模板: 韩国福利影视一区二区三区| 国产伦精品一区二区三区免.费| 无码一区二区波多野结衣播放搜索| 糖心vlog精品一区二区三区| 久久人妻内射无码一区三区| www.亚洲一区| 日本一区二区三区四区视频| 亚洲一区二区三区高清在线观看| 奇米精品视频一区二区三区| 国产一区二区三区久久精品| 亚洲国产一区视频| 免费精品一区二区三区在线观看| 精品成人乱色一区二区| 中文字幕一区一区三区| 亚洲中文字幕在线无码一区二区| 无码国产精品一区二区免费vr| 中文字幕国产一区| 精品一区二区在线观看| 岛国无码av不卡一区二区| 精品人妻一区二区三区毛片 | 亚洲乱色熟女一区二区三区丝袜| 日本人真淫视频一区二区三区| 国产天堂在线一区二区三区 | 亚洲AV永久无码精品一区二区国产 | 国产精品一区二区无线| 国产成人av一区二区三区在线 | 无码人妻精品一区二区蜜桃| 黑巨人与欧美精品一区| 日韩美女在线观看一区| 日韩AV无码一区二区三区不卡| 国产在线精品一区在线观看| 一区二区三区免费视频网站| 久久国产精品免费一区| 亚洲国产精品一区二区成人片国内 | 视频一区二区三区在线观看| 国产精品区AV一区二区| 国产精品日本一区二区不卡视频| 亚洲AV午夜福利精品一区二区| 2022年亚洲午夜一区二区福利| 国产精品一区二区久久精品涩爱| 日本一区二区三区在线看|