油猴脚本 之 网教通直播评论记录抓取

动机

看一看一天下来,谁在刷屏,谁在搞事呢。

我的一个同学用 Python 写了一个(Github 传送门)。于是我想用我自己的方法也实现一个,也让自己明白,自己每天上课在说些什么……

思路

通过浏览器的开发人员工具,可以发现,这个聊天框其实是一个指向 recreation-chat.sdp.101.com 域名下一个页面的 iframe。因此我们需要声明作用域为:

// ==UserScript==
// ...
// @match  https://recreation-chat.sdp.101.com/*
// ...
// ==/UserScript==

我们知道,我们之所以要去抓取这个,就是因为网教通的评论区里面只保留最近 100 条消息,其他的会被折叠,需要点击按钮才可以展开。那简单,定位到那个按钮,一句话的事:

var timer = setInterval(function () {
        if (document.querySelector('div._3Bl-4NTv1A._3bGFQ6biuP'))
            document.querySelector('div._3Bl-4NTv1A._3bGFQ6biuP').click();
    }, 100);

即每 100ms(0.1s)模拟点击一次按钮。

可是这也带来了一个问题:上面的代码,可以视作一个死循环,没有终点。所以我们需要能够停止这个 timer。我的想法是通过按下 Esc 键来停止点击,即:

document.onkeydown = function (e) {
    var ev = e || window.event;
    if (ev.keyCode == 27) { //Esc 键的 keycode 是 27
        clearInterval(timer);
    }
}

然后呢?然后我们就需要拿到这里已经加载出来的聊天记录,即聊天记录区域元素(其 idmsg-flow-wrapper)的 innerHTML。即

var data = document.getElementById('msg-flow-wrapper').innerHTML;

于是我们可以获取到(以下是已经把元素的所有不必要属性 去掉 并进行 代码格式化 之后的结果,以下是只有 一条 消息的情况):

<div>
    查看更多消息
</div>
<div>
    <div>
        <div>
            <div>
                <div>
                    <img src="..." />
                </div>
                <div>
                    <div>
                        <span><span title="林**">林**</span></span>
                    </div>
                    <div>
                        04-22 17:14:20
                    </div>
                    <div>
                        <div>
                            <span><span>噗哈哈哈哈哈</span></span>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

%¥#=^&$…@,这谁看得下去。必须整理这些HTML代码。开始吧。

首先是对HTML标签的处理。全部先换成换行符。正则表达式上场:

data = data.replace(/<[^>]+>/g, "\n");

导出来,发现有一个地方不对劲:理论上来说,每一条消息的结构都应该是一样的,怎么上一条的消息内容与下一条消息之间的换行符数量时多时少?而且有的消息内容还会被折成两行?

问题就在于其中添加的表情包。因为表情其实是一张图片,即一个 <img> 标签,所以也会被替换成换行符。解决方法很简单,在上面的那句话之前添上

data = data.replace(/<img[^>]+>/g, "");

即可,把所有的 <img> 标签都先去掉。

然后数一下,消息和消息之间应有 19 个换行符,名字与时间之间有 4 个换行符,时间与消息内容之间有 5 个换行符。由于4 个换行符是 5 个换行符的字串,5 个换行符又是 19 个换行符的字串,所以要注意排序的问题。

data = data.replace(/\n{19}/g, '\n');
data = data.replace(/\n{5}/g, ',');
data = data.replace(/\n{4}/g, ',');

另外要把顶上的那个“查看更多消息”或“数据加载中...”的字样删去:

data = data.replace(/^\n[^\n]+\n{2}/, "");

删去头尾多加的逗号:

data = data.replace(/^,{2}/, "");
data = data.replace(/,{2}$/, "");

看上去我们好像成功地处理出了有格式的字符串。但是你有没有想过:如果这个元素的 HTML 代码里面本来就有换行符怎么办?结构肯定会大乱,因为这种做法是通过 HTML 标签替换成的换行符来确定格式的。所以我们在替换 HTML 标签的时候不应当使用任何可能出现在这串代码里面的字符。

那应该怎么办呢?

那就换成一个不存在的字符吧。网络上经常有人喜欢用“龘”这个字来用作这个“不存在的字符”,但其实这不妥。这个字在 JS 的 Unicode 表示法中编码为\u9F98,那我们是不是可以动一下手脚……

只要 UTF-8 里面的空间没用完,字符 \uFFFF 就不存在,对吧?它的样子大家很熟悉:�。

于是刚刚的代码整合一下即

var data = document.getElementById('msg-flow-wrapper').innerHTML;
const linebreaker = '\n';
const attrbreaker = ',';

data = data.replace(/<img[^>]+>/g, "");
data = data.replace(/<[^>]+>/g, "\uFFFF");
data = data.replace(/\uFFFF{19}/g, linebreaker);
data = data.replace(/^\uFFFF[^\uFFFF]+\uFFFF{2}/, "");
data = data.replace(/\uFFFF{5}/g, attrbreaker);
data = data.replace(/\uFFFF{4}/g, attrbreaker);
data = data.replace(/\uFFFF/g, "");
data = data.replace(/^,{2}/, "");
data = data.replace(/,{2}$/, "");

然后我们现在把它导出为 .csv 文件。JS 的纯文本文件导出呢,这里给送给大家一个模板:

var element = document.createElement('a');
const blob = new Blob(['你的文件内容']);
element.download = '文件名.扩展名';
element.style = "display: none";
element.href = URL.createObjectURL(blob);
document.body.appendChild(element);
element.click();

setTimeout(function () {
    document.body.removeChild(element);
    window.URL.revokeObjectURL(blob);
}, 100);

于是就可以直接套用进来:

var element = document.createElement('a');
const blob = new Blob([data]);
element.download = '评论区记录.csv';
element.style = "display: none";
element.href = URL.createObjectURL(blob);
document.body.appendChild(element);
element.click();

setTimeout(function () {
    document.body.removeChild(element);
    window.URL.revokeObjectURL(blob);
}, 100);

最终程序实现

// ==UserScript==
// @name         评论记录批量加载
// @namespace    https://www.cnblogs.com/henrylin/
// @version      0.1
// @description  按 Esc 键停止记录
// @author       林洪平
// @match        https://recreation-chat.sdp.101.com/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';
    var x = false;
    var timer = setInterval(function () {
        if (document.querySelector('div._3Bl-4NTv1A._3bGFQ6biuP'))
            document.querySelector('div._3Bl-4NTv1A._3bGFQ6biuP').click();
    }, 100);
    document.onkeydown = function (e) {
        var ev = e || window.event;
        if (ev.keyCode == 27) {//esc
            if (x) { alert('Already done!'); return; }
            x = true;
            alert('Export?');
            clearInterval(timer);
            var data = document.getElementById('msg-flow-wrapper').innerHTML;
            const linebreaker = '\n';
            const attrbreaker = ',';
            data = data.replace(/<img[^>]+>/g, "");
            data = data.replace(/<[^>]+>/g, "\uFFFF");
            data = data.replace(/\uFFFF{19}/g, linebreaker);
            data = data.replace(/^\uFFFF[^\uFFFF]+\uFFFF{2}/, "");
            data = data.replace(/\uFFFF{5}/g, attrbreaker);
            data = data.replace(/\uFFFF{4}/g, attrbreaker);
            data = data.replace(/\uFFFF/g, "");
            data = data.replace(/^,{2}/, "");
            data = data.replace(/,{2}$/, "");

            var element = document.createElement('a');
            const blob = new Blob([data]);
            element.download = '评论区记录.csv';
            element.style = "display: none";
            element.href = URL.createObjectURL(blob);
            document.body.appendChild(element);
            element.click();

            setTimeout(function () {
                document.body.removeChild(element);
                window.URL.revokeObjectURL(blob);
            }, 100);
        }
    }
})();

这样,只要开启脚本,刷新页面,就会开始批量加载聊天记录,按下 Esc 键停止加载并导出。

注意事项

如果刚刚有认真看,就知道这个脚本不是实时记录,而是抓取之前的记录。所以,脚本应保持常闭状态,否则只要打开直播界面,就会开始不间断抓取评论(除非你每次都按下 Esc 键)。

这样导出来的 .csv 文件编码为 UTF-8,可能用 Excel 打开会乱码。建议直接用记事本打开,复制文本到 Excel 并使用分列功能还原表格。

更新

脚本有更新了!请前往 油猴脚本 之 网教通直播评论记录抓取 v2.0

posted on 2020-04-24 13:09  Henrylin666  阅读(838)  评论(0编辑  收藏  举报

导航

回到首页