Python爬虫小白入门(六)爬取披头士乐队历年专辑封面-网易云音乐

转自:https://www.cnblogs.com/Albert-Lee/p/6276847.html

一、前言


前文说过我的设计师小伙伴的设计需求,他想做一个披头士乐队历年专辑的瀑布图。

通过搜索,发现网易云音乐上有比较全的历年专辑信息加配图,图片质量还可以,虽然有大有小。

我的例子怎么都是爬取图片?(谁让你总是跟设计师小伙伴一起玩耍。。。)看来图片对于设计师来说还是有着很深的情节,那就看他用这些图片能做出什么样的作品啦,期待一下,后续会展示一下他的作品。

其实爬取网易云音乐跟之前爬取的网站稍稍有点不同,当然,爬虫写的多了就觉得套路都是固定的,见招拆招而已。

二、运行环境


我的运行环境如下:

  • 系统版本
    Windows10。

  • Python版本
    Python3.5,推荐使用Anaconda 这个科学计算版本,主要是因为它自带一个包管理工具,可以解决有些包安装错误的问题。去Anaconda官网,选择Python3.5版本,然后下载安装。

  • IDE
    我使用的是PyCharm,是专门为Python开发的IDE。这是JetBrians的产品,点我下载

三、实战


上面提到过,网易云音乐的网页跟普通的网页相比主要有两点不同:

  • 网页是 js 动态加载的
  • 使用了iframe框架

所以,
首先,网页请求不能使用requests库,需要使用Selenium + PhatomJS。
其次,使用Selenium + PhatomJS后,还需要针对 iframe 做特定处理。

废话不多说,看实际操作步骤:
首先打开网页 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()
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">request</span><span class="hljs-params">(self, url)</span>:</span>  <span class="hljs-comment">#封装的requests 请求</span>
    r = requests.get(url)  <span class="hljs-comment"># 像目标url地址发送get请求,返回一个response对象。有没有headers参数都可以。</span>
    <span class="hljs-keyword">return</span> r

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mkdir</span><span class="hljs-params">(self, path)</span>:</span>  <span class="hljs-comment">##这个函数创建文件夹</span>
    path = path.strip()
    isExists = os.path.exists(path)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isExists:
        print(<span class="hljs-string">'创建名字叫做'</span>, path, <span class="hljs-string">'的文件夹'</span>)
        os.makedirs(path)
        print(<span class="hljs-string">'创建成功!'</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">True</span>
    <span class="hljs-keyword">else</span>:
        print(path, <span class="hljs-string">'文件夹已经存在了,不再创建'</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">False</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_files</span><span class="hljs-params">(self, path)</span>:</span> <span class="hljs-comment">#获取文件夹中的文件名称列表</span>
    pic_names = os.listdir(path)
    <span class="hljs-keyword">return</span> pic_names</code></pre>

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
    <span class="hljs-keyword">self</span>.mkdir(<span class="hljs-keyword">self</span>.folder_path)  <span class="hljs-comment"># 创建文件夹</span>
    print(<span class="hljs-string">'开始切换文件夹'</span>)
    os.chdir(<span class="hljs-keyword">self</span>.folder_path)  <span class="hljs-comment"># 切换路径至上面创建的文件夹</span>

    file_names = <span class="hljs-keyword">self</span>.get_files(<span class="hljs-keyword">self</span>.folder_path)  <span class="hljs-comment"># 获取文件夹中的所有文件名,类型是list</span>

    all_li = BeautifulSoup(html, <span class="hljs-string">'lxml'</span>).find(id=<span class="hljs-string">'m-song-module'</span>).find_all(<span class="hljs-string">'li'</span>)
    <span class="hljs-comment"># print(type(all_li))</span>

    <span class="hljs-keyword">for</span> li <span class="hljs-keyword">in</span> <span class="hljs-symbol">all_li:</span>
        album_img = li.find(<span class="hljs-string">'img'</span>)[<span class="hljs-string">'src'</span>]
        album_name = li.find(<span class="hljs-string">'p'</span>, <span class="hljs-keyword">class</span><span class="hljs-number">_</span>=<span class="hljs-string">'dec'</span>)[<span class="hljs-string">'title'</span>]
        album_date = li.find(<span class="hljs-string">'span'</span>, <span class="hljs-keyword">class</span><span class="hljs-number">_</span>=<span class="hljs-string">'s-fc3'</span>).get_text()
        end_pos = album_img.index(<span class="hljs-string">'?'</span>)
        album_img_url = album_img[<span class="hljs-symbol">:end_pos</span>]

        photo_name = album_date + <span class="hljs-string">' - '</span> + album_name.replace(<span class="hljs-string">'/'</span>,<span class="hljs-string">''</span>).replace(<span class="hljs-string">':'</span>,<span class="hljs-string">','</span>) + <span class="hljs-string">'.jpg'</span>
        print(album_img_url, photo_name)

        <span class="hljs-keyword">if</span> photo_name <span class="hljs-keyword">in</span> <span class="hljs-symbol">file_names:</span>
            print(<span class="hljs-string">'图片已经存在,不再重新下载'</span>)
        <span class="hljs-symbol">else:</span>
            <span class="hljs-keyword">self</span>.save_img(album_img_url, photo_name)</code></pre>

其实相对于上篇博文的例子,这个爬虫的逻辑部分还是挺简洁的。

最后上一个完整的代码: 也可以从GitHub下载

from selenium import webdriver
from bs4 import BeautifulSoup
import requests
import os

class AlbumCover():

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self)</span>:</span>
    self.init_url = <span class="hljs-string">"http://music.163.com/#/artist/album?id=101988&amp;limit=120&amp;offset=0"</span> <span class="hljs-comment">#请求网址</span>
    self.folder_path = <span class="hljs-string">"C:\D\TheBeatles"</span> <span class="hljs-comment">#想要存放的文件目录</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_img</span><span class="hljs-params">(self, url, file_name)</span>:</span>  <span class="hljs-comment">##保存图片</span>
    print(<span class="hljs-string">'开始请求图片地址,过程会有点长...'</span>)
    img = self.request(url)
    print(<span class="hljs-string">'开始保存图片'</span>)
    f = open(file_name, <span class="hljs-string">'ab'</span>)
    f.write(img.content)
    print(file_name, <span class="hljs-string">'图片保存成功!'</span>)
    f.close()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">request</span><span class="hljs-params">(self, url)</span>:</span>  <span class="hljs-comment"># 封装的requests 请求</span>
    r = requests.get(url)  <span class="hljs-comment"># 像目标url地址发送get请求,返回一个response对象。有没有headers参数都可以。</span>
    <span class="hljs-keyword">return</span> r

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mkdir</span><span class="hljs-params">(self, path)</span>:</span>  <span class="hljs-comment">##这个函数创建文件夹</span>
    path = path.strip()
    isExists = os.path.exists(path)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isExists:
        print(<span class="hljs-string">'创建名字叫做'</span>, path, <span class="hljs-string">'的文件夹'</span>)
        os.makedirs(path)
        print(<span class="hljs-string">'创建成功!'</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">True</span>
    <span class="hljs-keyword">else</span>:
        print(path, <span class="hljs-string">'文件夹已经存在了,不再创建'</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">False</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_files</span><span class="hljs-params">(self, path)</span>:</span>  <span class="hljs-comment"># 获取文件夹中的文件名称列表</span>
    pic_names = os.listdir(path)
    <span class="hljs-keyword">return</span> pic_names

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">spider</span><span class="hljs-params">(self)</span>:</span>
    print(<span class="hljs-string">"Start!"</span>)
    driver = webdriver.PhantomJS()
    driver.get(self.init_url)
    driver.switch_to.frame(<span class="hljs-string">"g_iframe"</span>)
    html = driver.page_source

    self.mkdir(self.folder_path)  <span class="hljs-comment"># 创建文件夹</span>
    print(<span class="hljs-string">'开始切换文件夹'</span>)
    os.chdir(self.folder_path)  <span class="hljs-comment"># 切换路径至上面创建的文件夹</span>

    file_names = self.get_files(self.folder_path)  <span class="hljs-comment"># 获取文件夹中的所有文件名,类型是list</span>

    all_li = BeautifulSoup(html, <span class="hljs-string">'lxml'</span>).find(id=<span class="hljs-string">'m-song-module'</span>).find_all(<span class="hljs-string">'li'</span>)
    <span class="hljs-comment"># print(type(all_li))</span>

    <span class="hljs-keyword">for</span> li <span class="hljs-keyword">in</span> all_li:
        album_img = li.find(<span class="hljs-string">'img'</span>)[<span class="hljs-string">'src'</span>]
        album_name = li.find(<span class="hljs-string">'p'</span>, class_=<span class="hljs-string">'dec'</span>)[<span class="hljs-string">'title'</span>]
        album_date = li.find(<span class="hljs-string">'span'</span>, class_=<span class="hljs-string">'s-fc3'</span>).get_text()
        end_pos = album_img.index(<span class="hljs-string">'?'</span>)
        album_img_url = album_img[:end_pos]

        photo_name = album_date + <span class="hljs-string">' - '</span> + album_name.replace(<span class="hljs-string">'/'</span>, <span class="hljs-string">''</span>).replace(<span class="hljs-string">':'</span>, <span class="hljs-string">','</span>) + <span class="hljs-string">'.jpg'</span>
        print(album_img_url, photo_name)

        <span class="hljs-keyword">if</span> photo_name <span class="hljs-keyword">in</span> file_names:
            print(<span class="hljs-string">'图片已经存在,不再重新下载'</span>)
        <span class="hljs-keyword">else</span>:
            self.save_img(album_img_url, photo_name)

album_cover = AlbumCover()
album_cover.spider()

执行结果:

看看文件夹里面什么样:

历年的专辑封面已经到手啦,还有专辑的名称和发行日期。

四、后语


这个实战很好的运用了咱们之前讲解的知识:

  • 使用Selenium + PhatomJS 抓取动态页面
  • 使用Selenium 的switch_to.frame() 加载 iframe 中的内容
  • 使用requests 库获取图片
  • 使用BeautifulSoup 库解析抓取网页内容。
  • 使用os 库创建文件夹和获取文件夹中的文件名称列表

使用现有的知识编写简单的爬虫是不成问题了。接下来会讲讲爬虫框架,就从现在比较流行的Scrapy 框架讲起吧。

我得找找比较合适的例子,工作中用到的爬虫不太方便贴上来,有合适的实例我会继续更新。

有任何的疑问可以留言,就先酱紫吧。

See you then.

猜你喜欢

转载自blog.csdn.net/qq_32534441/article/details/85795065