大家好!我是霖hero
到点了上号网易云,很多人喜欢到夜深人静的时候,在网易云听音乐发表评论,正所谓:自古评论出人才,千古绝句随口来,奈何本人没文化,一句卧槽行天下!评论区集结各路大神,今天我们来爬取网易云音乐评论并做个词云图,看看大家都评论了啥。
爬取分析
首先我们打开网易云的某首歌曲,打开开发者工具,如下图所示:
通过观察可以发现,网易云音乐评论存放在class=cmmts j-flag的div中,那么我们打开网页的源代码键盘按Ctrl+F看看网页源代码有没有我们想要的音乐评论数据,如下图所示:
我们发现网页源代码没有音乐评论数据,我们推测网页是经过JavaScript处理数据后生成的结果,这些数据有可能是Ajax加载出来的,打开开发者工具,切换到Netword选项卡,重新刷新页面并下滑,可以发现这里出现了很多的条目,看看有没有和评论相关的Ajax请求,如下图所示:
通过观察可以发现音乐评论数据存放在get?csrf_token=请求中,但通过观察发现get?csrf_token=请求中没有任何规律,唯一不同的是Form Data中的params和encSecKey参数在每个get?csrf_token=都不一样,如下图所示:
通过这么大段人类看不懂的参数,可以推测这些参数是通过特殊加密方式来进行加密的参数,那么我们要破解这种特殊的加密方式,找到未加密过的参数并构造该特殊加密的函数进行对未加密的参数进行加密,这样就可以获取到我们要的网页请求的评论数据了。
很明显,这种情况下让初学爬虫的小白来破解,几乎不可能,那么怎么办好呢。对于初学爬虫的小白或者要求爬虫效率不高的爬虫项目来说,使用selenium自动化测试工具来进行爬取是个不错的选择。
正式爬取
在爬取之前,我们先来简单了解一下selenium是什么。
selenium
selenium是一个自动化测试工具,对于网络请求接口有很多加密参数,其规律难找,很难使用分析Ajax、通过JavaScript渲染加载得到的网页来说,selenium自动化测试工具是个不错的选择,我们可以通过selenium来模拟浏览器的运行方式,获取当前浏览器呈现网页的源代码,做到所见即所爬。
在使用selenium前,我们要先安装好selenium库和配置好ChromeDriver驱动。
selenium库的安装如下
pip install selenium
ChromeDriver驱动的安装:
首先查看自身的Chrome版本是多少,然后根据我们自身的版本号通过下载地址:http://npm.taobao.org/mirrors/chromedriver/来下载对应的ChromeDriver驱动。
例如:
Chrome版本为:92.0.4515.131(正式版本) (64 位)
则下载ChromeDriver驱动版本中的92.0.4515任意一个,如下图所示:
下载完毕后,把解压的chromedriver.exe文件拖到Python的Scripts目录下,如下图所示:
这样ChromeDriver驱动的安装与配置就完成了,接下来我们正式开始爬取网易云评论。
爬取网易云评论
获取子Frame内容
我们来爬取薛之谦的歌变废为宝,打开开发者工具,观察已经通过JavaScript渲染出来的网页代码,如下图所示:
通过观察发现,评论数据存放在class=”n-cmt”的div中,而且网页的代码有两个html标签,由于selenium打开网页后,默认是在父级Frame里面操作的,所以我们需要使用switch_to.frame()方法来切换Frame。具体代码如下:
def get_saving_data():
browser = webdriver.Chrome()
browser.get('https://music.163.com/#/playlist?id=5033464627')
browser.switch_to.frame(0)
browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
首先我们声明浏览器对象,这里我们选择了Chrome浏览器,接着通过浏览器对象发送get请求到网页中,再使用switch_to.frame()方法切换到子Frame,这里我们传入的参数是0,其意思是切换到第一个子Frame中,也就是上图中的第二个html标签,再使用execute_script()方法把网页拉到最下面方便我们进行评论页的跳转。
这样我们就成功获取上图中的第二个html标签里面的内容了,如下图所示:
获取评论
在上一步我们成功获取到子Frame中的内容,接下来将在内容中提取我们想要的数据,具体代码如下所示:
<span class="k">for</span> <span class="n">page</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
<span class="n">comments_divs</span> <span class="o">=</span> <span class="n">browser</span><span class="o">.</span><span class="n">find_elements_by_css_selector</span><span class="p">(</span><span class="s1">'.itm'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">comments_div</span> <span class="ow">in</span> <span class="n">comments_divs</span><span class="p">:</span>
<span class="n">comments</span><span class="o">=</span><span class="n">comments_div</span><span class="o">.</span><span class="n">find_element_by_css_selector</span><span class="p">(</span><span class="s1">'.cnt.f-brk'</span><span class="p">)</span><span class="o">.</span><span class="n">text</span>
<span class="n">comment</span><span class="o">=</span><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s1">':(.*)'</span><span class="p">,</span><span class="n">comments</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'text.txt'</span><span class="p">,</span><span class="n">mode</span><span class="o">=</span><span class="s1">'a'</span><span class="p">,</span><span class="n">encoding</span><span class="o">=</span><span class="s1">'utf-8'</span><span class="p">)</span><span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">comment</span><span class="p">)</span>
<span class="n">browser</span><span class="o">.</span><span class="n">find_element_by_css_selector</span><span class="p">(</span><span class="s1">'.zbtn.znxt'</span><span class="p">)</span><span class="o">.</span><span class="n">click</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
我们采用了CSS选择器来进行数据的获取,首先调用find_elements_by_css_selector()方法把所有class=”itm”的div获取出来,再通过for循环把每个class=”.cnt.f-brk”的div文本获取出来,然后使用正则表达式把获取到的文本提取我们要的评论数据,再使用click()方法点击下一页,这里我们还使用了sleep()方法,每点击下一页就休眠1秒,这样可以让JavaScript渲染完成再进行数据的获取,否则会因为JavaScript还没渲染完成,而无法获取数据从而导致报错,运行结果如下:
这里我们一共爬取了3页。
保存评论
好了,在上一步我们已经获取到评论,接下来我们将评论以追加的形式写入txt文本中,具体代码如下:
for page in range(3):
comments_divs = browser.find_elements_by_css_selector('.itm')
for comments_div in comments_divs:
comments=comments_div.find_element_by_css_selector('.cnt.f-brk').text
comment=re.findall(':(.*)',comments)[0]
with open('text.txt',mode='a',encoding='utf-8')as f:
f.write(comment)
browser.find_element_by_css_selector('.zbtn.znxt').click()
time.sleep(1)
结果展示
制作词云
制作词云我们需要jieba库,wordcloud库、imageio库,其安装方式如下:
pip install jieba
pip install wordcloud
pip install imageio
在前面的步骤中,我们已经成功获取到评论并把评论数据保存在txt文本中,接下来我们将开始制作词云,具体代码如下:
import jieba
import wordcloud
import imageio
img_read=imageio.imread('树.jpg')
file_open=open('text.txt','r',encoding='utf-8')
txt=file_open.read()
Cloud=wordcloud.WordCloud(width=1000,height=1000,background_color='white',mask=img_read,scale=8,font_path='C:\Windows\Fonts\msyhbd.ttc',stopwords={'的','了','是'})
txtlist=jieba.lcut(txt)
string=' '.join(txtlist)
Cloud.generate(string)
Cloud.to_file('树1.png')
首先我们导入jieba、wordcloud、imageio库,再调用imageio.imread()方法来读取词云的背景图,然后再调用wordcloud.WordCloud()方法,把词云图设置宽高为1000,背景色为白色,词云图背景为刚才读取的图片。
注意:当我们做的词云有中文时,我们要把系统文字路径传入到wordcloud.WordCloud()方法中,这里我们还把“的,了,是”在词云中屏蔽掉。
然后我们调用jieba.lcut()方法把text.txt文本中的文字进行切割,由于我们分割出来的文字是以列表的形式保存的,所以调用join()方法把列表转换为字符
最后调用generate()方法生成词云,调用to_file()方法保存词云图。
结果展示
好了,selenium爬取网易云评论并作词云就到这里了,注意不到万不得已,尽量不要使用selenium爬取,因为效率实在很低。