1 # -*- ecoding: utf-8 -*-
2 # @ModuleName: 2、梨视频最热视频爬取
3 # @Function:
4 # @Author: merry
5 # @Time: 2021/1/20 18:51
6 import requests
7 from lxml import etree
8 from multiprocessing.dummy import Pool
9 import random
10 import os
11 import time
12
13 url = 'https://www.pearvideo.com/category_5'
14 # 定义请求头
15 headers = {
16 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36'
17 }
18 # 获得当前页面所有数据
19 response = requests.get(url, headers=headers).text
20 # etree解析
21 tree = etree.HTML(response)
22 # 解析详情页面的url返回li标签列表
23 div_list = tree.xpath('//ul[@id="listvideoListUl"]/li')
24 # 定义一个视频列表用来存放所有视频的文件路径,文件名,视频真实地址的url
25 video_list = []
26 # 遍历list标签
27 for li in div_list:
28 # video_1716852 解析a标签的href熟悉获取详情页url
29 viedeo_url = li.xpath('./div/a/@href')[0]
30 # 获取视频的名称
31 viedeo_name = li.xpath('./div/a/div[2]/text()')[0]
32 # 1716852 分隔viedeo_url 得到一个数组,获取第一个元素得到数字ID
33 viedeo_contID = str.split(viedeo_url, 'video_')[1]
34 # 返回视频地址的url
35 url = 'https://www.pearvideo.com/videoStatus.jsp'
36 # 生成随机时间戳
37 time_mrd = random.random()
38 # 定义请求头,这个请求头需要传入一个上面解析到的viedeo_url
39 headers = {
40 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36',
41 'Referer': f'https://www.pearvideo.com/{viedeo_url}'
42 }
43 # 定义params参数,传入contId上面获取到的viedeo_contID,还需要传入一个mrd随机时间戳,上面已经生成time_mrd
44 # 这两个参数和上面的请求头的内容是必须的,否则响应回来的数据提示页面不存在,错误的json
45 params = {
46 'contId': viedeo_contID,
47 'mrd': time_mrd
48 }
49 # 请求视频地址的url,返回的是一个json的数据
50 reponse = requests.get(url, headers=headers, params=params).json()
51 # 使用字典的方式一次获取一个srcUrl的键值,这个地址不是真正的视频地址:如下
52 ## 此地址是返回json地址 https://video.pearvideo.com/mp4/third/20210120/1611144446807-10008579-085440-hd.mp4 这个地址拿去浏览器你会发现404 notfound根本找不到这个视频
53 # 拼接部分cont-viedeo_url
54 # 这是真实的视频地址 https://video.pearvideo.com/mp4/third/20210120/cont-1717106-10008579-085440-hd.mp4 这个真实地址是通过打开详情页面播放视频的时候抓包所获得
55 # 通过对比你发现在真实地址最后一个斜杠后面的参数是 cont-1717106-10008579-085440-hd.mp4
56 # 而请求返回的地址是 1611144446807-10008579-085440-hd.mp4
57 # 除了cont-1717106 和 1611144446807 不一样 其他都一样,说明有猫腻
58 # 分析真实地址的cont-1717106 发现1717106 是前面参数viedeo_url请求到的url链接的后半部分数字,
59 # 那么就需要截取字符串再将viedeo_url的数字部分和cont-进行拼接 得到类似cont-1717106-10008579-085440-hd.mp4这样的名称才是视频的真实地址
60
61 # 从json中拿到假的视频url链接
62 get_video_url = reponse['videoInfo']['videos']['srcUrl']
63 # 获取需要替换的随机时间戳
64 get_systemTime = reponse['systemTime']
65 # 使用viedeo_contID拼接字符串cont-得到一个新的new_viedeo_contID
66 new_viedeo_contID = 'cont-' + viedeo_contID
67 # 替换假url中的随机时间戳为上面拼接好的new_viedeo_contID得到一个真实视频下载的url
68 new_viedeo_url = str.replace(get_video_url, get_systemTime, new_viedeo_contID)
69 print(f'\033[32m拼接新的URL {new_viedeo_url} 完成')
70 # 定义个字典,将文件路径,视频的名称和真实地址的url放在里面
71 url_dict = {
72 'file_path': './mp4/',
73 'name': viedeo_name + '.mp4',
74 'new_viedeo_url': new_viedeo_url
75 }
76 # 将上面的字典添加的列表中方
77 video_list.append(url_dict)
78
79 # 判断存放视频的文件夹是否存在,如果不存在则新建文件夹
80 if not os.path.exists('./mp4'):
81 os.mkdir('mp4')
82
83
84 def get_video_data(dict):
85 """
86 :param dict: 得到真实视频的二进制数据并存入硬盘
87 :return:
88 """
89 # 定义请求头
90 headers = {
91 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36'
92 }
93
94 print(f'\033[31m开始下载{dict["name"]}')
95 # 定义开始时间
96 start_time = time.time()
97 # 获取传入进来字典的new_viedeo_url,的真实视频地址的url获取二进制响应数据
98 viedeo_content = requests.get(dict["new_viedeo_url"], headers=headers).content
99 # 存放到硬盘,获取字典的路径和文件名
100 with open(dict["file_path"] + dict['name'], 'wb') as fp:
101 # 写入硬盘
102 fp.write(viedeo_content)
103 # 下载结束时间
104 end_time = time.time()
105 # 计算下载耗时
106 print(f'\033[32m下载 {dict["name"]}完成---------耗时:{int(end_time - start_time)}秒钟!')
107
108
109 # 获取video_list视频列表的长度,根据列表长度开启对应的多线程
110 viedeo_total = len(video_list)
111 # 定义线程池,传入列表长度
112 pool = Pool(viedeo_total)
113 # 传入get_video_data函数和可迭代对象video_list
114 pool.map(get_video_data, video_list)
115 print()
116 print(f'\033[31m 下载所有文件完成!文件存放路径为:./mp4')