本文借鉴了@平胸小仙女的知乎回复 https://www.zhihu.com/question/36081767


写在前面:

文章有点长,操作有点复杂,需要代码的直接去文末即可。想要学习的需要有点耐心。当我理清所有逻辑后,我抑郁的(震惊的)发现,只需要改下歌曲ID就可以爬取其他任意歌曲的评论了!生成的TXT文件在程序同一目录。

有基础的可能觉得我比较啰嗦,因为我写博客一是为了记录下知识点,在遗忘的时候可以查看回顾下。二是因为我学编程的时候,搜到的很多帖子都是半残的,有些人是为了引流到自己的公众号,有些人干脆是骗流量,有的帖子质量很好,但是对小白不太友好,没有相关基础很难复现。这样就在搜索上浪费了很多时间。我写博客尽量把每一步操作都记录下来,这样别人能复现我的成果,对着一个可以运行的程序,才会有学习的欲望。先学会操作,再去弄懂原理,然后就可以写出自己的程序了!至于技术原理,网上的大牛太多了,想学的话很容易学到。我希望看到我的博客的小白,不至于在操作上浪费太多时间,能有时间用到学习技术原理上。

当然也有一部分是猎奇的,希望直接复制就能运行,这样的呢,给我点个赞就行啦!毕竟谁不是从白嫖怪一步一步成长起来的呢,好奇心是最好的老师。


可能遇到的麻烦:

ModuleNotFoundError: No module named ‘Crypto‘ 踩坑

用到的工具:

手把手教你下载安装配置Fiddler 和 Fiddler Everywhere

PyCharm中文指南2.0

词云清洗用到的stopwords.txt:

Python文本分析之常用最全停用词表(stopwords)

词云清洗的分析参见:

Python爬取你好李焕英豆瓣短评并利用stylecloud制作更酷炫的词云图


F12大法开启:

打开网易云网页版,找到一首喜爱的音乐,我选择的是柏松的《世间美好与你环环相扣》,然后开启F12大法!

现在开始作法:按F12,选中network(也可以右键–>检查–>network),然后F5刷新页面,就可以看到network的活动:


点击评论的下一页,发现只有评论会刷新成下一页的评论,网页的URL没有变,说明向服务器发送的请求是XHR(XMLHttpRequest)对象。所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)。XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

完全不懂的小白可以学这个入下门:

https://www.w3cschool.cn/ajax/ajax-xmlhttprequest-create.html


这样就缩小了排查范围,选中XHR,会发现少了很多,但是还有十多条,这就没办法了,可以自己瞎猜,我是一个一个点进去,然后选中Response,这样一个个的找到包含评论的数据包的。当然你操作的时候页面跟我的不太一样,因为这些框框都是可以拖动的,需要看哪一部分的时候可以拖动使得需要的部分变大,突出的显示出来。


对比上图,就是这个了:

点进去,选中Headers,就可以看到Request Headers

以及两个参数params和encSecKey

在Response里可以看到当前页的所有评论,但也只是当前页的,其他页的评论如何获取呢?

点击下一页的时候只刷新评论,而不会重新加载页面。那么既然这个进程是向服务器发起获取评论的请求,我们点击下一页看看这个进程会有什么变化。

第一页:

第二页:

第三页:

可以发现只有这两个参数 params和encSecKey会随之改变,进而Response也刷新成了下一页的评论。由此得出结论:这是通过不同页面的params以及encSecKey参数的不同来向服务器发起获取相应评论的请求。

js线上调试:

因此,下一步就是弄清楚不同页面的params以及encSecKey参数是如何改变的,这样我们就能在爬虫程序中通过生成随页面变化的这两个参数,发送至服务器获取相应的Response。

这两个参数一看就是js加密的,而这个进程的initiator是core_68ac1b3aadf40a20caba599a0ab2365d.js

一般这样的js都是没法看的,因此就点进去并save as将core_68ac1b3aadf40a20caba599a0ab2365d.js下载到本地查看。以下简称core.js。

切记切记!!!一定要先点这个美化按钮!!!然后再保存!!!没有一个教程帖子告诉我这点,然后我就傻不拉叽的直接保存了!!!!当然你也可以不点不美化,毕竟我第一次没有美化也做出来了。

右键,Save as 就可以保存了,选个自己知道的路径,等下有用!

打开core.js,我用的是Sublime Text。

这就是你不点美化的后果!!!找不同吧!!!

这是美化后的:

搜索params和encSecKey,查找这两个参数:

可以看到这两个参数都是bWv4z函数中的变量,(PS:细心的读者会注意到后面的截图不再是bWv4z,其他参数也有微小变化,是因为我出去吃了个饭,回来后core.js已经失效,网易云更新了这个,所以我重新下载编辑了,不影响学习。)而这个函数也就是window.asrea这个函数。暂且不管window.asrea这个函数是如何实现的,可以看到它有四个参数,先不管这四个参数是哪来的,可以先把它们输出到console看一下,这时候就需要线上调试js。首先将本地的core.js文件添加几行代码,以便使这四个参数显示出来:

把第2、3、4个参数先注释掉,因为要一个一个的获取。

接着要用到Fiddle Everywhere了,下载安置配置见本文开头链接。

点击打开AutoResponder,然后点Add New Rule

弹出

先看MATCH:

回到这里,这次点Open in new tab

在弹出的页面复制URL,注意不是美化后的,是美化前的。可以点叉把美化后的关了,再右键Open in new tab

复制URL

注意不是这个:

把复制的URL粘贴到MATCH

然后看ACTION

选中之前保存的core.js,就是core_68ac1b3aadf40a20caba599a0ab2365d.js

最后点Add to Queue

这样就完成了:

这步实际就是用本地修改过得core.js文件替换服务器的core.js对请求作出响应。

这些设置完之后,清除浏览器缓存,刷新页面,就可以在console里面看到输出的参数了。

什么?没看到?清除浏览器缓存啊,重启浏览器啊,重启Fiddle Everywhere啊,还看不到?

看不到就对了,因为在Sublime Text 中编辑完后没有保存啊!

保存下,再刷新:

如图,分别是第一页和第二页的第一个参数值

第一页:

第二页:

在这里,我遇到了最大的难题,至今未解决。

这是我试了N久得到的一些数据,至今没有找到规律用数学公式写出来。可能是网抑云的新的反爬方法吧。有人要试的话,记得每次都要F5刷新,有时候刷新一次还不行,比如在获取2万多页后的数据时,每次显示还不一样,我又清空浏览器缓存刷新几次,数据不变了才记录下来。因为页码是不能选的,只能这么一下下点击。

数据:

都做到这了,总不能半途而废吧,我就参考了其他人的数据:


我发现网上所有的教程截图全是这样的数据,偏偏我的不一样?????应该是网易云反爬的措施吧。

这样的数据好处理:rid就是R_SO_4_加上歌曲的id,offset就是(评论页数-1) * 20,total在第一页是true,其余是false。

百思不得其解怪来了!!!后面的代码用的就是这几个以前的参数,程序竟然跑起来了!!!

不管了,继续学原理!

按这样的方式可以得到其余三个参数:

这样,别忘了保存,然后清空浏览器缓存,再刷新:

第二个参数值:

010001

第三个参数值:

00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7

第四个参数值:

0CoJUm6Qyw8W8jud


再次警告:

下载到本地的core.js文件要赶快使用,我中午出去吃了个饭,回来接着写的时候发现已经失效了



你看,参数已经发生了变化,快使用!噢应该着急的是我,我应该快截图,不然前面的步骤又要再演示一次了。


分析加密函数:

可以发现,只有第一个参数随页面变化,其余三个参数都是不变的常量。至此 ,这四个参数我们都能够在程序中通过代码生成了。

那么,现在我们只要知道函数window.asrsea如何处理的就可以了,定位到这个函数:

我是在Sublime Text这么find来定位的,console中也可以,但是我用不太好,就在这find了:

这是结果:


纳尼?d? 什么鬼?函数?
再find:

然后搜到了好多好多的d,比找对象还难找!这谁受得了啊!

去隔壁控制台搜下:

回车!啪!应声而出,原来是你啊!

读代码,里面还有个b函数,console一下:

不行,没办法了,find吧,还好已经知道b有两个参数了,而且还是个小写的 b :

同样的办法揪出来c函数:

好了,bcd函数都就出来的啊,开始读吧!

得,变量也是abcdef,果然网抑云啊!我抑郁了!!!

首先d函数:

function d(d, e, f, g) {
var h = {}
, i = a(16);
return h.encText = b(d, g),
h.encText = b(h.encText, i),
h.encSecKey = c(i, e, f),
h
}

研究过后,你就会发现:i 就是一个长度为16的随机字符串,既然是随机的,就直接让他等于16个F了。这个encText明显就是params,encSecKey明显就是encSecKey。而b函数就是一个AES加密,encText的获得经过了两次加密,第一次对 d 也就是第一个参数加密,key是第四个参数,第二次对第一次加密结果进行加密,key是 i 。在b函数中我们可以看到:


  • 小注解:高级加密标准(AES,Advanced Encryption
    Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密和解密用相同的密钥。

function b(a, b) {
var c = CryptoJS.enc.Utf8.parse(b)
, d = CryptoJS.enc.Utf8.parse("0102030405060708")
, e = CryptoJS.enc.Utf8.parse(a)
, f = CryptoJS.AES.encrypt(e, c, {
iv: d,
mode: CryptoJS.mode.CBC
});
return f.toString()
}

密钥偏移量iv是0102030405060708,模式是CBC,那么就不难写出对于第一个参数的加密了。

接下来是第二个参数encSecKey,你会发现c函数是一个RSA加密:

function c(a, b, c) {
var d, e;
return setMaxDigits(131),
d = new RSAKeyPair(b,"",c),
e = encryptedString(d, a)
}

  • RSA加密是一种非对称加密。可以在不直接传递密钥的情况下,完成解密。这能够确保信息的安全性,避免了直接传递密钥所造成的被破解的风险。是由一对密钥来进行加解密的过程,分别称为公钥和私钥。两者之间有数学相关,该加密算法的原理就是对一极大整数做因数分解的困难性来保证安全性。通常个人保存私钥,公钥是公开的(可能同时多人持有)。

这里传入 c 的三个参数 i 是16个F,e 是第二个参数,f 是第三个参数,全部是固定的值,那么无论歌曲id或评论页数如何变化,这个encSecKey都不随之发生变化,所以这个encSecKey对我们来说就是个常量,抄一个下来就是可以使用的。

至此,我们就能在程序中通过代码获取params和encSecKey这两个参数了。

完整代码:

  1 # -*- coding:utf-8 -*-
2
3 import urllib.request
4 import http.cookiejar
5 import urllib.parse
6 import json
7 import time
8 import codecs
9 from Crypto.Cipher import AES
10 import base64
11 import os
12
13
14 class music:
15
16 #初始化
17 def __init__(self):
18 #设置代理,以防止本地IP被封
19 self.proxyUrl = "http://202.106.16.36:3128"
20 #request headers,这些信息可以在ntesdoor日志request header中找到,copy过来就行
21 self.Headers = {
22 'Accept': "*/*",
23 'Accept-Language': "zh-CN,zh;q=0.9",
24 'Connection': "keep-alive",
25 'Host': "music.163.com",
26 'User-Agent':"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36"
27
28 }
29
30 # 使用http.cookiejar.CookieJar()创建CookieJar对象
31 self.cjar = http.cookiejar.CookieJar()
32 # 使用HTTPCookieProcessor创建cookie处理器,并以其为参数构建opener对象
33 self.cookie = urllib.request.HTTPCookieProcessor(self.cjar)
34 self.opener = urllib.request.build_opener(self.cookie)
35 # 将opener安装为全局
36 urllib.request.install_opener(self.opener)
37 #第二个参数
38 self.second_param = "010001"
39 #第三个参数
40 self.third_param = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
41 #第四个参数
42 self.forth_param = "0CoJUm6Qyw8W8jud"
43
44 def get_params(self, page):
45 #获取encText,也就是params
46 iv = "0102030405060708"
47 first_key = self.forth_param
48 second_key = 'F' * 16
49
50
51 if page == 0:
52 first_param = '{rid:"", offset:"0", total:"true", limit:"20", csrf_token:""}'
53 else:
54 offset = str((page - 1) * 20)
55 first_param = '{rid:"", offset:"%s", total:"%s", limit:"20", csrf_token:""}' % (offset, 'false')
56
57
58
59 self.encText = self.AES_encrypt(first_param, first_key, iv)
60 self.encText = self.AES_encrypt(self.encText.decode('utf-8'), second_key, iv)
61 return self.encText
62
63 def AES_encrypt(self, text, key, iv):
64 #AES加密
65 pad = 16 - len(text) % 16
66 text = text + pad * chr(pad)
67 encryptor = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
68 encrypt_text = encryptor.encrypt(text.encode('utf-8'))
69 encrypt_text = base64.b64encode(encrypt_text)
70 return encrypt_text
71
72 def get_encSecKey(self):
73 #获取encSecKey
74 encSecKey = "257348aecb5e556c066de214e531faadd1c55d814f9be95fd06d6bff9f4c7a41f831f6394d5a3fd2e3881736d94a02ca919d952872e7d0a50ebfa1769a7a62d512f5f1ca21aec60bc3819a9c3ffca5eca9a0dba6d6f7249b06f5965ecfff3695b54e1c28f3f624750ed39e7de08fc8493242e26dbc4484a01c76f739e135637c"
75 return encSecKey
76
77 def get_json(self, url, params, encSecKey):
78 # post所包含的参数
79 self.post = {
80 'params': params,
81 'encSecKey': encSecKey,
82 }
83 # 对post编码转换
84 self.postData = urllib.parse.urlencode(self.post).encode('utf8')
85 try:
86 #发出一个请求
87 self.request = urllib.request.Request(url,self.postData,self.Headers)
88 except urllib.error.HTTPError as e:
89 print(e.code)
90 print(e.read().decode("utf8"))
91 #得到响应
92 self.response = urllib.request.urlopen(self.request)
93 #需要将响应中的内容用read读取出来获得网页代码,网页编码为utf-8
94 self.content = self.response.read().decode("utf8")
95 #返回获得的网页内容
96 return self.content
97
98
99
100
101 def get_hotcomments(self, url):
102 #获取热门评论
103 params = self.get_params(1)
104 encSecKey = self.get_encSecKey()
105 content = self.get_json(url, params, encSecKey)
106 json_dict = json.loads(content)
107 hot_comment = json_dict['hotComments']
108 f = open('HotComments.txt', 'w', encoding='utf-8')
109 for i in hot_comment:
110 #将评论输出至txt文件中
111 time_local = time.localtime(int(i['time'] / 1000)) # 将毫秒级时间转换为日期
112 dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
113 f.write('用户: ' + i['user']['nickname'] + '\n')
114 f.write('点赞数: ' + str(i['likedCount']) + '\n')
115 f.write('发表时间: ' + dt + '\n')
116 f.write('评论: ' + i['content'] + '\n')
117 f.write('-' * 40 + '\n')
118 f.close()
119
120
121
122 def get_allcomments(self, url):
123 #获取全部评论
124 params = self.get_params(1)
125 encSecKey = self.get_encSecKey()
126 content = self.get_json(url, params, encSecKey)
127 json_dict = json.loads(content)
128 comments_num = int(json_dict['total'])
129 f = open('AllComments.txt', 'w', encoding='utf-8')
130 present_page = 0
131 if (comments_num % 20 == 0):
132 page = comments_num / 20
133 else:
134 page = int(comments_num / 20) + 1
135 print("共有%d页评论" % page)
136 print("共有%d条评论" % comments_num)
137 # 逐页抓取
138 for i in range(page):
139 params = self.get_params(i + 1)
140 encSecKey = self.get_encSecKey()
141 json_text = self.get_json(url, params, encSecKey)
142 json_dict = json.loads(json_text)
143 present_page = present_page + 1
144 for i in json_dict['comments']:
145 # 将评论输出至txt文件中
146 time_local = time.localtime(int(i['time'] / 1000))# 将毫秒级时间转换为日期
147 dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
148 f.write('用户: ' + i['user']['nickname'] + '\n')
149 f.write('点赞数: ' + str(i['likedCount']) + '\n')
150 f.write('发表时间: ' + dt + '\n')
151 f.write('评论: ' + i['content'] + '\n')
152 f.write('-' * 40 + '\n')
153 print("第%d页抓取完毕" % present_page)
154
155 f.close()
156
157 mail = music()
158 mail.get_hotcomments("https://music.163.com/weapi/v1/resource/comments/R_SO_4_1363948882?csrf_token")
159 mail.get_allcomments("https://music.163.com/weapi/v1/resource/comments/R_SO_4_1363948882?csrf_token")

执行:

2万页!!!当然是我去睡觉让Python慢慢的爬啦!
早上起来看到:

打开看看:
对比下:

最后一页:

对比:

21239,哎呀,过了一夜又多了两页评论。

好了,到此结束!改下ID就可以爬其他人的啦!


情感分析:

接下来用stylecloud来进行情感分析。简单的把爬取的评论用词云展示出来不是分析,必须要经过词云清洗才行。所用的stopwords.txt是本文开头提到的,可以根据需要修改stopwords.txt。

由于评论过多的话爬取会消耗很多时间,所以情感分析另外建了个文件。

完整代码:

 1 from stylecloud import gen_stylecloud
2 import jieba
3
4
5 def jieba_cloud(file_name, icon):
6 with open(file_name, 'r', encoding='utf8') as f:
7 word_list = jieba.cut(f.read())
8
9 result = " ".join(word_list) # 分词用 隔开
10
11 # 设置停用词
12 stopwords_file = open('stopwords.txt', 'r', encoding='utf-8')
13 stopwords = [words.strip() for words in stopwords_file.readlines()]
14
15 # 制作中文词云
16 icon_name = " "
17 if icon == "1":
18 icon_name = "fas fa-grin-hearts"
19 elif icon == "2":
20 icon_name = "fas fa-space-shuttle"
21 elif icon == "3":
22 icon_name = "fas fa-heartbeat"
23 elif icon == "4":
24 icon_name = "fas fa-bug"
25 elif icon == "5":
26 icon_name = "fas fa-thumbs-up"
27 elif icon == "6":
28 icon_name = "fab fa-qq"
29 pic = str(icon) + '.png'
30 if icon_name is not None and len(icon_name) > 0:
31 gen_stylecloud(text=result,
32 size=1024, # stylecloud 的大小(长度和宽度)
33 icon_name=icon_name,
34 font_path='simsun.ttc',
35 max_font_size=250, # stylecloud 中的最大字号
36 max_words=5000, # stylecloud 可包含的最大单词数
37 # stopwords=TRUE, # 布尔值,用于筛除常见禁用词
38 custom_stopwords=stopwords, # 定制停用词列表
39 output_name=pic)
40 else:
41 gen_stylecloud(text=result, font_path='simsun.ttc', output_name=pic)
42 return pic
43
44
45 # 主函数
46 if __name__ == '__main__':
47 jieba_cloud("AllComments.txt", "1")
48 jieba_cloud("AllComments.txt", "2")
49 jieba_cloud("AllComments.txt", "3")
50 jieba_cloud("AllComments.txt", "4")
51 jieba_cloud("AllComments.txt", "5")
52
53 jieba_cloud("AllComments.txt", "6")

效果:





Python之手把手教你用JS逆向爬取网易云40万+评论并用stylecloud炫酷词云进行情感分析的更多相关文章

  1. python爬虫:了解JS加密爬取网易云音乐

    python爬虫:了解JS加密爬取网易云音乐 前言 大家好,我是"持之以恒_liu",之所以起这个名字,就是希望我自己无论做什么事,只要一开始选择了,那么就要坚持到底,不管结果如何 ...

  2. python爬取网易云音乐歌曲评论信息

    网易云音乐是广大网友喜闻乐见的音乐平台,区别于别的音乐平台的最大特点,除了“它比我还懂我的音乐喜好”.“小清新的界面设计”就是它独有的评论区了——————各种故事汇,各种金句频出.我们可以透过歌曲的评 ...

  3. Python爬取网易云热歌榜所有音乐及其热评

    获取特定歌曲热评: 首先,我们打开网易云网页版,击排行榜,然后点击左侧云音乐热歌榜,如图: 关于如何抓取指定的歌曲的热评,参考这篇文章,很详细,对小白很友好: 手把手教你用Python爬取网易云40万 ...

  4. python爬虫+词云图,爬取网易云音乐评论

    又到了清明时节,用python爬取了网易云音乐<清明雨上>的评论,统计词频和绘制词云图,记录过程中遇到一些问题 爬取网易云音乐的评论 一开始是按照常规思路,分析网页ajax的传参情况.看到 ...

  5. 如何用Python网络爬虫爬取网易云音乐歌曲

    今天小编带大家一起来利用Python爬取网易云音乐,分分钟将网站上的音乐down到本地. 跟着小编运行过代码的筒子们将网易云歌词抓取下来已经不再话下了,在抓取歌词的时候在函数中传入了歌手ID和歌曲名两 ...

  6. 爬取网易云音乐评论!python 爬虫入门实战(六)selenium 入门!

    说到爬虫,第一时间可能就会想到网易云音乐的评论.网易云音乐评论里藏了许多宝藏,那么让我们一起学习如何用 python 挖宝藏吧! 既然是宝藏,肯定是用要用钥匙加密的.打开 Chrome 分析 Head ...

  7. Python 爬取网易云歌手的50首热门作品

    使用 requests 爬取网易云音乐 Python 代码: import json import os import time from bs4 import BeautifulSoup impor ...

  8. Python爬取网易云音乐歌手歌曲和歌单

    仅供学习参考 Python爬取网易云音乐网易云音乐歌手歌曲和歌单,并下载到本地 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做 ...

  9. 用Python爬取网易云音乐热评

    用Python爬取网易云音乐热评 本文旨在记录Python爬虫实例:网易云热评下载 由于是从零开始,本文内容借鉴于各种网络资源,如有侵权请告知作者. 要看懂本文,需要具备一点点网络相关知识.不过没有关 ...

随机推荐

  1. NVIDIA GPU上的Tensor线性代数

    NVIDIA GPU上的Tensor线性代数 cuTENSOR库是同类中第一个GPU加速的张量线性代数库,提供张量收缩,归约和逐元素运算.cuTENSOR用于加速在深度学习训练和推理,计算机视觉,量子 ...

  2. SLAM架构的两篇顶会论文解析

    SLAM架构的两篇顶会论文解析 一. 基于superpoint的词袋和图验证的鲁棒闭环检测 标题:Robust Loop Closure Detection Based on Bag of Super ...

  3. GPU端到端目标检测YOLOV3全过程(中)

    GPU端到端目标检测YOLOV3全过程(中)   计算机视觉初级部分知识体系                       总结了一下自己在计算机视觉初级部分的知识框架,整理如下.  个人所学并不全面( ...

  4. 将十进制数据转换为二进制AND将二进制数据转换为十进制!

    一,将十进制数据转换为二进制:  //***********************************************            Console.WriteLine(&quo ...

  5. Appium_adb常用命令总结

    以下为在工作学习过程中总结的常用的adb命令,用来做后续参考和查阅: 一.常用命令 显示当前运行的全部模拟器: adb devices  安装应用程序: adb install 应用程序.apk 备注 ...

  6. 源码级别理解 Redis 持久化机制

    文章首发于公众号"蘑菇睡不着",欢迎来访~ 前言 大家都知道 Redis 是一个内存数据库,数据都存储在内存中,这也是 Redis 非常快的原因之一.虽然速度提上来了,但是如果数据 ...

  7. LuatOS | 全新在线模拟器,随时随地发挥创意

    LuatOS --运行在嵌入式硬件的实时操作系统,开启全新物联网生态. 聚焦嵌入式应用生态,内置功能可支持绝大多数物联网应用场景.深度整合Lua语言,只需少量内存和Flash空间就能运行.不局限于合宙 ...

  8. 【题解】Luogu p2016 战略游戏 (最小点覆盖)

    题目描述 Bob喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的办法.现在他有个问题. 他要建立一个古城堡,城堡中的路形成一棵树.他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能 ...

  9. mysql left join转inner join

    在日常优化过程中,发现一个怪事情,同一个SQL出现两个完全不一样执行计划,left join 连驱动表都可以变成不一样. 对于left join,如果where条件里有被关联表过滤,left join ...

  10. 一次 RocketMQ 顺序消费延迟的问题定位

    一次 RocketMQ 顺序消费延迟的问题定位 问题背景与现象 昨晚收到了应用报警,发现线上某个业务消费消息延迟了 54s 多(从消息发送到MQ 到被消费的间隔): 2021-06-30T23:12: ...