简书之旅

作者: 皮皮大 | 来源:发表于2020-07-18 21:24 被阅读0次

    这是自己爬取自己的文章,然后进行数据分析的!哈哈哈

    声明:本文中获取的数据仅供学习使用,未用作任何商业用途;如有转载,请注明作者和原文出处

    周末美食

    这是本周的美食,很赞👍制作特点总结一下:

    1. 蒜多点

    2. 放点红椒或者胡萝卜丁,颜色更好些

    3. 最后焖的时候,水少些😃

    image

    简书APP之旅

    最近爬取简书上面的数据,进行了数据分析和可视化处理,同时也制作了最终的词云图,主要是想知道最近2年都干了什么

    这次爬取和以往很大的不同的是:网页是ajax动态加载的,对网页构造的分析和花费了很长的时间,还去B站上看了崔庆才大佬的视频,所以整体上还是加大了难度。

    爬虫真的不简单呀😄东西越学越多,也越难咯!哈哈哈😃

    image

    爬取内容

    • 题目name

    • 简介abstract

    • 砖石数Masonry(后来省略)

    • 观看Watch

    • 评论comment

    • 点赞like(praise)

    • 时间time

    以简书上面的一篇文章为例来进行解释。本来也想把砖石数爬取下来进行分析,但不是每篇都有文章都有砖石数,所以没有获取这个数据Masonry

    image

    数据

    总共有530篇文章,但是不知道为什么爬出来总是只有527=58*9+5篇。在最后一页的爬取中总是只有5

    因为网页的代码结构是一样的,爬不出来,很是无解😭暂且就这样子咯

    image

    实际爬到的数据只有527条:

    image

    网页结构

    ajax网页

    这是第一次爬取ajax动态加载的网页数据,知乎上看到一篇关于ajax的讲解:

    AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。

    举一个例子,打开这个页面,先不要动,观察右边滚动条的长度,然后当你把滚动条下拉到底之后,滚动条就变短了,即页面变长了,也就是说有一部分数据是这个时候才加载出来的。这个过程就是动态加载,基于ajax技术。我们可以看到在拉动滚动条的时候,页面上的数据变多了,但是URL始终没有变化,它不是像翻页那样将数据存到了另一个网页。

    https://zhuanlan.zhihu.com/p/35682031

    网页更新

    比如在这个网页的源码中,当右侧栏中的滚动条静止不动的时候,只有9篇文章即9个<li></li>标签对

    image

    现在当滚动条向下滑动的时候,li标签会自动更新

    image

    网页规律

    针对ajax加载的网页,在右键—检查—Networks—XHR中查看:

    image

    通过headers获取一条URL地址:

    image

    这样我们就找到了整个爬取的URL地址,可以实现全网数据的爬取

    爬取数据

    导入库

    import re
    import requests
    import pandas as pd
    import csv
    import jieba
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 绘图
    import random
    
    import plotly as py
    import plotly_express as px
    import plotly.graph_objects as go
    import dash
    import dash_core_components as dcc
    import dash_html_components as html
    
    # 显示所有列
    # pd.set_option('display.max_columns', None)
    
    # 显示所有行
    # pd.set_option('display.max_rows', None)
    
    # 设置value的显示长度为100,默认为50
    # pd.set_option('max_colwidth',100)
    

    获取网页内容

    使用requests库获取网页内容

    url = "https://www.js.com/u/635acc145?order_by=shared_at&page=1"
    headers = {"User-Agent": "实际请求头"}
    
    response = requests.get(url=url,headers=headers)   # 得到响应
    res = response.content.decode('utf-8', 'ignore')  # 获取源码
    result.append(res)
    
    image

    爬取单个字段

    name为例:

    image

    其他字段的正则表达式为:

    abstract = re.findall('<p class="abstract">\s*(.*?)\n\s*</p>', result, re.S)
    watch = re.findall('class="iconfont ic-list-read"></i>(.*?)\s</a>',result, re.S)  # \s匹配空白字符;\S匹配任意非空白字符
    comment = re.findall('class="iconfont ic-list-comments"></i>(.*?)\s</a>',result, re.S) 
    like = re.findall('class="iconfont ic-list-like"></i>(.*?)</span>',result, re.S)
    time = re.findall('lass="time" data-shared-at=(.*?)></span>',result, re.S)
    

    最好是检查每个字段每页的个数是否为9个:

    image

    时间的爬取到的只是具体的时间,不要年月日

    image

    全网爬取

    URL地址和请求头需要进行更换

    # from multiprocessing import Pool
    
    import re
    import requests
    import pandas as pd
    import csv
    import jieba
    import numpy as np
    import matplotlib.pyplot as plt
    
    # !!!注意with语句的位置,放在开头:防止属性字段的重复写入到数据中
    
    # 写入文件
    with open("jianshu.csv", "a", encoding="utf-8") as f:  # 将写入的模式改成"a":表示追加模式
        writer =csv.DictWriter(f,fieldnames=["name","abstract","read","comment","like","time"])
        writer.writeheader()
            
        # url地址和请求头需要更换成实际内容
        for i in range(1,60):   # 爬取59页数据
            url = "https://www.js.com/u/6?shared_at&page={}".format(i)
            headers = {"User-Agent": "实际请求头"}
    
            response = requests.get(url=url,headers=headers)   # 得到响应
            res = response.content.decode('utf-8', 'ignore')
    
            name_list = re.findall('class="title" target="_blank".*?>(.*?)</a>',res, re.S)
            # \n:换行符;\s*:任意次数的空白;\.\.\.:匹配3个...
            abstract_list = re.findall('<p class="abstract">\s*(.*?)\n\s*</p>', res, re.S)
            # 单独写出匹配数字:{1}表示匹配1次;{1,}表示至少匹配1次
        #     masonry_list = re.findall(r'class="iconfont ic-paid1"></i>\s(\d{1,}.\d{1,}).*?</span>',res, re.S)
            read_list = re.findall('class="iconfont ic-list-read"></i>(.*?)\s</a>',res, re.S)  # \s匹配空白字符;\S匹配任意非空白字符
            comment_list = re.findall('class="iconfont ic-list-comments"></i>(.*?)\s</a>',res, re.S)
            like_list = re.findall('class="iconfont ic-list-like"></i>(.*?)</span>',res, re.S)
            time_list = re.findall('lass="time" data-shared-at=".*?T(.*?)\+.*?></span>',res, re.S)  
    
            result_list = []
            for j in range(len(name_list)):
                result = {
                    "name": name_list[j],
                    "abstract": abstract_list[j],
        #             "masonry": masonry_list[j],
                    "read": read_list[j],
                    "comment": comment_list[j],
                    "like": like_list[j],
                    "time": time_list[j]
                }
    
                result_list.append(result)
            writer.writerows(result_list)
    

    数据处理

    全网数据

    通过上面的代码得到了全网的数据:

    image

    字段信息

    image

    time字段

    排序

    首先根据time字段进行排序

    df_sort = df.sort_values("time")
    print(df_sort.dtypes)
    
    image
    属性改变

    解决的是将time字段的object数据类型改成和时间相关的。

    后面发现:不用处理好像也可以正常处理time字段😭

    image
    散点图

    横坐标是文章的名称name,纵坐标是发表的时间time,颜色是点赞次数like

    fig = px.scatter(df_sort,x="name",y="time",color="like",height=900,width=1350)
    
    app = dash.Dash()
    app.layout = html.Div([
        dcc.Graph(figure=fig)
    ])
    
    app.run_server()
    
    image

    time标记处理

    处理规则

    人为地将发表文章的时间分为4个阶段,并且用不同的数值表示:

    • 零点-早上8点:1

    • 早上8点-下午2点:2

    • 下午2点-晚上8点:3

    • 晚上8点-晚上12点:4

    image
    增加flag字段

    增加一个新的字段flag来标记上述信息:

    for i in range(len(df_sort)):
        if "00:00:00" <= df_sort.loc[i,"time"] < "08:00:00":
            df_sort.loc[i,"flag"] = 1
        elif "08:00:00" <= df_sort.loc[i,"time"] < "14:00:00":
            df_sort.loc[i,"flag"]= 2
        elif "14:00:00" <= df_sort.loc[i,"time"] < "20:00:00":
            df_sort.loc[i,"flag"] = 3
        else:
            df_sort.loc[i,"flag"] = 4
    
    image
    fig = px.scatter(df_sort,x="flag",y="time",color="flag",height=800,width=1350)
    
    app = dash.Dash()
    app.layout = html.Div([
        dcc.Graph(figure=fig)
    ])
    
    app.run_server()
    
    绘图
    image image
    结论

    通过上述图形可以看出来,在第4个阶段里面发表文章的概率是比较大的,说明大部分文章是晚上发表的:和实际情况也是符合的😃

    read字段

    数据信息
    image
    绘图
    # 绘图
    
    # 颜色的随机生成:#123456  # 加上6位数字构成
    def random_color_generator(number_of_colors):
        color = ["#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)])
                     for i in range(number_of_colors)]
        return color
    
    text = read.values
    
    trace = go.Bar(
        x = read.index,
        y = read.values,
        text = text,
        marker = dict(
            color = random_color_generator(232),
            line = dict(color='rgb(8, 48, 107)',   # 柱子的外围线条颜色和宽度
                        width = 1.5)
        ),
        opacity = 0.5   # 透明度设置
    )
    
    # 数据部分:一定是列表的形式
    data = [trace]
    
    # 布局设置
    layout = go.Layout(
        title = 'Information of read',   # 整个图的标题
        margin = dict(
            l = 100   # 左边距离
        ),
        xaxis = dict(
            title = 'Number of reading'   # 2个轴的标题
        ),
        yaxis = dict(
            title = 'Count of the number of reading'
        ),
        width = 70000,  # figure的宽高
        height = 800
    )
    
    fig = go.Figure(data=data, layout=layout)
    
    fig.update_traces(textposition="outside")   # 将每个占比显示出来,也就是y轴的值
    
    fig.show()
    
    image

    comment字段

    绘图

    主要是对每篇文章的评论数量进行统计分析和制图

    image image
    结论

    从上面的饼图看出来:评论数为0的文章占据了绝大多数,接近90%;评论数为2的文章其次

    还是很少人评论呀😭😭

    like字段

    数据
    image
    绘图
    # 绘图
    
    # 颜色的随机生成:#123456  # 加上6位数字构成
    def random_color_generator(number_of_colors):
        color = ["#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)])
                     for i in range(number_of_colors)]
        return color
    
    trace = go.Bar(
        x = like_number.index,
        y = like_number.values,
        text = text,
        marker = dict(
            color = random_color_generator(100),
            line = dict(color='rgb(8, 48, 107)',   # 柱子的外围线条颜色和宽度
                        width = 1.5)
        ),
        opacity = 0.7   # 透明度设置
    )
    
    # 数据部分:一定是列表的形式
    data = [trace]
    
    # 布局设置
    layout = go.Layout(
        title = 'Information of like',   # 整个图的标题
        margin = dict(
            l = 100   # 左边距离
        ),
        xaxis = dict(
            title = 'Number of like'   # 2个轴的标题
        ),
        yaxis = dict(
            title = 'Count of like'
        ),
        width = 900,  # figure的宽高
        height = 500
    )
    
    fig = go.Figure(data=data, layout=layout)
    
    fig.update_traces(textposition="outside")   # 将每个占比显示出来,也就是y轴的值
    
    fig.show()
    
    image
    结论
    • 点赞1次的文章最多,2次的文章其次

    • 最多的点赞次数是38次

    • 居然还有一篇文章一个赞👍都没有😭

    词性分析与词云图

    • 处理的是文章标题字段name,分析标题中哪些出现的频率高,则作为重点学习的对象

    • 主要是使用jieba分词与wordcloud制作词云图

    Jieba词性分析

    生成列表
    image
    实现分词
    # 2-实现分词
        
    for i in range(len(name_list)):
        seg_list = jieba.cut(name_list[i].strip(), cut_all=False)  # seg_list只是一个generator生成器:<class 'generator'>
        print(("Default Mode: " + "/ ".join(seg_list)))  #  用list方法展开
    
    image
    分词结果放入列表
    # 3-将分词的结果全部放入一个列表中,方便后续处理
    
    jieba_name = []
    
    for i in range(len(name_list)):
        seg_list = jieba.cut(name_list[i].strip(), cut_all=False)  # seg_list只是一个generator生成器:<class 'generator'>
        for str in list(seg_list):   # 对list(seg_list)中的每个元素进行追加
            jieba_name.append(str)
            
    jieba_name
    
    image
    1. 将待处理的句子放入列表中

    2. 对列表中的每个句子分词

    3. 将上面步骤中的分词结果放入到另一个列表中,方便后续处理

    jieba使用总结

    1. 将待处理的句子放入列表中

    2. 对列表中的每个句子分词

    3. 将上面步骤中的分词结果放入到另一个列表中,方便后续处理

    Wordcloud词云图

    绘图
    from wordcloud import WordCloud
    import matplotlib.pyplot as plt
    
    text = " ".join(i for i in jieba_name)   # 待处理的字符串
    
    # 先下载SimHei.ttf字体,放置到自己的某个目录下,然后将font换成自己的路径即可
    font = r'/Users/piqianchao/Desktop/spider/SimHei.ttf' 
    
    wc = WordCloud(collocations=False, font_path=font, # 路径
                   max_words=2000,width=4000, 
                   height=4000, margin=2).generate(text.lower())
    
    plt.imshow(wc)
    plt.axis("off")
    plt.show()
    
    wc.to_file('jianshu.png')  # 把词云保存下来
    
    初步结果
    image

    图中的札记实在是扎眼呀😃

    这是因为自己Python札记写了很多的原因;另外3个比较突出的词语:利用、进行、数据分析是因为自己看了《利用Python进行数据分析》这本书

    进一步处理

    删除几个无价值的信息之后再进行绘图,选择了一个背景图:

    noUse = ["札记","利用","进行","打卡","笔记","学习"]
    
    for col in noUse:
        while col in jieba_name:
            jieba_name.remove(col)
    
    from os import path
    from PIL import Image
    import numpy as np
    import matplotlib.pyplot as plt
    
    from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
    
    d = path.dirname('.')   # 在ide中使用这段代码
    # d = path.dirname(__file__)
    
    # 待处理的文件(去掉无效信息之后的)
    text = " ".join(i for i in jieba_name)   
    
    # read the mask / color image taken from
    # http://jirkavinse.deviantart.com/art/quot-Real-Life-quot-Alice-282261010
    alice_coloring = np.array(Image.open(path.join(d, "wordcloud.png")))
    
    # 设置停用词
    stopwords = set(STOPWORDS)
    stopwords.add("said")
    
    # 路径改成自己的
    font = r'/Users/piqianchao/Desktop/spider/SimHei.ttf'    
    
    # 你可以通过 mask 参数 来设置词云形状
    wc = WordCloud(background_color="white", font_path=font,
                   max_words=2000, mask=alice_coloring,
                   height=6000,width=6000,
                   stopwords=stopwords, max_font_size=40, random_state=42)
    
    # generate word cloud
    wc.generate(text)
    
    # create coloring from image
    image_colors = ImageColorGenerator(alice_coloring)
    
    # show
    # 在只设置mask的情况下,你将会得到一个拥有图片形状的词云
    plt.imshow(wc, interpolation="bilinear")
    plt.axis("off")
    plt.show()
    wc.to_file('jianshu_one.png')  # 把词云保存下来
    
    # recolor wordcloud and show
    # we could also give color_func=image_colors directly in the constructor
    # 直接在构造函数中直接给颜色:通过这种方式词云将会按照给定的图片颜色布局生成字体颜色策略
    plt.imshow(wc.recolor(color_func=image_colors), interpolation="bilinear")
    plt.axis("off")
    plt.show()
    wc.to_file('jianshu_two.png')  # 把词云保存下来
    
    image

    结论

    从上面优化后的图形中可以看出来:

    1. Python是最突出的。的确如此,写的文章很多是关于Python

    2. 数据分析是一个亮点:因为《利用Python进行数据分析》这本书,还有就是很多pandas的文章

    3. MySQL也是另一个亮点:学习了很多数据库的知识,包含:MySQL、SQL、Sqlzoo、数据库等

    4. 机器一词是出自于机器学习中的,吴恩达老师带我入门的,学了很多的入门知识:吴恩达老师的视频、算法、数据结构等

    5. 深圳二字主要是因为到了深圳之后写了很多关于深圳的文章

    数据还是非常准确的,很有参考价值👍数据不会说谎

    相关文章

      网友评论

        本文标题:简书之旅

        本文链接:https://www.haomeiwen.com/subject/zrnfkktx.html