4950浏览
查看: 4950|回复: 23

[高级教程] B站粉丝计数器 | ESP32学习之旅-Arduino版

[复制链接]
banner_green.png



本系列历史文章目录:



本期给大家带来的是:B 粉计数器(B站粉丝计数器)。
封面.png

感谢B站 UP 主 DaddyPeeka懒希芸 做的编程操作演示视频,他还是个小学生,大家一起关注鼓励下吧~



# 前言

如果大家有在玩一些自媒体平台的话,比如微信公众号、B站、知乎、抖音等,那么相信大家对自己的粉丝数、阅读数或者播放量等相关的数据会比较关注,但是每次手动去查看又比较麻烦。

那么有没有简单一些的方法呢?在创客技术宅眼里,万物皆可自动化!

今天就以 B 站为例,手把手教大家做一个桌面 B 站粉丝计数器!为什么是 B 站呢?因为 B 站做起来最简单啊……(哭……)

先来看一下效果,我用 Arduino 软件分别将程序上传至掌控板(ESP32)和 NodeMCU(ESP8266),看到的效果基本是一样的。本来还想做个外壳的,无奈被疫情隔离在家,设备不多,只能做个简易版了,大家将就看看吧。

掌控板显示效果 - 副本.jpg


NodeMCU显示效果 - 副本.jpg


纳尼!粉丝数竟然那么少!丢脸丢脸,还不赶紧三连支持一下喽~

B站二维码.jpg


干啥啥不行,骗粉第一名!!!

下面开始正式教程。

# 获取 B 站 API

首先用谷歌浏览器(推荐)打开 B 站个人主页,如下图所示,重点关注我圈出来的几个地方,分别是:关注数、粉丝数、点赞数、播放分数,这几个数据的含义,大家一看就明白了,就不解释了。另外还要关注右下角的 UID,这是一串数字,在 B 站中就是你的唯一 ID,这个数字很重要,后面会用到。

B站个人主页.png

然后在键盘上按下 `F12` 或者 `Ctrl+Shift+I` 进入浏览器调试模式,刷新一下 B 站个人主页,然后就会在 Network(网络)标签页下看到一堆返回的数据,从中我们可以看到数据请求的方法(比如 GET),以及对应的 Domain(网址域名)。这里我们要重点关注几行数据,那就是 Domain 为 `api.bilibili.com` 的几行,下图中我已经圈出来了。

B站网页调试.png

我们逐一点进去排查,切换到 Response 标签页,在这里我们看到了 2 个熟悉的数据:following(102)和 follower(133),这不正是我们在 B 站个人主页看到的我的关注数(102)和粉丝数(133)么?

粉丝数Response.png

我们在切换到 Headers 标签页,看到请求的网址(Request URL)如下,请求方法为 GET。

粉丝数Headers.png

我们将这个网址复制出来观察一下:



其中有一段 vmid=224425204,这后面的数字,不正是我们前面提到的 UID 么?后面的 jsonp、callback 应该是对应的一些回调函数,我们将这些删除,只保留前面部分:



并将它复制到浏览器地址栏去访问一下,看到什么了?是不是只留下一堆最简单的数据,里面包含了我们的关注数与粉丝数?而且这对数据是 JSON 格式的。

粉丝数API调用.png

我们将这些数据格式化一下,方便我们查看。这里使用了 VS Code,或者你也可以使用网上其他的 JSON 在线格式化工具,这里不展开了。

粉丝数JSON.png

同样的方法,我们去查一下播放数和获赞数在哪里可以查到。

相信你很快就找到了,如下图所示,先找到数据位置:

播放数与点赞数Response.png

再查看相应的 API 网址:



播放数与点赞数Headers.png

将末尾无关参数删除后:



然后再将这个网址在浏览器中打开,我们就看到了熟悉的播放数(view:9047)和获赞数(likes:57)。

播放数与点赞数API调用.png


在 VS Code 中将数据格式化,方便查看。

播放数与点赞数JSON.png




# 代码编写

## 联网设置

要去获取 B 站的数据,当然要联网啦。我们在程序的最开头,引入一堆联网相关的头文件。这里有一个比较特殊的地方,就是我们同时引入了 ESP32 和 ESP8266 对应的头文件,这样在编译程序的时候,就会根据所选择的的开发板,自动编译对应部分,而不会报错,做到了一套程序兼容两种开发板(ESP32 和 ESP8266)的目的。
  1. #if defined(ESP32)
  2.     #include <WiFi.h>
  3.     #include <HTTPClient.h>
  4. #elif defined(ESP8266)
  5.     #include <ESP8266WiFi.h>
  6.     #include <ESP8266HTTPClient.h>
  7. #else
  8.     #error "Please check your mode setting,it must be esp8266 or esp32."
  9. #endif
  10. #include <Wire.h>
  11. // Wi-Fi
  12. const char *ssid = "wifi_name";
  13. const char *password = "wifi_password";
  14. void setup()
  15. {
  16.     Serial.begin(115200);
  17.     WiFi.begin(ssid, password);
  18.     while (WiFi.status() != WL_CONNECTED)
  19.     {
  20.         delay(500);
  21.         Serial.print(".");
  22.     }
  23.     Serial.println("");
  24.     Serial.println("WiFi connected");
  25. }
  26. void loop()
  27. {
  28. }
复制代码
既然要联网,就需要定义你的网络名称和密码,对应修改就行:

  1. const char *ssid = "wifi_name";
  2. const char *password = "wifi_password";
复制代码


上面连接 Wi-Fi 的程序,就不展开了,基本是标准写法,在 WiFi 这个库里,找一下例程复制一下就行。

## 获取粉丝数

连接上网络之后,就可以去获取粉丝数了,除了上面已经引入的头文件 HTTPClient,这个库文件可以用来获取网站上的数据。我们还需要用到的 ArduinoJson 这个 JSON 解析库,可以用来解析网站返回的 JSON 数据,并且初始化一个 JSON 解析对象 jsonBuffer。

ArduinoJson 目前比较流行的有两个版本:V5 和 V6,V5 比较经典和稳定,V6 比较新。两个版本使用起来稍有差异,博主这里使用的是 V5 版本。
  1. #include <ArduinoJson.h>
  2. DynamicJsonBuffer jsonBuffer(256); // ArduinoJson V5
复制代码


另外,我们还需要定义 API 的网址、以及初始化粉丝数,单独拎出来是方便大家修改。注意,这里我们网址的访问方式为 http,而不是 https,因为 https 的话,代码会复杂一些

  1. // bilibili api: follower, view, likes
  2. String UID = "224425204";
  3. String followerUrl = "http://api.bilibili.com/x/relation/stat?vmid=" + UID;   // 粉丝数
  4. long follower = 0;   // 粉丝数
复制代码


然后再写一个获取粉丝数的函数 getFollower(String url),只要传入对应的 API 网址,就能利用 HTTPClient 中的 GET 方法,获取相应的数据,然后再用 ArduinoJson 库进行解析。

  1. void getFollower(String url)
  2. {
  3.     HTTPClient http;
  4.     http.begin(url);
  5.     int httpCode = http.GET();
  6.     Serial.printf("[HTTP] GET... code: %d\n", httpCode);
  7.     if (httpCode == 200)
  8.     {
  9.         Serial.println("Get OK");
  10.         String resBuff = http.getString();
  11.         // ---------- ArduinoJson V5 ----------
  12.         JsonObject &root = jsonBuffer.parseObject(resBuff);
  13.         if (!root.success())
  14.         {
  15.           Serial.println("parseObject() failed");
  16.           return;
  17.         }
  18.         follower = root["data"]["follower"];
  19.         Serial.print("Fans: ");
  20.         Serial.println(follower);
  21.     }
  22.     else
  23.     {
  24.         Serial.printf("[HTTP] GET... failed, error: %d\n", httpCode);
  25.     }
  26.     http.end();
  27. }
复制代码


最后再在 setup() 中调用一下,看看效果:

  1. void setup()
  2. {
  3.     // other setup codes ...
  4.     getFollower(followerUrl);
  5. }
复制代码


打开 Arduino 串口监视器,返回数据正常,说明成功了。

串口监视器-粉丝数.png


## 获取播放数与获赞数

这个与获取粉丝数的原理一样,不再赘述,直接看代码:

  1. // bilibili api: follower, view, likes
  2. String UID = "224425204";
  3. String followerUrl = "http://api.bilibili.com/x/relation/stat?vmid=" + UID;   // 粉丝数
  4. String viewAndLikesUrl = "http://api.bilibili.com/x/space/upstat?mid=" + UID; // 播放数、点赞数
  5. long follower = 0;   // 粉丝数
  6. long view = 0;   // 播放数
  7. long likes = 0;   // 获赞数
  8. void setup()
  9. {
  10.     // other setup codes ...
  11.     getFollower(followerUrl);
  12.     getViewAndLikes(viewAndLikesUrl);
  13. }
  14. void getViewAndLikes(String url)
  15. {
  16.     HTTPClient http;
  17.     http.begin(url);
  18.     int httpCode = http.GET();
  19.     Serial.printf("[HTTP] GET... code: %d\n", httpCode);
  20.     if (httpCode == 200)
  21.     {
  22.         Serial.println("Get OK");
  23.         String resBuff = http.getString();
  24.         // ---------- ArduinoJson V5 ----------
  25.         JsonObject &root = jsonBuffer.parseObject(resBuff);
  26.         if (!root.success())
  27.         {
  28.           Serial.println("parseObject() failed");
  29.           return;
  30.         }
  31.         likes = root["data"]["likes"];
  32.         view = root["data"]["archive"]["view"];
  33.         Serial.print("Likes: ");
  34.         Serial.println(likes);
  35.         Serial.print("View: ");
  36.         Serial.println(view);
  37.     }
  38.     else
  39.     {
  40.         Serial.printf("[HTTP] GET... failed, error: %d\n", httpCode);
  41.     }
  42.     http.end();
  43. }
复制代码


打开 Arduino 串口监视器,返回数据正常,说明成功了。

串口监视器-播放数与获赞数.png


## OLED屏:数据显示

获取到数据之后,我们总不能一直在串口监视器里面查看吧,所以我们利用一个 OLED 12864 屏幕,将数据显示到屏幕上。代码如下:

  1. #include <U8g2lib.h>
  2. // 1.3' OLED12864
  3. U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
  4. // 0.96' OLED12864
  5. //U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
  6. void setup()
  7. {
  8.     // other setup codes ...
  9.     u8g2.begin();
  10.     u8g2.enableUTF8Print();
  11.     u8g2.setFont(u8g2_font_wqy12_t_gb2312b);
  12.     u8g2.setFontPosTop();
  13.     u8g2.clearDisplay();
  14. }
  15. void loop()
  16. {
  17.     u8g2.firstPage();
  18.     do
  19.     {
  20.         display(follower, likes, view);
  21.     } while (u8g2.nextPage());
  22. }
  23. void display(long follower, long likes, long view)
  24. {
  25.     u8g2.clearDisplay();
  26.     u8g2.setCursor(5, 2);
  27.     u8g2.print("B站粉丝计数器");
  28.     u8g2.setCursor(5, 20);
  29.     u8g2.print("粉丝数:" + String(follower));
  30.     u8g2.setCursor(5, 36);
  31.     u8g2.print("获赞数:" + String(likes));
  32.     u8g2.setCursor(5, 52);
  33.     u8g2.print("播放数:" + String(view));
  34. }
复制代码

这里我们用到了 u8g2 库,它是专门用来控制各种显示屏的。在 setup() 中设置了相应的显示参数,定义了一个显示函数 display(long follower, long likes, long view),可以同时传入相应的数据,并显示出来。然后在 loop() 中调用,看看显示效果。



## 定时器:定时获取数据

有了前面的步骤,我们已经可以正常获取数据并且显示数据了,但是你们可能会问:为什么前面调试时把获取数据的函数放在 setup() 中呢?而不是 loop() 中呢?这样每次程序开机只能读取一次,岂不是太麻烦了?放在 loop() 不就可以不断读取并且更新了么?

不是这样的,如果我们放在 loop() 中,不做其他干预,不断去读取的话,就会由于访问频率太高,触发 B 站的保护机制。不信你就不断刷新 B 站的某个 API 地址试试,相信你一定会看到如下“该页面无法访问”的页面。

无法访问.png


所以呢,我们就需要定时器,隔一段时间去获取一次数据,而且我们也不是大 V,数据变化不会很快,所以每隔 10 分钟去获取一次数据就绰绰有余了。直接看代码:

  1. #include <Ticker.h>
  2. Ticker timer;
  3. int count = 0;
  4. boolean flag = true;
  5. void setup()
  6. {
  7.     // other setup codes ...
  8.     timer.attach(600, timerCallback); // 每隔10min
  9. }
  10. void loop()
  11. {
  12.     while (flag)
  13.     {
  14.         if (count == 0)
  15.         {
  16.             // display data
  17.             Serial.println("count = 0, display data");
  18.             u8g2.firstPage();
  19.             do
  20.             {
  21.             display(follower, likes, view);
  22.             } while (u8g2.nextPage());
  23.             flag = false;
  24.         }
  25.         else if (count == 1)
  26.         {
  27.             // get follower
  28.             Serial.println("count = 1, get follower");
  29.             getFollower(followerUrl);
  30.             flag = false;
  31.         }
  32.         else if (count == 2)
  33.         {
  34.             // get view and likes
  35.             Serial.println("count = 2, get view and likes");
  36.             getViewAndLikes(viewAndLikesUrl);
  37.             flag = false;
  38.         }
  39.     }
  40. }
  41. void timerCallback()
  42. {
  43.     count++;
  44.     if (count == 3)
  45.     {
  46.         count = 0;
  47.     }
  48.     flag = true;
  49. }
复制代码

我们使用了 ESP32 和 ESP8266 自带的定时器库 Ticker。设置一个 count 变量,根据 count = 0、1、2 分别做不通的工作:显示屏刷新数据、读取粉丝数、读取播放数和获赞数。用 flag 变量去标记是否执行相应的功能。 这两个参数在定时器回调函数 timerCallback() 每隔 10 分钟就会改变一次,然后就会在 loop() 中触发相应的功能。

至此,B站粉丝计数器就制作完成了,最终效果,大家可以在文章开头看一看。

# 附:掌控板 mPython 图形化程序

这是一个彩蛋,同样的功能,用掌控板 mPython 图形化编程软件也能实现,用图形化是不是看起来简单多了?
mPython图形化程序.png


# 代码下载

关注微信公众号“铁熊玩创客”,回复“B站”获取完整代码,三连支持一下哦。

微信二维码引导2.png

铁熊  初级技神
 楼主|

发表于 2020-2-23 12:21:38

Shuuei 发表于 2020-2-23 10:58
学习了!原来还可以这样找到对应信息!我是直接wget一个html页面然后查找出数据用bash脚本来得到最后数据来 ...

针对没有提供 API 的网站,可以用这种方式的。或者直接在代码里用正则表达式筛选有效信息
回复

使用道具 举报

Shuuei  中级技师

发表于 2020-2-23 12:48:32

铁熊 发表于 2020-2-23 12:21
针对没有提供 API 的网站,可以用这种方式的。或者直接在代码里用正则表达式筛选有效信息 ...

收到!对了,我发现如果用wget来每秒下载html页面的话知乎没有对我进行什么措施... 是不是api接口的话不一样啊?
回复

使用道具 举报

铁熊  初级技神
 楼主|

发表于 2020-2-23 12:59:54

Shuuei 发表于 2020-2-23 12:48
收到!对了,我发现如果用wget来每秒下载html页面的话知乎没有对我进行什么措施... 是不是api接口的话不 ...

看网站有没有做反爬虫措施的,一般网站连续不断访问的话,会短暂封 IP 的
回复

使用道具 举报

DFrJ5KYVQaH  中级技匠

发表于 2020-2-14 10:59:35

收藏,以后学习中
回复

使用道具 举报

gray6666  初级技神

发表于 2020-2-14 18:04:55

铁熊出品必属精品,果断收藏
回复

使用道具 举报

白吃白给  中级技师

发表于 2020-2-15 10:32:34

一键三连
回复

使用道具 举报

帅猫  高级技师

发表于 2020-2-15 20:52:50

所有API地址失效是什么情况
回复

使用道具 举报

帅猫  高级技师

发表于 2020-2-15 21:52:51

帅猫 发表于 2020-2-15 20:52
所有API地址失效是什么情况

手动撤回,换成自己的UID就可以了
回复

使用道具 举报

rzegkly  版主

发表于 2020-2-16 08:35:07

精品收藏
回复

使用道具 举报

铁熊  初级技神
 楼主|

发表于 2020-2-16 12:20:21

帅猫 发表于 2020-2-15 21:52
手动撤回,换成自己的UID就可以了

互相关注一波~
回复

使用道具 举报

且歌且行  中级技师

发表于 2020-2-17 15:20:02

铁熊老师是超级牛咖~看看掌控板显示的数据不复杂,实际要调用出来真不简单。突然发现这个坛子没有收藏~
回复

使用道具 举报

帅猫  高级技师

发表于 2020-2-17 15:26:40

又发现了:我连点器都上了还是没能把API地址刷到安全机制......
回复

使用道具 举报

铁熊  初级技神
 楼主|

发表于 2020-2-17 16:44:31

帅猫 发表于 2020-2-17 15:26
又发现了:我连点器都上了还是没能把API地址刷到安全机制......

那你试试在 loop() 里不断刷新试试,连点器太慢了
回复

使用道具 举报

铁熊  初级技神
 楼主|

发表于 2020-2-17 16:45:00

且歌且行 发表于 2020-2-17 15:20
铁熊老师是超级牛咖~看看掌控板显示的数据不复杂,实际要调用出来真不简单。突然发现这个坛子没有收藏~ ...

谢谢夸奖
回复

使用道具 举报

kylinpoet  高级技匠

发表于 2020-2-17 23:31:30

这个好,必须支持。
回复

使用道具 举报

风悠扬0539  中级技师

发表于 2020-2-18 10:53:30

十分的拜膜啊
回复

使用道具 举报

#嘉诚欧巴#  高级技师

发表于 2020-2-18 16:09:39

回复

使用道具 举报

Shuuei  中级技师

发表于 2020-2-23 10:58:52

学习了!原来还可以这样找到对应信息!我是直接wget一个html页面然后查找出数据用bash脚本来得到最后数据来着...
回复

使用道具 举报

gada888  版主

发表于 2020-2-23 11:44:30

不错的分享
回复

使用道具 举报

Shuuei  中级技师

发表于 2020-2-23 14:07:24

铁熊 发表于 2020-2-23 12:59
看网站有没有做反爬虫措施的,一般网站连续不断访问的话,会短暂封 IP 的 ...

奥... 我就一个wget命令请求他们的HTML页面,对方应该分不清我是爬虫还是人类吧...
回复

使用道具 举报

12下一页
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2021 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail