Fork me on GitHub

BaiDu-TieBa

百度贴吧之旅

本文中介绍的如何通过$\color{red}{正则表达式}$来爬取百度贴吧中的内容,并且通过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条信息

单个网页

导入库

1
2
3
4
5
6
7
8
9
10
11
12
13
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使用

1
2
3
4
5
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

源码

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
# 爬取单个网页的内容到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模块使用,写入到文件中
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
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来进行分词操作,所以需要先进行安装

1
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上面给的一个入门案例,仅供参考学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 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中每个字符串进行分词
1
2
3
4
5
6
# 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的处理
1
2
3
4
5
6
7
8
9
10
11
12
# 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

安装

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

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

数据处理

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

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

效果如下:

生成词云

初步处理

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- 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默认的图片背景生成的初步效果如下:

进一步处理

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

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

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

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

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

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

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

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 人为设置无效的信息
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. 家庭对男生、女生的养育态度不同:穷养与富养

大学女生就是有钱😊

本文标题:BaiDu-TieBa

发布时间:2020年07月12日 - 23:07

原始链接:http://www.renpeter.cn/2020/07/12/BaiDu-TieBa.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Coffee or Tea