我用Python爬虫爬取并分析了C站前100用户最高访问的2000篇文章

简介: 我用Python爬虫爬取并分析了C站前100用户最高访问的2000篇文章

写在前面

最近系统地学习了正则表达式,发现正则表达式最大的应用之一——网络爬虫之前一直没有涉猎,遂借此契机顺带写一个爬虫的demo备用。选择对象是CSDN排行榜前100用户,各自按访问量从高到低排序的前20篇文章,使用一些简单的数据分析手段看看技术热点,方便今后拓宽技术栈。

项目总述

主要爬取的数据是文章标题和访问量,先总体可视化总体文章的技术关键词;然后按访问量分组,可视化每个访问段的技术热点。

数据爬取

获得服务器API

首先我们要知道通过什么接口可以获得网站数据:首先进入博客总榜,按F12进入控制台,选中Network选项卡监视网络请求,然后刷新网页。从下图可以看到在API"https://blog.csdn.net/phoenix/web/blog/all-rank?page=1&pageSize=20"中我们可以拿到我们想要的用户信息——主要是用户名


image.png

现在到用户博客首页,同样地,按F12进入控制台,选中Network选项卡监视网络请求,然后点击按访问量排序,则可以发现另一个关键APIhttps://blog.csdn.net/community/home-api/v1/get-business-list?page=1&size=20&businessType=blog&orderby=ViewCount&noMore=false&username={},如下图所示。


image.png

我们与服务器的交互就依靠这两个API进行。

程序总体设计

思考一下,我们总共有如下的公共变量:

# 请求头
headers = {
            'User-Agent':
            'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
        }
# 排行榜url
rankUrl = "https://blog.csdn.net/phoenix/web/blog/all-rank?page={}&pageSize=20"
# 按访问量排行的文章列表
mostViewArtical = "https://blog.csdn.net/community/home-api/v1/get-business-list?page=1&size=20&businessType=blog&orderby=ViewCount&noMore=false&username={}"
userNames =[] # 用户名列表
titleList = []  # 文章标题列表
viewCntList = [] # 访问量列表

为便于管理,引入一个类进行爬虫,专门负责与服务器进行数据交互

class GetInfo:
    def __init__(self) -> None:
        # 请求头
        self.headers = {
            'User-Agent':
            'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
        }
        # 排行榜url
        self.rankUrl = "https://blog.csdn.net/phoenix/web/blog/all-rank?page={}&pageSize=20"
        # 按访问量排行的文章列表
        self.mostViewArtical = "https://blog.csdn.net/community/home-api/v1/get-business-list?page=1&size=20&businessType=blog&orderby=ViewCount&noMore=false&username={}"
        self.userNames = []
        self.titleList, self.viewCntList = [], []

交互完成后,再使用别的库进行数据分析,将两个过程分离开

用户名爬取

定义一个私有的初始化函数

def __initRankUsrName(self):
    usrNameList = []
    for i in range(5):
        response = requests.get(url=self.rankUrl.format(i),
                                headers=self.headers)
        response.encoding = 'utf-8'
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'html.parser')
        information = json.loads(str(soup))
        for item in information['data']['allRankListItem']:
            usrNameList.append(item['userName'])
    return usrNameList

这里获取用户名主要是为了动态生成第二个API

文章爬取

再定义一个私有函数,输入参数是用户名列表:

def __initArticalInfo(self, usrList):
    titleList = []
    viewCntList = []
    for name in usrList:
        url = self.mostViewArtical.format(name)
        # print(url)
        response = requests.get(url=url, headers=self.headers)
        response.encoding = 'utf-8'
        response.raise_for_status()
        titleList.extend(re.findall(r"\"title\":\"(.*?)\"", response.text))
        viewCntList.extend(re.findall(r"\"viewCount\":(.*?),", response.text))
    return titleList, viewCntList

这里我使用正则表达式直接处理字符串,并返回文章标题列表、访问量列表。可以随便访问一个API做实验,这里以我的用户名为例,可以看到要获取文章标题就是以\"title\":\"(.*?)\"去匹配,其中\用于转义;要获取访问量就是以\"viewCount\":(.*?),去匹配,访问数字没有加引号。


事实上,用正则匹配不需要将返回的字符串加载为Json字典,可能有更快的处理效率(但不如json灵活)


image.png

这个爬虫类就设计好了,完整代码如下:

class GetInfo:
    def __init__(self) -> None:
        # 请求头
        self.headers = {
            'User-Agent':
            'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
        }
        # 排行榜url
        self.rankUrl = "https://blog.csdn.net/phoenix/web/blog/all-rank?page={}&pageSize=20"
        # 按访问量排行的文章列表
        self.mostViewArtical = "https://blog.csdn.net/community/home-api/v1/get-business-list?page=1&size=20&businessType=blog&orderby=ViewCount&noMore=false&username={}"
        self.userNames = self.__initRankUsrName()
        self.titleList, self.viewCntList = self.__initArticalInfo(
            self.userNames)
    def __initArticalInfo(self, usrList):
        titleList = []
        viewCntList = []
        for name in usrList:
            url = self.mostViewArtical.format(name)
            # print(url)
            response = requests.get(url=url, headers=self.headers)
            response.encoding = 'utf-8'
            response.raise_for_status()
            titleList.extend(re.findall(r"\"title\":\"(.*?)\"", response.text))
            viewCntList.extend(
                re.findall(r"\"viewCount\":(.*?),", response.text))
        return titleList, viewCntList
    def __initRankUsrName(self):
        usrNameList = []
        for i in range(5):
            response = requests.get(url=self.rankUrl.format(i),
                                    headers=self.headers)
            response.encoding = 'utf-8'
            response.raise_for_status()
            soup = BeautifulSoup(response.text, 'html.parser')
            information = json.loads(str(soup))
            for item in information['data']['allRankListItem']:
                usrNameList.append(item['userName'])
        return usrNameList
info = GetInfo()

使用也很方便,只需要实例化调用其中的列表属性即可。

数据分析

数据存储

将文本数据存成csv格式,先设计表头:

if not os.path.exists("articalInfo.csv"):
    #创建存储csv文件存储数据
    with open('articalInfo.csv', "w", encoding="utf-8-sig", newline='') as f:
        csv_head = csv.writer(f)
        csv_head.writerow(['title', 'viewCnt'])

注意编码格式为utf-8-sig,否则会乱码

接下来存数据:

length = len(info.titleList)
for i in range(length):
    if info.titleList[i]:
        with open('articalInfo.csv', 'a+', encoding='utf-8-sig') as f:
            f.write(info.titleList[i] + ',' + info.viewCntList[i] + '\n')

总体数据可视化

新建一个模块专门用于可视化数据,与爬虫分离开,因为爬虫是慢IO过程,会影响调试效率,后面可以试试用协程来处理爬虫。

首先,把爬虫的信息读取到txt文件去

df = pd.read_csv('articalInfoNor.csv', encoding='utf-8-sig',usecols=['title', 'viewCnt'])
titleList = ','.join(df['title'].values)
with open('text.txt','a+', encoding='utf-8-sig') as f:
    f.writelines(titleList)

如何返回分词结果:

def getKeyWordText():
    # 读取文件信息
    file = open(path.join(path.dirname(__file__), 'text.txt'), encoding='utf-8-sig').read()
    return ' '.join(jieba.cut(file))

借助词云库可视化一下:

bg_pic = imread('2.jpg')
#生成词云
wordcloud = WordCloud(font_path=r'C:\Windows\Fonts\simsun.ttc',mask=bg_pic,background_color='white',scale=1.5).generate(text)
image_colors = ImageColorGenerator(bg_pic)
#显示词云图片
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
#保存图片
wordcloud.to_file('test.jpg')


image.png

这个大大的“的”是什么鬼?显然高频关键词里有太多语气助词、连接词,我们最好设置一个停用词列表把这些明显不需要的词屏蔽掉。我这里采用修饰器的方法让代码更简洁,关于修饰器的内容可以参考Python修饰器

def splitText(mode):
    stopWords = ["的","与","和","建议","收藏","使用","了","实现","我","中","你","在","之","年","月","日"]
    def warpper(func):
        def warp():
            textSplit = func()
            if mode:
                temp = [word for word in textSplit if word not in stopWords]
                return ' '.join(temp)
            else:
                return ' '.join(textSplit)
        return warp
    return warpper

当mode=True时启用屏蔽,否则关闭屏蔽,那么之前的函数应该修改为:

# 返回关键词文本
@splitText(False)
def getKeyWordText():
    # 读取文件信息
    file = open(path.join(path.dirname(__file__), 'text.txt'), encoding='utf-8-sig').read()
    return jieba.cut(file)

再来一次:


image.png

现在就正常多了。可以看到Python和Java是绝对的领先,之后是各位总结的方法论等等,算法的词频反而不高?

数据分组

我把数据进一步分层为

1、访问量>10W

2、访问量5W~10W

3、访问量1W~5W

4、访问量5K~1W

5、访问量5K以下

先来看看数据分布情况:


image.png

我猜如果分段分得再细一点可能趋于正态分布~

分组可视化看看:


image.png

>10W的词云


image.png

5~10W的词云


image.png

1~5W的词云


image.png

5k~1W的词云

感觉从这里开始更百花齐放一些,似乎也更关注具体问题的解决


image.png

5k以下的词云

不得不感叹python在每个阶段都是牌面

完整代码

import requests
from bs4 import BeautifulSoup
import os, json, re, csv
class GetInfo:
    def __init__(self) -> None:
        # 请求头
        self.headers = {
            'User-Agent':
            'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
        }
        # 排行榜url
        self.rankUrl = "https://blog.csdn.net/phoenix/web/blog/all-rank?page={}&pageSize=20"
        # 按访问量排行的文章列表
        self.mostViewArtical = "https://blog.csdn.net/community/home-api/v1/get-business-list?page=1&size=20&businessType=blog&orderby=ViewCount&noMore=false&username={}"
        self.userNames = self.__initRankUsrName()
        self.titleList, self.viewCntList = self.__initArticalInfo(
            self.userNames)
    def __initArticalInfo(self, usrList):
        titleList = []
        viewCntList = []
        for name in usrList:
            url = self.mostViewArtical.format(name)
            # print(url)
            response = requests.get(url=url, headers=self.headers)
            response.encoding = 'utf-8'
            response.raise_for_status()
            titleList.extend(re.findall(r"\"title\":\"(.*?)\"", response.text))
            viewCntList.extend(
                re.findall(r"\"viewCount\":(.*?),", response.text))
        return titleList, viewCntList
    def __initRankUsrName(self):
        usrNameList = []
        for i in range(5):
            response = requests.get(url=self.rankUrl.format(i),
                                    headers=self.headers)
            response.encoding = 'utf-8'
            response.raise_for_status()
            soup = BeautifulSoup(response.text, 'html.parser')
            information = json.loads(str(soup))
            for item in information['data']['allRankListItem']:
                usrNameList.append(item['userName'])
        return usrNameList
info = GetInfo()
if not os.path.exists("articalInfo.csv"):
    #创建存储csv文件存储数据
    with open('articalInfo.csv', "w", encoding="utf-8-sig", newline='') as f:
        csv_head = csv.writer(f)
        csv_head.writerow(['title', 'viewCnt'])
length = len(info.titleList)
for i in range(length):
    if info.titleList[i]:
        with open('articalInfo.csv', 'a+', encoding='utf-8-sig') as f:
            f.write(info.titleList[i] + ',' + info.viewCntList[i] + '\n')
from wordcloud import WordCloud,ImageColorGenerator
import matplotlib.pyplot as plt
from imageio import imread
import jieba
import pandas as pd
from os import path
df = pd.read_csv('articalInfoCom.csv', encoding='utf-8-sig',usecols=['title', 'viewCnt'])
titleList = ','.join(df['title'].values)
with open('text.txt','a+', encoding='utf-8-sig') as f:
    f.writelines(titleList)
def splitText(mode):
    stopWords = ["的","与","和","建议","收藏","使用","了","实现","我","中","你","在","之","年","月","日"]
    def warpper(func):
        def warp():
            textSplit = func()
            if mode:
                temp = [word for word in textSplit if word not in stopWords]
                return ' '.join(temp)
            else:
                return ' '.join(textSplit)
        return warp
    return warpper
# 返回关键词文本
@splitText(True)
def getKeyWordText():
    # 读取文件信息
    file = open(path.join(path.dirname(__file__), 'text.txt'), encoding='utf-8-sig').read()
    return jieba.cut(file)
text = getKeyWordText()
#读取txt文件、背景图片
bg_pic = imread('2.jpg')
#生成词云
wordcloud = WordCloud(font_path=r'C:\Windows\Fonts\simsun.ttc',mask=bg_pic,background_color='white',scale=1.5).generate(text)
image_colors = ImageColorGenerator(bg_pic)
#显示词云图片
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
#保存图片
wordcloud.to_file('test.jpg')


目录
相关文章
|
5天前
|
数据采集 缓存 定位技术
网络延迟对Python爬虫速度的影响分析
网络延迟对Python爬虫速度的影响分析
|
13天前
|
数据采集 存储 JSON
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第27天】本文介绍了Python网络爬虫Scrapy框架的实战应用与技巧。首先讲解了如何创建Scrapy项目、定义爬虫、处理JSON响应、设置User-Agent和代理,以及存储爬取的数据。通过具体示例,帮助读者掌握Scrapy的核心功能和使用方法,提升数据采集效率。
57 6
|
23天前
|
数据采集 JSON 数据处理
抓取和分析JSON数据:使用Python构建数据处理管道
在大数据时代,电商网站如亚马逊、京东等成为数据采集的重要来源。本文介绍如何使用Python结合代理IP、多线程等技术,高效、隐秘地抓取并处理电商网站的JSON数据。通过爬虫代理服务,模拟真实用户行为,提升抓取效率和稳定性。示例代码展示了如何抓取亚马逊商品信息并进行解析。
抓取和分析JSON数据:使用Python构建数据处理管道
|
7天前
|
数据采集 存储 JSON
Python爬虫开发中的分析与方案制定
Python爬虫开发中的分析与方案制定
|
14天前
|
数据可视化 开发者 Python
Python GUI开发:Tkinter与PyQt的实战应用与对比分析
【10月更文挑战第26天】本文介绍了Python中两种常用的GUI工具包——Tkinter和PyQt。Tkinter内置于Python标准库,适合初学者快速上手,提供基本的GUI组件和方法。PyQt基于Qt库,功能强大且灵活,适用于创建复杂的GUI应用程序。通过实战示例和对比分析,帮助开发者选择合适的工具包以满足项目需求。
55 7
|
11天前
|
数据采集 Web App开发 JavaScript
爬虫策略规避:Python爬虫的浏览器自动化
爬虫策略规避:Python爬虫的浏览器自动化
|
14天前
|
存储 数据处理 Python
Python科学计算:NumPy与SciPy的高效数据处理与分析
【10月更文挑战第27天】在科学计算和数据分析领域,Python凭借简洁的语法和强大的库支持广受欢迎。NumPy和SciPy作为Python科学计算的两大基石,提供了高效的数据处理和分析工具。NumPy的核心功能是N维数组对象(ndarray),支持高效的大型数据集操作;SciPy则在此基础上提供了线性代数、信号处理、优化和统计分析等多种科学计算工具。结合使用NumPy和SciPy,可以显著提升数据处理和分析的效率,使Python成为科学计算和数据分析的首选语言。
22 3
|
14天前
|
数据采集 前端开发 中间件
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第26天】Python是一种强大的编程语言,在数据抓取和网络爬虫领域应用广泛。Scrapy作为高效灵活的爬虫框架,为开发者提供了强大的工具集。本文通过实战案例,详细解析Scrapy框架的应用与技巧,并附上示例代码。文章介绍了Scrapy的基本概念、创建项目、编写简单爬虫、高级特性和技巧等内容。
39 4
|
14天前
|
存储 机器学习/深度学习 算法
Python科学计算:NumPy与SciPy的高效数据处理与分析
【10月更文挑战第26天】NumPy和SciPy是Python科学计算领域的两大核心库。NumPy提供高效的多维数组对象和丰富的数学函数,而SciPy则在此基础上提供了更多高级的科学计算功能,如数值积分、优化和统计等。两者结合使Python在科学计算中具有极高的效率和广泛的应用。
31 2
|
19天前
|
数据采集 机器学习/深度学习 搜索推荐
Python自动化:关键词密度分析与搜索引擎优化
Python自动化:关键词密度分析与搜索引擎优化