Mac 下 Python+Selenium 自动上传西瓜视频

news2024/12/30 1:50:54

背景

研究下 Python+Selenium 自动化测试框架,简单实现 Mac 下自动化批量上传视频西瓜视频并发布,分享给需要的同学(未做过多的异常处理)。

脚本实现

首先通过手工手机号登录,保存西瓜视频网站的 cookie 文件
之后加载 cookie 内容,使用脚本批量上传视频,保存到草稿(也可自动发布,为了二次编辑,如修改封面)
最后通过遍历视频草稿列表,来进行草稿视频发布,PS: 同一天上传或发布视频太多时,会被西瓜视频限流。

安装依赖

# 安装依赖
        保存网站 cookie

# 安装 chromedriver
$ brew install chromedriver

脚本内容

#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
import json
import os
import shutil
import sys

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver import ActionChains
from pykeyboard import PyKeyboard
from pymouse import PyMouse
import pyperclip

class XiGua:
    """
    Mac 西瓜视频自动上传视频及发布草稿
    """
    def __init__(self):
        """
        初始化,打开浏览器
        """
        self.driver = webdriver.Chrome()

    def save_cookies(self, cookies_file_name):
        """
        保存 cookies
        cookies_file_name: cookies 文件名称
        """
        # 预留 20 秒,来进行手工登录
        time.sleep(20)
        # 登录成功后,保存 cookies 文件
        with open(cookies_file_name, 'w') as cookies_file:
            cookies_file.write(json.dumps(self.driver.get_cookies()))

    def load_cookies(self, cookies_file_name):
        """
        加载 cookie
        cookies_file_name: cookies 文件名称
        """
        # 加载 cookies 文件
        with open(cookies_file_name, 'r') as cookies_file:
            cookies_list = json.load(cookies_file)
            for cookie in cookies_list:
                if 'expiry' in cookie:
                    del cookie['expiry']
                self.driver.add_cookie(cookie)
        # 加载 cookie 后,刷新页面生效
        self.driver.refresh()

    def is_exist_element_by_xpath(self, xpath):
        """
        判断元素是否存在
        """
        flag = True
        try:
            self.driver.find_element_by_xpath(xpath)
            return flag
        except Exception as e:
            flag = False
            print("xpath: [%s] 的元素不存在,错误:%s" % xpath, e)
            return flag

    def upload_video(self, video_file_path):
        """
        上传视频
        video_file_path: 上传视频路径
        """
        # 打开上传视频页面
        self.driver.get("https://studio.ixigua.com/upload?from=post_article")
        # 点击上传
        self.driver.find_element_by_class_name("byte-upload-trigger-drag").click()
        time.sleep(5)
        # 选择视频文件
        k = PyKeyboard()
        m = PyMouse()
        # 打开
        k.press_keys(['Command', 'Shift', 'G'])
        x_dim, y_dim = m.screen_size()
        k.press_keys(['Shift'])
        m.click(x_dim // 2, y_dim // 2, 1)
        # 复制视频文件路径
        pyperclip.copy(video_file_path)
        # 粘贴
        k.press_keys(['Command', 'V'])
        time.sleep(2)
        k.press_key('Return')
        time.sleep(2)
        k.press_key('Return')
        time.sleep(2)
        # 设置转载选项
        self.driver.find_element_by_xpath(
            '//*[@id="js-video-list-content"]/div/div[2]/div[4]/div[2]/div/div/label[2]/span/span').click()
        time.sleep(1)
        # 同步到抖音
        # self.driver.find_element_by_class_name("byte-checkbox-mask").click()
        # 循环判断视频上传成功,不成功等待10秒后,再次判断,直到成功
        while '上传成功' not in self.driver.find_element_by_xpath(
                '//*[@id="js-video-list-content"]/div/div[1]/div[1]/div[2]/div[2]').text:
            print("循环等待视频上传成功,等待10秒")
            time.sleep(10)
        # 设置视频封面
        self.driver.find_element_by_class_name("m-xigua-upload").click()
        print('点击-上传封面')
        time.sleep(5)
        try:
            reload = self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div[1]/div/div/div/div[2]')
            # 视频封面解析失败处理,循环刷新
            if reload != '':
                print('视频封面解析失败处理,开始循环刷新')
                while XiGua.is_exist_element_by_xpath(self,
                                                      '/html/body/div[3]/div/div[2]/div/div[1]/div/div/div/div[2]'):
                    # 点击循环
                    self.driver.find_element_by_xpath(
                        '/html/body/div[3]/div/div[2]/div/div[1]/div/div/div/div[2]').click()
                    print('刷新失败后,等待5秒,再次刷新')
                    time.sleep(5)
                # 选择第一个图片
                img = self.driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div/div[1]/div/div/div[1]/img')
                img.click()
        except Exception as e:
            print('封面解析正常,无需刷新')
            pass
        # 下一步
        cover_next_element = WebDriverWait(self.driver, 30).until(
            lambda x: x.find_element_by_xpath(
                '/html/body/div[3]/div/div[2]/div/div[2]/div')
        )
        cover_next_element.click()
        print('点击-封面下一步')
        try:
            # 完成裁剪
            cover_crop_element = WebDriverWait(self.driver, 30).until(
                lambda x: x.find_element_by_xpath(
                    '//*[@id="tc-ie-base-content"]/div[2]/div[2]/div[2]/div/div[2]/div/div/div[2]')
            )
            if cover_crop_element != '':
                cover_crop_element.click()
                print('点击-封面完成裁剪')
            else:
                print('封面无需裁剪')
        except Exception as e:
            print('裁剪封面出现异常:%s' % e)
            pass
        time.sleep(5)
        # 确定
        self.driver.find_element_by_xpath('//*[@id="tc-ie-base-content"]/div[2]/div[2]/div[3]/div[3]/button[2]').click()
        print('点击-封面确定')
        time.sleep(1)
        # 再次确定
        self.driver.find_element_by_xpath('/html/body/div[4]/div/div[2]/div/div[2]/button[2]').click()
        print('点击-封面再次确定')
        time.sleep(5)
        # 存草稿
        draft_element = WebDriverWait(self.driver, 30).until(
            lambda x: x.find_element_by_xpath('//*[@id="js-submit-draft-0"]/button')
        )
        action = ActionChains(self.driver)
        print('点击-保存草稿')
        # 移动滚动条到底部
        js = "window.scrollTo(0,document.body.scrollHeight)"
        self.driver.execute_script(js)
        # 移动到 存草稿 按钮点击
        action.move_to_element(draft_element).click().perform()

    def close(self):
        """
        关闭浏览器
        """
        self.driver.close()

    def batch_upload(self, videos_dir_path):
        """
        批量上传视频
        videos_dir_path: 上传视频存储路径
        """
        files = os.listdir(videos_dir_path)
        # 降序排序上传,草稿发布时,视频序号则为顺序
        files.sort(reverse=True)
        # 批量上传视频
        for file in files:
            if os.path.splitext(file)[1] == '.mp4':
                full_file_path = os.path.join(videos_dir_path, os.path.splitext(file)[0])
                print("==开始上传视频:%s" % full_file_path)
                self.upload_video(full_file_path)
                src = os.path.join(videos_dir_path, file)
                dst = os.path.join(videos_dir_path, 'bak', file)
                # 发布完成后,移到到备份目录
                shutil.move(src, dst)

    def videos_release(self):
        """
        草稿视频发布
        """
        self.driver.get("https://studio.ixigua.com/content")
        time.sleep(2)
        # 点击草稿导航
        draft_navigation_element = WebDriverWait(self.driver, 30).until(
            lambda x: x.find_element_by_xpath('//*[@id="app"]/div/section/div/div[1]/ul/li[3]')
        )
        draft_navigation_element.click()
        print('点击-草稿导航')
        time.sleep(2)
        # 草稿列表
        draft_elements = self.driver.find_elements_by_class_name('content-card__title ')
        # 草稿列表为空,则退出
        if len(draft_elements) == 0:
            print("草稿列表为空")
            XiGua.close(self)
            sys.exit()
        # 循环发布草稿,每次都发布第一个
        for i in range(1, 99999):
            # 草稿列表为空,退出
            if draft_elements == '':
                print('草稿发布完成,总共:%s' % str(i))
                XiGua.close(self)
                sys.exit()
            print('当前发布数量 %s, 发布视频: %s' % (str(i), draft_elements[0].text))
            # 发布草稿第一个视频
            draft_elements[0].click()
            time.sleep(3)
            # 立即发布
            element2 = WebDriverWait(self.driver, 30).until(
                lambda x: x.find_element_by_xpath('//button[contains(text(), "发布")]')
            )
            element2.click()
            print('点击-视频发布')
            # 判断是否发布失败,如标题超长
            try:
                # 错误处理
                if XiGua.is_exist_element_by_xpath(self, '/html/body/div[3]/div/div/div/span'):
                    print('发布出现错误,退出,请检查错误,如标题超长等')
                    sys.exit()
            except Exception as e:
                print('草稿发布异常:%s' % e)
                pass
            # 处理封面分辨率低提示
            try:
                # 封面分辨率低
                cover_cancel_element = self.driver.find_element_by_xpath('//div[contains(text(), "取消")]')
                print('封面分辨率低处理,直接取消')
                # 错误处理
                if cover_cancel_element != '':
                    print('取消封面分辨率低')
                    cover_cancel_element.click()
                    # 立即发布
                    cover_publish_element = WebDriverWait(self.driver, 30).until(
                        lambda x: x.find_element_by_xpath('//button[contains(text(), "发布")]')
                    )
                    cover_publish_element.click()
            except Exception as e:
                print('封面分辨率低出现异常:%s' % e)
                pass
            # 点击草稿
            draft_publish_element = WebDriverWait(self.driver, 30).until(
                lambda x: x.find_element_by_xpath('//*[@id="app"]/div/section/div/div[1]/ul/li[3]')
            )
            draft_publish_element.click()
            time.sleep(2)
            print('重新获取草稿列表')
            draft_elements = self.driver.find_elements_by_class_name('content-card__title ')
            print(draft_elements)

    def xigua_videos_release(self, base_url, cookies_file_path):
        """
        西瓜视频发布草稿
        base_url: 西瓜视频网站
        cookies_file_path: 西瓜视频 cookies 文件路径
        """
        self.driver.get(base_url)
        # 加载 cookies
        XiGua.load_cookies(self, cookies_file_path)
        # 草稿发布视频
        XiGua.videos_release(self)
        # 关闭浏览器
        XiGua.close(self)

    def xigua_batch_upload(self, base_url, cookies_file_path, videos_dir_path):
        """
        西瓜视频批量发布视频
        base_url: 西瓜视频网站
        cookies_file_path: 西瓜视频 cookies 文件路径
        videos_dir_path: 上传视频存储路径
        """
        self.driver.get(base_url)
        XiGua.load_cookies(self, cookies_file_path)
        XiGua.batch_upload(self, videos_dir_path)
        XiGua.close(self)

    def xigua_save_cookies(self, base_url, cookies_file_path):
        """
        保存网站 cookie
        base_url: 网站地址
        cookies_file_path: 网站 cookies 文件路径
        """
        self.driver.get(base_url)
        # 保存 cookies
        XiGua.save_cookies(self, cookies_file_path)
        XiGua.close(self)

if __name__ == '__main__':
    xi_gua = XiGua()
    # 西瓜视频
    base_url = 'https://www.ixigua.com/'
    xigua_cookies = '/tmp/xigua_update_video/xigua_cookies.txt'
    videos_dir_path = '/tmp/rm'
    ## 1. 保存 cookie
    # xi_gua.xigua_save_cookies(base_url, 'xigua_cookies.txt')
    ## 2. 批量上传
    xi_gua.xigua_batch_upload(base_url, xigua_cookies, videos_dir_path)
    ## 3. 批量发布草稿
    # xi_gua.xigua_videos_release(base_url, xigua_cookies)


              【下面是我整理的2023年最全的软件测试工程师学习知识架构体系图】


一、Python编程入门到精通

二、接口自动化项目实战 

三、Web自动化项目实战

四、App自动化项目实战 

五、一线大厂简历

六、测试开发DevOps体系 

七、常用自动化测试工具

八、JMeter性能测试 

九、总结(尾部小惊喜)

生命不息,奋斗不止。每一份努力都不会被辜负,只要坚持不懈,终究会有回报。珍惜时间,追求梦想。不忘初心,砥砺前行。你的未来,由你掌握!

生命短暂,时间宝贵,我们无法预知未来会发生什么,但我们可以掌握当下。珍惜每一天,努力奋斗,让自己变得更加强大和优秀。坚定信念,执着追求,成功终将属于你!

只有不断地挑战自己,才能不断地超越自己。坚持追求梦想,勇敢前行,你就会发现奋斗的过程是如此美好而值得。相信自己,你一定可以做到! 

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

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

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

相关文章

有什么项目可以大量使用c++实现?

有什么项目可以大量使用c实现? 在开始前我有一些资料,是我根据自己从业十年经验,熬夜搞了几个通宵,精心整理了一份「c的资料从专业入门到高级教程工具包」,点个关注,全部无偿共享给大家!&#x…

《opencv实用探索·十七》calcBackProject直方图反向投影

在了解反向投影前需要先了解下直方图的概念,可以看我上一章内容:opencv直方图计算calcHist函数解析 直方图反向投影是一种图像处理技术,通常用于目标检测和跟踪。通过计算反向投影,可以将图像中与给定模式(目标对象&a…

工作流程flowable(二)流程节点跟踪

flowable 节点跟踪 一、发起1-1 流程模型1-1-1 流程模型定义1-1-2 部署流程模型 {modelId} 1-2 部署管理1-2-1 流程定义列表查询 新的改变 二、我的申请新的改变 三、我的待办新的改变 三、我的已办新的改变 一、发起 1-1 流程模型 1-1-1 流程模型定义 1-1-2 部署流程模型 {…

我的隐私计算学习——隐私集合求交(2)

笔记内容来自多本书籍、学术资料、白皮书及ChatGPT等工具,经由自己阅读后整理而成。 前篇可见:我的隐私计算学习——隐私集合求交(1) (三)PSI应用场景问题 ​在目前的实际应用中,衍生出一些新…

美食大赛的题解

目录 原题描述: 题目描述: 输入格式: 输出格式: 样例输入: 样例输出: 数据规模: 题目大意: 主要思路: 注: 代码: 原题描述&#xff1a…

updateBatch批量更新

java代码 Dao层 int updateBatch(Param("userList") List<User> userList);sql配置文件 <update id"updateBatch" parameterType"java.util.List" ><foreach collection"userList" item"user" separator&quo…

Next.js中的App Router与Page Router,各自的作用和使用方式,如何理解和配置使用?

App Router介绍 Next.js中的App Router是全局的路由器&#xff0c;它用于在应用程序的所有页面之间进行导航。它可以用于在页面之间传递状态和数据&#xff0c;类似于React中的Context。 App Router是通过_app.js文件中的getInitialProps方法来配置的。 在 Next.js 中&#xf…

算法通关村第十三关—数学与数学基础问题(青铜)

数学与数学基础问题 一、统计专题 1.1 符号统计 LeetCode1822给定一个数组&#xff0c;求所有元素的乘积的符号&#xff0c;如果最终答案是负的返回-1&#xff0c;如果最终答案是正的返回1&#xff0c;如果答案是0返回0。  题目比较简单&#xff0c;正数对结果完全没影响&…

RK3568驱动指南|第八篇 设备树插件-第75章ConfigFS的核心数据结构

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

【Docker】进阶之路:(九)Docker网络

【Docker】进阶之路&#xff1a;&#xff08;九&#xff09;Docker网络 Docker网络模式简介bridge网络模式host网络模式none网络模式container网络模式user-defined网络模式1.创建自定义的bridge网络2.使用自定义网络 高级网络配置docker network命令 为什么要了解容器的网络模…

谈谈MYSQL主从复制原理

目录 概述 要点binlog日志 主从复制过程 总结 概述 MySQL 主从复制是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点。 MySQL 默认采用异步复制方式。从节点不用一直访问主服务器来更新自己的数据&#xff0c;数据的更新可以在远程连接上进行&#xff0…

Python应用利器:缓存妙用,让你的程序更出色更快速!

在 Python 应用程序中&#xff0c;使用缓存能够显著提高性能并降低资源消耗。本文将详细介绍如何在 Python 中实现缓存机制&#xff0c;包括内置 functools 模块提供的 lru_cache 装饰器以及自定义缓存机制。 使用 functools 模块的 lru_cache functools 模块提供了 lru_cach…

【CDP】CDP 集群通过Knox 访问Yarn Web UI,无法跳转到Flink Web UI 问题解决

一、前言 记录下在CDP 环境中&#xff0c;通过Knox 访问Yarn Web UI&#xff0c;无法跳转到Flink Web UI 的BUG 解决方法。 二、问题复现 登录 Knox Web UI 找到任一 Flink 任务 点击 ApplicationMaster 跳转 Flink WEB UI 出问题 内容空白&#xff0c;无法正常跳转到…

【MySQL】mysql | mysql5.7升级8.0注意事项

一、说明 1、应公司要求&#xff0c;mysql5.7安全漏洞较多&#xff0c;需要升级到8.0 2、记录注意事项备不时之需 二、注意事项 1、加密算法 1&#xff09;加密算法8.0改了&#xff0c;导致navicat无法连接 2&#xff09;解决&#xff1a; use mysql; ALTER USER root% IDENT…

利用冒泡排序了解如何将数组作为参数传递给函数

目录 前言:冒泡排序简介步骤动图演示 错误的冒泡排序函数数组名正确的冒泡排序函数 前言:冒泡排序 简介 冒泡排序是一种简单直观的排序算法。 它重复地访问要排序的数&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。访问数需要重复地进行直到…

DS冲刺整理做题定理(一)二叉树专题

&#xff08;只总结博主自己记得不熟的~&#xff09; 数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下&#xff0c;精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技…

微服务——分布式事务

事务理论基础 分布式锁保证多线程下数据库操作安全保障 分布式事务发生异常可以回滚. 使用postman发送请求插入一条新订单。 然后现在库存只剩下8个商品&#xff0c;如果买10个的话应该统一失败。 CAP定理 假如node03在独立时将所有请求都堵塞并等待恢复和其余节点的连接的话以…

【Spring Boot】快速入门

一、引言 1、什么是spring boot&#xff1f; Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。通过这种方式&#xff…

【c++】stl_priority_queue优先级队列

目录 一、priority_queue的介绍 二、 priority_queue的本质 三、priority_queue的使用 四、priority_queue的模拟实现 总结 一、priority_queue的介绍 首先让我们通过阅读优先级队列的官方文档 简单翻译一下 1. 优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准…