Skip to content

Commit aeec3ae

Browse files
committedAug 28, 2019
Create QQMusicMV.py
1 parent 78ec9eb commit aeec3ae

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed
 

‎Spiders/QQMusicMV/QQMusicMV.py

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#!/usr/local/bin/python3
2+
#-*- coding:utf-8 -*-
3+
# Author: liuchuan
4+
# Contact: liuchuan910927@gmail.com
5+
# File: QQMusicMV.py
6+
# Datetime: 2019/12/9 22:54
7+
# Software: PyCharm
8+
9+
import requests
10+
import os
11+
12+
from multiprocessing import Pool
13+
from selenium import webdriver # 引入 selenium 的浏览器驱动接口
14+
from selenium.webdriver.chrome.options import Options # 引入 chrome 选项
15+
16+
17+
# 请求头
18+
HEADERS = {
19+
'referer': 'https://y.qq.com/portal/mv_lib.html',
20+
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36'
21+
}
22+
23+
24+
def selenium_chrome(html):
25+
'''
26+
使用 Selenium启动Chrome浏览器测试
27+
:param html: 浏览器需要打开的网址
28+
:return: WebDriver对象
29+
'''
30+
'''
31+
Chrome-headless 模式: Google 针对 Chrome 浏览器 59版 新增加的一种模式,可以让你不打开UI界面的情况下使用 Chrome 浏览器,所以运行效果与 Chrome 保持完美一致。
32+
webdriver安装路径: macOS:复制webdriver到/usr/local/bin目录下之后,就不需要指定path
33+
'''
34+
# 配置Chrome浏览器 (Headless无界面启动模式)
35+
option = Options()
36+
option.headless = True # 设置是否启动无界面化
37+
browser = webdriver.Chrome(options=option) # 打开chrome浏览器 声明 chrome 浏览器
38+
browser.get(html) # 访问网页
39+
browser.implicitly_wait(5) # 等待5秒, 1秒加载完毕, 5秒是最长时间
40+
# print(browser.page_source) # 将源码打印到终端
41+
return browser
42+
43+
44+
45+
def make_dir(folder_name):
46+
"""
47+
新建文件夹并切换到该目录下
48+
:param folder_name: 文件夹名称
49+
:return:
50+
"""
51+
# join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串。
52+
# path = os.path.join(Dir_PATH, folder_name)
53+
54+
# 根据文件名,检查路径是否存在?
55+
# 如果目录已经存在,就不用再次爬取,避免重复,提高效率。存在返回 Flase,否则反之
56+
if not os.path.exists(folder_name):
57+
os.makedirs(folder_name) # 如果不存在路径,根据 folder_name 创建文件夹
58+
# os.chdir(folder_name) # 改变当前工作目录
59+
return True
60+
print("文件夹已经存在!")
61+
return False
62+
63+
64+
def download_music(music_url, dst_path):
65+
'''
66+
下载音乐到本地
67+
:param music_url: 音乐链接地址
68+
:param dst_path: 保存路径
69+
:return:
70+
'''
71+
try:
72+
req = requests.get(url=music_url, headers=HEADERS)
73+
with open(dst_path, "wb+") as f: # 将下载的图片保存到对应的路径
74+
f.write(req.content)
75+
print(f"[INFO]: 已保存视频文件: {dst_path}")
76+
f.close()
77+
except requests.exceptions.RequestException as e:
78+
raise e
79+
80+
81+
def get_mv_max_page_numbers():
82+
'''
83+
获取网页中 MV 的最大页码
84+
:return: MV最大页码
85+
'''
86+
baseURL = 'https://y.qq.com/portal/mv_lib.html'
87+
88+
browser = selenium_chrome(baseURL)
89+
90+
# 最大页码
91+
MaxPageNumber = browser.find_element_by_xpath('//div[@class="mod_page_nav js_pager"]/a[4]').get_attribute('data-index')
92+
# print(f'最大页码: {MaxPageNumber}')
93+
94+
return MaxPageNumber
95+
96+
97+
def parse_all_pages_to_get_url():
98+
'''
99+
解析所有页面得到网址
100+
'''
101+
# 最大页面
102+
max_page = get_mv_max_page_numbers()
103+
104+
# 构造分页数字列表
105+
page_indexs = range(0, int(max_page), 1)
106+
107+
for idx in page_indexs: # 通过循环获取所有页面 URL
108+
109+
# 每一页URL
110+
url = f'https://y.qq.com/portal/mv_lib.html#page={idx}&&order=1&&version=7&area=15&t6=1'
111+
112+
get_all_urls_of_mv(url)
113+
114+
115+
def get_all_urls_of_mv(html):
116+
'''
117+
根据所有页面网址, 获取所有 MV 的 data_vid
118+
'''
119+
120+
# WebDriver浏览器对象
121+
browser = selenium_chrome(html)
122+
123+
# 构造分页数字列表
124+
page_indexs = range(1, 21, 1)
125+
126+
for idx in page_indexs:
127+
# MV 的id
128+
data_vid = browser.find_element_by_xpath(f'//*[@id="mv_list"]/li[{idx}]').get_attribute('data-vid')
129+
print()
130+
print(f'data-vid: {data_vid}')
131+
132+
get_mv_info(data_vid)
133+
134+
135+
def get_mv_info(vedio_vid):
136+
'''
137+
根据视频的vid, 获取视频信息(链接\名称\艺人..等等)
138+
:param vedio_vid: 视频 id
139+
'''
140+
141+
# https://y.qq.com/n/yqq/mv/v/o0032i34nid.html
142+
143+
# MV 播放真正链接
144+
realURL = f'https://y.qq.com/n/yqq/mv/v/{vedio_vid}.html'
145+
146+
# WebDriver浏览器对象
147+
browser = selenium_chrome(realURL)
148+
149+
# MV 名称
150+
mv_name = browser.find_element_by_xpath('//div/h1/span[1]').get_attribute('title')
151+
# MV 艺人
152+
mv_singer = browser.find_element_by_xpath('//*[@id="mv_control"]/h1/a').text
153+
# MV 播放链接
154+
mv_link = browser.find_element_by_tag_name('video').get_attribute('src')
155+
156+
print('================ MV真实下载链接 ================')
157+
print(f'MV名称 = {mv_name}')
158+
print(f'艺人 = {mv_singer}')
159+
print(f'下载链接 = {mv_link}')
160+
161+
# ====================== 根据获取到视频链接,下载到本地 ==================
162+
163+
# 文件路径名
164+
fileName = f'/Users/liuchuan/Desktop/QQ音乐/{mv_name}'
165+
166+
# 目录名
167+
dirname = f'{fileName}/{mv_name}.mp4'
168+
169+
make_dir(fileName)
170+
171+
download_music(mv_link, dirname)
172+
173+
browser.close() # 关闭浏览器
174+
175+
176+
if __name__ == '__main__':
177+
'''
178+
主函数.程序的入口
179+
'''
180+
181+
# 创建多个进程
182+
# 进程池, 表示可以同时执行的进程数量
183+
# Pool默认大小是 CPU 的核心数
184+
pool = Pool(2)
185+
186+
for i in range(5):
187+
# 创建进程, 放入进程池 统一管理
188+
pool.apply_async(parse_all_pages_to_get_url(), args=(i, ))
189+
print('父进程结束!!!')
190+
191+
pool.close()
192+
pool.join() # 在调用join之前,必须close.在调用 close 之后,就不能再添加新的进程.

0 commit comments

Comments
 (0)
Please sign in to comment.