百度贴吧之旅
本文中介绍的如何通过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条信息
单个网页
导入库
代码语言:javascript复制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使用
代码语言:javascript复制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
源码
代码语言:javascript复制# 爬取单个网页的内容到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个输入参数:- 需要分词的字符串
- cut_all 参数用来控制是否采用全模式
- HMM 参数用来控制是否使用 HMM 模型
- use_paddle 参数用来控制是否使用paddle模式下的分词模式,paddle模式采用延迟加载方式,通过enable_paddle接口安装paddlepaddle-tiny,并且import相关代码;
jieba.cut_for_search
方法接受2个参数:- 需要分词的字符串;
- 是否使用 HMM 模型。该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细
注意点
- 待分词的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建议直接输入 GBK 字符串,可能无法预料地错误解码成 UTF-8
- jieba.cut
以及
jieba.cut_for_search返回的结构都是一个可迭代的
generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode),或者用
- jieba.lcut
以及
jieba.lcut_for_search直接返回 list
- jieba.Tokenizer(dictionary=DEFAULT_DICT)
新建自定义分词器,可用于同时使用不同词典。
jieba.dt` 为默认分词器,所有全局分词相关函数都是该分词器的映射。
demo
这个是jieba
在GitHub
上面给的一个入门案例,仅供参考学习
# 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))
处理
- 我们需要处理的
content
字段。jieba
处理的是列表类型的数据,所以现将全部的content
字段中的信息放到一个列表中:
- 将上述步骤中实现的列表
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方法展开
分词之后的效果如下:
- 将分词的结果放入到另一个列表
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
安装
第一次使用,所以还是需要先进行安装
代码语言:javascript复制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
默认的图片背景生成的初步效果如下:
进一步处理
从上面的词云图中看出来,有多个非常明显没有任何价值的字符信息,比如:
代码语言:javascript复制"男生","女生","就是","因为","可以","不是","自己","什么","知道","这个","地方","而且","东西","这些","或者","基本","所以","怎么","时候","你们","我们","content","女孩","的","我",",","视频","关于","也","就","了","*","是","不",","
大写的color{red}{男生、女生}占据了整个词云图的绝大部分空间,这样的效果肯定不是我们需要的。我们先在待处理的信息中将它们人为地删除掉:
代码语言:javascript复制# 人为设置无效的信息
noUse = ["男生","女生","就是","因为","可以","不是","自己","什么","知道","这个","地方","而且","东西","这些","或者","基本","所以","怎么","时候","你们","我们","content","女孩","的","我",",","视频","关于","也","就","了","*","是","不",","]
for col in noUse: # 对于noUse中的每个元素,如果也在comment中,则将comment中将其删除
while col in comment:
comment.remove(col)
进行删除操作之后,comment
中的无效信息会被删除,再绘制一次词云图。
不同的是,在这里使用了一个图片作为最终结果的背景:
代码语言:javascript复制# 人为设置无效的信息
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') # 把词云保存下来
效果如下:
另一种颜色对比:
结论
关键词
从改进后的词云图中可以看出来,回复的评论中出现频率高、且有意义(作者的意见?)的词语是:
- 家里
- 有钱
- 男朋友
- 电脑、游戏、键盘(应该是男生相关的)
- 穷养
- 富养
- 花钱
- 化妆品
结论
- 男生都喜欢电脑和游戏,把钱花在了游戏和装备上
- 女生则喜欢化妆品
- 女生的钱可能一部分来自家庭,一部分来自男朋友
- 家庭对男生、女生的养育态度不同:穷养与富养