Python爬虫实战案例——第二例

news2024/12/23 14:23:42

某某美剧剧集下载(从搜索片名开始)
本篇文章主要是为大家提供某些电影网站的较常规的下载电影的分析思路与代码思路(通过爬虫下载电影),我们会从搜索某部影片的关键字开始直到成功下载某一部电影。

地址:aHR0cHM6Ly93d3cuOTltZWlqdXR0LmNvbS9pbmRleC5odG1s

先来分析页面

在这里插入图片描述

打开开发者工具,然后再搜索框输入任意内容开始搜索影片(如搜索战火)并抓包

在这里插入图片描述

从XHR来看的话返回的都是js文件,所以我们可以先考虑document中的html文档是否包含了我们需要的有效数据。

在这里插入图片描述

document中只返回了一个包,并且通过预览来看的话我们可以看到通过关键字搜索出来的电影是存在于这个html中的,所以我们就可以直接通过xpath解析将这些电影的片名解析出来,便于后面我们对影片进行选择。然后就可以进入到电影的详情页面(xpath解析出详情页的url)了。例如此处我们选择《兄弟连》这部电影。

在这里插入图片描述

进入到详情页之后,我们需要判断这部影片是否已经更新完成,因为下面我们需要选择播放线路,不同的播放线路已更新的剧集可能不同,但是经过对多部影片的详情页分析(此处不再贴图,大家自己去观察)发现,已完结的影片是不会存在上述问题的。但是正在连载中的影片可能就存在这样的问题,所以我们需要判断一下已经连载的剧集与这些播放线路中的剧集集数是否相等,如果相等的话才是可用的线路,否则是不可用的线路。当然也有可能存在一条线路都无法播放的情况,这个就是服务器的问题了,咱们客户端这边是没办法处理的。之后我们就要根据选择的线路去到播放页面就可以准备下载电视剧了。

此处我们选择的是九九云线路,来到播放页面之后通过抓包我们会发现并没有媒体文件,但是存在着m3u8与ts的包,因此我们能够判断出这个站点的视频是被分割成很多分的片段了。

在这里插入图片描述

接下来就是要想办法把这些ts视频下载下来了,通常情况下,这些文件的url会存在于一个m3u8的文件之中,所以我们需要先将m3u8下载下来。从播放页面的源码中我们可以解析出m3u8文件的下载地址(为了方便此处我就不再去请求源码了,直接从elements中看,大家平时的时候一定是养成习惯把源码下载到本地进行分析)

在这里插入图片描述

然后将next后面的url解析出来再进行请求,就会看到里面存在着一个新的m3u8文件的地址。

在这里插入图片描述

接下来就是通过正则将这个文件中存在的这个地址提取出来进行拼接再进行请求就能够获取到所有的ts文件所在的地址了。

在这里插入图片描述

下一步就是将这些ts文件的地址提取出来,同样我们选择正则进行提取(或者使用专门处理m3u8的第三方包进行提取),提取出来后拼接成正常的链接,存放到一个列表中,然后再遍历列表依次请求这些url并按照顺序将视频进行保存。

在这里插入图片描述

保存之后通过ffmpeg对视频进行合成,关于ffmpeg的配置请大家自行查阅一下相关资料。

在这里插入图片描述

合成后的视频

在这里插入图片描述

由于时间关系,只下载了200个片段进行合成,有兴趣的朋友可以改写成并发请求的方式下载所有的片段进行合成。完整代码如下:

import os.path
import re
import requests
import urllib3
from lxml import etree



class SendRequest:
    """基本请求模板,待完善"""
    urllib3.disable_warnings()
    def __init__(self):
        self.ABS_PATH = os.path.abspath(os.path.dirname(__file__))
        self.url = ''
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
        }
        self.cookies = {}  # cookie设置
        self.data = {}  # 表单数据
        self.page = 1  # 翻页控制参数
        self.session = requests.session()
        # self.movie = '测试'
        # print(f'{self.ABS_PATH}/{self.movie}(临时文件)/{self.movie}.m3u8')
        # print(f'{self.ABS_PATH}/{self.movie}/{self.movie}.mp4')

    @property
    def UGetRequest(self):
        response = self.session.get(url=self.url, headers=self.headers, cookies=self.cookies, verify=False)
        return response

    @UGetRequest.setter
    def UGetRequest(self, kwargs: dict):
        if kwargs.get('url'):
            self.url = kwargs.get('url')
        if kwargs.get('referer'):
            self.headers['referer'] = kwargs.get('referer')

    @property
    def UPostRequest(self):
        response = self.session.post(url=self.url, headers=self.headers, cookies=self.cookies, data=self.data,
                                     verify=False)
        return response

    @UPostRequest.setter
    def UPostRequest(self, kwargs: dict):
        if kwargs.get('url'):
            self.url = kwargs.get('url')
        if kwargs.get('referer'):
            self.headers['referer'] = kwargs.get('referer')


class MeiJu99(SendRequest):
    def __init__(self):
        super().__init__()

    def synthesis(self):
        """合成视频"""
        if not os.path.exists(self.movie):
            os.mkdir(self.movie)
        cmd = f'ffmpeg.exe -f concat -safe 0 -i {self.ABS_PATH}\\{self.movie}(临时文件)\\{self.movie}.m3u8 -c copy {self.ABS_PATH}\\{self.movie}\\{self.movie}.mp4'
        os.system(cmd)

    def download_mvs(self, total_mv_urls):
        """下载所有片段"""
        if not os.path.exists(self.movie+'(临时文件)'):
            os.mkdir(self.movie+'(临时文件)')
        num = 1
        # 按照ffmpeg的格式将ts文件的路径写入到一个m3u8文件之中用于合成视频
        new_m3u8_file = open(self.movie+'(临时文件)'+'/'+self.movie+'.m3u8', 'a', encoding='utf-8')
        for url in total_mv_urls:
            self.UGetRequest = {'url': url}
            res = self.UGetRequest
            with open(self.movie+'(临时文件)'+'/'+str(num)+'.ts', 'wb')as f:
                f.write(res.content)
                new_m3u8_file.write("file '%s\%s\%d.ts'" % (self.ABS_PATH, self.movie+'(临时文件)', num))
                new_m3u8_file.write('\n')
                print(str(num) + '下载成功')
                num+=1
            if num == 201:
                break
        new_m3u8_file.close()
        self.synthesis()

    def play_page(self, play_pages_url):
        """播放页面提取下载链接"""
        self.UGetRequest = {'url': play_pages_url}
        response = self.UGetRequest
        text_html = response.content.decode()
        with open('playpage.html', 'w', encoding='utf-8')as f:
            f.write(text_html)
        m3u8_url = re.findall('var next="(.*?)";var prePage=', text_html)[0]    # 提取播放页面中的m3u8文件的地址
        self.UGetRequest = {'url': m3u8_url}
        m3u8_file = self.UGetRequest.content
        with open('1.m3u8', 'wb')as f:
            f.write(m3u8_file)
        # 请求上方获取到的m3u8_url以获取存放了ts地址的m3u8
        last_m3u8_url = m3u8_url.split('/2')[0] + re.search('/\d+/\w+/[\d+kb/]*\w+/index\.m3u8', m3u8_file.decode()).group()
        self.UGetRequest = {'url': last_m3u8_url}
        response = self.UGetRequest.content.decode()
        # 解析并保存所有的ts地址
        total_mv_urls = [m3u8_url.split('/2')[0]+i for i in re.findall('/\d+/\w+/\d+\w+/hls/\w+\.ts', response)]
        self.download_mvs(total_mv_urls)

    def index(self, index_url):
        """电影详情页面"""
        self.UGetRequest = {'url': index_url}
        response = self.UGetRequest
        text_html = response.content.decode()
        with open('index.html', 'w', encoding='utf-8')as f:
            f.write(text_html)
        tree = etree.HTML(text_html)
        using_lines = tree.xpath('//*[@id="playTab"]/div[1]/ul//li//text()')    # 可使用线路(名称)
        play_tab = tree.xpath('//*[@id="playTab"]/div')     # 下载线路
        mv_information = ''.join(tree.xpath('//*[@id="zanpian-score"]/ul//text()'))     # 电影信息
        status = ''.join(tree.xpath('//*[@id="zanpian-score"]/ul/li[2]//text()'))
        if '完结' not in status:
            numbers_sets = ''.join(re.findall('集数:共(.*?)集 每集\d+分钟|状态:更新至(.*?)集', mv_information)[0])
            for i, tab in zip(range(len(using_lines)), play_tab[1:]):
                tab_num = len(tab.xpath('./ul/li'))
                if tab_num == int(numbers_sets):
                    print('%d.' % (i+1), using_lines[i]+'(可用)', end='\t')
                else:
                    print('%d.' % (i+1), using_lines[i]+'(不可用)', end='\t')
        else:
            for i, tab in zip(range(len(using_lines)), play_tab[1:]):
                print('%d.' % (i + 1), using_lines[i], end='\t')
        print()
        download_num = int(input('请选择下载线路(输入编号):'))
        play_pages_urls = ['https://www.99meijutt.com'+i for i in play_tab[download_num].xpath('./ul//li/a/@href')]
        for play_pages_url in play_pages_urls:
            self.play_page(play_pages_url)
            break

    def search(self):
        """搜索页面采集"""
        titles = []
        self.UPostRequest = {'url': 'https://www.99meijutt.com/search.php'}
        self.data['searchword'] = input('请输入影片关键字或主演名:')
        response = self.UPostRequest
        text_html = response.content.decode()
        with open('search.html', 'w', encoding='utf-8') as f:
            f.write(text_html)
        tree = etree.HTML(text_html)
        div_lst = tree.xpath('//*[@id="content"]/div')
        print('搜索到的电影如下:')
        for i, div in zip(range(1, len(div_lst)), div_lst):  # 遍历数组与div列表为标题设置编号
            title = div.xpath('./div[1]/a/@title')[0]
            if i % 2 != 0 and i != len(div_lst)-1:
                print(str(i) + '.' + title, end='\t\t')
            else:
                print(str(i) + '.' + title)
            titles.append(title)
        num = int(input('请输入您要下载的电影序号:'))
        self.movie = titles[num-1]
        index_url = 'https://www.99meijutt.com' + div_lst[num-1].xpath('./div[1]/a/@href')[0]
        self.index(index_url)


if __name__ == '__main__':
    mj = MeiJu99()
    mj.search()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/919020.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么选择网络安全?为什么说网络安全是IT行业最后的红利?

一、为什么选择网络安全? 这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护2.0》等一系列政策/法规/标准的持续落地,网络安全行业地位、薪资随之水涨船高。 未来3-5年,是安全行业的黄金发展期,提前踏入…

60.每日一练:回文数(力扣)

目录 问题描述 代码解决以及思想 解法(一) 知识点 解法(二) 问题描述 代码解决以及思想 解法(一) class Solution { public:bool isPalindrome(int x) {string arr to_string(x); // 将整数转换为…

IDEA常用插件之代码扫描SonarLint

文章目录 SonarLint 查找隐藏的bug下载安装插件扫描代码查看结果 SonarLint 查找隐藏的bug 下载安装插件 扫描代码 项目右键 -> Analyze -> Analyze with SonarLint 查看结果 扫描完成结果在下面可以直接查看到报告

第2篇:ESP32 helloword第一个程序示范点亮板载LED

1.选择ESP32开发板 2.寻找串口号,win10自动安装驱动 手动安装驱动参考: 百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可…

typora

图像 偏好设置 图床 picgosmms图床 YAML typora-copy-images-to: upload 在将文件复制到本地文件夹后,再上传 Typora_PicGo_SMMS图床配置 第一步 在偏好设置-图像中 1.设置为上传图片 2.下载picogo并设置路径 第二步 获取https://sm.ms/home/apitoken的a…

Django会话技术

文章目录 Cookie实践运行结果 CSRF防止CSRF Session实践 Cookie 理论上,一个用户的所有请求燥作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆,而web应用程序是使用HTTP协议传输数据的。HTT…

Navicat for Mysql 显示 emoji 表情符号乱码问题

系统环境: 操作系统:MAC OS 10.11.6 MySQL:Server version: 5.6.21 MySQL Community Server (GPL) Navicat for MySQL: version 9.3.1 - standard 1、问题发现 在客户端执行用户注册,用户名支持 emoji 表情,注册完成后…

【⑬MySQL | 数据类型(一)】简介 | 整数 | 浮点 | 定点类型

前言 ✨欢迎来到小K的MySQL专栏,本节将为大家带来MySQL数据类型简介 | 整数 | 浮点 | 定点类型的分享✨ 目录 前言0.数据类型简介1 整数类型2 浮点类型3 定点类型4 日期/时间类型总结 0.数据类型简介 数据类型(data_type)是指系统中所允许的…

musl libc ldso 动态加载研究笔记:动态库的搜索路径

前言 在手动设置动态库的存放路径的情况下,发现 musl ldso 依旧可以加载位于 /lib 目录下的 动态共享库 动态库的存放路径或者说搜索路径,是否可以手动配置?比如 Linux 上 有个配置文件可以配置,可以改变 动态库的搜索次序&#…

花了4天,做了一个公司的管理系统,只需几个步骤

一、背景 我是企业的管理人员,公司发展到现阶段,感觉进入到了瓶颈期,每个员工的工作都已经饱和,很难再挤出时间做其它的事情,需要一款合适的管理软件来协作我们的工作。 本来打算买一套管理软件就行了,现实…

jupyter notebook 插件nbextensions的安装

安装步骤: 1、打开 jupyter notebook,新建一个 python 文件; 2、 分别输入以下代码,然后运行,出现 warning 不影响使用,如果出现 errors,则说明下载有问题: !python -m pip install…

Elasticsearch(十四)搜索---搜索匹配功能⑤--全文搜索

一、前言 不同于之前的term。terms等结构化查询,全文搜索首先对查询词进行分析,然后根据查询词的分词结果构建查询。这里所说的全文指的是文本类型数据(text类型),默认的数据形式是人类的自然语言,如对话内容、图书名…

本地生活服务平台加盟哪家公司好?

本地生活的竞争从年初的火热到现在,已经进入了下半场,随着优胜劣汰的筛选,那么直到现在,想做本地生活服务平台加盟,哪家公司比较好呢,应该如何选择呢? 首先我们得弄懂,我们加盟本地…

c#设计模式-结构型模式 之 外观模式

概述 外观模式(Facade Pattern)又名门面模式,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。该模式…

命令行一键启动和关闭java应用

背景 近来在搞springcloud微服务,有服务的注册和发现,即nacos。有网关服务,即spring-gateway。有认证中心服务。还有用户中心服务,文件中心服务,springboot admin等等。部署的时候,这么多服务,…

2023-8-23 KMP字符串

题目链接&#xff1a;KMP字符串 #include <iostream>using namespace std;const int N 100010, M 1000010;int n, m; char p[N], s[M]; int ne[N];int main() {cin >> n >> p 1 >> m >> s 1;// 求ne&#xff0c;也就是求next数组的过程for…

司徒理财:8.23晚间黄金多空走势分析及操作策略

黄金走势分析&#xff1a;      黄金下跌遇阻&#xff0c;短线开启震荡调整走势&#xff0c;但跌势依旧没有改变&#xff0c;没有突破1906压力前&#xff0c;还是偏空走势&#xff0c;反弹继续干空。趋势行情&#xff0c;不要轻言翻转&#xff01;即便下跌结束&#xff0c;…

从零开始搭建公司后台技术栈

整个后台技术栈我的理解包括 4 个层面的内容&#xff1a; 语言&#xff1a;用了哪些开发语言&#xff0c;如&#xff1a;C/Java/Go/PHP/Python/Ruby 等等&#xff1b;组件&#xff1a;用了哪些组件&#xff0c;如&#xff1a;MQ 组件&#xff0c;数据库组件等等&#xff1b;流…

网络安全(黑客)自学,看这篇就够了!

前言 想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01; 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、…

Spring缓存深入解析:@Cacheable的使用详解

摘要&#xff1a;在本文中&#xff0c;我们将深入研究Spring框架中的Cacheable注解。我们会通过详细的Java示例&#xff0c;探讨如何使用这个功能强大的注解来提升应用程序性能。 一、什么是缓存&#xff1f; 在计算机科学中&#xff0c;缓存是一种存储技术&#xff0c;用于保…