Python 网络爬虫微实战 (爬虫爬取贴吧消息词云显示) 超详解
写作缘由
现学习到Python的抓包与中间人爬虫,被各种反爬机制折磨的够呛。回想起刚接触爬虫时踩过的各种坑,想拿出来跟大家分享交流一下。闲暇时喜欢滑板,最近得知自己的滑板偶像再次夺得年度冠军,所以借此话题完成一次菜鸟入门级的贴吧爬虫微实战,以从未接触过爬虫的姿态将踩过的坑再悉数来一遍并由此分享各种bug以及解决问题与思考的过程。
就先放一张偶像的图片吧,(´・_・`)不知道怎么调整图片大小…
偶像信息:Nike赞助滑手,巴西天才滑板艺术家------Luan Oliveria
好像有点跑题了,回归正传。
写在前面
第一篇博客,首次使用Markdown,若排版不佳,词不达意,还望见谅。文章内容为原创,一些概念性的问题都是根据自己理解来叙述,如有雷同,不胜荣幸 请私信指出。
准备工作:
硬件:
- 500ml水----平缓多次遇到bug时的情绪
- 舒服的座椅
- 草稿纸,笔
软件:
- Pycharm(示例为2017版)
- chrome浏览器
- 必要时可以备上有道词典
知识储备:
- html网站开发知识少许(知道各个标签意思即可)
- 正则表达式
- 文件的基本读写
- Python基础知识少量(相信自己细致入微的描述能让大家清晰易懂)
爬虫作为一种提高效率的工具,学习时肯定需要静心投入,学成之时的喜悦与好处便会自然显现。爬虫涉及面蛮广:网页内容的提取仅仅是爬虫的一小部分,真正有技术含量和考验能力的时突破各种反爬虫策略以及应用一些反常态的技术获得数据。许多新手包括当时的我在内,都想着能快速做点什么神奇的操作,越快越好。本末倒置,失去了学习Python爬虫最初的目的与学习过程的乐趣。今天我们以百度贴吧滑板吧的某一精品贴为例,获取帖子里面所有的用户名、发帖内容和发帖时间,形成一个CSV表格 删繁就简,仅针对帖子单页内容与发帖内容,获取该页多有的发帖内容,最后以词云的形式呈现分析结果。
正式开始
Python第三方库的引用
import requests
import lxml.html
本文提及的库可以在任务命令窗口完成下载(左下角搜索框输入cmd)
pip install requests
pip install lxml
第三方库lxml有一定几率会安装不成功,可查找资料自行安装
这里我不会一次性将所有代码展示,而是将代码按照知识点分块,从而整个程序细化为小小的结点,逐个击破,最终实现微实战。
知识点 一:网页源代码的获取
示例网址:https://tieba.baidu.com/p/3679385687?pn=1&traceid=
特地选了一个文字量不大,网址短小的帖子。
html知识少许:chrome浏览器打开网站后在任意处单击右键,点击检查,会呈现出如下图片效果
了解html的朋友都知道,网页上呈现的所有文字,图片,超链接网址,排版方式等等都会呈现在这个检查元素当中,因为某些反爬原因我们当然不可能完完全全一清二楚的知晓。通过html5,Javascript,CSS,Query等等组合而成,我们本次示例中只需要用到html5的最基本的标签阅读知识即可,在图上显示的代码框中鼠标左击?一下,后**Ctrl+F(上图底部的查找框)**查找一条回复的关键字,代码会自动跳跃定位。这时候就可以解释一下lxml库的作用了:通过代码语句定位到你要获取的内容前后,随后进行提取。而刚刚说到所有文字信息都会呈现在源代码中,所以要想使lxml库发挥定位提取作用,就首先要获取该网页的html源代码。当时作为纯新手的我,首先想到的是如何复制粘贴,结果发现鼠标怎么拖动也不见效,这时候就需要先用到requests库,个人感觉该库比urllib要好用,理解起来更容易
request库具体用法:
html = requests.get('https://tieba.baidu.com/p/3679385687?pn=1').content.decode()
代码看起来有点长,我们来拆解一下
html = requests.get('网址') # 使用requests库中的get方法获取网页,得到一个Response对象
html_bytes = html.content # 使用.content()这个属性来显示字节型的网页源代码
html_string = html_bytes.decode()
单词decode:解码!将bytes型的网页源代码解码为字符串型源代码(这才是给人看的!),于是合并这三行代码即可以缩减为一行长代码。源代码获取完毕,可以使用print检查法检验一下成果。
print(html) # 在pycharm运行框显示大堆源代码
对requests一行长代码中的 ‘.属性’ 的理解: 最早在学习C++的时候了解到,.属性好像是用于传递一个什么参数或者方法给点号前面的,当时总是不理解,后来干脆用自己的“歪办法”:前后顺倒序各自像学英语那样“翻译”一遍。
正向 ‘翻译’ : 将点号理解成中文的 ‘的’ ,便是:requests(库)的get()方法(括号里面为网址)的content()属性的decode解码方法。
反向 ‘翻译’ : 将点号理解成中文的 ‘来自:’ decode解码方法/属性来自get方法中的content属性,而get方法又来自requests库。
小锦囊: '获取网页源代码的两种方式’
requests库中的获取网页方式:两种,get 和 post。get:顾名思义,获得,可直接通过输入网址访问的页面;post:邮递 只能通过另一个页面单击某个链接或者按钮以后跳转得到。现爬虫大多为get方式,所以"套路为":
html = requests.get('网址').content.decode()
第一步快速获取网页源代码完成
知识点 二:lxml库中xpath的用法
先来看看xpath的基本格式
import lxml.html
resolver = lxml.html.fromstring('网页源代码') # 网页源代码用刚刚聊的requests获得,该条命令用于解析html源代码,供后面的xpath操作
message = resolver.xpath('xpath语句') # 解释见下面
括号中的xpath就是你想要爬取的内容的"地址",这里就需要少许html识标签的基础:
注意看上图标黑代码对应的网页文字和红笔标出的地方,箭头们并不怎么听话,刚进去的时候箭头们都是缩着的,根本没办法看到各条评论在哪里。于是 ctrl+F一下,在搜索框随便搜索一条你看到的评论,快速"逼出"评论区域的代码段!!而后,可以发现每一条评论区都被包裹在div代码块中,且标签class为"d_post_content j_d_post_content",这时候为了谨慎起见,应该将这个标签放进 ctrl+F 产生的搜索框内,多次回车,跳跃检查一下是否每一条评论所在的div代码块中的class标签都是一样的,如下图:
经过多次快速而惊喜的跳动后发现都一样!!
只有你想要的内容们所对应的class标签都是一样的时候才好使用xpath,当然这里只是一小部分知识,有些ul,li标签根本就没有class给你跳来跳去,这个时候就要层层网上定位,用我自己总结的话来说就是倒序寻地标,知道找到唯一为止,这里也涉及一个相当非常重要的爬虫思想:先抓大在抓小,我们不可能每一次定位都能那么精确,恰巧我选用的这个网站比较好。不过这些都是后话了,超纲了我们今天微实战的主题哈。
开始使用xpath
刚刚说到括号中的xpath就是你想要爬取的内容的"地址",所以我们来看看xpath的语句格式:
# 分两种格式
# 获取文本,也就是获取文字信息
'//标签1[@属性1="属性值1"]/标签2[@属性2="属性值2"]/..../text()'
# 获取属性的值,class为属性名,class后面对应的d_post_content j_d_post_content为属性值
'//标签1//[@属性1="属性值1"]/标签2[@属性2="属性值2"]/..../@属性n'
中括号中的内容不是必须的,刚刚说过有些标签如ul,根本没得class给你搞。
好,开始djpost!
xpath('//div[@class="d_post_content j_d_post_content "]/text()')
有人会问,我看到那个class前面有个id呀,那个也是属性,我这里选用了class作为属性而已。完成后用print()检查法检查一下爬取成果
import lxml.html
resolver = lxml.html.fromstring('网页源代码') # 网页源代码用刚刚聊的requests获得
message = resolver.xpath('//div[@class="d_post_content j_d_post_content "]/text()')
print(message)
显示如下图,返回的是一个列表,是列表!
内容不多,够说明问题足矣,结果发现这里面有很多无用的空格,而且表情和图片回复无法识别(\u3000),这个时候就可以用.replace()这个属性来替换当中的字符串,具体用法:
robert = ' Robert love skatebording as well as python'
robert.replace(' ', "") # robert是我的英文名...,两个参数,前:旧字符串,后:新字符串
开始replace!!
a = message.replace(' ', "")
print(a) # 习惯性的print检查一下
结果报错。。
当然我在前面加粗说明了message返回的是list,是列表,是列表!
所以应该先str()一下转成字符串格式
message = str(message)
a = message.replace(' ', "")
print(a)
同理,替换掉检测不到的\u3000, 因为懒,给大家一个自我尝试的机会,实现如下图:
建议可以再来个这个为以上爬取到的信息能在即将创建的文件中能排成一竖显示:
.replace(',', "\n") # 在展示的txt文件中呈现换行后一竖的效果
告一段落
小反思:
- requests快速获取源代码格式
- html巧用ctrl+F逼出想要的内容,让小箭头里的内容不再畏畏缩缩
- lxml中的xpath用法
- 没事print一下看看成果
- 注释掉错误地方,错误段,再print一下看成果
- 一页的贴吧回复有时候未必少了一点(有些人只是回复个 ‘厉害啊’,就算了),可以使用同样的方法多爬取几页,只是网址不同罢了,写文件的时候顺便练习追加写将两个文件写在一起
爬取完毕,写入文件
with open('Luan.txt', 'w', encoding='utf-8') as f:f.write(message) # 这里的只有第一页的回帖信息
以偶像名字.txt命名的文件,‘w’:以写(write)的方式打开,如果本来不存就自动新建一个文本文件,如果本来存在就覆盖,记住是覆盖!!全部给你覆盖掉。如果希望能在后面追加写上第二页的,即在第一页的内容末尾录入爬取的第二页的信息,应用 ‘a’,append:添加 ,
with open('Luan.txt', 'a', encoding='utf-8') as f:f.write(message_2) # 第二页的信息message_2
于是就能看见pycharm左栏显示:
注意这里我重现了自己当时傻傻的开了两个py文件分别爬取第一第二页,这里有两个坑点:
- 如果你也想用这个 ‘笨办法’ 来重现菜鸟历程,那一定要注意第一页.py中的文件操作是’w’,创建了文件’Luan.txt’,而第二页.py中的文件操作则使用’a’,
在这个已经创建了的文件中追加写入新的信息。 - 第二个坑点真的让当时的我又好笑要好气,那就是 运行问题:第一页py运行完毕以后运行第二个,好,这是一轮,之后就不要再手多多再去又在去运行第一个或者第二个了,不管是哪个,运行几次结果都会得到意想不到的结果,下面举一下我当时傻乎乎的反复运行程序体会成功滋味的搞笑后果:(下面用1py与2py表示第一页和第二页的python文件)
- 运行1py以后再运行2py数次:发现每一次运行后Luan.txt文件的内容都增多,因为反复追加写如第二页爬取的数据。当时还傻乎乎的以为 真厉害,竟然越爬越多,自己真是优秀 后来无意对比一下才发现咦怎么各种数据每隔一段就重复 。。-_-
- 顺序运行完1py,2py后发现1py有些东西改,改完运行一下1py后发现数据无缘无故少了第二页的。。
- 各种出错的组合,总之记住当时如果是经历这种笨办法的话,一定是先一后二之后乖乖别动。
不过现在想起来提早犯这些错误也是不错的,更加超级深刻的理解文件的读写操作,身边不少朋友也是在w与a,覆盖写与追加写这两种方式上反复吃过亏。
最后的生成文档结果
可以看到第36行的 ][ 便是追加写紧跟在尾部的内容,这里共有七十多行。
词云显示
给大家看一张政治味很浓的图片就可以胜过我千言万语的解释了
轮廓告诉我们了要叙述内容是在中国这个国家,显示的所有词告诉我们各种大会的关注点,词的大小反映出关注度和在大会上出现的频率。让我瞬间想到也可以用来描述2019数学建模美国赛C题中的:美国合成类药物&毒品频率的显示,字内容改变,轮廓换成美国地图即可。在上总之一句话:使浏览网页者只要一眼扫过文本,就可以领略文本主旨,甚至能够记忆不少信息。
说干就干,如下图:
文档共有24065个词,重复出现程度未知,不知为何有点花,这是没用美国地图作为背景轮廓版(地图突然不能用了…).
词云准备工作:
如下库:
import jieba
from PIL import Image
from wordcloud import WordCloud # 用来生成词云的库
import matplotlib.pyplot as plt # Python画图库
from scipy.misc import imread # Python读取各类图像的库
from collections import Counter # Counter:计算器,计数员,用来统计词频
首先需要了解一点,中英词云构建难度不一样,中文要困难一些。
- 在生成词云之前都必须分词,即将词语按一定规范分切成单独的词,在英文的行文表达中,单词之间是通过空格作为自然分隔符的;而至于中文,只是字,句和段能通过明显的分隔符来简单的划分,唯独词用没有一个形式上的分隔符,举个栗子:
I love python. 根据空格自然就能实现分词:I/ love/ python
我爱爬虫。 没有类似空格的形式单独切分,理想条件应分成:我/ 爱/ 爬/ 虫
好在Python提供了强大的第三方库jieba,python有近三万个第三方库…至于jieba这个名字的来源,我的一个比较接地气的猜测,根据拼音:jieba jieba,截词霸王,简称截霸,虽然感觉有点傻,不过这样一来好理解多了。
因为第三方库的功能强大,可以直接上手使用,便使用边理解,并没有什么实质性的难度。快速过一下jieba的用法, 看看如下两段代码
- 出现红字不一定表示就有错误,还可能是built succesfully
- 精确模式:顾名思义,精确的截断词语,不会生成多余累赘的
- 反义:当cut后括号中的cut_all这个参数设置为True时为全模式,all:全部的;截断完以后还会生成一些累赘的
- join()属性的用法,点号前面跟你希望的连接形式,如
a = ','.join(被截词霸王截好的"残词") # 用逗号连接
从效果图也看到wordcloud会从含大量词语的文本中检索每个词出现的频率,出现频率最高的词显示在图片中也最大,最小的也最小,当然最大与最小词频的字号也可以自己设置。而令人惊喜的是,jieba亦有此功能,先一睹为快
Counter:计算者!括号中是被计算词频的内容,pycharm的一大优点是相关属性会给予你完整提示,输入完C,自动弹出counter,加个点号,自动弹出most_common() ,看代码英文也能理解就是选择词频排名前10的。但是输出结果有点不尽人意,单引号,换行符,u,a这些等等都不是我们想要的,为什么会有这样的情况呢,因为截词霸王太强有些词包括标点符号都被切分成单个单个的,而无用的东西有些如 ‘[’ 等等都是单独存在的,通常词语长度大于等于二就是有实际意义的了,这时候需要反复的对每一个小小的切片进行判断,大于等于二的才留下,所以可以用一个for循环。
这里贪快用了一个升级的for循环写法外加if与append函数内部嵌套,只放在一句代码里面
这样写比较酷,其实应该这样写,酷不酷其次,可读性第一
useful_words = [] # 习惯操作:定义一个空列表用于for循环中的append函数末尾添加的元素
for x in jieba.cut(content):if len(x) >= 2:useful_words.append(x)
词频试验成功
激动人心的一步,绘制词云(从无轮廓入手)
content = open('Luan.txt', encoding='utf-8') # 导入文件
my_list = list(content) # 用list()函数将导入的文件变成列表模式word_list = [" ".join(jieba.cut(sentence)) for sentence in my_list]
# 逐一从my_list列表中抽出句子来给jieba这家伙截,截了以后用join,以空格作为间隔连接起来
# 不用逗号连接起来的原因,避免不必要的干扰useful_list = ' '.join(word_list) # 这一行也很关键,消除换行符的影响
如果难以理解的话可以用之前讲的print()检验法来分别print一下word_list和useful_listwordcloud = WordCloud(font_path='simhei.ttf', # simhei.ttf:黑体background_color="black"
).generate(useful_list)
# generate()属性产生,WordCloud后的括号包裹住你希望生成的词云的各种属性
# 如:允许显示出来的最大词数,最大最大频率词的大小,等等
plt.imshow(wordcloud)
plt.axis('off') # axis: 轴,off掉,不显示坐标轴,否则会显示出以图片频率为xy轴的坐标轴
plt.show()
useful_list = ’ '.join(word_list)
这一行很重要,可以消除换行符的影响,如果还是不理解就可以用之前讲到的print检验法分别print一下useful_list和word_list
效果图!
现在通过图片说明一下我们能够了解到的讯息,比干说或者纯文档有趣易读多了!!!!
- 首先大家对偶像Luan Oliveria的主要评价是卧槽,男神
- 知道他的中国行常出没在深圳
- 当然,主角luan这个单词是会出现的
结合频率也还蛮高的楼主和视频我们也可以进行大胆猜想,常玩贴吧的朋友肯定不会陌生,如果楼层中总是出现楼主二字的话无非就两种情况:
- 楼主的帖子很棒,赞扬楼主或者鼓励楼主多搞点相关的东西,比如图中的视频
- 楼主的帖子太烂,骂楼主,但是我们没有在途中看到有对楼主的不满
- 乱入的客户端,会员在回复时会自带“来自xxx的会员”,所以“来自”二字也蛮大
既然喜欢滑板,那为什么不能将背景轮廓换成滑板呢?
只需要添加这两行代码
# 在WordCloud外添加
pac_mask = imread('滑板.jpg')# 在Wordcloud的括号里面添加元素
mask=pac_mask
pac_mask = imread('滑板.jpg')
wordcloud = WordCloud(font_path='simhei.ttf', # simhei.ttf:黑体background_color="black",mask=pac_mask
).generate(useful_list)
当然,滑板.jpg这个图片也是要精心挑选一下,看看我的:
虽然没有用偶像的人像,不过以后我会分享人像词云,人像词云会复杂一点,可能需要photoshop的阈值处理一下,简要概括一下图片选择的注意事项,否则像我前面的美国国旗那样显示不出来就搞笑了
- 背景最好纯白,清晰明了
- 不要过于花哨,人像图要经过处理,我以前是白雪公主人像,结果没有处理就直接上了,最后显示得像一坨印度飞饼。
- 其他就自己发掘和尝试了哈
生成图
程序优化
声明:
本篇博客的示例只是最最基础的哈,因为百度贴吧似乎没有什么反爬措施,我用基础方法如正则表达式,xpath或者beautifulsoup爬取等方法都试过几个贴吧,连请求头request甚至user-agent都不用换就可以了,也可能是我还没遇到,不过大麦网或者猫眼电影的演出,电影信息爬虫就遇到了反爬。所以只是以贴吧的一页为练习举例说明,没有涉及自动换页爬取等,中间用的一些语法语句也是比较 ‘低级’,甚至有点啰嗦冗余,不过我自己在爬虫纯新手的时候就是这么一步一步过来的哈,下面分享一下自己在爬虫基础入门以后的感想哈,看看各位大神们能不能找到自己当初的影子。
优化点&感悟:
- 因为爬取的源网址不止一个,所以可以定义一个专门用来获取源代码的函数
- 在替换字符串的过程中有点冗余,也建议定义函数
- 建议也定义写入文件的函数,因为网址也不止一个。
- 程序不是顺着写完的,而是一步一步调试,有时候会上下结合调试,分几个脚本写几个程序结合完成
- 当时想不到要定义函数没有关系,当发现有些代码需要重复使用的时候再想起来定义也不迟,这种情况遇到个几次以后就会醒悟需要定义函数了。
- 爬虫获取数据不是仅靠xpath方法就可以了,建议正则表达式,lxml,beautifulsoup都是一下,有时候可以结合使用,后两者有时候似乎都不行的时候可以像做高中数学题一样用re库(regular expression)正则表达式这个 ‘笨办法’ 试一下
- 不求一次写出完全工整简洁的代码,先将功能实现,其次优化后考虑可读性,再考虑维护性,艺不艺术性就看个人了哈
写在后面
以滑板经历引出,编程与滑板一样都不容易,滑板不为耍酷,只求轻松自由的感觉;编程不为炫技,只求解决问题的成就感与充实感。新的一年,对各位的祝福如下:
- 各位祝福如下:学业有成、瘦成维密,有奖即中,买包有货,买鞋有码,口红有号,买课有劵。
- 借用自家对联顺祝:祝大家未来: 面包爱情两得意,前程似锦超容易!
好我先去滑板了。
Python 网络爬虫微实战 (爬虫爬取贴吧消息词云显示) 超详解相关推荐
- 每日一练:Python爬虫爬取全国新冠肺炎疫情数据实例详解,使用beautifulsoup4库实现
Python 爬虫篇 - 爬取全国新冠肺炎疫情数据实例详解 效果图展示 第一章:疫情信息的下载与数据提取 ① 爬取页面数据到本地 ② json 字符串正则表达式分析 ③ 提取数据中的 json 字符串 ...
- python爬豆瓣电视剧_python requests库爬取豆瓣电视剧数据并保存到本地详解
首先要做的就是去豆瓣网找对应的接口,这里就不赘述了,谷歌浏览器抓包即可,然后要做的就是分析返回的json数据的结构: https://movie.douban.com/j/search_subject ...
- python爬取微博数据词云_用Python爬取微博数据生成词云图片
原标题:用Python爬取微博数据生成词云图片 欢迎关注天善智能 hellobi.com,我们是专注于商业智能BI,大数据,数据分析领域的垂直社区,学习.问答.求职,一站式搞定! 对商业智能BI.大数 ...
- 爬取唐诗宋词生成词云
Python 高并发线程爬取诗词之诗词分析 本节所讲内容: 1.5分钟快速了解爬虫概念 2.beautifulsoup 匹配原则 3.wordcloud 使用详情 实战:爬取中国唐诗宋词,体验文人雅士 ...
- python网络爬虫--项目实战--scrapy爬取人人车(5)
一.目标 爬取多页人人车的车辆信息 二.分析 2.1 网站分析 在网页源代码中可以搜索到页面中的数据,所以可以判断该页面为静态加载的 三.完整代码 renrenche.py import scrapy ...
- Python网络爬虫--项目实战--scrapy爬取人人车
一.目标 爬取多页人人车的车辆信息 二.分析 2.1 网站分析 在网页源代码中可以搜索到页面中的数据,所以可以判断该页面为静态加载的 三.完整代码 renrenche.py import scrapy ...
- python壁纸数据抓取_python爬虫多线程实战:爬取美桌1080p壁纸图片
本人纯手工码字哦,请耐心看完,有信心可以带你完整学会这个实战案例 一.需求分析: 1.下载 http://www.win4000.com/wallpaper.html 下指定分类 指定尺寸 的图片 2 ...
- python3 [爬虫入门实战]scrapy爬取盘多多五百万数据并存mongoDB
总结:虽然是第二次爬取,但是多多少少还是遇到一些坑,总的结果还是好的,scrapy比多线程多进程强多了啊,中途没有一次被中断过. 此版本是盘多多爬取数据的scrapy版本,涉及数据量较大,到现在已经是 ...
- 爬虫爬取新闻并生成词云
爬取豆瓣应用的函数同样可以用来爬取新闻.这里面主要是正则表达式的提取和对爬出的链接再进行爬取解析.对网页不 import re # 正则 from bs4 import BeautifulSoup # ...
- python爬取豆瓣影评生成词云的课程设计报告_简单爬取《小丑》电影豆瓣短评生成词云...
导语 在前段时间看了杰昆菲尼克斯的小丑电影,心里很好奇大部分观众看完这部电影之后对此有什么评价,然后看了看豆瓣短评之后,觉得通过python把短评中出现最多的单词提取出来,做成一张词云,看看这部电影给 ...
最新文章
- python 启动django时报错MySQLdb._exceptions.OperationalError: (2059, <NULL>)和django.db.utils.OperationalEr
- linux虚拟机如何加网卡,linux虚拟机添加新的网卡
- 认识Java中volatile关键字
- jquery动态插入行,不用拼写html,简洁版
- ios 图片居中裁剪_iOS实现图片的缩放和居中显示
- jrtplib linux编译,jrtplib+jthread 交叉编译
- dcp7080d怎么加墨粉_兄弟打印机DCP 7080D提示更换墨粉该怎么办-
- U盘写保护,不能被格式化
- macbook重装系统 选择方案_Mac 重装系统
- VMware Workstation 英文改中文界面
- 低电压的1.8V SDHC 接口静电保护
- supervisor+cesi多服务器进程集中管理
- flume多节点集群搭建
- 编程软件IAR安装使用及程序下载
- hash路由实现微信登陆后的重定向
- 李建忠设计模式——策略模式Strategy
- JavaScript进阶(三)
- 网易视频云郭再荣:打造一体化多场景的视频云平台
- matlab模拟出现较大误差是什么原因,【求助】matlab 对复杂计算会出现较大误差吗?...
- 小组取什么名字好_唐三和其他女神组CP取什么名字?当看到答案后,瞬间让人甜到掉牙...