由于歌手個人主頁的網頁源代碼中還嵌入了一個子網頁(框架源代碼里面包含了我們需要的信息),因此我們不能使用requests庫來爬取,而使用selenium。
接下來,讓我詳細講解整個爬取過程.
一,構造歌手個人主頁的URL
前段時間我們獲取了網易云音樂全部歌手的id號
今天我們就利用全部歌手的id號來構造歌手個人主頁的URL,從而實現用爬取全部歌手的熱門歌曲及其id號的目的
以歌手薛之謙的個人主頁 為例,來看一下他的主頁的URL為:
https://music.163.com/#/artist?id=5781
因此只需要根據歌手對應的id就可以構造出歌手的個人主頁,在歌手的個人主頁我們能看到熱門作品這一欄網易云音樂全部歌手id號點擊獲取(csv文件)
二,分析網頁源代碼
現在我們就要用Python爬蟲去爬取這些內容.如果你用requests庫去爬取的話,返回的網頁源代碼中根本就沒有這些信息
這時我們打開薛之謙的個人主頁鼠標右鍵分別查看網頁的源代碼和查看框架的源代碼
你會發現網頁源代碼和用requests庫請求返回的源代碼一摸一樣(里面沒有我們要爬取的信息),而在框架源代碼中有我們要爬取的熱門作品的信息
因此我們只需要將框架源代碼爬取下來
然后再解析即可得到我們需要的歌手的熱門作品的信息.
三,網頁源代碼和框架源代碼的區別
網頁源代碼是指父級網頁的源代碼.另外網頁中還有一種節點叫iframe,也就是子Frame,相當于網頁的子頁面
它的結構和外部網頁的結構完全一致,框架源代碼就是這個子網頁的源代碼.
四,獲取框架源代碼
這里我們使用selenium庫來爬取,在selenium打開頁面后
默認是在父級frame里面進行操作,而此時頁面中還有子frame,它是不能獲取到子frame里面的節點的,因此這時我們需要使用swith_to.frame()方法來切換到子frame中去
這時請求得到的代碼就從網頁源代碼切換到了框架源代碼
于是我們便能夠提取我們需要的熱門作品的信息了
通過歌手的個人主頁的URL來爬取其框架源代碼,具體爬取框架源代碼的函數:
def get_html_src(url): # 可以任意選擇瀏覽器,前提是要配置好相關環境,更多請參考selenium官方文檔 driver = webdriver.Chrome() driver.get(url) # 切換成frame driver.switch_to_frame("g_iframe") # 休眠3秒,等待加載完成! time.sleep(3) page_src = driver.page_source driver.close() return page_src
返回結果為歌手個人主頁的框架源代碼,里面包含了我們需要的信息.
五,解析源代碼
我們使用bs4庫進行解析,需要的信息包含在HTML5的下面代碼片段中:
<span class="txt"><a href="/song?id=(d*)"><b title="(.*?)">
因此可定義下面函數對其進行解析:
def parse_html_page(html): # pattern = '<span class="txt"><a href="/song?id=(d*)"><b title="(.*?)">' # 這里是使用lxml解析器進行解析,lxml速度快,文檔容錯能力強,也能使用html5lib soup = BeautifulSoup(html, 'lxml') items = soup.find_all('span', 'txt') return items
六,寫入csv文件
def write_to_csv(items, artist_name):
with open("music163_songs.csv", "a") as csvfile: writer = csv.writer(csvfile) writer.writerow(["歌手名字", artist_name]) for item in items: writer.writerow([item.a['href'].replace('/song?id=', ''), item.b['title']]) print('歌曲id:', item.a['href'].replace('/song?id=', '')) song_name = item.b['title'] print('歌曲名字:', song_name) csvfile.close()
七,讀取csv文件,構造全部歌手的個人主頁
# 獲取歌手id和歌手姓名 def read_csv(): with open("music163_artists.csv", "r", encoding="utf-8") as csvfile: reader = csv.reader(csvfile) for row in reader: artist_id, artist_name = row if str(artist_id) is "artist_id": continue else: yield artist_id, artist_name # 當程序的控制流程離開with語句塊后, 文件將自動關閉
八,程序主函數
# 主函數 def main(): for readcsv in read_csv(): artist_id, artist_name = readcsv url = "https://music.163.com/#/artist?id=" + str(artist_id) print("正在獲取{}的熱門歌曲...".format(artist_name)) html = get_html_src(url) items = parse_html_page(html) print("{}的熱門歌曲獲取完成!".format(artist_name)) print("開始將{}的熱門歌曲寫入文件".format(artist_name)) write_to_csv(items, artist_name) print("{}的熱門歌曲寫入到本地成功!".format(artist_name))