Loading...
墨滴

Peter

2021/03/22  阅读:38  主题:萌绿

爬虫案例-BD贴吧数据分析

百度贴吧之旅

本文中介绍的如何通过 来爬取百度贴吧中的内容,并且通过Jieba分词和wordcloud来实现图云展示

选取的话题是:为何大学女生比男生看起来有钱得多

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

项目成果

  • 如何获取网页源码
  • re模块中正则表达式的使用
  • 如何写入csv文件
  • jieba分词
  • wordcloud绘制图云

爬取内容

()表示提取其中的内容,找到3个需要提取信息的源码特点,写出了如下的正则表达式

  • 名称:class="d_name".*?target="_blank">([\u4e00-\u9fa5+\w])
  • 内容:class="d_post_content j_d_post_content.*?">(.*?)<
  • 时间:class="tail-info">(20.*?)<

所有的时间都以20开头

数据

爬取的数据总共有15页,每页大概30条信息

单个网页

导入库

import re   # 正则表达式
import requests  # 获取网页内容
import csv  #  保存成csv文件
import pandas as pd  # 数据处理

# 显示所有列
# 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://tieba.baidu.com/p/6792642821?pn=1"
headers = {"User-Agent""实际请求头"}

response = requests.get(url=url,headers=headers)   # 得到响应
res = response.content.decode('utf-8''ignore')  # 获取整个网页源码

正则使用

username_list

通过获取username_list为例,来讲解正则表达式的使用

效果如下:我们只需要username_list部分,保存到相应的列表中

content_list

元素如果为空,表示回复的是图片,无法抓取到相应的内容

reply_time_list

源码

# 爬取单个网页的内容到tieba1.csv中

import re
import requests
import csv
import pandas as pd

url = "https://tieba.baidu.com/p/6792642821?pn=1"
headers = {"User-Agent""更换成实际请求头"}

response = requests.get(url=url,headers=headers)   # 得到响应
res = response.content.decode('utf-8''ignore')  # 获取源码

username_all = re.findall('class="d_name".*?target="_blank">(.*?)</a>',res,re.S)
username_list = []

for name in username_all:
    user = re.sub("<img.*?/>","",name)  # 将username中的img标签去掉
    username_list.append(user)

content_list = re.findall('"d_post_content j_d_post_content.*?">(.*?)<',res,re.S)
reply_time_list = re.findall('class="tail-info">(20.*?)<',res,re.S)

result_list = []
for i in range(len(username_list)):
    result = {
        "username": username_list[i],
        "content":content_list[i],
        "reply_time":reply_time_list[i]
    }
    result_list.append(result)

# 保存文件:csv模块使用
with open("tieba1.csv""a", encoding="utf-8"as f:
    writer =csv.DictWriter(f,fieldnames=["username","content","reply_time"])  # 构造实例化对象
    writer.writeheader()  # 写入表头
    writer.writerows(result_list)   # 将列表中的内容全部写入实例对象中

全网爬取

过程

  • format()方法实现URL地址的更新
  • requests库的使用
  • 正则表达式获取3项内容
  • csv模块使用,写入到文件中
import re
import requests
import csv
import pandas as pd

# 总共有15页
for i in range(1,16):  
    url = "https://tieba.baidu.com/p/6792642821?pn={}".format(i)  # 通过format函数实现
    headers = {"User-Agent""实际请求头"}

    response = requests.get(url=url,headers=headers)   # 得到响应
    res = response.content.decode('utf-8''ignore')  # 获取源码
    # 先获取名称,有些名称是带有img标签的,需要去掉
    username_all = re.findall('class="d_name".*?target="_blank">(.*?)</a>',res,re.S)

    username_list = []
    for name in username_all:
        user = re.sub("<img.*?/>","",name)  # 将img标签替换成空格,得到完整的只含有中文和英文字母的标签
        username_list.append(user)   # 将全部的user放到username_list中

    content_list = re.findall('"d_post_content j_d_post_content.*?">(.*?)<',res,re.S)
    reply_time_list = re.findall('class="tail-info">(20.*?)<',res,re.S)
    
    result_list = []
    for j in range(len(username_list)):
        result = {
            "username": username_list[j],
            "content":content_list[j],
            "reply_time":reply_time_list[j]
        }
        result_list.append(result)

    with open("tieba.csv""a", encoding="utf-8"as f:  # 将写入的模式改成"a":表示追加模式
        writer =csv.DictWriter(f,fieldnames=["username","content","reply_time"])
        writer.writeheader()
        writer.writerows(result_list)
    
df = pd.read_csv("tieba.csv")  # 读取文件
df

全网数据

通过爬取15个页面得到的数据如下的表格:

  • 3个属性字段
  • 464条记录

我们进行的处理是content字段,分析哪些词语是高频词语

Jieba

参考

https://blog.csdn.net/FontThrone/article/details/72782971

https://github.com/fxsjy/jieba

安装jieba

这是我第一次使用jieba来进行分词操作,所以需要先进行安装

pip install -i https://pypi.douban.com/simple jieba

分词

  • jieba.cut 方法接受4个输入参数:
    1. 需要分词的字符串
    2. cut_all 参数用来控制是否采用全模式
    3. HMM 参数用来控制是否使用 HMM 模型
    4. use_paddle 参数用来控制是否使用paddle模式下的分词模式,paddle模式采用延迟加载方式,通过enable_paddle接口安装paddlepaddle-tiny,并且import相关代码;
  • jieba.cut_for_search 方法接受2个参数:
    1. 需要分词的字符串;
    2. 是否使用 HMM 模型。该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细

注意点

  1. 待分词的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建议直接输入 GBK 字符串,可能无法预料地错误解码成 UTF-8

  2. jieba.cut以及jieba.cut_for_search返回的结构都是一个可迭代的generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode),或者用

  3. jieba.lcut以及jieba.lcut_for_search直接返回 list

  4. jieba.Tokenizer(dictionary=DEFAULT_DICT)新建自定义分词器,可用于同时使用不同词典。jieba.dt` 为默认分词器,所有全局分词相关函数都是该分词器的映射。

demo

这个是jiebaGitHub上面给的一个入门案例,仅供参考学习

# encoding=utf-8
import jieba

jieba.enable_paddle()# 启动paddle模式。 0.40版之后开始支持,早期版本不支持
strs=["我来到北京清华大学","乒乓球拍卖完了","中国科学技术大学"]
for str in strs:
    seg_list = jieba.cut(str,use_paddle=True# 使用paddle模式
    print("Paddle Mode: " + '/'.join(list(seg_list)))

seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list))  # 全模式

seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精确模式

seg_list = jieba.cut("他来到了网易杭研大厦")  # 默认是精确模式
print(", ".join(seg_list))

seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  # 搜索引擎模式
print(", ".join(seg_list))

处理

  1. 我们需要处理的content字段。jieba处理的是列表类型的数据,所以现将全部的content字段中的信息放到一个列表中:
  1. 将上述步骤中实现的列表strings中每个字符串进行分词
# encoding=utf-8
import jieba
    
for i in range(len(strings)):
    seg_list = jieba.cut(strings[i].strip(), cut_all=False)  # seg_list只是一个generator生成器:<class 'generator'>
    print(("Default Mode: " + "/ ".join(seg_list)))  #  用list方法展开

分词之后的效果如下:

  1. 将分词的结果放入到另一个列表comment中,方便后续wordcloud的处理
# encoding=utf-8
import jieba

comment = [] 
for i in range(len(strings)):
   # 对每个元素分别进行分词操作
    seg_list = jieba.cut(strings[i].strip(), cut_all=False)  # seg_list只是一个generator生成器:<class 'generator'>  ;用list(seg_list))  #  用list方法展开
    
    for str in list(seg_list):   # 对lsit(seg_list)中的每个元素进行追加
        comment.append(str)
        
comment

wordcloud

参考

wordcloud十五分钟快速入门与进阶

GitHub-wordcloud

wordcloud

安装

第一次使用,所以还是需要先进行安装

pip install -i https://pypi.douban.com/simple wordcloud

数据处理

wordcloud处理的是一个字符串信息。因此,在进行绘制词云图之前,我们需要先将上面comment字典中的全部元素放置在一起,然后转成一个整体的字符串。

text = " ".join(i for i in comment)
text

效果如下:

生成词云

初步处理

wordcloud生成词云图是依赖Matplotlib包,所以需要先安装Matplotlib(如果没有)

# -*- coding: utf-8 -*-

from wordcloud import WordCloud
import matplotlib.pyplot as plt

text = " ".join(i for i in comment)   # 待处理的字符串

# 先下载SimHei.ttf字体,放置到自己的某个目录下,然后将font换成自己的目录即可
font = r'/Users/peter/Desktop/spider/SimHei.ttf'   # 改成相应的字体的路径即可
wc = WordCloud(collocations=False, font_path=font, # 路径
               max_words=2000,width=4000
               height=4000, margin=4).generate(text.lower())

plt.imshow(wc)
plt.axis("off")
plt.show()

wc.to_file('show_Chinese.png')  # 把词云保存下来

wordcloud默认的图片背景生成的初步效果如下:

进一步处理

从上面的词云图中看出来,有多个非常明显没有任何价值的字符信息,比如:

"男生","女生","就是","因为","可以","不是","自己","什么","知道","这个","地方","而且","东西","这些","或者","基本","所以","怎么","时候","你们","我们","content","女孩","的","我",",","视频","关于","也","就","了","*","是","不",","

大写的 占据了整个词云图的绝大部分空间,这样的效果肯定不是我们需要的。我们先在待处理的信息中将它们人为地删除掉:

# 人为设置无效的信息
noUse = ["男生","女生","就是","因为","可以","不是","自己","什么","知道","这个","地方","而且","东西","这些","或者","基本","所以","怎么","时候","你们","我们","content","女孩","的","我",",","视频","关于","也","就","了","*","是","不",","]

for col in noUse:  # 对于noUse中的每个元素,如果也在comment中,则将comment中将其删除
    while col in comment:
        comment.remove(col)

进行删除操作之后,comment中的无效信息会被删除,再绘制一次词云图。

不同的是,在这里使用了一个图片作为最终结果的背景:

# 人为设置无效的信息
noUse = ["男生","女生","就是","因为","可以","不是","自己","什么","知道","这个","地方","而且","东西","这些","或者","基本","所以","怎么","时候","你们","我们","content","女孩","的","我",",","视频","关于","也","就","了","*","是","不",","]

for col in noUse:  # 对于noUse中的每个元素,如果也在comment中,则将comment中将其删除
    while col in comment:
        comment.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 comment)   

# 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/peter/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('show_Chinese_three.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('show_Chinese_four.png')  # 把词云保存下来

效果如下:

另一种颜色对比:

结论

关键词

从改进后的词云图中可以看出来,回复的评论中出现频率高、且有意义(作者的意见😃)的词语是:

  • 家里
  • 有钱
  • 男朋友
  • 电脑、游戏、键盘(应该是男生相关的)
  • 穷养
  • 富养
  • 花钱
  • 化妆品

结论

  1. 男生都喜欢电脑和游戏,把钱花在了游戏和装备上
  2. 女生则喜欢化妆品
  3. 大学女生的钱可能一部分来自家庭,一部分来自男朋友
  4. 家庭对男生、女生的养育态度不同:穷养与富养

大学女生就是有钱😊

Peter

2021/03/22  阅读:38  主题:萌绿

作者介绍

Peter

厦门大学