全国到底有多少人在看直播?我用Node写了个爬虫统计了一下

25,275

火爆的直播

近几年直播可以说是相当火爆,但是直播间里动辄成百上千万的人气让人禁不住产生疑惑,怎么会有这么高的人气?难道全国人民都在看直播?

为了解决这个困扰我已久的问题,我专门去学习了node爬虫的相关知识,下面就跟大家分享一下。

斗鱼

先从平常看的最多的斗鱼开始,后来也证明斗鱼是最简单的一个。

通过去分析斗鱼的网站发现,斗鱼有一个全部分类的页面https://www.douyu.com/directory

这个页面已经统计好了所有分类下的总人气,接下来要做的就是通过DOM解析操作拿到每个分类下的总人数,然后把这些人数相加就可以。

在这里用到了cheerio这个包,这个包就相当于服务端的jQuery,用在服务器端需要对DOM进行操作的地方,使用方法也跟jQuery差不多。

因为斗鱼的网页经过了gzip压缩,还要用到zlib,这个包的作用是解压缩。

// 引入https模块
const https = require('https')
// zlib包,用于解压缩
const zlib = require('zlib')
// cheerio包,提供了类似jQuery的功能
const cheerio = require("cheerio");

function douyu () {
    // 创建请求对象
    let req = https.request('https://www.douyu.com/directory', res => {
        // 接收数据
        let chunks = []
        // 监听到数据就存储起来
        res.on('data', chunk => {
            chunks.push(chunk)
        })
        // 数据传输结束
        res.on('end', () => {
            // 拼接数据
            var buffer = Buffer.concat(chunks)
            // 使用zlib解压缩
            zlib.gunzip(buffer, function (err, decoded) {
                // gzip解压后的html文本
                let html = decoded.toString()
                // 使用cheerio解析html
                let $ = cheerio.load(html)
                // 获取包含直播数据的元素列表
                let list = $('#allCate .layout-Module-container .layout-Classify-list .layout-Classify-item .layout-Classify-card')
                // 解析dom,取出标签中的数据
                const dataList = {}
                Array.prototype.map.call(list, item => {
                    let key = '', value = ''
                    item.children.forEach(childrenItem => {
                        if (childrenItem.name === 'strong') {
                            key = childrenItem.children[0] ? childrenItem.children[0].data : '空'
                        } else if (childrenItem.name === 'div') {
                            value = $(childrenItem).find('span').html()
                            value = unescape(value.replace(/&#x/g, '%u').replace(/;/g, ''))
                        }
                    })
                    dataList[key] = value
                })
                // 相加得出总人数
                let total = 0
                for (let key in dataList) {
                    let value = dataList[key]
                    // 处理单位为万的数字
                    if (value.indexOf('万') != -1) value = Number.parseFloat(value) * 10000
                    total += Number.parseFloat(value) ? Number.parseFloat(value) : 0
                }
                console.log(`斗鱼:${total}`)
            })
        })
    })
    // 发送请求
    req.end()
}

虎牙

查看虎牙的网站发现并没有像斗鱼一样提供统计的分类列表,但是找到了该网站查询直播间列表信息的接口https://www.huya.com/cache.php?m=LiveList&do=getLiveListByPage&tagAll=0&page=1。通过该接口可以拿到所有直播间的信息,其中就包含了每个直播间的人数,接下来就是把每个直播间的人数相加就可以得到该平台的总人数。

// 引入https模块
const https = require('https')

function huya {
    // 初始化总数
    let total = 0
    // 初始化总页数
    let totalPage = 1
    // 初始化当前页数
    let currentPage = 1
    // 与斗鱼一样的开始获取、处理数据
    huyaGetData(currentPage)
    function huyaGetData(currentPage) {
        let req = https.request(`https://www.huya.com/cache.php?m=LiveList&do=getLiveListByPage&tagAll=0&page=${currentPage}`, res => {
            const chunks = []
            res.on('data', chunk => {
                chunks.push(chunk)
            })
            res.on('end', () => {
                const data = JSON.parse(Buffer.concat(chunks).toString('utf-8')).data
                const dataList = data.datas
                // 拿到总页数
                totalPage = data.totalPage
                // 累加直播间的人数
                total = dataList.reduce((total, item) => {
                    return total + Number.parseInt(item.totalCount)
                }, total)
                // 获取下一页的数据
                currentPage += 1
                if (currentPage < totalPage) {
                    huyaGetData(currentPage)
                } else {
                    console.log(`虎牙:${total}`)
                }
            })
        })
        req.end()
    }
}

哔哩哔哩

哔哩哔哩页面中也有与虎牙相似的全部直播间查询接口,统计方法与虎牙差不多,这里就不再赘述了。

YY

在YY的网页中并没有提供全部直播间的查询接口,只提供了单一分类的查询接口https://www.yy.com/more/page.action?biz=sing&subBiz=idx&page=3&moduleId=308&pageSize=60,而每个分类查询要传过去的参数值都不一样,又没有任何规律可循,所以现在要拿到每个分类查询所需要的信息,之后的查询就跟虎牙一样了。

那现在要做的就是拿到接口所需信息,通过分析页面发现每个分类直播的列表页都有一个pageInfo的全局变量,这里边就包含了查询所需的所有信息。我们可以拿到这些分类页面的html文件,然后解析出其中的pageInfo变量。但是这里为了演示,我们使用另一个方法Selenium来解决这个问题。

Selenium是一个Web应用的自动化测试框架,用在爬虫中可以使用它打开浏览器,用代码去模拟人的真实操作去爬取需要的信息,突破反爬虫手段的限制。

要使用Selenium,首先要根据平台去下载对应的webdriver这里chromeDriver的下载地址,根据自己电脑上Chrome的版本下载对应的chromeDriver,下载好之后复制到项目根目录。别的浏览器可以自行去找对应的包下载。

然后在项目中安装selenium-webdriver包的依赖。

const { Builder, By } = require('selenium-webdriver')

async function getYYPageInfoList() {
    // 构建WebDriver对象
    let driver = await new Builder().forBrowser('chrome').build();
    // 打开网页
    await driver.get('https://www.yy.com/catalog');
    // 获取分类标签列表
    let aList = await driver.findElements(By.css('.w-video-module-cataloglist a'))
    // 获取分类页面地址列表
    let hrefList = []
    for (let i = 0; i < aList.length - 1; i++) {
        let href = await aList[i].getAttribute('href')
        hrefList.push(href)
    }
    // 打开分类页面
    for (let i = 0; i < hrefList.length - 1; i++) {
        await driver.get(hrefList[i])
        // 在页面中执行 return pageInfo, 取到pageInfo
        driver.executeScript('return pageInfo').then(function (obj) {
            // 存储pageInfo信息
            pageInfoList.push(obj)
        })
    }
    // 退出浏览器
    driver.quiet()
    return pageInfoList
}

统计结果

统计结果的地址:http://liupenglong.com/live/index.html(每五分钟统计一次)

可以看到目前仅仅统计出了斗鱼、虎牙、哔哩哔哩、YY的数据,总人数已经超过全国总人口了,算上别的没有统计上的。。。结果可想而知,估计全球人民都在看中国的直播了。

接下来有时间会把别的平台的也统计出来,最终希望能推算出来真正在看直播的人数,大家如果有什么好的想法可以评论交流一下。