千千音樂專案
1、千千音樂概述
1.1 目的
-
通過在程式入口,輸入歌手的名字,便能獲取該歌手的所有歌曲的詳細資訊
-
預覽
python run.py
# 資料通過檔案返回,檔名字為run.py同級目錄下song_list.txt檔案中
# 需要說明的是,該連結自帶時間戳,隔天會失效{
"song_name": "演員",
"song_id": "242078437",
"song_href": "http://music.taihe.com/song/242078437",
"author_tinguid": "2517",
"author_url": "http://music.taihe.com/artist/2517",
"song_album": "初學者",
"song_publish_date": "2016-07-18",
"song_publish_company": "海蝶(天津)文化傳播有限公司",
"download_url": "http://audio04.dmhmusic.com/71_53_T10040589078_128_4_1_0_sdk-cpm/cn/0206/M00/90/77/ChR47F1_nqiAfD0hAD_MGBybIdk026.mp3?xcode=aefbd591c37efa806a6c2b65cae142a973974a6",
"mv_download_url_info": {},
"song_mv_id": null
}
********************
{
"song_name": "你還要我怎樣",
"song_id": "100575177",
"song_href": "http://music.taihe.com/song/100575177",
"author_name": "薛之謙",
"author_tinguid": "2517",
"author_url": "http://music.taihe.com/artist/2517",
"song_album": "意外",
"song_publish_date": "2013-11-11",
"song_publish_company": "華宇世博音樂文化(北京)有限公司",
"download_url": "http://audio04.dmhmusic.com/71_53_T10038986648_128_4_1_0_sdk-cpm/cn/0208/M00/E5/61/ChR46119DrGAW4d4AEvErRDwLyg867.mp3?xcode=6b725779223e2de86a6bbb3ac7a1959393d994b",
"song_lrc_link": "http://qukufile2.qianqian.com/data2/lrc/0a6ef3d9a86dd4f1aa782a114d9f288e/672463341/672463341.lrc",
"mv_download_url_info": {},
"song_mv_id": null
}
......
1.2 開發環境
-
抓包分析平臺:Window
-
抓包分析軟體:Chrome
-
程式碼開發平臺:Linux
-
程式碼開發軟體:pycharm
-
技術棧
-
傳送資料請求:requests
-
資料儲存:mongodb
-
資料處理:lxml, re, json, pymongo, logging
-
2、專案設計
2.1 流程設計
2.2 專案流程概述
-
專案目錄
-
專案工作流程概述
-
bin
模組-
run.py
為程式入口,通過run.py能夠呼叫core.spiders
中的各個爬蟲模組,實現功能 -
log.log
為日誌記錄 -
song_list.txt
為寫入的歌手資料,為使用者視覺化的效果
-
-
core
模組-
spiders
為爬蟲模組-
artist_total.py
獲取所有的歌手資訊,並存入mongodb
資料庫 -
author_info_update
更新歌手的資訊,包括生日,簡介等 -
song_total.py
獲取單個歌手的所有歌曲資訊 -
song_info_total_update.py
更新歌手的歌曲資訊,包括下載連結,mv連結(如果有)
-
-
-
utils
模組-
log.py
提供log的功能 -
mongo_pool.py
操作資料庫的功能 -
random_useragent.py
提供隨機的user_agent
-
-
settings.py
檔案提供配置
-
3、模型設計
3.1 歌手模型
-
效果圖
-
欄位解釋
-
author_name
歌手名字 -
author_url
歌手主頁 -
author_tinguid
歌手的id
-
author_birthday
歌手生日 -
author_constellation
歌手星座 -
author_from_area
歌手國籍 -
author_gender
歌手性別 -
author_hot
歌手熱度 -
author_image_url
歌手圖片的url -
author_intro
歌手簡介 -
author_share_num
歌手主頁被分享的次數 -
author_songs_total
歌手的總歌曲數量 -
author_stature
歌手的身高 -
auhtor_weight
歌手的體重
-
3.2 歌曲模型
-
效果圖
-
欄位解釋
-
author_info
歌手的資訊-
author_name
歌手名字 -
author_tinguid
歌手id -
author_url
歌手主頁url -
案例
-
-
song_list
歌手的所有歌曲-
song_name
歌曲名字 -
song_id
歌曲id -
song_href
歌曲主頁 -
author_name
歌手名字 -
author_tinguid
歌手id -
author_url
歌手主頁 -
song_album
歌曲所屬專輯 -
song_publish_date
歌曲發行時間 -
song_publish_company
歌曲發行公司 -
download_url
歌曲下載地址 -
song_lrc_link
歌曲的歌詞連結 -
mv_download_url_info
歌曲mv的下載資訊 -
song_mv_id
歌曲mv的id -
案例
-
-
4、settings.py
-
說明
-
實現全域性的配置
-
-
程式碼
import logging
# 配置MONGO_URL
MONGO_URL = "mongodb://127.0.0.1:27017"
# 配置log
LOG_FMT = "%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s:%(message)s"
LOG_DATE_FMT = "%D-%m-%d %H:%M:%S"
LOG_FILENAME = 'log.log'
LOG_LEVEL = logging.DEBUG
# 配置user_agent
USER_AGENT_LIST = [
"Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E; rv:11.0) like Gecko",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60",
"Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50",
"Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10",
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36"
]
5、utils模組
5.1 random_useragent.py
-
說明
-
實現隨機生成一個user_agent的功能
-
通過property裝飾方法,使其可以方便呼叫
-
-
程式碼
import random
from qianqianyinyue.settings import USER_AGENT_LIST
class RandomUserAgent(object):
def __init__(self):
self.user_agent_list = USER_AGENT_LIST
5.2 log.py
-
說明
-
檔案儲存
-
方便後續檢視日誌
-
-
控制檯輸出
-
方便及時發現數據的變動
-
-
-
程式碼
import logging
import sys
from qianqianyinyue.settings import LOG_FMT, LOG_LEVEL, LOG_DATE_FMT, LOG_FILENAME
class Logger(object):
def __init__(self):
self._logger = logging.getLogger()
self.formatter = logging.Formatter(fmt=LOG_FMT, datefmt=LOG_DATE_FMT)
self._logger.addHandler(hdlr=self.get_file_handler(filename=LOG_FILENAME))
self._logger.addHandler(hdlr=self.get_console_handler())
self._logger.setLevel(level=LOG_LEVEL)
def get_file_handler(self, filename):
file_handler = logging.FileHandler(filename=filename, encoding='utf8')
file_handler.setFormatter(fmt=self.formatter)
return file_handler
def get_console_handler(self):
console_handler = logging.StreamHandler(stream=sys.stdout)
console_handler.setFormatter(fmt=self.formatter)
return console_handler
5.3 mongo_pool.py
-
說明
-
將集合設定為變數
collection
,方便例項化時選擇相應的資料 -
實現了
-
插入功能,並記錄日誌
-
更新功能,並記錄日誌
-
條件查詢更新
-
-
查詢單條資料功能
-
通過特定條件,實現查詢單條資料
-
-
查詢所有資料
-
返回一個生成器
-
-
-
-
程式碼
from pymongo import MongoClient
from qianqianyinyue.settings import MONGO_URL
from qianqianyinyue.utils.log import logger
class MongoPool(object):
def __init__(self, collection):
self.mongo_client = MongoClient(MONGO_URL)
self.collections = self.mongo_client['qianqianyinyue'][collection]
def __del__(self):
self.mongo_client.close()
def insert_one(self, document):
self.collections.insert_one(document)
logger.info("插入了新的資料:{}".format(document))
def update_one(self, conditions, document):
self.collections.update_one(filter=conditions, update={"$set": document})
logger.info("更新了->{}<-的資料".format(conditions))
def find_one(self, conditions):
collections = self.collections.find(filter=conditions)
for item in collections:
item.pop('_id')
return item
def find_all(self):
collections = self.collections.find()
for item in collections:
item.pop('_id')
yield item
6、core模組
6.0 url分析
-
首頁url分析
-
url:
http://music.taihe.com/artist
-
返回一個html_str
-
請求體
-
GET請求
-
User-Agent
-
-
目標資料
-
author_name
-
author_url
-
author_tinguid
-
-
-
-
歌手主頁url分析
-
url:
http://music.taihe.com/artist/2517
-
請求體
-
GET請求
-
Referer:
http://music.taihe.com/aritst
-
-
目標資料
-
song_name
-
song_url
-
song_id
-
song_mv_url
-
song_mv_Id
-
-
-
url:
http://music.taihe.com/data/user/getsongs?start=15&size=15&ting_uid=2517
-
說明
-
這是ajax返回的第二頁資料,start為當前頁數減1後乘15,size固定值,ting_uid為歌手的author_tinguid
-
返回的資料在data欄位中,為xml格式字串,需要re或lxml來提取資料
-
如何獲取最大頁數?通過歌手主頁的首頁,拿到頁面下標的最大值,然後通過該值構造下一頁的請求
-
-
請求體
-
GET請求
-
Referer:
http://music.taihe.com/artist/2517
-
User-Agent
-
-
-
url:
http://music.taihe.com/data/tingapi/v1/restserver/ting?method=baidu.ting.artist.getInfo&from=web&tinguid=2517
-
說明
-
每一個歌手的author_tinguid對應一個json資料
-
如果同一時刻,傳送的請求過多,會封禁IP
-
設定time.sleep(),能夠多拿到一些資料
-
我將時間控制在1到3秒之間,拿到了更多的資料,如果不是為了立刻拿到資料,不妨將時間設定的長一點,即拿到了資料,也降低了被對方伺服器發現的可能
time.sleep(random.uniform(1,3))
-
-
-
-
-
歌曲主頁url分析
-
url:
http://music.taihe.com/song/242078437
-
請求體
-
User-Agent
-
-
目標資料
-
所屬的專輯
-
發行時間
-
發行公司
-
-
-
url:
http://musicapi.taihe.com/v1/restserver/ting?method=baidu.ting.song.playAAC&format=jsonp&songid=242078437&from=web
-
請求體
-
Referer:
http://music.taihe.com/song/242078437
-
User-Agent
-
-
目標資料
-
下載連結
-
歌詞連結
-
-
-
url:
http://music.taihe.com/mv/601422013
-
目標資料
-
mv的id
song_mv_id
-
-
-
url:
http://musicapi.taihe.com/v1/restserver/ting?method=baidu.ting.mv.playMV&mv_id=XXXX
-
說明
-
通過來源url,用正則表示式,解析出mv_id,需要注意的是資料庫中的mv_id,可能提取的與目標mv_id是不相符的,如果用那個mv_id,可能提取不到資料
-
-
目標資料
-
下載mv的url
-
-
-
6.1 artist_total.py
-
程式碼
import requests
from lxml import etree
from qianqianyinyue.utils.mongo_pool import MongoPool
from qianqianyinyue.utils.random_useragent import random_user_agent
from qianqianyinyue.utils.log import logger
class ArtistTotal(object):
"""
預先執行的程式,獲取該網站曲庫,所有的音樂作者的資訊
資訊包括:
作者id: 對應資料庫欄位--->>>“author_tinguid”
作者的首頁url:對應資料庫欄位--->>>“author_url”
作者的名字:對應資料庫欄位--->>>“author_name”
"""
def __init__(self):
self.mongo_pool = MongoPool(collection='artist')
self.url = "http://music.taihe.com/artist"
self.user_agent = random_user_agent
def get_response(self, url, user_agent):
headers = {
"User-Agent": user_agent
}
try:
response = requests.get(url=url, headers=headers)
if response.ok:
return response.content.decode()
except Exception as e:
logger.warning(e)
def parse_html_str(self, html_str):
html_str = etree.HTML(html_str)
b_li_list = html_str.xpath("//ul[@class=\"container\"]/li[position()>1]")
for b_li in b_li_list:
s_li = b_li.xpath('./ul/li')
for li in s_li:
item = dict()
item["author_name"] = li.xpath('./a/@title')[0] if li.xpath('./a/@title') else None
author_url = "http://music.taihe.com"+li.xpath('./a/@href')[0] if li.xpath('./a/@href') else "-1"
item["author_url"] = author_url
item["author_tinguid"] = author_url.rsplit('/', 1)[-1]
yield item
def insert_to_mongodb(self, documents):
for document in documents:
self.mongo_pool.insert_one(document=document)
def run(self):
response = self.get_response(url=self.url, user_agent=self.user_agent)
author_infos = self.parse_html_str(html_str=response)
self.insert_to_mongodb(documents=author_infos)
if __name__ == "__main__":
obj =