举个栗子直观地看一下线程的作用吧
import time def get_text(char): print("正在下载",char) time.sleep(2) print("成功加载",char) return char #单线程运行: #运行文本 text=['a','b','c','d'] #记录开始时间 start_time=time.time() for i in text: get_text(i) #记录结束时间 end_time=time.time() print("单进程共需要时间:",end_time-start_time) #多线程运行 #导库 from multiprocessing.dummy import Pool #实例化4线程池 pool=Pool(4) start_time=time.time() text_list=pool.map(get_text#进行多线程的目标函数名,没有() ,text#传入数据的列表 ) #该函数返回值为函数return值组成的列表,顺序和传入列表相对应 end_time=time.time() print("多进程共需要时间:",end_time-start_time) print(text_list) 结果为: 正在下载 a 成功加载 a 正在下载 b 成功加载 b 正在下载 c 成功加载 c 正在下载 d 成功加载 d 单进程共需要时间:8.001578569412231 正在下载 a 正在下载 b 正在下载 c 正在下载 d 成功加载 b成功加载 d 成功加载 c 成功加载 a 多进程共需要时间:2.0006678104400635 ['a', 'b', 'c', 'd'] 我们发现:多线程输出的顺序结束时间是随机的,也证实了多线程之间运行并不会相互影响
梨视频网址
import os from lxml import etree import re import requests from multiprocessing.dummy import Pool header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36 SLBrowser/7.0.0.5211 SLBChan/25" } # 梨视频url url = 'https://www.pearvideo.com/' # cookie破解 session = requests.Session() session.get(url=url, headers=header) # 获取前台代码并解析目标url 讲解【1】 response = session.get(url=url, headers=header).text tree = etree.HTML(response).xpath('/html/body/div[2]/div[9]/div[2]/div[2]/ul/li') # 存储名字和地址的列表 content_name_loc = [] for each_tree in tree: # each_tree.xpath('./div/a/@href')[0]解析地址不完整,要完整化 讲解【1】 each_loc = 'https://www.pearvideo.com/' + each_tree.xpath('./div/a/@href')[0] # 确定视频存储名称 讲解【1】 each_name = each_tree.xpath('./div/a/div[2]/text()')[0] + ".mp4" # url_2是each_loc进入界面内部的一个包,我们不能直接从前台界面直接获取包,所以要获取一个新的url # 这个url_2是对于所有视频详情界面共有的地址,通过不同的params里的'contId'值实现不同视频的呈现 # 讲解【2】 url_2 = 'https://www.pearvideo.com/videoStatus.jsp?' header_1 = { # 讲解【2】 'Referer': each_loc , "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36 SLBrowser/7.0.0.5211 SLBChan/25" } # 我们发现'contId'参数并不是毫无规律,而是我们在主界面获取each_loc的后面的一串数字 # 栗子: # 若each_loc=https://www.pearvideo.com/video_1733139 # 那么'contId'参数就是‘1733373’,所以我们通过‘_’来进行分割 params = { # 这个参数位置在哪见讲解【2】 'contId': each_loc.split('_')[-1] } each_response = session.get(url=url_2, params=params, headers=header_1).json() # 看下面讲解 str_1 = each_response["videoInfo"]["videos"]["srcUrl"] """ each_response的json字符串 { "resultCode":"1", "resultMsg":"success", "reqId":"9424322a-fe48-48a8-86fd-85070498a1e8", "systemTime": "1624770918308", "videoInfo":{"playSta":"1","video_image":"https://image1.pearvideo.com/cont/20210624/11429718-143425-1.png", "videos":{"hdUrl":"","hdflvUrl":"","sdUrl":"","sdflvUrl":"","srcUrl":"https://video.pearvideo.com/mp4/third/20210624/1624770918308-11429718-143415-hd.mp4"}} } 我们需要提取的是"https://video.pearvideo.com/mp4/third/20210624/1624770918308-11429718-143415-hd.mp4" 但是我们在网页上打开该网址发现显示:404 Not Found 这是为什么呢? 因为真正的网址是这个形式的'https://video.pearvideo.com/mp4/third/20210624/cont-1733139-11429718-143415-hd.mp4' 所以发现失败了,那么我们想,视频界面是ajax,不能解析出来,用抓包又不是我们想要的 但是在山重水复时,静下心来发现我们刚才提取的网址和视频网址那么相像 于是有了一个得到的想法: 分割已有的提取的网址来组成我们的目标网址!!! each_loc='https://www.pearvideo.com/video_1733139' str_1="https://video.pearvideo.com/mp4/third/20210624/1624770918308-11429718-143415-hd.mp4" new_loc='https://video.pearvideo.com/mp4/third/20210624/cont-1733139-11429718-143415-hd.mp4' new_loc就是我们的目标,怎么得到呢? 就是把str_1的 1624770918308换成cont-1733139 而cont我想就是参数名,每个视频相同 对于1733139就是each_loc的后面一串 于是也就有了下面这一段代码 那么我们开始一步步剖析它: 1. a = 'cont-' + each_loc.split('_')[-1] each_loc.split('_')结果就是 ['https://www.pearvideo.com/video''1733139'] each_loc.split('_')[-1]就是'1733139',在加上'cont-',就是我们想要的结果 2. re_1 = "(.*)/.*?-(.*)" b = re.findall(re_1, str_1) 这两句就是使用正则表达式提取str_1中的 'https://video.pearvideo.com/mp4/third/20210624'(因为最后一个/结束,所以用的贪婪匹配) 和 '11429718-143415-hd.mp4' 3. new_loc = b[0][0] + "/" + a + "-" + b[0][1] 对结果进行拼接得到结果 不要忘了"/" 和"-" 这就得到了我们苦苦寻找的视频地址了 """ a = 'cont-' + each_loc.split('_')[-1] print(str_1) re_1 = "(.*)/.*?-(.*)" b = re.findall(re_1, str_1) new_loc = b[0][0] + "/" + a + "-" + b[0][1] print(new_loc) # 到这里可能你已经忘了还有多线程的事,嘻嘻嘻 # 这里为了最后多线程爬取内容更加方便 # 我们还用字典的格式对数据视频名称each_name和new_loc放于content_name_loc中 content_name_loc.append({"name": each_name, "loc": new_loc}) # 在这里输出一下看看结果是否符合预期 print(content_name_loc) # 创建文件夹 if not os.path.exists("./梨视频"): os.mkdir("./梨视频") # 分装线程池 def get_content(dic): # 请求数据 video_data = session.get(url=dic['loc'], headers=header).content # 打开文件并写入 fp = open("./梨视频/" + dic["name"], mode="wb") fp.write(video_data) fp.close() # 计算时间(跟网速有关) start = time.time() # 实例化Pool,定个数 pool = Pool(len(content_name_loc)) # 多线程运行 pool.map(get_content, content_name_loc) # 关闭线程 pool.close() # 等待所有线程结束 pool.join() end = time.time() # 输出时间 print(end - start)
看结果:
(提前说明一下,这可不是博主喜欢的内容啊,完全是巧合,要信我,嘻嘻嘻~~~)
讲解【1】
简单的几条线就讲解完了???
对,没错!!!此处无声胜有声
讲解【2】
蓝色内容就是我们想获取的视频地址! 咦?这是什么吗?我直接爬不得了?还用你的分割? 但是你爬取后就会发现爬去数据里根本找不到这个网址 为什么呢?让我们刷新一下界面
那个网址就会消失 所以我们怀疑是ajxa,所以我们复制在包里寻找 结果无结果,在XHR中只有一个包。 这个包就是我们提到的合成new_loc的包 另有refer参数需要在headers中 该包的返回值代码中已经提到,这里不再展示
【持续更新ing】