上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖

[油猴脚本开发指南]实战React数据提取抖音视频

[复制链接]
  • TA的每日心情
    慵懒
    2024-10-28 07:07
  • 签到天数: 193 天

    [LV.7]常住居民III

    712

    主题

    5961

    回帖

    6760

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6760

    荣誉开发者喜迎中秋油中2周年生态建设者

    发表于 2021-12-7 12:05:18 | 显示全部楼层 | 阅读模式

    前文

    之前我们已经在元素规则校验中https://bbs.tampermonkey.net.cn/forum.php?mod=viewthread&tid=1250

    研究过网页的校验如何解决,这节课我们可以实战一下提取react页面的数据

    我发现抖音是react页面

    本文基于cxxjackie提供的理论,再次感谢

    开始

    依然以抖音页面为例https://www.douyin.com/user/MS4wLjABAAAA__EF83GW-y2bDHV0jmune1pZFG1TRajSgvywS7KYGbQ

    图片.png

    如何判断是react页面?我们可以安装React Developer Tools

    如果不是则显示

    图片.png

    如果是页面则显示

    图片.png

    但是注意,这个可能存在问题,最准确的还是打开页面进行调试

    比如输出window.__查看是否存在react属性

    图片.png

    确定是react页面后,我们可以安装插件,然后点击这里

    图片.png

    然后通过

    图片.png

    选择到元素

    注意,推荐选择到相应元素的最上级,然后一层一层往下找相关的数据以及事件

    我们在这里找到了视频的信息

    图片.png

    这时候我们就可以开始写代码了

    首先他第一页是没有post的,属于网页渲染出来的数据,但是这时候也可以使用react属性提取地址,相对之前的xhr劫持会好很多。

    我们先获取第一页的内容,然后进行mutationobserve监听视频部分的绘制

    并抽离一个函数,专门用于处理对象,我起名叫ControlShowCheckAndNew

    let list=document.querySelectorAll('.knrjsN15 ul li')
    const targetNode = document.querySelector('.knrjsN15 ul')
    // 观察器的配置(需要观察什么变动)
    const config = {
        childList: true, // 观察目标子节点的变化,添加或删除
        attributes: true, // 观察属性变动
        subtree: true, //默认是false,设置为true后可观察后代节点
    };
    
    // 当观察到变动时执行的回调函数
    const callback = function(mutationsList, observer) {
        // Use traditional 'for loops' for IE 11
        console.log('mutationsList',mutationsList)
        for(let mutation of mutationsList) {
            if (mutation.type === 'childList') {
                mutation.addedNodes.forEach((item)=>{
                    ControlShowCheckAndNew(item)
                })
            }
        }
    };
    // 创建一个观察器实例并传入回调函数
    const observer = new MutationObserver(callback);
    // 以上述配置开始观察目标节点
    observer.observe(targetNode, config);
    unsafeWindow.onload=()=>{
        //处理循环
    list.forEach((item)=>{
        ControlShowCheckAndNew(item)
    })
    }

    然后我们还需要插入一个按钮,用于提取数据

    图片.png

    这里的innerHTML是直接改的网页按钮,然后复制粘贴的

    let parenttagert=document.querySelector('.Z0NF3RWY')
    let div=document.createElement("div");
    div.innerHTML=`<div class="q6zgm94p k-vFWw3W FDOWibym BgSUKoKp"><span class="_891e9d38c00e1b78e2eae43ab8b92359-scss" style="cursor: pointer;">复制视频</span><div class="_421d3aff42f03ac25665dc94de7ceadb-scss _6e84962fcb7da3b1e8100d798c94fd0a-scss" style="display: none;"><div class="a508b8e520c4938b699e76f52758e1b5-scss"><div class="f34e96e88162611d7208f348d4f89234-scss"><img src="//p6.douyinpic.com/img/aweme-qrcode/HfLOWW6996335373702006541~c5_720x720.png?from=1247829622" alt="3.82 wfB:/ 这样子的小风车你们喜欢吗~%%夹子音 %%夹子音挑战 %%夹子音变装 @DOU+小助手 @抖音小助手  https://v.douyin.com/d1FgV77/ 复制此链接,打开Dou音搜索,直接观看视频!" class=""></div></div><div class="c970dfb43b7e68344f353625de339de0-scss"><div class="_6ed090956a2566bf4d47a648b75d87ef-scss">打开抖音扫码或复制口令粘贴给微信/QQ好友</div><div class="_5d025eb178c1c97d99eb9717cb4f0290-scss"><span class="_95cfb8df7d5be42fc93f8f4464a1c648-scss">3.82 wfB:/ 这样子的小风车你们喜欢吗~%%夹子音 %%夹子音挑战 %%夹子音变装 @DOU+小助手 @抖音小助手  https://v.douyin.com/d1FgV77/ 复制此链接,打开Dou音搜索,直接观看视频!</span><button class="abace09bde29f9d2077ba2a9e9e2b67d-scss _3c25ad295260cb707e35da1ec8d93a51-scss _14339689bca6b9eda19c146a14df625e-scss _047cfcad258573fad8a7513577bb9f75-scss"><span>复制</span></button></div></div></div></div>`
    div.onclick=function(event){
        let size=Object.keys(saveurl)
        let text=size.join('\n')
        GM_setClipboard(text)
        alert('已设置到剪辑版共'+size.length+"个")
    };
    parenttagert.append(div);

    接下来我们要开始写核心功能函数了

    我对网页插入了一个单选框以及一个new的标签提示,这时候mutationobserver也会监听到,所以一旦检测到就不进行任何操作

    然后判断是否存在prop属性,如果不存在也直接跳过

    找到的话则直接根据targer的react属性的children.props.aweInfo提取出来数据

    进行一些处理,然后插入一个new标签以及一个单选框

    图片.png

    NEW标签如下

    图片.png

    代码如下

    function ControlShowCheckAndNew(target){
        if(target.className.indexOf('injectvideo')!=-1){
            return true;
        }
        if(target.className.indexOf('control-pos')!=-1){
            return true;
        }
        const prop = Object.keys(target).find(p => p.startsWith('__reactProps'));
        if(prop===undefined){
            return;
        }
        let info=target[prop].children.props.awemeInfo
        if(info===undefined){
            console.log('test')
        }
        let createTime=info.createTime*1000
        let videourl=info.video.playApi
        videourl='https://'+videourl.replace('https://','').replace('http://','').replace('//','')
        target.classList.add('injectvideo')
        var select=document.createElement('label')
        select.className='container control-pos'
        select.innerHTML=` <input  type="checkbox"><div class="checkmark"></div>`
        target.append(select)
        select.onclick=()=>{
            console.log('选中变化了',select.children[0].checked)
            if(select.children[0].checked){
                //选中
                saveurl[videourl]=true;
            }else{
                //未选中
                if(saveurl[videourl]){
                    delete saveurl[videourl]
                }
            }
        }
        var getdate=new Date(createTime)
        if(fullyear===getdate.getFullYear()){
            if(currentmonth===getdate.getMonth()){
                if(currentday===getdate.getDate()){
                    console.log('选中新的')
                    let newbutton=document.createElement('button')
                    newbutton.innerText='NEW'
                    newbutton.classList.add('newbutton')
                    target.append(newbutton)
    
                }
            }
        }
    }

    这里我设置了一个对象用于存储到底选中了哪个视频,一旦选中了,则设置到对象上,如果取消选中,则相应的删除对象

    关于按钮的样式,我是从https://cssbuttons.io/找的

    图片.png

    这里有很多好看的样式,我们可以直接对抄出来

    设置CSS的时候可以通过GM函数设置,也可以创建CSS标签

    这里为了演示,所以我这里选择创建了CSS标签,然后插入

    let cssstyle = document.createElement("style");
    cssstyle.innerHTML =(`
    .injectvideo{
    position: relative;
    }
    .control-pos{
    bottom: 20.5px;
    right: 12.1px;
         position: absolute;
    }
    .container input {
     position: absolute;
     opacity: 0;
     cursor: pointer;
     height: 0;
     width: 0;
    }
    
    .container {
     display: block;
     cursor: pointer;
     font-size: 20px;
     user-select: none;
    }
    
    /* Create a custom checkbox */
    .checkmark {
     position: relative;
     top: 0;
     left: 0;
     height: 1.3em;
     width: 1.3em;
     background-color: #ccc;
     border-radius: 25px;
     transition: 0.15s;
    }
    
    /* When the checkbox is checked, add a blue background */
    .container input:checked ~ .checkmark {
     background-color: #ff4242;
     border-radius: 25px;
     transition: 0.15s;
    }
    
    /* Create the checkmark/indicator (hidden when not checked) */
    .checkmark:after {
     content: "";
     position: absolute;
     display: none;
    }
    
    /* Show the checkmark when checked */
    .container input:checked ~ .checkmark:after {
     display: block;
    }
    
    /* Style the checkmark/indicator */
    .container .checkmark:after {
     left: 0.45em;
     top: 0.25em;
     width: 0.25em;
     height: 0.5em;
     border: solid white;
     border-width: 0 0.15em 0.15em 0;
     transform: rotate(45deg);
    }
    .newbutton,
    .newbutton::after {
     padding: 5px 4px;
     font-size: 18px;
     background: linear-gradient(45deg, transparent 5%, #ff013c 5%);
     border: 0;
     color: #fff;
     letter-spacing: 3px;
     line-height: 1;
     box-shadow: 6px 0px 0px #00e6f6;
     outline: transparent;
     position: relative;
    }
    
    .newbutton::after {
     --slice-0: inset(50% 50% 50% 50%);
     --slice-1: inset(80% -6px 0 0);
     --slice-2: inset(50% -6px 30% 0);
     --slice-3: inset(10% -6px 85% 0);
     --slice-4: inset(40% -6px 43% 0);
     --slice-5: inset(80% -6px 5% 0);
     content: "HOVER ME";
     display: block;
     position: absolute;
     top: 0;
     left: 0;
     right: 0;
     bottom: 0;
     background: linear-gradient(45deg, transparent 3%, #00e6f6 3%, #00e6f6 5%, #ff013c 5%);
     text-shadow: -3px -3px 0px #f8f005, 3px 3px 0px #00e6f6;
     clip-path: var(--slice-0);
    }
    
    .newbutton:hover::after {
     animation: 1s glitch;
     animation-timing-function: steps(2, end);
    }
    .newbutton{
    position: absolute;
    left: 15.5px;
    top: 0;
    }
    `);
    document.body.appendChild(cssstyle);

    那么到这里我们就学会了如何提取react数据来实现提取抖音视频~

    脚本地址

    https://bbs.tampermonkey.net.cn/thread-1404-1-1.html

    结语

    撒花~

    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
  • TA的每日心情
    奋斗
    2023-7-6 11:14
  • 签到天数: 253 天

    [LV.8]以坛为家I

    41

    主题

    318

    回帖

    2178

    积分

    荣誉开发者

    累计点滴改进,迈向完美品质。

    积分
    2178

    荣誉开发者油中2周年生态建设者

    发表于 2021-12-7 12:32:29 | 显示全部楼层
    ggnb!!!
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    2023-6-12 15:07
  • 签到天数: 6 天

    [LV.2]偶尔看看I

    7

    主题

    91

    回帖

    155

    积分

    荣誉开发者

    积分
    155

    荣誉开发者油中2周年生态建设者喜迎中秋

    发表于 2021-12-7 13:06:35 | 显示全部楼层
    图不错

    内容忽略
    回复

    使用道具 举报

  • TA的每日心情
    开心
    昨天 13:37
  • 签到天数: 213 天

    [LV.7]常住居民III

    305

    主题

    4189

    回帖

    4056

    积分

    管理员

    积分
    4056

    管理员荣誉开发者油中2周年生态建设者喜迎中秋油中3周年挑战者 lv2

    发表于 2021-12-7 13:48:22 | 显示全部楼层
    maxzhang 发表于 2021-12-7 13:06
    图不错

    内容忽略

    直接开冲?
    上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。
    回复

    使用道具 举报

  • TA的每日心情
    郁闷
    昨天 15:34
  • 签到天数: 773 天

    [LV.10]以坛为家III

    76

    主题

    519

    回帖

    871

    积分

    专家

    脚本猫首席体验官

    积分
    871

    油中2周年生态建设者新人报道油中3周年挑战者 lv2喜迎中秋

    发表于 2022-5-9 21:37:54 | 显示全部楼层
    ggnb!
    回复

    使用道具 举报

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表