前文说过个人设计师小伙伴的设计需求,他想作一个披头士乐队历年专辑的瀑布图。html
经过搜索,发现网易云音乐上有比较全的历年专辑信息加配图,图片质量还能够,虽然有大有小。git
个人例子怎么都是爬取图片?(谁让你老是跟设计师小伙伴一块儿玩耍。。。)看来图片对于设计师来讲仍是有着很深的情节,那就看他用这些图片能作出什么样的做品啦,期待一下,后续会展现一下他的做品。github
其实爬取网易云音乐跟以前爬取的网站稍稍有点不一样,固然,爬虫写的多了就以为套路都是固定的,见招拆招而已。web
个人运行环境以下:框架
系统版本
Windows10。ide
Python版本
Python3.5,推荐使用Anaconda 这个科学计算版本,主要是由于它自带一个包管理工具,能够解决有些包安装错误的问题。去Anaconda官网,选择Python3.5版本,而后下载安装。函数
IDE
我使用的是PyCharm,是专门为Python开发的IDE。这是JetBrians的产品,点我下载。工具
上面提到过,网易云音乐的网页跟普通的网页相比主要有两点不一样:网站
因此,
首先,网页请求不能使用requests库,须要使用Selenium + PhatomJS。
其次,使用Selenium + PhatomJS后,还须要针对 iframe 作特定处理。atom
废话很少说,看实际操做步骤:
首先打开网页 http://music.163.com
在右上角的搜索框中输入“The Beatles”,而后会有一个下拉选项,选择歌手 The Beatles (红框中的内容)。
而后看到以下页面,选择红框中的“全部专辑”,点击。
这样就会看见全部的专辑列表,以及下方的翻页按钮。
咱们须要的就是全部专辑的图片、专辑名和专辑出版时间。看到这就能够构想一下爬虫的爬取逻辑了。定位到该页面,而后获取页码,而后挨个请求页面来爬取页面中的内容。
点击一下翻页按钮看看url 有没有什么规律。
点击第二页后,看到上面的地址栏!!!看到这个地址栏我都懒得翻页了。。。
limit 参数是限制一个页面加载专辑的个数
offset 参数是前面过滤多少个专辑,如今是一页12个专辑,因此第二页是offset=12,第三页offset=24,以此类推。。。
一共9页,一页12个,也不到120个。So... ... 改一下url 就不用翻页了!!
limit 参数等于120,offset 参数 等于0,就搞定了! 输入下面的url,看看是否是全部的专辑都加载出来了。
http://music.163.com/#/artist/album?id=101988&limit=120&offset=0
下面就开始爬虫代码了。
这里咱们会用到上一篇博文中写好的几个工具方法:
def save_img(self, url, file_name): ##保存图片 print('开始请求图片地址,过程会有点长...') img = self.request(url) print('开始保存图片') f = open(file_name, 'ab') f.write(img.content) print(file_name,'图片保存成功!') f.close() def request(self, url): #封装的requests 请求 r = requests.get(url) # 像目标url地址发送get请求,返回一个response对象。有没有headers参数均可以。 return r def mkdir(self, path): ##这个函数建立文件夹 path = path.strip() isExists = os.path.exists(path) if not isExists: print('建立名字叫作', path, '的文件夹') os.makedirs(path) print('建立成功!') return True else: print(path, '文件夹已经存在了,再也不建立') return False def get_files(self, path): #获取文件夹中的文件名称列表 pic_names = os.listdir(path) return pic_names
OK, 开始咱们的爬虫逻辑部分:
这里值得注意的是,该页面使用frame 框架,使用Selenium + PhantomJS 后并不会加载iframe 框架中的网页内容。iframe 框架至关于在页面中又加载了一个页面,须要使用Selenium 的 switch_to.frame() 方法加载(官网给的方法是switch_to_frame(),可是IDE提醒使用前面的方法替代该方法)。
看下面的网页结构,iframe的id是“g_iframe”:
加载 iframe 框架中的内容:
driver = webdriver.PhantomJS() driver.get(self.init_url) driver.switch_to.frame("g_iframe") html = driver.page_source
而后找到全部的封面元素:
根据上图的网页结构能够看出,全部的专辑信息都在ul 标签里面,每个专辑在一个li 标签里。li 标签中包含了图片url、专辑名字、以及专辑时间。
抓取其中的内容就行了。
all_li = BeautifulSoup(html, 'lxml').find(id='m-song-module').find_all('li') for li in all_li: album_img = li.find('img')['src'] album_name = li.find('p', class_='dec')['title'] album_date = li.find('span', class_='s-fc3').get_text()
这里获取到的图片url 依然是有图片宽高参数的,因此要过滤宽高参数:
http://p4.music.126.net/pLA1GX0KtU-vU4ZA6Cr-OQ==/1401877340532770.jpg?param=120y120
把问号后面的参数过滤掉:
end_pos = album_img.index('?') #找到问号的位置 album_img_url = album_img[:end_pos] #截取问号以前的内容
图片命名逻辑:专辑时间 + 专辑名。
专辑名可能有一些特殊字符,须要替换掉!
photo_name = album_date + ' - ' + album_name.replace('/','').replace(':',',') + '.jpg'
再使用上一篇博文例子中的去重逻辑,修改后的爬虫逻辑部分以下:
def spider(self): print("Start!") driver = webdriver.PhantomJS() driver.get(self.init_url) driver.switch_to.frame("g_iframe") html = driver.page_source self.mkdir(self.folder_path) # 建立文件夹 print('开始切换文件夹') os.chdir(self.folder_path) # 切换路径至上面建立的文件夹 file_names = self.get_files(self.folder_path) # 获取文件夹中的全部文件名,类型是list all_li = BeautifulSoup(html, 'lxml').find(id='m-song-module').find_all('li') # print(type(all_li)) for li in all_li: album_img = li.find('img')['src'] album_name = li.find('p', class_='dec')['title'] album_date = li.find('span', class_='s-fc3').get_text() end_pos = album_img.index('?') album_img_url = album_img[:end_pos] photo_name = album_date + ' - ' + album_name.replace('/','').replace(':',',') + '.jpg' print(album_img_url, photo_name) if photo_name in file_names: print('图片已经存在,再也不从新下载') else: self.save_img(album_img_url, photo_name)
其实相对于上篇博文的例子,这个爬虫的逻辑部分仍是挺简洁的。
最后上一个完整的代码: 也能够从GitHub下载
from selenium import webdriver from bs4 import BeautifulSoup import requests import os class AlbumCover(): def __init__(self): self.init_url = "http://music.163.com/#/artist/album?id=101988&limit=120&offset=0" #请求网址 self.folder_path = "C:\D\TheBeatles" #想要存放的文件目录 def save_img(self, url, file_name): ##保存图片 print('开始请求图片地址,过程会有点长...') img = self.request(url) print('开始保存图片') f = open(file_name, 'ab') f.write(img.content) print(file_name, '图片保存成功!') f.close() def request(self, url): # 封装的requests 请求 r = requests.get(url) # 像目标url地址发送get请求,返回一个response对象。有没有headers参数均可以。 return r def mkdir(self, path): ##这个函数建立文件夹 path = path.strip() isExists = os.path.exists(path) if not isExists: print('建立名字叫作', path, '的文件夹') os.makedirs(path) print('建立成功!') return True else: print(path, '文件夹已经存在了,再也不建立') return False def get_files(self, path): # 获取文件夹中的文件名称列表 pic_names = os.listdir(path) return pic_names def spider(self): print("Start!") driver = webdriver.PhantomJS() driver.get(self.init_url) driver.switch_to.frame("g_iframe") html = driver.page_source self.mkdir(self.folder_path) # 建立文件夹 print('开始切换文件夹') os.chdir(self.folder_path) # 切换路径至上面建立的文件夹 file_names = self.get_files(self.folder_path) # 获取文件夹中的全部文件名,类型是list all_li = BeautifulSoup(html, 'lxml').find(id='m-song-module').find_all('li') # print(type(all_li)) for li in all_li: album_img = li.find('img')['src'] album_name = li.find('p', class_='dec')['title'] album_date = li.find('span', class_='s-fc3').get_text() end_pos = album_img.index('?') album_img_url = album_img[:end_pos] photo_name = album_date + ' - ' + album_name.replace('/', '').replace(':', ',') + '.jpg' print(album_img_url, photo_name) if photo_name in file_names: print('图片已经存在,再也不从新下载') else: self.save_img(album_img_url, photo_name) album_cover = AlbumCover() album_cover.spider()
执行结果:
看看文件夹里面什么样:
历年的专辑封面已经到手啦,还有专辑的名称和发行日期。
这个实战很好的运用了我们以前讲解的知识:
使用现有的知识编写简单的爬虫是不成问题了。接下来会讲讲爬虫框架,就从如今比较流行的Scrapy 框架讲起吧。
我得找找比较合适的例子,工做中用到的爬虫不太方便贴上来,有合适的实例我会继续更新。
有任何的疑问能够留言,就先酱紫吧。
See you then.