python爬虫实战(五) 爬取微博明星粉丝基本信息+可视化

目录

        • 一、微博爬虫类型介绍
        • 二、明星粉丝信息爬虫
        • 三、可视化

一、微博爬虫类型介绍

微博有关的爬虫,由于根据网址的不同,可分为三种类型:

1.移动端爬取:利用selenium去模拟登录然后再去爬取,比较麻烦,但是可以根据个人需求依据关键词进行指定爬取。

2.手机端爬取:网址为手机端微博网址,这在我之前的博客中也有提及微博超话内容爬取,在此不再赘述。无需登录,利用Chrome进行抓包即可实现,而且较selenium来说,性能也是更高一点。(不过要注意设置随机睡眠时间

3.旧版网址爬取:这是微博最简化版本的网址weibo.cn(感觉回到了诺基亚的年代),界面简单无需抓包,直接利用正则或者xpath等方式提取即可(需要拿到登录后的cookie值)

建议:如果只是爬取指定用户的评论、基本信息这些,后两种方法就够用了;如果涉及到更复杂的需求时再考虑selenium爬取

二、明星粉丝信息爬虫

旧版的网址,粉丝数量只显示了前20页,一页10个,总共才200个。手机端下的粉丝列表总共也只能显示5000个,对于5000个之后的粉丝信息,微博出于隐私保护,是爬取不了。详细的情况可参考新浪微博如何获取用户全部粉丝列表

1.微博用户URL说明:
旧版网址用户首页的URL为:http://weibo.cn/u/用户ID
手机端用户首页的URL为:https://m.weibo.cn/u/用户ID
以邓紫棋为例,她的ID为1705586121,首页如下:
在这里插入图片描述

她的粉丝信息抓包结果如下:
在这里插入图片描述
在这里插入图片描述
这里的用户信息只有用户的简介(screen_name)关注数(follow_count)粉丝数(follow_count),对应用户性别和地区是没有显示的。

所以,考虑先用手机端爬取用户ID,再用旧版网址登录,提取信息。(旧版网址个人信息更全一些)

2.爬取邓紫棋粉丝所有的user_id
考虑到部分用户可能是机器人,初步筛选条件为粉丝数大于等于20

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
import requests
import json
import random
import os
os.chdir('C:/Users/dell/Desktop')
import time
base_url='https://m.weibo.cn/api/container/getIndex?containerid=231051_-_fans_-_1705586121&since_id='
head=[
    "Opera/12.0(Windows NT 5.2;U;en)Presto/22.9.168 Version/12.00",
    "Opera/12.0(Windows NT 5.1;U;en)Presto/22.9.168 Version/12.00",
    "Mozilla/5.0 (Windows NT 5.1) Gecko/20100101 Firefox/14.0 Opera/12.0",
    "Opera/9.80 (Windows NT 6.1; WOW64; U; pt) Presto/2.10.229 Version/11.62",
    "Opera/9.80 (Windows NT 6.0; U; pl) Presto/2.10.229 Version/11.62",
]
header={
    'user-agent':random.choice(head)
}
with open('user_id.txt','w') as f:
    for page in range(2,251): #注意是从2开始才是用户信息
        try:
            url=base_url+str(page)
            r=requests.get(url,headers=header)
            data=json.loads(r.text)
            all_user=data['data']['cards'][0]['card_group']
            for user in all_user:
                fans=int(user.get('desc2').split(':')[1])
                if fans >=20:
                    f.write(str(user.get('user')['id'])+'\n')
            print('第{}页用户id爬取完毕'.format(page))
            time.sleep(random.randint(1,3))
        except:
            print('未爬到数据')

爬取的部分用户ID如下:
在这里插入图片描述

3.以旧版本界面,爬取粉丝基本信息

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
33
34
35
36
37
38
39
40
41
42
import numpy as np
import pandas as pd
import requests
from lxml import etree
import random
import time
header={'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36',
       'cookie':'你自己的cookie',
       'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
       }
url_new='https://weibo.cn/u/'
data=[]
count=0  
def  get_id(path):
    with open(path,'r') as f:
        user_id=f.readlines()
        user_id=np.char.rstrip(user_list,'\n')
        return user_id
def gethtml(url,header):
    r=requests.get(url,headers=header)
    if r.status_code==200:
        return r.text
    else:
        print('网络连接异常')
for user_id in get_id('user_id.txt'):
    try:
        url=url_new+user_id
        r_text=gethtml(url,header)
        html=etree.HTML(r_text.encode('utf-8'))
        user_name=html.xpath('//span[@class="ctt"]/text()')[0]
        inf=html.xpath('//span[@class="ctt"]/text()')[1]
        weibo_number=html.xpath('//div[@class="tip2"]/span[@class="tc"]/text()')[0].replace('微博','').strip('[]')
        focus_number=html.xpath('//div[@class="tip2"]/a[1]/text()')[0].replace('关注','').strip('[]')
        fan_number=html.xpath('//div[@class="tip2"]/a[2]/text()')[0].replace('粉丝','').strip('[]')
        data.append([user_name,inf,weibo_number,focus_number,fan_number])
        count+=1
        print('第{}个用户信息写入完毕'.format(count))
        time.sleep(random.randint(1,2))
    except:
        print('用户信息不完全')
df=pd.DataFrame(data,columns=['user_id','inf','weibo_num','focus_num','fans_num'])
df.to_csv('weibo_user.csv',index=False,encoding='gbk')

最终爬取到粉丝数量大于等于20的用户共1362个,清洗后的部分数据如下:
在这里插入图片描述

三、可视化

(1) 粉丝性别占比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import matplotlib.pyplot as plt
plt.style.use('ggplot')
plt.rcParams['font.sans-serif']=['SimHei']
%config InlineBackend.figure_format='svg'
df=pd.read_csv('weibo_user.csv',encoding='gbk')
gender=pd.DataFrame(df['gender'].value_counts())
plt.pie(gender['gender'],
        labels=['女','男'],
        startangle=90,
        shadow=False,
        autopct='%1.1f%%',
        textprops={'fontsize': 13, 'color': 'w'},
        colors=['#f26d5b','#2EC4B6']
       )
plt.legend(loc='best')
plt.title('邓紫棋粉丝性别占比')
plt.axis('equal')  
plt.tight_layout()
plt.show()

从饼图来看,女粉较男粉稍多一些

(2) 全国粉丝分布情况
注意:使用的是pycharts最新版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pyecharts.charts import Map
from pyecharts import options as opts
loc=pd.DataFrame(df['loc'].value_counts()).drop(loc.index[0]) #去除其他
city=np.char.rstrip(list(loc.index))
map1=Map(init_opts=opts.InitOpts(width="1200px",height="800px"))
map1.set_global_opts(
    title_opts=opts.TitleOpts(title="邓紫棋粉丝地区分布"),
    visualmap_opts=opts.VisualMapOpts(max_=200, is_piecewise=True,
    pieces=[
    {"max": 200, "min": 150, "label": ">150", "color": "#754F44"},
    {"max": 149, "min": 100, "label": "100-149", "color": "#EC7357"},
    {"max": 99, "min": 50, "label": "50-99", "color": "#FDD692"},
    {"max": 49, "min": 1, "label": "1-49", "color": "#FBFFB9"},
    {"max": 0, "min": 0, "label": "0", "color": "#FFFFFF"},
    ], ))  #最大数据范围,分段

map1.add("",[list(z) for z in zip(city,loc['loc'])],
maptype='china',is_roam=False,
        is_map_symbol_show=False)
map1.render('fans_loc.html')

在这里插入图片描述
从地区分布来看,广东粉最多,其次在四川、湖北、河南、山东、江苏、浙江分布较多

(3) 性别与地区交叉分布情况

1
2
3
4
5
6
7
df_new=df[~df['loc'].isin(['其他'])]
index=df_new.groupby('loc').count().sort_values(by='gender',ascending=False).index
plt.figure(figsize=(15,6))
sns.countplot(data=df_new,x='loc',hue='gender',order=index,palette=['#f26d5b','#2EC4B6'])
plt.xticks(rotation=90)
plt.legend(loc='upper right')
plt.show()

在这里插入图片描述
大部分省份女粉是大于男粉的,也有部分地区如北京、上海、安徽等,男粉丝数量大于女粉数量。

(4) 用户微博数、关注数、粉丝量基本情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
gender_group=df.pivot_table(aggfunc=np.mean,
                            index=df['gender'],
                           values=df[['weibo_num','focus_num','fans_num']]).round(0)

f,ax=plt.subplots(1,3,figsize=(15,6))
sns.barplot(x=gender_group.index,y=gender_group['weibo_num'],
            palette=['#f26d5b','#2EC4B6'],alpha=0.8,ax=ax[0])
ax[0].set_title('男女粉丝平均微博数')
ax[0].set_xlabel('性别')
ax[0].set_ylabel('平均微博数')

sns.barplot(x=gender_group.index,y=gender_group['focus_num'],
            palette=['#f26d5b','#2EC4B6'],alpha=0.8,ax=ax[1])
ax[1].set_title('男女粉丝平均关注数')
ax[1].set_xlabel('性别')
ax[1].set_ylabel('平均关注数')

sns.barplot(x=gender_group.index,y=gender_group['fans_num'],
            palette=['#f26d5b','#2EC4B6'],alpha=0.8,ax=ax[2])
ax[2].set_title('男女粉丝平均粉丝数')
ax[2].set_xlabel('性别')
ax[2].set_ylabel('平均粉丝数')
plt.suptitle('邓紫棋粉丝微博基本信息') #子图添加总标题
plt.show()

在这里插入图片描述

(5) 微博数、关注量、粉丝量相关性

1
2
3
4
plt.figure(figsize=(10,6))
sns.heatmap(df[['weibo_num','focus_num','fans_num']].corr(),cmap='YlGn',annot=True)
plt.title('微博数、关注数、粉丝数相关热力图')
plt.show()

在这里插入图片描述
相关系数的范围在-1到1之间。越接近1,正相关性越强,越接近-1,负相关性越强。
从上图来看,基本上三者之间的相关性还是很弱的,也就微博数与关注数相关性相对较高一点,但也没有超过0.5。

需要原数据的可以在评论回复"weibo"