• CSDN热榜分析3:实现领域热榜的爬取



    热榜分析系列: 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    领域热榜按钮

    接下来添加一个领域热榜的按钮,考虑到这些领域实在太多,不太好每个领域给个热榜,故而用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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    setFrmHeat函数在之前的博客中已经定义了,上面只给出需要添加的Menubutton的代码。其中SUB_HEATS被做成了一个全局变量,便于引用。

    SUB_HEATS = ["C++", "云原生", "人工智能", "前沿技术", "软件工程", 
        "后端", "Java", "JavaScript", "PHP", "Python", "区块链", 
        "大数据", "移动开发", "嵌入式", "开发工具", "结构与算法", 
        "微软技术", "测试", "游戏", "网络", "运维"]
    
    • 1
    • 2
    • 3
    • 4

    界面效果如下

    在这里插入图片描述

    功能实现

    接下来要实现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"所有领域热榜都已读取完毕")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    然后就是重头戏,领域热榜的爬取逻辑。由于每个领域都有自己的榜单,所以存储领域热榜的数据用字典最为合适,故而添加一个类成员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)}个领域")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    同时,热榜导出函数也要更新

    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, "各领域热榜博客")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    Oracle在Logstash需求中碰到的问题处理方式
    腾讯云轻量级服务器和云服务器什么区别?轻量服务器是干什么用的
    Leetcode(347)——前 K 个高频元素
    国家开放大学电大《钢结构》形考任务答案
    iOS 17.4 Not Installed
    java基于springboot+vue农村土地农委成员管理系统 elementui 前后端分离
    【2022秋线上作业-第5次-第11-13周】选择题
    iOS 集成Jenkins 完整流程 (自由风格)
    如何完成视频合并操作?这几个方法值得一试
    【浏览器缓存机制】
  • 原文地址:https://blog.csdn.net/m0_37816922/article/details/132316542