分析并构造请求网址

爬取酷狗音乐TOP500 的‘音乐名’,‘歌手’,‘歌名’,‘播放时间’,‘网址’这几个数据网址如下:https://www.kugou.com/yy/rank/home/1-8888.html?from=rank,浏览器打开网址分析,第一页只显示了22首歌曲:

python如何爬取酷狗音乐 python爬取酷狗音乐榜单数据_深度学习


我们可以看到,其中有一个 1-8888 这个参数,打开上述网址后我们只能看到前 22 首歌,想继续查看后面的歌曲就得翻页,就像“淘宝”那样查看下一页商品需要翻页,这里也是一样的道理,把 1-8888 改成 2-8888 ,就会看到下一页的 22 首歌,如下:

python如何爬取酷狗音乐 python爬取酷狗音乐榜单数据_深度学习_02


翻到第 200首音乐那一页,可以看到页码如下有 10 页,那么可以如下构造URL地址:

"""将所有URL构造成一个列表集合"""
URLs=["https://www.kugou.com/yy/rank/home/{}-8888.html?from=rank".format(str(i)) for i in range(1,10)]

构造请求

try:
        res=requests.get(url)
        res.encoding=res.apparent_encoding  # 转换编码,不然中文会显示乱码,也可以r.encoding = 'utf-8'
        if res.status_code== 200:
            html=res.text
            return html
    except:
        print("请求失败")

使用 requests库的 get方法,去访问网页,res其中里面有很多结果:状态响应码,网页源码,二进制等。调用请求结果 res中的 status_code查看请求状态码response.status_code == 200

返回响应结果的 html,代表返回网页 html源码

解析数据

soup=BeautifulSoup(html,"lxml")
  """
  html 表示被解析的html格式的内容
  lxml 表示解析用的解析器
  """

提取数据

分析网页源代码:

python如何爬取酷狗音乐 python爬取酷狗音乐榜单数据_爬虫_03


可以看到需要的一些信息如‘音乐名’,‘歌手’,‘歌名’,‘播放时间’,‘网址’等分别在如图标注的地方,每个歌曲信息所在的标签结构如下:所有歌曲信息都在<div>标签下,每首歌曲都在各自的<ul>标签,然后歌曲自身的‘音乐名’,‘歌手’,‘歌名’等信息都分别由一个<li>标签包裹。

把每首歌曲的‘音乐名’,‘歌手’,‘歌名’,‘播放时间’,‘网址’的值都取出来,并且把每组数据都各自装在一个列表中。

(1)使用select方法定位

"""(1)使用`select`方法定位,提取数据 ,返回结果为列表,下面使用循环依次取出数据"""
    try:
        nums=soup.select('.pc_temp_num' )   #定位排名
        titles=soup.select('.pc_temp_songname') #定位歌手-歌名
        times=soup.select('.pc_temp_time' ) #定位播放时间
    except:
        print("位置定位失败")

select方法主要采用的是CSS定位,如果不熟悉的话,可以参考http://www.w3school.com.cn/cssref/css_selectors.asp了解。常用的select搜索:

.intro

选择 class="intro"的所有元素

#firstname

选择id="firstname"的所有元素

div p

选择<div>元素内部的所有 <p> 元素

div>p

选择父元素为<div>元素的所有<p> 元素

这里因为排名class="pc_temp_num"唯一,所以直接定位到该标签。歌手-可以通过class="pc_temp_songname"定位到<a>标签,然后在获取属性值。

(2)使用find方法定位

"""(2)使用`find`方法定位,提取数据 ,返回结果为列表,下面使用循环依次取出数据"""
    try:
        div=soup.find("div",class_="pc_temp_songlist")   #先定位 div
        lis=div.find("ul").children      #获取 ul标签下面的 子标签->li
        for li in lis:          #通过循环获取每个 li 标签
         if isinstance(li,bs4.element.Tag):      #确保 li 对象的类型是bs4.element.Tag,也就是标签对象。只有标签对象才能使用find方法
            nums=li.select(".pc_temp_num")
            titles=[li.a.attrs]
            times=li.find(class_="pc_temp_time")
    except:
        print("位置定位失败")

(3)使用find_all方法定位

"""(3)使用`find_all`方法定位,提取数据 ,返回结果为列表,下面使用循环依次取出数据"""
    try:
        nums=soup.find_all(class_="pc_temp_num")
        titles=soup.find_all(class_="pc_temp_songname")
        times=soup.find_all(class_="pc_temp_time")
    except:
        print("位置定位失败")

获得数据

"""打印信息 """
    for num,title,time in zip(nums,titles,times):
        data={"排名":num.get_text().replace('\n','').replace('\t','').replace('\r',''),
          "歌手-歌名":title.string,
          "播放时间":time.string.replace('\n','').replace('\t','').replace('\r',''),
          "网址":title.attrs["href"]}
        print(data)

用了zip 函数,意思是把对应的排名,歌名歌手,播放时间打包,可以这样理解 zip 函数的结果是一个列表 [(排名,歌手歌名,播放时间),(排名,歌手歌名,播放时间)]

每一次循环的 num,title,time,href 对应一次元组中的元素

get_text()

我们提取到的是这个数据所在的标签信息,并不是实际数据,所以需要使用 get_text() 获得实际数据

.replace('\n','').replace('\t','').replace('\r','')

去掉实际数据中多余的字符串;
最后把数据打包成字典打印。

最终代码

from bs4 import BeautifulSoup
import requests
import bs4
def get_html(url):
    try:
        res=requests.get(url)
        res.encoding=res.apparent_encoding  # 转换编码,不然中文会显示乱码,也可以r.encoding = 'utf-8'
        if res.status_code== 200:
            html=res.text
            return html
    except:
        print("请求失败")


def get_infos(html):
    soup=BeautifulSoup(html,"lxml")
    """
    html 表示被解析的html格式的内容
    lxml 表示解析用的解析器
    """

    """(1)使用`select`方法定位,提取数据 ,返回结果为列表,下面使用循环依次取出数据"""
    try:
        nums=soup.select('.pc_temp_num' )   #定位排名
        titles=soup.select('.pc_temp_songname') #定位歌手-歌名
        times=soup.select('.pc_temp_time' ) #定位播放时间
    except:
        print("位置定位失败")

    # """(2)使用`find`方法定位,提取数据 ,返回结果为列表,下面使用循环依次取出数据"""
    # try:
    #     div=soup.find("div",class_="pc_temp_songlist")   #先定位 div
    #     lis=div.find("ul").children      #获取 ul标签下面的 子标签->li
    #     for li in lis:          #通过循环获取每个 li 标签
    #      if isinstance(li,bs4.element.Tag):      #确保 li 对象的类型是bs4.element.Tag,也就是标签对象。只有标签对象才能使用find方法
    #         nums=li.select(".pc_temp_num")
    #         titles=[li.a.attrs]
    #         times=li.find(class_="pc_temp_time")
    #         for num,title,time in zip(nums,titles,times):
    #             data={"排名":num.get_text().replace('\n','').replace('\t','').replace('\r',''),
    #             "歌手-歌名":title["title"],
    #             "播放时间":time.string.replace('\n','').replace('\t','').replace('\r',''),
    #             "网址":title["href"]}
    #             print(data)
    # except:
    #     print("位置定位失败")

    # """(3)使用`find_all`方法定位,提取数据 ,返回结果为列表,下面使用循环依次取出数据"""
    # try:
    #     nums=soup.find_all(class_="pc_temp_num")
    #     titles=soup.find_all(class_="pc_temp_songname")
    #     times=soup.find_all(class_="pc_temp_time")
    # except:
    #     print("位置定位失败")
    #
    #
    """打印信息时,供select和find_all共同使用,find方法需写在try里面,因为多了一个if判断,层级下了一级"""
    for num,title,time in zip(nums,titles,times):
        data={"排名":num.get_text().replace('\n','').replace('\t','').replace('\r',''),
          "歌手-歌名":title["title"],
          "播放时间":time.string.replace('\n','').replace('\t','').replace('\r',''),
          "网址":title["href"]}
        print(data)

def main():
    """主接口"""
    urls=["https://www.kugou.com/yy/rank/home/{}-6666.html?from=rank".format(str(i)) for i in range(1,10)]
    for url in urls:
        get_html(url)
        html=get_html(url)
        get_infos(html)

if __name__ == '__main__':
    main()