文章目录
- 领域热榜爬取函数
- 领域热榜按钮
- 功能实现
 
热榜分析系列: CSDN热榜分析🔥 UI界面🔥 领域热榜
领域热榜爬取函数
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, "各领域热榜博客")



















