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

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

[复制链接]
  • TA的每日心情
    开心
    前天 00:00
  • 签到天数: 49 天

    [LV.5]常住居民I

    350

    主题

    3064

    帖子

    3077

    积分

    荣誉开发者

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

    Rank: 10Rank: 10Rank: 10

    积分
    3077

    猫咪币纪念章国庆纪念章中秋纪念章荣誉开发者家财万贯

    发表于 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

    结语

    撒花~

    图片.png
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
  • TA的每日心情
    无聊
    2022-8-1 11:32
  • 签到天数: 136 天

    [LV.7]常住居民III

    41

    主题

    355

    帖子

    1826

    积分

    荣誉开发者

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

    Rank: 10Rank: 10Rank: 10

    积分
    1826

    荣誉开发者活跃会员三好学生中秋纪念章国庆纪念章热心会员推广达人宣传达人突出贡献猫咪币纪念章

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

    使用道具 举报

  • TA的每日心情
    无聊
    4 天前
  • 签到天数: 5 天

    [LV.2]偶尔看看I

    3

    主题

    67

    帖子

    102

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    102

    荣誉开发者突出贡献猫咪币纪念章中秋纪念章国庆纪念章三好学生活跃会员热心会员宣传达人

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

    内容忽略
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    7 小时前
  • 签到天数: 64 天

    [LV.6]常住居民II

    150

    主题

    1976

    帖子

    2119

    积分

    管理员

    Rank: 10Rank: 10Rank: 10

    积分
    2119

    猫咪币纪念章热心会员活跃会员突出贡献三好学生中秋纪念章国庆纪念章管理员家财万贯

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

    内容忽略

    直接开冲?
    上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。/ 微信公众号:一之哥哥
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    昨天 18:11
  • 签到天数: 132 天

    [LV.7]常住居民III

    23

    主题

    320

    帖子

    290

    积分

    高级工程师

    The CV Moilion :-)

    Rank: 6Rank: 6

    积分
    290

    中秋纪念章活跃会员国庆纪念章

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

    使用道具 举报

    发表回复

    本版积分规则

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