查看原文
其他

新鲜出炉,Python 告诉你程序员最关注的技术竟然是……

灵动的艺术 CSDN 2019-02-22

作者 | 灵动的艺术

责编 | 张红月

CSDN博客每年会产生上百万篇优质技术博文,大家都喜欢写哪些技术文章呢?本文作者基于18年入选博客之星活动的200名博主开始,透过他们的技术博文,分析大家最爱研究技术方向都有哪些。


CSDN 2018博客之星入选名单


闲话不多说,如果你关注过这个活动,那么你应该知道,CSDN2018博客之星评选活动参选人员入选博主名单可以在以下网址看到 :

  • https://bss.csdn.net/m/topic/blog_star2018

即:

时间尚未截止,有心怡的博主入选还没有投票的,赶紧去投上一票吧。

那么,截止到本文出炉为止,200位入选博主的原创/粉丝/喜欢/评论/等级/访问/积分/排名/获得选票 各指标信息如何呢?

由上面的表格我们可以得出的结论。

  • 目前获得票数最高的是徐刘根博主,并且获得选票高达2823票,人气相当火爆。

  • 目前文章最高总访问次数由china_jeffery博主拿下,并且访问次数高达11084501。

  • 目前文章总点赞次数由foruok博主拿下,并且已经获得了6466点赞。

  • 截止目前为止,其他各项指标,包括最佳排名(12),最佳收藏(2327),最多原创(13687181),最高积分(107000),最高等级(10),最多粉丝(22564),最多评论(7360)均由迂者-贺利坚博主砍下,这个成绩可是相当恐怖的。


得票前十名的博主


第二名与第一名的差距还是相当的大啊。大家要火热开启投票节奏啊!


最爱分享的技术


在投票之余,也可以多多关注一下大佬博主们的文章,看看这些博主专家们的研究方向,看看技术热点,作为我们2019年的学习方向。这里还是老规矩为大家贡献一张目前已入选博主的所有专栏相关课题研究方向热力图供大家参考。

由热力图可得知,目前大家最爱研究的方向有机器学习、android、Java、Python、设计模式、各种技术框架等。

接下来,我们再看一下,入选的200位博主,目前博客等级排名:

由此看来,报名入选博主在CSDN的等级分布还是比较均匀的,纵使你目前的博客等级不是很高,只有文章质量可以也还是有机会的。

接下来,我们再看一下,入选的200位博主目前已经获得的荣誉勋章情况如下:

即:

  • 6位作者已经拿过博客之星勋章,并且发起了再次冲击。

  • 3位作者拿到了1024超级勋章勋章,这意味着有超过3位报名作者的原创文章超过了1024篇,堪称超级写手。

  • 191位已经拿到博客专家,当然也有9位目前尚未拿到博客专家称号,说明没有博客专家称号也还是有机会的。


技术实现分析


知其然亦知其所以然,如果大家也有兴趣做类似的技术分析或者想要进一步深入技术发掘分析,我们再一起看看我们是怎么分析的。

数据挖掘

首先,我们知道 入选博主名单可以在 https://bss.csdn.net/m/topic/blog_star2018 网址看到,那么自然我们分析信息需要从这里出发。

想必你也猜到了,我们需要首先爬取所有的入选博主。例如,我们可以从这里拿到入选博主的基础信息。

# -*- coding: utf-8 -*-
import scrapy
import json
from tutorial.items import BlogStar2018Item


class BlogStar2018Spider(scrapy.Spider):
    name = 'blog_star2018'
    allowed_domains = ['blog.csdn.net']
    start_urls = ['https://bss.csdn.net/m/topic/blog_star2018']

    def parse(self, response):
        for user_info in response.xpath('//div[@class="user-info"]'):
            info = {}
            user_id = user_info.xpath('./div[@class="user-id"]/text()').extract_first()
            user_addr = user_info.xpath('./div[@class="avatar"]/a/@href').extract_first()
            user_name  = user_info.xpath('./div[@class="user-name"]/span/text()').extract_first()
            user_number = user_info.xpath('./div[@class="user-number"]/span/em/text()').extract_first()
            print(user_id,user_addr,user_name,user_number)
            info['user_id'] = user_id
            info['user_addr'] = user_addr
            info['user_name'] = user_name
            info['user_number'] = user_number
            yield scrapy.Request(user_addr,
                                 callback=lambda response, info=info: self.parse_blog_user_info(response,info))

当然,只知道那些博主入选了本次评选是远远不够的,我们还需要知道包括但不限于 原创/粉丝/喜欢/评论/等级/访问/积分/排名/专栏/技术方向 等等信息。例如,我们可以利用分析工具抓取所有的入选博主的专栏信息。

    def parse_blog_user_info(self,response,info):
        item = BlogStar2018Item()
        item['link'] = response.request.url
        item['blogstar_vote'] = info
        item['blog_title'] = response.xpath('//div[@class="title-box"]/h1[@class="title-blog"]/a/text()').extract_first()
        item['description'] = response.xpath('//p[@class="description"]/text()').extract_first()
        item['avatar_pic'] = response.xpath('//div[@class="profile-intro d-flex"]/div[@class="avatar-box d-flex justify-content-center flex-column"]/a/img/@src').extract_first()

        for data in response.xpath('//div[@class="data-info d-flex item-tiling"]/dl[@class="text-center"]'):
            data_key = data.xpath('./dt/a/text() | ./dt/text()').extract_first()
            data_value = data.xpath('./@title').extract_first()
            if data_key.find('原创') > -1:
                item['original'] = int(data_value)
            elif data_key.find('粉丝') > -1:
                item['fans'] = int(data_value)
            elif data_key.find('喜欢') > -1:
                item['star'] = int(data_value)
            elif data_key.find('评论') > -1:
                item['comment'] = int(data_value)
        for grade in response.xpath('//div[@class="grade-box clearfix"]/dl'):
            grade_key = grade.xpath('./dt/text()').extract_first()
            grade_value = grade.xpath('./dd/@title | ./dd/a/@title | ./@title').extract_first()
            if grade_key.find('等级') > -1:
                item['level'] = int(grade_value.replace('级,点击查看等级说明',''))
            elif grade_key.find('访问') > -1:
                item['visit'] = int(grade_value)
            elif grade_key.find('积分') > -1:
                item['score'] = int(grade_value)
            elif grade_key.find('排名') > -1:
                item['rank'] = int(grade_value)

        #勋章
        item['medal'] = response.xpath('//div[@class="badge-box d-flex"]/div[@class="icon-badge"]/@title').extract()

        blog_expert = ''.join(response.xpath('//div[@class="user-info d-flex justify-content-center flex-column"]/p[@class="flag expert"]/text()').extract()).replace('\n','').replace(' ','')
        if blog_expert and '' is not blog_expert :
            item['medal'].append(blog_expert)

        #博主专栏
        colunms = []
        for li in response.xpath('//div[@id="asideColumn"]/div[@class="aside-content"]/ul/li'):
            colunms.append({'colunm_name':li.xpath('./div[@class="info"]/p/a/text()').extract_first()
                               ,
                            'colunm_count': li.xpath('./div[@class="info"]/div[@class="data"]/span/text()').extract_first().replace(' ''').replace('篇',
                                                                                                                 '')
                               ,
                            'colunm_read': li.xpath('./div[@class="info"]/div[@class="data"]/span/text()').extract()[-1].replace(' ''')})

        item['colunms'] = colunms
        yield item

当然,我们也可以继续抓取更多信息,这里就留给大家扩展了。

数据存储与分析

我们都知道,空有了数据来源,不能对数据做出存储与分析,等于什么都没做,所以,我们需要对我们的数据作出存储与分析。

数据存储

我们这里还是使用Elasticsearch 做数据存储,如果对Elasticsearch有兴趣,希望学习的话,可以到我的专栏查看,我这里就不展开了,我这里给大家分享一下,我这边建立的数据存储索引。

PUT blogstar2018
{
  "mappings":{
    "blogstar2018":{
      "properties":{
        "link": {
          "type""keyword",
          "index"false
        },
        "blog_title":{
          "type""text",
          "analyzer""ik_smart",
          "fielddata"true
        },
        "description":{
          "type""text",
          "analyzer""ik_smart",
          "fielddata"true
        },
        "avatar_pic": {
          "type""keyword",
          "index"false
        },
        "original":{
          "type""long"
        },
        "fans":{
          "type""long"
        },
        "star":{
          "type""long"
        },
        "comment":{
          "type""long"
        },
        "level":{
          "type""long"
        },
        "visit":{
          "type""long"
        },
        "score":{
          "type""long"
        },
        "rank":{
          "type""long"
        },
        "medal":{
          "type""text",
          "analyzer""ik_smart",
          "fielddata"true
        },
        "colunms":{
          "type""nested",
          "properties": {
            "colunm_name":{
              "type""text",
              "analyzer""ik_smart",
              "fielddata"true
            },
            "colunm_count":{
              "type""long"
            },
            "colunm_read":{
              "type""long"
            }
          }
        },
        "blogstar_vote":{
          "type""object"
          "properties": {
            "user_id":{
              "type""keyword"
            },
            "user_addr":{
              "type""keyword"
            },
            "user_name":{
              "type""keyword"
            },
            "user_number":{
              "type""long"
            }
          }
        }
      }
    }
  }
}

数据字段不是很多,我这里就不再做详细分析了,这里需要注意,我们的文本使用的大多都是中文,所以我们需要使用中文的分词起IK_SMART。

数据分析

当我们成功抓取到数据并存储以后呢,我们需要对我们的数据加以分析才能够发挥出它的作用。

#! /usr/bin/env python3
# -*- coding:utf-8 -*-
import elasticsearch

class BlogStar2018(object):
    index = 'blogstar2018'
    es = elasticsearch.Elasticsearch(['sc.es.com:80'])

    @classmethod
    def index_doc(cls,body):
        cls.es.index(index=cls.index, doc_type=cls.index, body=body)

    @classmethod
    def match_all(cls):
        body = {
          "query": {
            "match_all": {}
          },
          "size"1000
        }
        try:
            res = cls.es.search(index=cls.index, doc_type=cls.index, body=body)
        except Exception as e:
            print('查询失败 ', str(e))
            res = None
        return res

    @classmethod
    def count_doc(cls):
        try:
            res = cls.es.count(index=cls.index, doc_type=cls.index,)
        except Exception as e:
            print('查询失败 ', str(e))
            return 0
        return res['count']

    @classmethod
    def stats_aggs(cls,field):
        body = {
          "size"0,
          "aggs": {
            "stats_"+field: {
              "stats": {
                "field": field
              }
            }
          }
        }
        try:
            res = cls.es.search(index=cls.index, doc_type=cls.index, body=body)
        except Exception as e:
            print('查询失败 ', str(e))
            res = None
        return res

    @classmethod
    def term_aggs(cls,field,size=10):
        body = {
          "size"0,
          "aggs": {
            "term_"+field: {
              "terms": {
                "field": field,
                "size": size,
                "order": {
                  "_key""desc"
                }
              }
            }
          }
        }
        try:
            res = cls.es.search(index=cls.index, doc_type=cls.index, body=body)
        except Exception as e:
            print('查询失败 ', str(e))
            res = None
        return res

    @classmethod
    def term_query(cls,field,value):
        body = {
          "query": {
            "bool": {
              "filter": {
                "term": {
                  field: value
                }
              }
            }
          }
        }

        try:
            res = cls.es.search(index=cls.index, doc_type=cls.index, body=body)
        except Exception as e:
            print('查询失败 ', str(e))
            res = None
        return res

    @classmethod
    def username_term_query(cls,field,value):
        body = {
          "query": {
            "bool": {
              "filter": {
                "term": {
                  field: value
                }
              }
            }
          },
          "_source": ["blogstar_vote.user_name"]
        }

        try:
            res = cls.es.search(index=cls.index, doc_type=cls.index, body=body)
        except Exception as e:
            print('查询失败 ', str(e))
            res = None
        return res

    @classmethod
    def stat_colunm_name(cls):
        body = {
            "aggs": {
                "colunms": {
                    "nested": {
                        "path""colunms"
                    },
                    "aggs": {
                        "colunm_name": {
                            "terms": {
                                "field""colunms.colunm_name",
                                "size"1000
                            }
                        }
                    }
                }
            }
        }
        try:
            res = cls.es.search(index=cls.index, doc_type=cls.index, body=body)
        except Exception as e:
            print('查询失败 ', str(e))
            res = None
        return res

好了,关于本次项目分析源码,大家可以前往https://github.com/Jaysong2012/tutorial查看。

目前,CSDN博客之星活动即将接近尾声,官方这次除了设置丰厚博客之星奖品外,还特地设置了投票奖,只要参与投票就有机会赢超跑免费驾驶。投票奖品由王子出行(王子车库科技有限公司)提供,秉承“让租车比买车更划算!”的商业理念,专注于为广大出行需求用户提供高端车型订制出行服务。

点击阅读原文或者扫描下方海报二维码即可参与投票,赢取开超跑机会。

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存