很多人学习python,不知道从何学起。
直达模块:
导入:咳咳!言归正传,当我们在写爬虫实例时,就肯定会对每个网页的链接进行分析,然后才能模拟请求。就 目前而言 ,根据我的 不完全统计 显示。有以下三种情况:
(其实这第一种和第二种也可以说成为是一种情况,因为 URL加密 是浏览器自动加密的。在模拟请求时不需要做任何处理。) 分析及代码实现:为了以后下载想看的电影或者电视剧,所以我直接从搜索影片入手:当我以 传闻中 为关键词搜索时:情况如下:
你看出来了吗?没错,困扰我这么几个小时的疑惑总算是解决了。 from urllib.parse import quote def user_ui(): print('***********************************************************') keyword = '传闻中' # input('请输入搜索的视频关键字:') url = 'https://www.****.tv/s/go.php?q=' quote(keyword, 'utf-8') # 进行url加密 URL = (url.split('go.php?q=')[0] url.split('go.php?q=')[1]).replace('%', '_') '.html' # 根据网页情况,更改加密后的url print(URL) user_ui ''' 输出: https://www.****.tv/s/_E4_BC_A0_E9_97_BB_E4_B8_AD.html '''
由于整个运行过程中需要多次发送请求,所以,为减少代码量,直接写一个请求函数: import requests referer = 'https://www.****.tv' header = { 'referer': referer, 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37' } proxies = ['HTTP://60.13.42.120:9999', 'HTTP://163.204.244.207:9999', 'HTTP://113.121.39.121:9999', 'HTTP://125.117.134.99:9000', 'HTTP://123.169.114.81:9999', 'HTTP://58.253.159.230:9999', 'HTTP://182.46.99.247:9999', 'HTTP://171.15.48.235:9999', 'HTTP://120.83.105.226:9999', 'HTTP://113.124.95.210:9999', 'HTTP://171.13.103.241:9999', 'HTTP://123.163.96.42:9999', 'HTTP://27.42.168.46:49345', 'HTTP://122.234.27.22:9000', 'HTTP://125.108.73.47:9000', 'HTTP://125.108.97.209:9000', 'HTTP://113.124.85.24:9999', 'HTTP://27.220.51.228:9000', 'HTTP://113.124.84.205:9999', 'HTTP://110.243.31.147:9999'] def requests_url(url): # 请求网址 try: proxy = {'http': random.choice(proxies)} print(proxy) r = requests.get(url, proxies=proxy, headers=header) r.raise_for_status() r.encoding = 'UTF-8' return r except: print("*********请求失败!***********")
经过以上处理后,我们已经获得了搜索的正确链接和请求函数。接下来我们开始根据源码来提取出我们所需要的各种视频信息,保存在 videos_info 中:
html = requests_url(URL).text # 请求网页 # 搜索时间以及数量 search_time = parsel.Selector(html).xpath('//div[@class="breadcrumbs"]/text()').extract()[0] # 视频信息标签 videos_info = parsel.Selector(html).xpath('//dd').extract() print(search_time) print(videos_info)
html = requests_url(URL).text # 搜索时间以及数量 search_time = parsel.Selector(html).xpath('//div[@class="breadcrumbs"]/text()').extract()[0] # 视频信息标签 videos_info = parsel.Selector(html).xpath('//dd').extract() try: # 若搜索结果小于10项,则无页码信息 print(search_time, end='') # 页码 page_num = parsel.Selector(html).xpath('//div[@class="pages"]/a/text()').extract()[-2] print(',共{}页。每页10项。'.format(page_num)) except: print(end='\n')
现在,我们已经获取了搜索页面的所有视频的信息标签(此时只有一条),接下来就开始提取关键信息:
def split_info(videos_info): num = 1 for video_info in videos_info: # 去掉关键字高亮标签 info = str(video_info).replace('<em>', '').replace('</em>', '') # 去掉源码中红色字体的标注 soup = BeautifulSoup(info, 'html.parser') info = soup.find_all('p') # 将通过关键字搜索到的视频名与对应链接保存下来 # 对应链接为拼接后的完整链接,如:https://www.*****.tv/tv/wNiNmMnRjZ.html video[info[0].strong.a.string info[0].span.string] = 'https://www.' info[0].strong.a.attrs['href'] name2_info = info[1].string # 又名 area_info = info[2].string # 地区与类型 actor_info = info[3].string # 演员 introduction = info[4].string # 简介 print('***********************************************************') print('{:0>2d}: '.format(num), end='') num = 1 print('\t' info[0].strong.a.string info[0].span.string) # 名称 print('\t' name2_info) print('\t' area_info) print('\t' actor_info) print('\t' introduction)
运行结果:
choice = input('\n请输入序号选择:') NAME = list(video.keys())[int(choice) - 1] # 通过下标确定进一步搜索的类容 URL = video[NAME]
当我们选择好要下载视频时,就可以通过名字来获取视频的主页链接,之后就可以请求具体的视频链接了: 如下图:我们通过控制台的预览可以发现,请求当前的网址获得的并不是我们所需要的!
为了更方便的来理解并构造新的请求链接:我去查看了一下其他类型的链接,又发现了以下的几种情况:
def get_video(url): # 正确的链接拼接 URL = 'https://www.****.tv/ajax/downurl/' url.split('/')[-1].split('.')[0] '_' url.split('/')[3] '/' # 根据重定向拼接真实的链接 提示: 下载链接最后的 “ / ” ,是必须要加上的,没有的话,依然会加载不出来。
import requests from bs4 import BeautifulSoup url='https://www.****.tv/ajax/downurl/wNiNmMnRjZ_tv/' header = { 'referer': 'https://www.****.tv', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37' } r = requests.get(url, headers=header) soup=BeautifulSoup(r.text,'html.parser') print(soup.prettify())
结果如下:
def get_video(url): # 根据重定向拼接真实的链接 URL = 'https://www.****.tv/ajax/downurl/' url.split('/')[-1].split('.')[0] '_' url.split('/')[3] '/' # 请求视频链接 response = requests_url(URL) # 找到第一集的播放链接 soup = BeautifulSoup(response.text, 'html.parser').find('ul', attrs={'class': "player ckp"}).find('li').a.attrs['href'] # 重新拼接每一集主页链接 URL1 = 'https://www.****.tv' soup
知道了如何请求播放链接,就以第一集来作为例子进行分析:
我们再次查看 预览 ,可以看到:其实第一个 index. m3u8 的内容指向了第二个 index. m3u8 文件。而视频的内容(ts 文件)就储存在这里面。
打开 第一集 的网页源码可以看到:这第一个 index. m3u8 文件的链接就在源代码中:
至此,我们也看到了所有的链接都包含在 <script>…</script> 的标签中。可是,标签的内容全是字符串,既不是 json 格式,也没有网页标签。该如何提取呢? def get_video(url): # 正确的链接拼接 URL = 'https://www.****.tv/ajax/downurl/' url.split('/')[-1].split('.')[0] '_' url.split('/')[3] '/' # 根据重定向拼接真实的链接 # 请求视频链接 response = requests_url(URL) # 找到每一集的链接 soup = BeautifulSoup(response.text, 'html.parser').find('ul', attrs={'class': "player ckp"}).find('li').a.attrs['href'] # 重新拼接每一集主页链接 URL1 = 'https://www.****.tv' soup response1 = requests_url(URL1).text soup1 = BeautifulSoup(response1, 'html.parser').find_all('script')[12] # 获取script标签 # 通过正则表达式找出所有下载链接 p = re.compile(r"https://.*?/index.m3u8") information = p.findall(str(soup1)) num = 1 for info in information: download['第{}集'.format(num)] = str(info).replace('index.m3u8','1000k/hls/index.m3u8') num = 1
现在我们已经获取到了每一集的第一个 index. m3u8 文件链接,怎么才能跳转到第二个 index. m3u8 文件链接呢?
看出来了吗?对了,就是增添了路径而已,这样的话,继续添加代码: def get_video(url): ''' 与上面的代码一样,所以就省略了 ''' response1 = requests_url(URL1).text # 获取script标签 soup1 = BeautifulSoup(response1, 'html.parser').find_all('script')[12] # 通过正则表达式找出所有下载链接 p = re.compile(r"https://.*?/index.m3u8") information = p.findall(str(soup1)) num = 1 for info in information: # download = {} 是全局变量 download['第{}集'.format(num)] = str(info).replace('index.m3u8','1000k/hls/index.m3u8') num = 1
到目前为止,我们已经获得了所有的 m3u8 格式的真实链接。接下来就是下载的问题了,简单点就是通过 ffmpy3 下载,这里我就不过多的讲了。因为在我之前的文章中有写到使用方法:Python 爬虫用最普通的方法爬取ts文件并合成为mp4格式。 源码及运行过程示例:源码:import re import os import time import ffmpy3 import random import parsel import requests from bs4 import BeautifulSoup from urllib.parse import quote from multiprocessing.pool import ThreadPool referer = 'https://www.****.tv' header = { 'referer': referer, 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37' } proxies = ['HTTP://60.13.42.120:9999', 'HTTP://163.204.244.207:9999', 'HTTP://113.121.39.121:9999', 'HTTP://125.117.134.99:9000', 'HTTP://123.169.114.81:9999', 'HTTP://58.253.159.230:9999', 'HTTP://182.46.99.247:9999', 'HTTP://171.15.48.235:9999', 'HTTP://120.83.105.226:9999', 'HTTP://113.124.95.210:9999', 'HTTP://171.13.103.241:9999', 'HTTP://123.163.96.42:9999', 'HTTP://27.42.168.46:49345', 'HTTP://122.234.27.22:9000', 'HTTP://125.108.73.47:9000', 'HTTP://125.108.97.209:9000', 'HTTP://113.124.85.24:9999', 'HTTP://27.220.51.228:9000', 'HTTP://113.124.84.205:9999', 'HTTP://110.243.31.147:9999'] path = './Spider/' video = {} # 保存搜索记录 download = {} # 保存下载链接 def requests_url(url): # 请求网址 try: proxy = {'http': random.choice(proxies)} print(proxy) r = requests.get(url, proxies=proxy, headers=header) r.raise_for_status() r.encoding = 'UTF-8' return r except: print("*********请求失败!***********") def split_info(videos_info): num = 1 for video_info in videos_info: info = str(video_info).replace('<em>', '').replace('</em>', '') # 去掉源码中红色字体的标注 soup = BeautifulSoup(info, 'html.parser') info = soup.find_all('p') # 将通过关键字搜索到的视频名与对应链接保存下来 (对应链接为拼接后的完整链接,如:https://www.****.tv/tv/wNiNmMnRjZ.html) video[info[0].strong.a.string info[0].span.string] = 'https://www.' info[0].strong.a.attrs['href'] name2_info = info[1].string # 又名 area_info = info[2].string # 地区与类型 actor_info = info[3].string # 演员 introduction = info[4].string # 简介 print('***********************************************************') print('{:0>2d}: '.format(num), end='') num = 1 print('\t' info[0].strong.a.string info[0].span.string) # 名称 print('\t' name2_info) print('\t' area_info) print('\t' actor_info) print('\t' introduction) def get_video(url): global referer referer = url # 根据重定向拼接真实的链接 URL = 'https://www.****.tv/ajax/downurl/' url.split('/')[-1].split('.')[0] '_' url.split('/')[3] '/' # 请求视频链接 response = requests_url(URL) soup = BeautifulSoup(response.text, 'html.parser').find('ul', attrs={'class': "player ckp"}).find('li').a.attrs[ 'href'] # 找到每一集的链接 URL1 = 'https://www.****.tv' soup # 重新拼接每一集主页链接 response1 = requests_url(URL1).text soup1 = BeautifulSoup(response1, 'html.parser').find_all('script')[12] # 获取script标签 # 通过正则表达式找出所有下载链接 p = re.compile(r"https://.*?/index.m3u8") information = p.findall(str(soup1)) num = 1 for info in information: download['第{}集'.format(num)] = str(info).replace('index.m3u8', '1000k/hls/index.m3u8') num = 1 def video_download(name): # 通过ffmpy3下载 try: if os.path.exists(path NAME '/'): pass else: os.makedirs(path NAME '/') ffmpy3.FFmpeg(inputs={download[name]: None}, outputs={path NAME '/' name '.mp4': None}).run() print('************' name '下载成功!' '************') except: print('============' name '下载失败!' '============') def user_ui(): global NAME, page_num print('***********************************************************') keyword = '传闻中' # input('请输入搜索的视频关键字:') url = 'https://www.****.tv/s/go.php?q=' quote(keyword, 'utf-8') # 进行url加密 URL = (url.split('go.php?q=')[0] url.split('go.php?q=')[1]).replace('%', '_') '.html' # 根据网页,更改加密后的url html = requests_url(URL).text search_time = parsel.Selector(html).xpath('//div[@class="breadcrumbs"]/text()').extract()[0] # 搜索时间以及数量 videos_info = parsel.Selector(html).xpath('//dd').extract() # 视频信息标签 try: # 若搜索结果小于10项,则无页码信息 print(search_time, end='') page_num = parsel.Selector(html).xpath('//div[@class="pages"]/a/text()').extract()[-2] print(',共{}页。每页10项。'.format(page_num)) except: print(end='\n') split_info(videos_info) # 拆分显示视频信息 choice = input('\n请输入序号选择:') NAME = list(video.keys())[int(choice) - 1] # 通过下标确定进一步搜索的类容 URL = video[NAME] get_video(URL) # 获取下载链接 user_ui() time1 = time.time() pool = ThreadPool(10) # 开启线程池 results = pool.map(video_download, download.keys()) pool.close() pool.join() time2 = time.time() print('*********耗时:{}*********'.format(time2 - time1))
运行过程示例:最后的最后:来点小福利: 来源:https://www./content-1-745851.html |
|