从廖雪峰官网学习完Python之后,花了两天时间在网易云课堂找了个课程进行了爬虫入门学习。所以接下来,想找一个网页爬一爬,实战一下。可能代码有些地方比较臃肿,但总的来说功能实现了。
首先说需求:爬取网易云音乐首页的三个榜单中,每个榜单中排名前10歌曲的赞数超1000的评论。包括歌名,评论者昵称,获赞数,评论内容。
首先从获取每一首歌的评论开始:
1.对于静态页面,学习完网易云课堂这个课之后应该就可以处理了。但爬虫抓取数据时有些数据是动态数据,例如用js动态加载的,使用普通的requests
是抓不到相关数据的,明明在浏览器里有相应的信息,但是在requests抓取的时候却发现获得的html缺失了太多数据,这是因为网页用js异步加载数据,再动态显示出来。一种处理方式是找出相应的js接口,但是有时这是非常难得,因为还得分析js的调用参数,而有些参数是有加密的,还得进行解密操作;另一种出来方式是用selenium
。下面先用第一种方法:
import requests
import json
import os
def getcommets(id_song, name_song, listtype):
url = 'https://music.163.com/weapi/v1/resource/comments/R_SO_4_' + id_song + '?csrf_token='
header = { # 请求头部
'User-Agent': 'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
}
data = { # 表单信息,服务器根据表单信息的正确与否,决定是否发送数据。
'params': 'N+zv4smEmpA+qRJcbrvXQiaVifgDskCoylBFjGqvxojjHciXzycxTE3xa50Ec//+6OdpTuxmgWXjty/gMoudXLFnQVnzKDuWGuHqEme1861T7R5x8L/hwaKgsMS4zQ0t7scgEJs46HbQFSSzcGA1teqRumGen4C3Xhv8jIyvJsmJTaknNx53tjhDxeV8xo0p',
'encSecKey': 'd0a60f2145711ddf491d4c946d1003662399c1f10f2c4392f49e5cbdc9ee276b06e7af2e5eb354f613a25902ef36ba4f04ded9efc64a523ba6f7a634dbfe7c06f661577e84523174ab62ddd768992e4cc6101683aabce48e8ad82aac05573b1ac3723e678476e9400fe6ec972096b087795bb17a066e61efd74be2412afbd6fb'}
r = requests.post(url, data=data, headers=header)
comments = json.loads(r.text)
our_comment = list(filter(lambda s: s.get('likedCount') >= 1000, comments.get('hotComments')))
print(our_comment)
filedir1 = os.path.join(os.getcwd(), '网易云三榜歌曲热评')
if not os.path.exists(filedir1):
os.mkdir(filedir1)
filedir2 = os.path.join(filedir1, listtype) # 路径拼接最好用.join(),因为拼接内容会因操作系统而变化
if not os.path.exists(filedir2):
os.mkdir(filedir2)
with open(os.path.join(filedir2, '%s.txt' % name_song), mode='w', encoding='utf-8') as f:
f.write('《' + name_song + '》' + '\n')
for x in our_comment:
f.write(x.get('user').get('nickname') + ':' + ' 获赞数,' + str(x.get('likedCount')) + '\n' + x.get(
'content') + '\n' + '------------------------------------------------------' + '\n')
这里首先用到了https://blog.csdn.net/fengxinlinux/article/details/77950209提供的方法。这里重点就是找到了获取评论信息的POST请求的url,但如果想通过此url获取评论,需要提交相应的表单(<form>标签)信息,这里的表单信息是加密的,这里原作者经过细致的观察,采用了一个偷懒 的方法来实现。
2.接下来是id_song, name_song, listtype(每首歌的id值,歌曲名称,所属榜单)信息的获取
这里用到了Selenium,首先要进行安装:
没有安装anaconda的:这个方法不推荐,因为如果电脑中同时安装了python2.x和python3.x,那这样安装的Selenium默认安装到python2.x。而我是在python3.x下编程的,所以要费时处理这个问题。
$ sudo pip install selenium
安装了anaconda的:这样安装省心
$ conda install selenium
但是出现了下列问题:
CondaIOError: Missing write permissions in: /usr/local/anaconda3
#
# You don't appear to have the necessary permissions to install packages
# into the install area '/usr/local/anaconda3'.
# However you can clone this environment into your home directory and
# then make changes to it.
# This may be done using the command:
#
# $ conda create -n my_root --clone="/usr/local/anaconda3"
问题在于 anaconda所在文件夹只有root用户才有权限,而 root下并没有conda命令,解决方案是修改文件夹权限给当前用户,其中usr是你的用户名,foldername是anaconda安装路径/usr/local/anaconda3:
$ sudo chown -R usr foldername
至此,Selenium的安装就结束了。然后为了使Selenium能够驱动一个浏览器(这里我们选择了chrome),需要安装相应的驱动chromedriver,要注意与自己的chrome版本相匹配。这里的最新版本是:2.41,可别搞错了。
安装chromedriver步骤:
切换到下载的目录,解压下载的.zip文件
$ unzip chromedriver_linux64.zip
转移解压后的文件:
$ sudo mv chromedriver /usr/bin/
切换到root用户,进入/usr/bin/,给刚刚的文件添加权限:
# chmod +x chromedriver
至此,Selenium的安装就结束了。在web应用中,前台网页的设计一般会用到iframe/frame表单嵌套页面的应用,这种异步加载方式。简单的说就是一个页面签嵌套多个HEML/JSP文件。selenium webdriver 只能在同一页面识别定位元素,可以狭隘的理解成只能识别当前所在位置的页面上的元素。对于不同的iframe/frame表单中的元素是无法直接定位的,requests模块也是束手无策,对此类页面解析,基本上什么数据都得不到。此时,需要结合driver.switch_to.frame('g_iframe')方法切换到指定的frame/iframe中。driver.switch_to.frame('g_iframe')函数参数默认的是取表单的ID或name属性。如果没有id和name ,可通过Xpath路径定位。
from selenium import webdriver
from bs4 import BeautifulSoup
from getcomment import getcommets
def getmusics(id_toplist):
url = 'https://music.163.com/#' + id_toplist
driver = webdriver.Chrome() # 用webdriver打开chrome
driver.get(url) # 打开对应网址
driver.switch_to.frame('contentFrame') # 将当前driver转入到相应的iframe中去
# 这里面的内容,用request模块是解析不到的
html = driver.page_source # 获得当前iframe所包含的源码
soup = BeautifulSoup(html, 'lxml') # 利用美丽汤进行一些列 定位、获取信息 处理
listType = soup.find('title').text.split(' ')[0] # 是为了截取比较好看的榜单名字
songs = soup.body.findAll(lambda tag: tag.name == 'tr' and tag.has_attr('id'))[:10] # 取榜单中前10名歌曲
keyinfo = [x.find('span', class_='txt') for x in songs]
songid = [x.find('a').attrs['href'].lstrip('/song?id=') for x in keyinfo]
songname = [str(x.find('b').attrs['title']) for x in keyinfo]
i = 0
while i < len(songs):
getcommets(songid[i], songname[i], listType)
i=i+1
可以看到,这里程序在运行过程中,要频繁的打开浏览器新窗口去加载相应页面,所以为了不必要的麻烦,可以使用Selenium+Phantomjs的模式,来进行动态页面的爬虫工作。
d3.接下来是从https://music.163.com分别进入三个榜单的操作:
from selenium import webdriver
from bs4 import BeautifulSoup
from getmusic import getmusics
def getlists(url):
drivier = webdriver.Chrome()
drivier.get(url)
drivier.switch_to.frame('g_iframe')
html = drivier.page_source
soup = BeautifulSoup(html, 'lxml')
link = soup.findAll('div', class_='cver u-cover u-cover-4')
links = [x.find('a').attrs['href'] for x in link]
for x in links:
getmusics(x)
getlists('https://music.163.com')
最后执行程序,得到正确的结果:
《不染 - (电视剧《香蜜沉沉烬如霜》主题曲)》
武装心碎: 获赞数,72315
海雷老师所作歌词有古风韵味,简老师作曲丝丝入扣,毛不易的醇厚嗓音,带着不符合他年纪的沧桑,诠释着词曲里的爱别离,放不下。独特的搭配,竟如此刚好,如斯惊艳。不禁在想,不染,是不沾染,还是一尘不染……希望初心不染杂质,期盼爱情不染功利。若有幸携手,于这孤独世间行走,愿你青春,黑发不染
------------------------------------------------------
有味是清欢_: 获赞数,46207
昨天听到了《不染》的试听版,惊艳毛不易的高音。可能许多人都停留在他在《消愁》《像我这样的人》一系列歌中的低吟浅唱,其实毛不易在《imagine》《请记住我》的高音也非常棒!感谢作词作曲老师们让我们听到这么好听的歌,配合电视剧一定超棒!感谢毛不易献唱,期待他越来越好。少年未来可期[爱心]
------------------------------------------------------