CSDN热榜共有21个子领域,包括C++、云原生、人工智能、前沿技术、软件工程、后端、Java、JavaScript、PHP、Python、区块链、大数据、移动开发、嵌入式、开发工具、结构与算法、微软技术、测试、游戏、网络、运维等。
这些领域热榜的地址https://blog.csdn.net/rank/list/content?type=?,问好表示所在榜单名称,例如Python领域热榜的地址是https://blog.csdn.net/rank/list/content?type=python。
而且领域热榜和总榜的html结构是完全相同的,也就是说之前用于总榜的getHeatInfos可以继续使用。唯一不同是,领域热榜只排到第50名,而且有些可能不足50名,所以需要做一个标记来终止其运行,最终getHeatInfos修改如下
def getHeatInfos(callback, key=None):
URL_ALL = 'https://blog.csdn.net/rank/list'
URL_SUB = f'https://blog.csdn.net/rank/list/content?type={key}'
nBlogs = 50 if key else 100
driver = openEdge(URL_SUB if key else URL_ALL)
titleClass = "floor-rank-item"
ts, nTs, it = [], 0, 0
# 获取100篇热榜博客
while len(ts) < nBlogs:
script = "window.scrollTo(0,document.body.scrollHeight)"
driver.execute_script(script)
ts = driver.find_elements(By.CLASS_NAME, titleClass)
time.sleep(0.5)
info = f"已读取到{len(ts)}篇热榜博客"
if callback: callback([], info)
it = it+1 if len(ts) == nTs else 0
nTs = len(ts)
if it > 5 : break
callback([], f"已读取到所有热榜博客,开始处理")
blogs = []
for t in ts:
ws = t.text.split('\n')
blogs.append([ws[i] for i in [0, 1, 10, 2, 4, 6, 8]])
b = blogs[-1]
callback(blogs, f"正在处理第{b[0]}篇博客,热度{b[-1]}")
callback(blogs, f"全部热榜博客处理完毕")
return blogs
接下来添加一个领域热榜的按钮,考虑到这些领域实在太多,不太好每个领域给个热榜,故而用Menubutton来实现。又考虑到一个一个去爬太费劲,所以除了各领域之外,提供一个全部爬取的选项。
def setFrmHeat(self, frmHeat):
# 省略。。。
mb = ttk.Menubutton(frmHeat, width=10, text="领域内容榜")
mb.pack(side=tk.LEFT)
m = tk.Menu(mb, tearoff=False)
m.add_command(label="全部领域",
command = lambda : self.mbSubHeatCrawler(None))
for key in SUB_HEATS:
m.add_command(label=key,
command = lambda : self.mbSubHeatCrawler(key))
mb.config(menu=m)
# 省略。。。
def mbSubHeatCrawler(self, key):
pass
setFrmHeat函数在之前的博客中已经定义了,上面只给出需要添加的Menubutton的代码。其中SUB_HEATS被做成了一个全局变量,便于引用。
SUB_HEATS = ["C++", "云原生", "人工智能", "前沿技术", "软件工程",
"后端", "Java", "JavaScript", "PHP", "Python", "区块链",
"大数据", "移动开发", "嵌入式", "开发工具", "结构与算法",
"微软技术", "测试", "游戏", "网络", "运维"]
界面效果如下
接下来要实现mbSubHeatCrawler函数,同时需要更新导出热榜按钮的功能,即既可以导出总榜,也可以导出领域热榜。
同时,为了能够一次性把所有领域热榜都爬取一遍,需要再实现一个函数,这个函数写在类外面,和getHeatInfos级别相同。
def getAllSubHeatInfos(callback):
blogs = {}
for key in SUB_HEATS:
callback(blogs, f"正在读取{key}领域热榜")
blogs[key] = getHeatInfos(callback, key)
callback(blogs, f"{key}已经读取完成")
callback(blogs, f"所有领域热榜都已读取完毕")
然后就是重头戏,领域热榜的爬取逻辑。由于每个领域都有自己的榜单,所以存储领域热榜的数据用字典最为合适,故而添加一个类成员self.subHeats。有关各领域热榜的实现逻辑如下。
def mbSubHeatCrawler(self, field):
if not field:
self.subHeats = {}
Thread(target = getAllSubHeatInfos,
args=(self.backAllSubHeat, ), daemon=True).start()
else:
func = lambda L, info : self.backOneSubHeat(L, info, field)
Thread(target = getHeatInfos,
args=(func, field), daemon=True).get()
def backOneSubHeat(self, L, info, field=None):
self.subheats[field] = L
self.infoCSDN.update(dct)
if info.endswith("完毕"):
n = len(self.subheats[field])
self.addLogs(f"共读取了{key}领域{n}篇博客")
def backAllSubHeat(self, dct, info):
if type(dct) == dict:
self.subHeats.update(dct)
self.infoCSDN.set(info)
if info.endswith("完毕"):
self.addLogs(f"共读取了{len(self.subHeats)}个领域")
同时,热榜导出函数也要更新
def mbExportHeat(self):
heatHead = ["序号", "标题", "作者", "浏览", "评论", "收藏", "热度"]
if len(self.heatBlogs) > 0:
self.mbExport(self.heatBlogs, heatHead, "热榜博客")
if len(self.subHeats) > 0:
subs = []
for k,v in self.subHeats.items():
subs.extend([[k]+L for L in v])
subHead = ["领域"] + heatHead
self.mbExport(subs, subHead, "各领域热榜博客")