当前位置:   article > 正文

给boss直聘的搜索结果加上hr活跃状态,少看点半年活跃的岗位,有书签版,油猴版_boss直聘半年前活跃还招人吗

boss直聘半年前活跃还招人吗

背景:这段时间找工作,无奈大环境不好,所在城市大部分公司都投了。就是没几个回复的,要么送达,要么已读不回,要么拿了简历没见邀约。然后boss为了争取我们多浏览网站,把一些陈年老醋也拿上台面,让我们误以为有新公司发布职位,结果一点击进去,什么本月活跃,4月内活跃,半年活跃,这些明显就已经不招或者已经招到,只是hr忘了关闭岗位的。还不给过滤条件我们来过滤掉这些岗位,浪费我们时间。

为了过滤一下这些数据,只好自己动手丰衣足食。原理很简单,就是用js代码把没有活跃状态的列表数据用css都给隐藏掉,避免影响我们观感。代码也简单。还有为了不每次都得输入代码,我们要把对应的代码保存到浏览器书签。这样就可以通过点击书签来快速便捷地执行js代码。具体操作就是随意新建一个书签,然后到书签栏找到书签,修改书签。

书签名随你心意,重点是地址栏要把url替换成代码块里面的代码。

  1. javascript: (function() {
  2. function filterOnlineList() {
  3. let lis = document.querySelectorAll(".job-card-wrapper");
  4. for (let i = 0; i < lis.length; i++) {
  5. let online = lis[i].querySelector('.boss-online-tag');
  6. if (!online) {
  7. lis[i].style.display = 'none'
  8. }
  9. }
  10. alertBox('过滤成功');
  11. }
  12. function alertBox(msg) {
  13. var div = document.createElement('div');
  14. div.style.position = 'fixed';
  15. div.style.top = '20%';
  16. div.style.left = '50%';
  17. div.style.transform = 'translate(-50%, -50%)';
  18. div.style.backgroundColor = 'rgb(0 190 189)';
  19. div.style.borderRadius = '5px';
  20. div.style.color = '#fff';
  21. div.style.zIndex = 9999;
  22. div.style.padding = '20px 100px';
  23. div.style.fontSize = '20px';
  24. div.style.boxShadow = '0px 0px 10px rgba(0,0,0,.2)';
  25. div.innerHTML = msg;
  26. document.body.appendChild(div);
  27. setTimeout(function() {
  28. document.body.removeChild(div);
  29. },
  30. 1000);
  31. }
  32. function reBindClick() {
  33. let pages = document.querySelectorAll('.options-pages a');
  34. console.log("因为分页每次点击都会重新渲染,所以需要点击后用定时器重新触发绑定点击事件,不然点击后就没有点击事件监听了");
  35. for (let i = 0; i < pages.length; i++) {
  36. pages[i].addEventListener('click',
  37. function() {
  38. setTimeout(function() {
  39. reBindClick();
  40. filterOnlineList();
  41. },
  42. 1000);
  43. })
  44. }
  45. }
  46. reBindClick();
  47. filterOnlineList();
  48. })()

不保证后续代码会不会失效,但原理都是相同的,仔细看一下代码就知道如果失效了,该怎么改,本来是想看能不能过滤不仅限于在线状态的,还有一些3日,本周,本月活跃之类的,但仔细看过接口后发现查询列表接口没返回这个状态,只有一个online的布尔值。其它的数据是后端渲染html页面生成的数据,不是通过接口返回的数据,所以就没办法,只能退而求之。  

注意:如果你们需要修改代码,要注意以下几点,代码结束分号要有,不然可能会报错。好像也无法使用window,可能是chrome的安全考虑。前面的javascript:是必须的,不然点击书签的时候,浏览器不会执行代码,这里加了个提示框来提示代码是否有成功执行,不用提示框也是可以的,只是可能不知道有没有执行成功

进阶版

后面又想了下,折腾了有一天多吧,算是折腾出一个更好一点的版本

思路都注释在代码里面了。用的fetch去获取对应的html页面,再从html页面里面拿出对应的数据,原理简单,但中间经历的坑也是不少的。

坑一,也是最主要的坑:boss的html页面如果获取速度慢了,会给个302的临时重定向返回回来,你们看到的效果就是点击查询列表页面的任意一个打开的页面会显示一个loading的效果,实际这个loading就是重定向的一个页面,后面它响应到资源了才会再重定向回来目标页面,用户无感,但是对于我们这种只是想拿点数据的就是灾难了,因为它一重定向,浏览器就自动重定向,我们就拿不到数据。最后只能是fetch可以拿到数据的就fetch,fetch拿不到数据了就用iframe加载实际页面,拿到数据后,再删除iframe.

坑二:不能短时间内频繁触发请求,不然所有请求都给你弄成302,所以我只好牺牲速度,保证效果,用Promise await慢慢获取数据,避免触发boss的302 策略。

坑三:用iframe也有坑,本来想用DOMContentLoaded来加快循环速度的,就是拿到html就可以了,因为所需数据就在html页面,不需要css js 图片那些乱七八糟的东西,计划拿完就删掉它,浏览器自然就取消请求了。结果实验了却监听不了这个,最后还是老老实实用回onload,可是用回onload也还是有坑,用iframe加载页面,某些页面也会触发302,这时候你立马去拿值,是拿不到值的,因为真正的页面还没回来,所以只好又用定时器再延时获取一下数据,但这个时间实在不好把控,还有每次拿完要删了iframe,不然你后面再用这个iframe,不会触发onload。

坑四:有些hr是没有状态的,例如一些猎头还有一些奇怪的hr,是空值来的

贴一下效果图,样式随便弄了一下

压缩书签版本,复制到书签去的版本

javascript:(function(){function initBossHrStatus(){let isPageChange=false;function alertBox(msg){var div=document.createElement('div');div.style.cssText='position: fixed; top: 20%; left: 50%; transform: translate(-50%, -50%); background-color: rgb(0 190 189); border-radius: 5px; color: #fff; z-index: 9999; padding: 20px 100px; font-size: 20px; box-shadow: 0px 0px 10px rgba(0,0,0,.2);';div.innerHTML=msg;document.body.appendChild(div);setTimeout(function(){document.body.removeChild(div)},2000)}function reBindClick(){let pages=document.querySelectorAll('.options-pages a');for(let i=0;i<pages.length;i++){pages[i].addEventListener('click',function(){isPageChange=true;setTimeout(function(){new initBossHrStatus()},1000)})}}async function getListStatus(){alertBox('开始更新状态....,网站安全策略问题,更新会比较缓慢。');let links=Array.from(document.querySelectorAll('.job-list-box .job-card-left')).filter((node)=>{let online=node.querySelector('.boss-online-tag');if(online){setText(node,'在线')}return!online});function setText(node,statusTxt){let pNode=document.createElement('p');pNode.innerHTML=statusTxt;console.log(statusTxt);pNode.style.cssText="display:flex;padding:5px;background:#e1f5e3;color:green;";node.querySelector('.job-info').after(pNode)}for(let i=0;i<links.length;i++){if(isPageChange){break}await new Promise((resolve)=>{setTimeout(()=>{resolve()},2000)});let node=links[i];let link=node.href;let statusTxt=await getHtml(link);console.log(statusTxt);if(statusTxt===''){statusTxt='未知状态'}setText(node,statusTxt);if(i===links.length){alertBox('更新完成')}}}function getHtml(link){function fetchHtml(){return fetch(link,{redirect:'error'}).then((res)=>{return res.text()}).then(async(data)=>{const divNode=document.createElement("div");divNode.insertAdjacentHTML('afterbegin',data);const node=divNode.querySelector('.boss-active-time');return node?node.textContent:'猎头,或者没状态的hr'}).catch(async(error)=>{return await getStatusByIframe(link)})}return fetchHtml()}async function getStatusByIframe(link){let iframe=document.createElement('iframe');iframe.src=link;iframe.id='tempIframe';iframe.style.cssText="width:0;height:0;";document.body.appendChild(iframe);return await new Promise((resolve)=>{let iframe=document.querySelector('#tempIframe');iframe.onload=function(){let node=iframe.contentWindow.document.querySelector('.boss-active-time');function returnVal(){let status=node?node.textContent:'可能不存在状态,也可能没获取到真实数据,大概率后者';resolve(status);setTimeout(()=>{document.body.removeChild(iframe)},500)}if(node){returnVal()}else{setTimeout(()=>{node=iframe.contentWindow.document.querySelector('.boss-active-time');returnVal()},2000)}}})}reBindClick();getListStatus();return true}new initBossHrStatus()})()

 源码版,给你们看代码的

  1. javascript: (function() {
  2. function initBossHrStatus(){
  3. let isPageChange = false;
  4. function alertBox(msg) {
  5. var div = document.createElement('div');
  6. div.style.cssText = 'position: fixed; top: 20%; left: 50%; transform: translate(-50%, -50%); background-color: rgb(0 190 189); border-radius: 5px; color: #fff; z-index: 9999; padding: 20px 100px; font-size: 20px; box-shadow: 0px 0px 10px rgba(0,0,0,.2);';
  7. div.innerHTML = msg;
  8. document.body.appendChild(div);
  9. setTimeout(function() {
  10. document.body.removeChild(div);
  11. },
  12. 2000);
  13. }
  14. function reBindClick() {
  15. let pages = document.querySelectorAll('.options-pages a');
  16. /*因为分页每次点击都会重新渲染,所以需要点击后用定时器重新运行方法"*/
  17. for (let i = 0; i < pages.length; i++) {
  18. pages[i].addEventListener('click',
  19. function() {
  20. isPageChange = true;
  21. setTimeout(function() {
  22. new initBossHrStatus();
  23. },
  24. 1000);
  25. })
  26. }
  27. }
  28. async function getListStatus(){
  29. alertBox('开始更新状态....,网站安全策略问题,更新会比较缓慢。');
  30. let links = Array.from(document.querySelectorAll('.job-list-box .job-card-left')).filter((node)=>{
  31. let online = node.querySelector('.boss-online-tag');
  32. if(online){
  33. setText(node,'在线');
  34. }
  35. return !online;
  36. });
  37. function setText(node,statusTxt){
  38. let pNode = document.createElement('p');
  39. pNode.innerHTML = statusTxt;
  40. console.log(statusTxt);
  41. pNode.style.cssText = "display:flex;padding:5px;background:#e1f5e3;color:green;";
  42. node.querySelector('.job-info').after(pNode);
  43. }
  44. /*要把在线的过滤掉,一来减少请求,二来请求的数据也不存在下面代码的class .boss-active-time,会报错'*/
  45. for (let i = 0; i < links.length; i++) {
  46. if(isPageChange){
  47. /*切换了分页, 要中断循环*/
  48. break;
  49. }
  50. await new Promise((resolve) => {
  51. setTimeout(()=>{
  52. /*做个延时处理,好像boss有做ddos处理,频繁请求会触发302重定向,最终导致拿不到html页面数据*/
  53. resolve();
  54. }, 2000);
  55. });
  56. let node = links[i];
  57. let link = node.href;
  58. let statusTxt = await getHtml(link);
  59. console.log(statusTxt);
  60. if(statusTxt===''){
  61. statusTxt = '未知状态';
  62. }
  63. setText(node,statusTxt);
  64. if(i===links.length){
  65. alertBox('更新完成');
  66. }
  67. }
  68. }
  69. function getHtml(link){
  70. function fetchHtml(){
  71. /*设置不允许重定向,让其报错,报错后通过iframe来获取数据,虽然慢点,但起码可以获取到数据*/
  72. return fetch(link, { redirect: 'error' })
  73. .then((res)=>{
  74. return res.text();
  75. }).then(async(data)=>{
  76. const divNode = document.createElement("div");
  77. divNode.insertAdjacentHTML('afterbegin', data);
  78. const node = divNode.querySelector('.boss-active-time');
  79. return node?node.textContent:'猎头,或者没状态的hr';
  80. }).catch(async(error)=>{
  81. /*请求被302临时重定向了,无法获取到数据,需要用iframe来获取了*/
  82. return await getStatusByIframe(link);
  83. })
  84. }
  85. return fetchHtml();
  86. }
  87. async function getStatusByIframe(link){
  88. let iframe = document.createElement('iframe');
  89. iframe.src = link;
  90. iframe.id = 'tempIframe';
  91. iframe.style.cssText = "width:0;height:0;";
  92. document.body.appendChild(iframe);
  93. return await new Promise((resolve)=>{
  94. let iframe = document.querySelector('#tempIframe');
  95. iframe.onload = function(){
  96. let node = iframe.contentWindow.document.querySelector('.boss-active-time');
  97. function returnVal(){
  98. let status = node?node.textContent:'可能不存在状态,也可能没获取到真实数据,大概率后者';
  99. resolve(status);
  100. setTimeout(()=>{
  101. document.body.removeChild(iframe);
  102. },500)
  103. }
  104. if(node){
  105. returnVal();
  106. }else{
  107. /*应该是iframe页面也触发了302临时重定向,导致这时候获取,不能拿到真正的数据,所以延迟试试,但这个延时不好控制,调小了,获取不到数据,调大了,显得反应很慢(不过慢其实没啥影响,因为你不可能一下子浏览全部列表数据,一条一条来,应该是可以的)*/
  108. setTimeout(()=>{
  109. node = iframe.contentWindow.document.querySelector('.boss-active-time');
  110. returnVal();
  111. },2000)
  112. }
  113. }
  114. })
  115. }
  116. reBindClick();
  117. getListStatus();
  118. return true;
  119. }
  120. /*虽然反应速度可能不是很理想,但只是个小功能,所以有效果就行,不想继续优化了*/
  121. new initBossHrStatus();
  122. })()

我又更新了。。。。,应该是最终版了吧

书签版

javascript:(function(){'use strict';class ShowBossActiveTime{constructor(options){this.startTime=null;this.statusOptions=localStorage.getItem('bossActiveStatusList')?.split(',').filter(t=>t.length)||['半年前活跃','近半年活跃','4月前活跃','2月内活跃','2周内活跃'];this.removeStatusList=[];this.options=Object.assign({listElement:'.job-card-wrapper',onlineElement:'.boss-online-tag',chatElement:'.start-chat-btn',hunterElement:'.job-tag-icon',linkElement:'.job-card-left',paginationElement:'.options-pages',hideChated:false},options);this.queque=[];this.list=[];this.addStatusFilter();this.addStyleSheet();this.observeLoadingData();this.request=this.requestInit();this.init()}addStyleSheet(){const style=`.show-active-status{display:flex;padding:5px 10px;background:#e1f5e3;color:green;width:80%;border-radius:4px;margin-top:10px}.show-active-status.status{}.show-active-status.chat{}#alertBox{position:fixed;top:20%;left:50%;transform:translate(-50%,-50%);background-color:rgb(0 190 189);border-radius:5px;color:#fff;z-index:9999;padding:20px 40px;font-size:20px;box-shadow:0px 0px 10px rgba(0,0,0,.2)}#removeFilterDataContainer{position:fixed;right:70px;top:70px;z-index:20000;background:#00bebd;color:#fff;display:flex;flex-direction:column;padding-bottom:10px}#removeFilterDataContainer.hide{height:28px;overflow:hidden}#removeFilterDataContainer.title{display:flex;justify-content:space-around}#removeFilterDataContainer.title label{align-items:center;padding:0 15px}#removeFilterDataContainer.hide#boss-active-time-arrow svg{transform:rotate(180deg)}#removeFilterDataContainer#boss-active-time-arrow{cursor:pointer;font-size:24px;background:#009796;padding:2px 10px;line-height:1}#removeFilterDataContainer.tips{font-size:16px;margin:5px 20px}#removeFilterDataContainer label{display:flex;padding:0 20px}#removeFilterDataContainer label input{margin-right:5px}`;const styleEle=document.createElement('style');styleEle.id='show-boss-active-time-css';styleEle.innerHTML=style;document.head?.appendChild(styleEle)}getList(){Array.from(document.querySelectorAll(this.options.listElement)).forEach((node,index)=>{const status=node.querySelector(this.options.onlineElement);this.list.push(node);if(!status){this.queque.push(node)}})}setText(node,text,status){const html=`<div class="show-active-status"><p class="status">${text}</p>&nbsp;&nbsp;&nbsp;&nbsp;<p class="chat">${status}</p></div>`;node.querySelector('.job-info').insertAdjacentHTML('afterend',html);let aEle=node.querySelector('a');aEle.style.height='auto';aEle.style.paddingBottom='0';if(!this.statusOptions.includes(text)&&text!=='在线'){this.statusOptions.push(text);localStorage.setItem('bossActiveStatusList',this.statusOptions)}}async getListStatus(){this.alertBox('开始更新状态....,网站安全策略问题,更新会比较缓慢。');const startTime=new Date().getTime();this.startTime=startTime;for(let i=0;this.queque.length>0;i++){let node=this.queque.shift();let link=node.querySelector(this.options.linkElement).href;let chat=node.querySelector(this.options.chatElement).textContent;await new Promise((resolve)=>{setTimeout(async()=>{await this.request(link,node,chat,this.queque.length);resolve()},1000)});if(startTime!==this.startTime){return}}if(this.queque.length===0){this.startTime=null;setTimeout(()=>{this.alertBox('查询完毕,更新即将完成')},1500)}}requestInit(){if(window.GM_xmlhttpRequest){return(link,node,chat,index)=>{return GM_xmlhttpRequest({method:'GET',url:link,onload:(response)=>{if(/security-check.html/.test(response.finalUrl)){this.getStatusByIframe(response.finalUrl,index).then((text)=>{if(text===''){text='未知状态'}this.setText(node,text,chat);this.toggleDom(node)})}else{const html=response.responseText;const parser=new DOMParser();const doc=parser.parseFromString(html,'text/html');const text=this.getStatusText(doc);this.setText(node,text,chat);this.toggleDom(node)}}})}}else{return(link,node,chat,index)=>{return fetch(link,{redirect:'error'}).then((res)=>{return res.text()}).then(async(data)=>{const doc=document.createElement('div');doc.insertAdjacentHTML('afterbegin',data);const text=this.getStatusText(doc);this.setText(node,text,chat);this.toggleDom(node)}).catch(async(error)=>{this.getStatusByIframe(link,index).then((text)=>{if(text===''){text='未知状态'}this.setText(node,text,chat);this.toggleDom(node)})})}}}async getStatusByIframe(link,id){let iframe=document.createElement('iframe');iframe.src=link;iframe.id='tempIframe'+id;iframe.style.cssText='width:0;height:0;';document.body.appendChild(iframe);return await new Promise((resolve)=>{let tempIframe=document.querySelector('#tempIframe'+id);tempIframe.onload=()=>{setTimeout(()=>{if(tempIframe.contentWindow?.document){const text=this.getStatusText(tempIframe.contentWindow.document);resolve(text);console.log('用iframe获取',text);setTimeout(()=>{document.body.removeChild(tempIframe)},500)}},5000)}})}observeLoadingData(){const container=document.querySelector('.search-job-result');const observer=new MutationObserver((mutations)=>{mutations.forEach((mutation)=>{if(mutation.type==='childList'){const addNode=mutation.addedNodes;const removedNode=mutation.removedNodes;if(addNode.length&&addNode[0].className==='job-loading-wrapper'){console.log('触发了请求列表数据')}if(removedNode.length&&removedNode[0].className==='job-loading-wrapper'){console.log('加载完成');this.clear();this.init()}}})});const config={attributes:false,childList:true,subtree:false};observer.observe(container,config);const listContainer=document.querySelector('#wrap');const listObserver=new MutationObserver((mutations)=>{mutations.forEach((mutation)=>{if(mutation.type==='childList'){const wrapper=document.querySelector('.job-list-wrapper');const removeNode=document.querySelector("#removeFilterDataContainer");if(!wrapper&&removeNode){document.body.removeChild(removeNode);listObserver.disconnect();this.clear()}}})});listObserver.observe(listContainer,config)}alertBox(msg){let div=document.createElement('div');div.id='alertBox';div.innerHTML=msg;document.body.appendChild(div);setTimeout(function(){document.body.removeChild(div)},2000)}getStatusText(doc){const timeNode=doc.querySelector('.boss-active-time');if(timeNode){return timeNode.textContent}else{const isHunter=['.certification-tags','.boss-info-attr'].filter((name)=>{const node=doc.querySelector(name);return/猎头|人力|经纪/.test(node?.textContent)});const status=isHunter?'猎头,没有活跃状态':'获取到数据了,但不知道是什么数据';return status}}toggleDom(node){const status=node.querySelector('.status')?.textContent;const chat=node.querySelector(this.options.chatElement).textContent;node.style.display='block';if(this.options.hideChated&&chat==='继续沟通'){node.style.display='none'}if(status&&chat){if(this.removeStatusList.includes(status)){node.style.display='none'}if(this.options.hideChated&&chat==='继续沟通'){node.style.display='none'}}}toggleDoms(){this.list.forEach((node)=>{this.toggleDom(node)})}addStatusFilter(){const container=document.createElement('div');container.id='removeFilterDataContainer';const html=`<label><input type="checkbox"name="hideChated"value="1">过滤已经沟通过的</label><div id="boss-active-time-arrow"><svg stroke="currentColor"fill="none"stroke-width="2"viewBox="0 0 24 24"aria-hidden="true"height="1em"width="1em"xmlns="http:/**/www.w3.org/2000/svg"><path stroke-linecap="round"stroke-linejoin="round"d="M19 9l-7 7-7-7"></path></svg></div>`;const title=document.createElement('div');title.className='title';title.innerHTML=html;const tips=document.createElement('div');tips.innerHTML='过滤掉勾选的数据';tips.className='tips';container.appendChild(title);container.appendChild(tips);container.querySelector('#boss-active-time-arrow').addEventListener('click',function(){container.classList.contains('hide')?container.classList.remove('hide'):container.classList.add('hide')});this.statusOptions.forEach((option)=>{const label=document.createElement('label');const el=document.createElement('input');el.type='checkbox';el.name=option;el.value=option;el.className='status-checkbox';label.appendChild(el);label.appendChild(document.createTextNode(option));container.appendChild(label)});container.addEventListener('change',()=>{const selectedValues=Array.from(container.querySelectorAll('.status-checkbox:checked')).map((el)=>el.value);this.removeStatusList=selectedValues;const hideNode=document.querySelector('input[name="hideChated"]');this.options.hideChated=hideNode?.checked;this.toggleDoms()});document.body.appendChild(container)}clear(){this.queque.length=0;this.list.length=0;this.startTime=null}init(){this.getList();this.list.forEach((node)=>{const chat=node.querySelector(this.options.chatElement).textContent;const online=node.querySelector(this.options.onlineElement);if(online){this.setText(node,'在线',chat)}});this.toggleDoms();this.getListStatus()}}function start(){const Lis=document.querySelectorAll('.job-card-wrapper');if(Lis.length){new ShowBossActiveTime()}else{console.log('no start');setTimeout(start,2000)}}start()})();

源码版

  1. javascript:(function () {
  2. 'use strict';
  3. class ShowBossActiveTime {
  4. constructor(options) {
  5. this.startTime = null;/*记录是否是当前列表数据的循环,如果没查询完毕就重新触发的新的实例化,需要去停止旧的查询循环*/
  6. this.statusOptions = localStorage
  7. .getItem('bossActiveStatusList')
  8. ?.split(',').filter(t=>t.length) || [
  9. '半年前活跃',
  10. '近半年活跃',
  11. '4月前活跃',
  12. '2月内活跃',
  13. '2周内活跃'
  14. ];
  15. this.removeStatusList = [];
  16. this.options = Object.assign(
  17. {
  18. listElement: '.job-card-wrapper',
  19. onlineElement: '.boss-online-tag',
  20. chatElement: '.start-chat-btn',
  21. hunterElement: '.job-tag-icon',
  22. linkElement: '.job-card-left',
  23. paginationElement: '.options-pages',
  24. hideChated: false
  25. },
  26. options
  27. );
  28. this.queque = []; /*查询队列*/
  29. this.list = []; /*数据列表*/
  30. /*添加过滤条件,因为要保存选择数据,所以这个不能切换时清空*/
  31. this.addStatusFilter();
  32. this.addStyleSheet();
  33. /*监听请求数据事件*/
  34. this.observeLoadingData();
  35. this.request = this.requestInit();
  36. this.init();
  37. }
  38. addStyleSheet() {
  39. const style = `
  40. .show-active-status{display:flex;padding:5px 10px;background:#e1f5e3;color:green;width:80%;border-radius:4px;margin-top:10px;}
  41. .show-active-status .status{}
  42. .show-active-status .chat{}
  43. #alertBox{position: fixed; top: 20%; left: 50%; transform: translate(-50%, -50%); background-color: rgb(0 190 189); border-radius: 5px; color: #fff; z-index: 9999; padding: 20px 40px; font-size: 20px; box-shadow: 0px 0px 10px rgba(0,0,0,.2);}
  44. #removeFilterDataContainer{
  45. position: fixed;right: 70px;top: 70px;z-index: 20000;background: #00bebd; color: #fff;display: flex;flex-direction: column;padding-bottom:10px
  46. }
  47. #removeFilterDataContainer.hide{height:28px;overflow:hidden}
  48. #removeFilterDataContainer .title {display:flex;justify-content: space-around;}
  49. #removeFilterDataContainer .title label{align-items:center;padding:0 15px;}
  50. #removeFilterDataContainer.hide #boss-active-time-arrow svg{transform: rotate(180deg);}
  51. #removeFilterDataContainer #boss-active-time-arrow {cursor: pointer;font-size: 24px;background: #009796;padding:2px 10px;line-height:1;}
  52. #removeFilterDataContainer .tips{font-size:16px;margin:5px 20px;}
  53. #removeFilterDataContainer label{display:flex;padding:0 20px;}
  54. #removeFilterDataContainer label input{margin-right:5px;}
  55. `;
  56. const styleEle = document.createElement('style');
  57. styleEle.id = 'show-boss-active-time-css';
  58. styleEle.innerHTML = style;
  59. document.head?.appendChild(styleEle);
  60. }
  61. /*获取节点列表*/
  62. getList() {
  63. Array.from(document.querySelectorAll(this.options.listElement)).forEach(
  64. (node, index) => {
  65. const status = node.querySelector(this.options.onlineElement);
  66. this.list.push(node);
  67. /*不在线*/
  68. if (!status) {
  69. this.queque.push(node);
  70. }
  71. }
  72. );
  73. }
  74. /*设置文本内容*/
  75. setText(node, text, status) {
  76. const html = `
  77. <div class="show-active-status">
  78. <p class="status">${text}</p>&nbsp;&nbsp;&nbsp;&nbsp;
  79. <p class="chat">${status}</p>
  80. </div>
  81. `;
  82. node.querySelector('.job-info').insertAdjacentHTML('afterend', html);
  83. let aEle = node.querySelector('a');
  84. aEle.style.height = 'auto';
  85. aEle.style.paddingBottom = '0';
  86. if(!this.statusOptions.includes(text)&&text!=='在线'){
  87. this.statusOptions.push(text);
  88. localStorage.setItem('bossActiveStatusList',this.statusOptions);
  89. }
  90. }
  91. async getListStatus() {
  92. this.alertBox('开始更新状态....,网站安全策略问题,更新会比较缓慢。');
  93. const startTime = new Date().getTime();
  94. this.startTime = startTime;
  95. for (let i = 0; this.queque.length > 0; i++) {
  96. let node = this.queque.shift();
  97. let link = node.querySelector(this.options.linkElement).href;
  98. let chat = node.querySelector(this.options.chatElement).textContent;
  99. await new Promise((resolve) => {
  100. setTimeout(async () => {
  101. /*做个延时处理,频繁请求会触发302重定向,最终导致拿不到html页面数据*/
  102. await this.request(link, node, chat, this.queque.length);
  103. resolve();
  104. }, 1000);
  105. });
  106. if(startTime!==this.startTime){
  107. /*中断旧的循环查询*/
  108. return;
  109. }
  110. }
  111. if (this.queque.length === 0) {
  112. this.startTime = null;
  113. setTimeout(()=>{
  114. this.alertBox('查询完毕,更新即将完成');
  115. },1500);
  116. }
  117. }
  118. requestInit(){
  119. /*如果是油猴,使用GM_xmlhttpRequest,如果不是,使用fetch*/
  120. if (window.GM_xmlhttpRequest) {
  121. return (link, node, chat, index) => {
  122. return GM_xmlhttpRequest({
  123. method: 'GET',
  124. url: link,
  125. onload: (response) => {
  126. if (/security-check.html/.test(response.finalUrl)) {
  127. /*用GM_xmlhttpRequest获取触发了302,用finaleUrl通过iframe来获取,不用link,看是否能省略302这个步骤,加快速度*/
  128. this.getStatusByIframe(response.finalUrl, index).then(
  129. (text) => {
  130. if (text === '') {
  131. text = '未知状态';
  132. }
  133. this.setText(node, text, chat);
  134. this.toggleDom(node);
  135. }
  136. );
  137. } else {
  138. const html = response.responseText;
  139. const parser = new DOMParser();
  140. const doc = parser.parseFromString(html, 'text/html');
  141. const text = this.getStatusText(doc);
  142. this.setText(node, text, chat);
  143. this.toggleDom(node);
  144. }
  145. }
  146. });
  147. };
  148. } else {
  149. return (link, node, chat, index) => {
  150. /*设置不允许重定向,让其报错,报错后通过iframe来获取数据,虽然慢点,但起码可以获取到数据*/
  151. return fetch(link, { redirect: 'error' })
  152. .then((res) => {
  153. return res.text();
  154. })
  155. .then(async (data) => {
  156. const doc = document.createElement('div');
  157. doc.insertAdjacentHTML('afterbegin', data);
  158. const text = this.getStatusText(doc);
  159. this.setText(node, text, chat);
  160. this.toggleDom(node);
  161. })
  162. .catch(async (error) => {
  163. /*请求被302临时重定向了,无法获取到数据,需要用iframe来获取了*/
  164. this.getStatusByIframe(link, index).then((text) => {
  165. if (text === '') {
  166. text = '未知状态';
  167. }
  168. this.setText(node, text, chat);
  169. this.toggleDom(node);
  170. });
  171. });
  172. };
  173. }
  174. }
  175. async getStatusByIframe(link, id) {
  176. let iframe = document.createElement('iframe');
  177. iframe.src = link;
  178. iframe.id = 'tempIframe' + id;
  179. iframe.style.cssText = 'width:0;height:0;';
  180. document.body.appendChild(iframe);
  181. return await new Promise((resolve) => {
  182. let tempIframe = document.querySelector('#tempIframe' + id);
  183. tempIframe.onload = () => {
  184. setTimeout(() => {
  185. if (tempIframe.contentWindow?.document) {
  186. const text = this.getStatusText(
  187. tempIframe.contentWindow.document
  188. );
  189. resolve(text);
  190. console.log('用iframe获取', text);
  191. setTimeout(() => {
  192. document.body.removeChild(tempIframe);
  193. }, 500);
  194. }
  195. }, 5000);
  196. };
  197. });
  198. }
  199. observeLoadingData() {
  200. const container = document.querySelector('.search-job-result');
  201. const observer = new MutationObserver((mutations) => {
  202. mutations.forEach((mutation) => {
  203. if (mutation.type === 'childList') {
  204. const addNode = mutation.addedNodes;
  205. const removedNode = mutation.removedNodes;
  206. if (
  207. addNode.length &&
  208. addNode[0].className === 'job-loading-wrapper'
  209. ) {
  210. console.log('触发了请求列表数据');
  211. }
  212. if (
  213. removedNode.length &&
  214. removedNode[0].className === 'job-loading-wrapper'
  215. ) {
  216. console.log('加载完成');
  217. this.clear();
  218. this.init();
  219. }
  220. }
  221. });
  222. });
  223. const config = { attributes: false, childList: true, subtree: false };
  224. observer.observe(container, config);
  225. /*监听是否是搜索列表页,不是就移除dom*/
  226. const listContainer = document.querySelector('#wrap');
  227. const listObserver = new MutationObserver((mutations) => {
  228. mutations.forEach((mutation) => {
  229. if (mutation.type === 'childList') {
  230. const wrapper = document.querySelector('.job-list-wrapper');
  231. const removeNode = document.querySelector("#removeFilterDataContainer");
  232. if(!wrapper&&removeNode){
  233. document.body.removeChild(removeNode);
  234. listObserver.disconnect();
  235. /*清除查询*/
  236. this.clear();
  237. }
  238. }
  239. });
  240. });
  241. listObserver.observe(listContainer, config);
  242. }
  243. alertBox(msg) {
  244. let div = document.createElement('div');
  245. div.id = 'alertBox';
  246. div.innerHTML = msg;
  247. document.body.appendChild(div);
  248. setTimeout(function () {
  249. document.body.removeChild(div);
  250. }, 2000);
  251. }
  252. getStatusText(doc) {
  253. const timeNode = doc.querySelector('.boss-active-time');
  254. if (timeNode) {
  255. return timeNode.textContent;
  256. } else {
  257. /*没有获取到状态,但页面是已经加载到的了*/
  258. const isHunter = ['.certification-tags', '.boss-info-attr'].filter(
  259. (name) => {
  260. const node = doc.querySelector(name);
  261. return /猎头|人力|经纪/.test(node?.textContent);
  262. }
  263. );
  264. const status = isHunter
  265. ? '猎头,没有活跃状态'
  266. : '获取到数据了,但不知道是什么数据';
  267. return status;
  268. }
  269. }
  270. toggleDom(node){
  271. const status = node.querySelector('.status')?.textContent;
  272. const chat = node.querySelector(this.options.chatElement).textContent;
  273. /*先显示全部*/
  274. node.style.display = 'block';
  275. /* 首先判断是否隐藏已沟通*/
  276. if (this.options.hideChated && chat === '继续沟通') {
  277. node.style.display = 'none';
  278. }
  279. /*状态数据已经获取了*/
  280. if (status && chat) {
  281. if (this.removeStatusList.includes(status)) {
  282. node.style.display = 'none';
  283. }
  284. if (this.options.hideChated && chat === '继续沟通') {
  285. node.style.display = 'none';
  286. }
  287. }
  288. }
  289. toggleDoms(){
  290. this.list.forEach((node) => {
  291. this.toggleDom(node);
  292. });
  293. }
  294. addStatusFilter() {
  295. const container = document.createElement('div');
  296. container.id = 'removeFilterDataContainer';
  297. const html = `
  298. <label><input type="checkbox" name="hideChated" value="1">过滤已经沟通过的</label>
  299. <div id="boss-active-time-arrow"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true" height="1em" width="1em" xmlns="http:/**/www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path></svg></div>
  300. `;
  301. const title = document.createElement('div');
  302. title.className = 'title';
  303. title.innerHTML = html;
  304. const tips = document.createElement('div');
  305. tips.innerHTML = '过滤掉勾选的数据';
  306. tips.className = 'tips';
  307. container.appendChild(title);
  308. container.appendChild(tips);
  309. container
  310. .querySelector('#boss-active-time-arrow')
  311. .addEventListener('click', function () {
  312. container.classList.contains('hide')
  313. ? container.classList.remove('hide')
  314. : container.classList.add('hide');
  315. });
  316. this.statusOptions.forEach((option) => {
  317. const label = document.createElement('label');
  318. const el = document.createElement('input');
  319. el.type = 'checkbox';
  320. el.name = option;
  321. el.value = option;
  322. el.className = 'status-checkbox';
  323. label.appendChild(el);
  324. label.appendChild(document.createTextNode(option));
  325. container.appendChild(label);
  326. });
  327. container.addEventListener('change', () => {
  328. const selectedValues = Array.from(
  329. container.querySelectorAll('.status-checkbox:checked')
  330. ).map((el) => el.value);
  331. this.removeStatusList = selectedValues;
  332. const hideNode = document.querySelector('input[name="hideChated"]');
  333. this.options.hideChated = hideNode?.checked;
  334. this.toggleDoms();
  335. });
  336. document.body.appendChild(container);
  337. }
  338. clear(){
  339. this.queque.length = 0;
  340. this.list.length = 0;
  341. this.startTime = null;
  342. }
  343. init() {
  344. /*获取列表数据*/
  345. this.getList();
  346. /*先给在线的数据设置状态*/
  347. this.list.forEach((node) => {
  348. const chat = node.querySelector(this.options.chatElement).textContent;
  349. const online = node.querySelector(this.options.onlineElement);
  350. if (online) {
  351. this.setText(node, '在线', chat);
  352. }
  353. });
  354. /*判断是否要隐藏已经沟通过的数据*/
  355. this.toggleDoms();
  356. /*请求数据,给不在线的设置状态*/
  357. this.getListStatus();
  358. }
  359. }
  360. function start() {
  361. const Lis = document.querySelectorAll('.job-card-wrapper');
  362. if (Lis.length) {
  363. new ShowBossActiveTime();
  364. } else {
  365. console.log('no start');
  366. setTimeout(start, 2000);
  367. }
  368. }
  369. start();
  370. })();

有同学说要油猴版,又去看了下油猴怎么弄

油猴脚本的链接:showBossActiveTime 

git项目地址: GitHub - shrimpbighead/showBossActiveTime

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/726810
推荐阅读
相关标签
  

闽ICP备14008679号