1. 程式人生 > >python爬取網易雲歌單

python爬取網易雲歌單

背景

這學期報了一門海量資料處理,在資料處理前需要爬取一些內容。所以做了一個小練習,爬取網易雲的歌單。其中包括歌單名稱,播放量和url地址。
網易雲還是具有一些反爬措施的,這裡主要說以下幾個方面以及我的應對措施。
  1. 無法直接訪問 我沒有實踐過直接訪問網易雲音樂會不會被報404,所以這一條本質上不太確定的,我的做法是直接在瀏覽器中通過F12檢視請求頭,在程式碼中將請求頭加入其中以此來模擬我是瀏覽器訪問的,但是後期因為使用了PhantomJS,也就不需要請求頭的,後期的時候請求頭就沒有再用了。
  2. 動態頁面載入 動態的頁面載入導致正常的頁面爬蟲無法工作,在瀏覽器上看到的很多內容,通過python讀取到html後在內部找不到對應內容。對應的方法是使用PhantomJS。PhantomJS按照我自己的理解,是模擬了一個瀏覽器,這樣子可以最大程度上讓伺服器認為我是普通使用者的瀏覽器,從而防止被反爬。此外上一條中說到使用這個以後不需要再加請求頭也是這個原因,我的身份已經變成了一個瀏覽器,所以也就不需要再使用請求頭來偽裝身份。
  3. PhantomJS效率過低 PhantomJS畢竟是一個瀏覽器,在速度上有很大劣勢,所以在使用時需要對其進行配置。從直觀上來看,就是關閉瀏覽器的圖片載入功能,開啟瀏覽器快取功能等等,所有的配置都和瀏覽器正常設定差不多,其程式碼如下:
service_args=[]
service_args.append('--load-images=no')  ##關閉圖片載入
service_args.append('--disk-cache=yes')  ##開啟快取
service_args.append('--ignore-ssl-errors=true') ##忽略https錯誤
  1. 部分頁面需要滾輪下拉才能顯示更多內容
    我的讀取方式是讀取使用者的個人主頁,其中包括‘自建歌單’以及‘收藏歌單’。可以點選https://music.163.com/#/user/home?id=113975098看到。 爬蟲對自建歌單以及收藏歌單都進行爬取,一個使用者爬取完後,在收藏歌單中隨機找一個歌單進去,進入後是歌單內容詳情,此時點選上方的作者,再次連結到該作者的個人主頁,繼續爬取自建歌單以及收藏歌單。 但是部分使用者會自建很多歌單,這樣導致收藏歌單不會直接顯示,在瀏覽器中需要滾輪下滑才能繼續載入,這就導致程式不能夠讀取到收藏歌單,也就不能繼續爬取了。 這裡有兩種方法,一種是通過F12分析滾輪下滑後發出的請求,在程式中發出該請求就可以讀取到後續列表。 老實說,這是我第一次爬頁面,之前對html從來沒有過了解,此外馬上就要交作業了,簡單看了看頁面以後,決定用一種偷懶的方法去解決它。 我每次對一個使用者的所有收藏歌單都讀取到以後,將所有歌單的url讀取到並存在一個list內部。爬完一個使用者後,在list內部隨機找到一個url作為下一個爬取物件,同時將該url在list內刪除。當爬取到某個頁面無法找到收藏歌單時,從該list內部再隨機拿取一個重新進行分析。 這裡有一個bug,假如很多使用者都沒有收藏歌單,剩餘使用者有很少的收藏歌單時,某一次執行中可能會出現list為空的情況,也就沒有url可以用來分析了。另外盲目地擴充list對於記憶體的佔用也是一大筆開銷。但是對於2000條歌單的課程要求來說足夠了。 實際上我爬了一萬條歌單,執行正常。

以下是結果:

在這裡插入圖片描述

程式碼

from selenium import webdriver
import csv
import random
import os

#讀取檔案 若檔案不存在則新建檔案 同時寫入表頭
if os.path.exists('playList.csv'):
    csvFile = open('playList.csv', 'a+', newline='', encoding="utf-8")
    writer = csv.writer(csvFile)
else:
    csvFile = open('playList.csv', 'a+', newline='', encoding="utf-8")
    writer = csv.writer(csvFile)
    writer.writerow(['標題', '播放數', '連結'])

#配置PhantomJS,提綱爬取速度
service_args=[]
service_args.append('--load-images=no')  ##關閉圖片載入
service_args.append('--disk-cache=yes')  ##開啟快取
service_args.append('--ignore-ssl-errors=true') ##忽略https錯誤


playUrl = 'https://music.163.com/#/user/home?id=1320157310'
runCnt = 0                      #程式執行次數計數
cPlayerList = []                #url列表 噹噹前url不合適時,從內部隨機取出一個繼續爬取
while runCnt < 10000:            #爬取兩千條記錄
    driver = webdriver.PhantomJS("D:\Python\Python37\Scripts\phantomjs.exe", service_args=service_args)
    print(playUrl)              #列印當前爬取的url
    driver.get(playUrl)         #獲取連結
    try:                        #在網頁中若出現錯誤及時捕獲
        #-----------------讀取使用者自建歌單-------------------
        driver.switch_to.frame('contentFrame')
        data = driver.find_element_by_id('cBox').find_elements_by_tag_name('li')
        for i in range(len(data)):
            nb = data[i].find_element_by_class_name('nb').text
            msk = data[i].find_element_by_css_selector('a.msk')
            writer.writerow([msk.get_attribute('title'),
                            nb, msk.get_attribute('href')])
            runCnt += 1
            print('runCnt:', runCnt)

        #-----------------讀取使用者收藏歌單-------------------
        data = driver.find_element_by_id('sBox').find_elements_by_tag_name('li')
        for i in range(len(data)):
            nb = data[i].find_element_by_class_name('nb').text
            nb.replace(u'\xa0', u' ');
            msk = data[i].find_element_by_css_selector('a.msk')
            #UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' in position 2: illegal multibyte sequence
            #csvFile = open('playList.csv', 'w', newline='', encoding="utf-8")
            writer.writerow([msk.get_attribute('title'),
                            nb, msk.get_attribute('href')])
            runCnt += 1
            print('runCnt:', runCnt)
            cPlayerList.append(msk.get_attribute('href'))

        #從url列表中隨機讀取一個作為下一爬取的url
        randIndex = int(random.uniform(0, len(cPlayerList)))
        playUrl = cPlayerList[randIndex]
        del cPlayerList[randIndex]      #列表中取走後需要在列表中將該url刪除
        #轉到的頁面是歌單的詳細頁面,以下程式碼負責讀取該頁面中的作者頁面,跳轉到作者頁面以便繼續爬取
        driver.get(playUrl)
        driver.switch_to.frame('contentFrame')
        data = driver.find_element_by_id('m-playlist').find_element_by_class_name('cntc').find_element_by_class_name('name')
        playUrl = data.find_element_by_css_selector('a.s-fc7').get_attribute('href')
    except:
        #若出現錯誤,從url列表中繼續取出一個url
        randIndex = int(random.uniform(0, len(cPlayerList)))
        playUrl = cPlayerList[randIndex]
        del cPlayerList[randIndex]
        print('頁面發生異常,取出一個備用url,當前url剩餘:', len(cPlayerList))
        driver.get(playUrl)
        driver.switch_to.frame('contentFrame')
        data = driver.find_element_by_id('m-playlist').find_element_by_class_name('cntc').find_element_by_class_name(
            'name')
        playUrl = data.find_element_by_css_selector('a.s-fc7').get_attribute('href')

插入連結與圖片

以後希望自己能多寫一些機器學習相關的部落格,現在進入研一也有一個多月了,慢慢的更確立了自己的學習方向。對爬蟲有些興趣但不是主要的學習方向,以後可能除非專案需要,否則不太會用爬蟲去爬一些資料。 立下flag,以後多寫機器學習。