# 进程池练习
'''
进程池练习
进程池使用原则:不是所有的代码都需要进程池,只有那些会阻塞且耗时的部分使用进程池
有接过视频下载不下来,没查到原因
'''
import random
import re
import sys
import time
from multiprocessing.dummy import Pool
import os
import requests
from lxml import etree
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0'
}
# 获取视频名称以及视频url方法
def spider21():
# 获取列表页所有视频的url
url = 'https://www.pearvideo.com/popular'
resp = requests.get(url=url, headers=headers)
resp.encoding = resp.apparent_encoding
tree = etree.HTML(resp.text)
video_list = tree.xpath('//div[@class="popular-bd"]/ul/li')
# 获取所有视频的名称和相对应的url
urls = []
for video in video_list:
video_name = video.xpath('./div[2]/a/h2/text()')[0] + '.mp4'
video_url = video.xpath('.//div[2]/a/@href')[0]
# print(video_name + video_url)
# 因为此处即将通过首页进入详情页,所以需要添加referer越过防盗链
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0',
'Referer': 'https://www.pearvideo.com/' + video_url
}
'''
从视频详情页面中,视频的地址是由ajax发起的请求,无法在页面元素中获取,所以使用网页抓包工具查看XHR
在网页抓包工具中发现,视频链接地址在
https://www.pearvideo.com/videoStatus.jsp?contId=1644685&mrd=0.7149222847866049
响应的json数据中.
请求地址为:https://www.pearvideo.com/videoStatus.jsp,
并且需要携带两个参数:contId是视频id,mrd是一组随机数
'''
id_ = ''.join(video_url).split("_")[-1]
params = {
'contId': id_,
'mrd': str(random.random())
}
detail = requests.get('https://www.pearvideo.com/videoStatus.jsp',
headers=header, params=params).json()
# 再返回的json数据中按层级获取视频的url,但是视频的url是经过加密的,在网页的检查元素中可以看到真正的url
detail_url = detail['videoInfo']['videos']['srcUrl']
system_time = detail['systemTime']
# 加密url:https://video.pearvideo.com/mp4/adshort/20191223/1678177521310-14728575_adpkg-ad_hd.mp4
# 真正的url: https://video.pearvideo.com/mp4/adshort/20200120/cont-1644685-14825963_adpkg-ad_hd.mp4
# 使用re模块中的sub方法将 加密连接中的systemTime替换成'cont-'+视频id
real_url = re.sub(system_time, 'cont-' + id_, detail_url)
# 因为下载操作肯定比较慢,所以下载使用进程池操作,为此,我们需要将视频名称和视频地址放入可迭代对象中备用
# 并且需要让视频和名字一一对应
video_dic = {
'name': video_name,
'url': real_url
}
urls.append(video_dic)
return urls
# 视频下载方法,
def download(urls_):
url_ = urls_['url']
print('正在下载:',urls_['name'],url_)
try:
video = requests.get(url=url_,headers=headers).content
video_path = './video/'+urls_['name']
with open(video_path,'wb') as fp:
fp.write(video)
print(urls_['name'], "下载完成")
time.sleep(2)
except Exception as e:
print('下载失败', urls_['name'], url_)
errType, errInfo, errTraceBack = sys.exc_info()
print('异常信息为:',errType,errInfo,errTraceBack)
pass
if __name__ == '__main__':
if not os.path.exists('./video'):
os.mkdir('./video')
pool = Pool(4)
urls = spider21()
# map方法需要一个方法名和一个可迭代对象
pool.map(download, urls)
# pool.close 告诉池不接受任何新作业。
# pool.join 通知池等待所有作业完成然后退出,从而有效清理池。
pool.close()
pool.join()