脚本四:视频自动发布到平台
好了,影片、封面、标题及实时频道监测,一切准备就绪,我们终于来到了自动化搬运脚本的最后一步:发布。由于可发布的自媒体平台众多,我们需要针对每个平台的Web结构编写独立的脚本,这部分有不少的工作量。但好在这些平台的HTML样式及交互流程基本类似,熟悉了自动发布脚本的编写方法后,扩展到任何一个平台都没有什么难度。
1. Selenium(浏览器自动化工具)
首先,我们先得了解一个大前提,所有平台的视频上传对于脚本来说都只能通过web端进行,并不是通过APP端,但好在他们都提供了web版的“创作中心”。自动发布脚本之所以能自动发布视频的核心原理是让浏览器可以模拟真人进行网页操作,从而完成整个视频上传发布流程。那么接下来就让我隆重推出这个用来模拟用户在浏览器上动作的第三方开源工具-Selenium。
Selenium 是一系列工具和库的综合项目,这些工具和库支持 web 浏览器的自动化。
常见的Selenium执行动作有:将文本输入到字段中,选择下拉值和复选框,并单击文档中的链接。 它还提供许多其他控件,比如鼠标移动、任意 JavaScript 执行等等。虽然Selenium主要用于网站的前端测试,但像我们这样用于网页自动化的项目,也是一个很好的选择。
让我们先用pip安装Selenium:
$ pip install selenium
WebDriver
使用Selenium的核心就是WebDriver,我们需要根据想要操作的浏览器类型,下载对应的WebDriver驱动文件,如Firefox的geckodriver:https://github.com/mozilla/geckodriver/releases,Chrome的chromedriver:https://chromedriver.chromium.org/downloads。对应找到符合你操作系统类型的驱动文件,进行下载。我这篇文章中的案例将采用Firefox浏览器进行。
导入WebDriver后,我们就可以尝试让Selenium快速打开一个网页了(就用快手平台的创作者中心吧):
from selenium import webdriver
driver = webdriver.Firefox(executable_path = './geckodriver')
driver.get("https://cp.kuaishou.com/article/publish/video")
地址栏背景变成红色且出现机器人图标,说明这个页面是由自动化脚本打开的:
你可能会注意到,每次由selenium访问网站时都需要重新登录,即使上一次已经登录成功。那是因为,每次调用webdriver生成一个新的访问session时,浏览器都会为这次session新建一个profile目录,用以存储网页缓存和登录状态,cookie等,因此之前访问过的登录信息默认不会被传递过来。由于我们的脚本是要求浏览器保留登录状态的,避免需要人工介入登录,因此我们要调用webdriver中的profile指定路径的功能,使每次新的访问session都能够获取到已经存在的登录状态,从而跳过登录页面。
首先,我们要找到Firefox默认的profile存放地址,方法是:
打开Firefox
地址栏输入about:support
-
找到Profile Folder行即是profile存放地址
有了这个目录地址,使用以下代码拷贝该地址到webdriver的profileDir里:
from selenium import webdriver
profileDir = r'/Users/yeyu/Library/Application Support/Firefox/Profiles/czstyl61.default-release/'
profile = webdriver.FirefoxProfile(profileDir)
driver = webdriver.Firefox(executable_path = './geckodriver', firefox_profile = profile)
driver.get("https://cp.kuaishou.com/article/publish/video")
最后,不要忘了手动打开一次 https://cp.kuaishou.com/article/publish/video,通过手机扫码或输入用户名密码,成功登录一次快手平台,生成profile,以后只要登录信息不过期,selenium每次新建网页session时,都能直接进入上传页面而不需要再进行登录了。
2. 自动脚本的基本编写过程
在编写代码前,让我们以最简单的方式了解怎么写一个自动脚本,总结下来就是以下两步:
- 读取需要进行交互的HTML组件标记
- 对该标记进行状态判断(clickable)、写入(Send keys)或点击(Click)
参考下图,以Firefox为例,读取HTML组件标记只需要 1)鼠标右击需要操控的HTML组件, 2)点击菜单里Inspect(检查)项,3)读取Inspector窗口中高亮的标记内容,如button类型,class名称等等。
虽然我们创建的driver对象有find_element()方法能够找到并生成组件对象,但是在一般的网络交互中,页面打开的时间受网速等很多因素影响,当一个页面切换的时候,无法保证下一个页面中我们感兴趣的元素能够多久刷新出来,因此直接使用find_element()配合固定延时,不是很高效。Selenium提供了一个很实用的WebDriverWait(driver, timeout).until()方法,可以在给定的时间里等待元素可用,一旦可用就立即返回元素对象。对于上面的“上传视频”按钮,直接看代码,我们用到了CSS_SELECTOR来定位button类型元素的class名称:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
button = WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"button[class='SOCr7n1uoqI-']")))
button.click()
不单是button类型,其他的类型如div等都是支持click()的。另外,input类型包括text/file,都可以send_keys()进行输入操作。这部分会在后面的实际平台操作中详细介绍。
3. 快手平台上传发布
打开快手上传页面:快手创作者服务平台
视频文件上传
根据上两个章节讲到的,免去登录流程后,首先映入眼帘的就是“上传视频”按钮,找到它的组件元素:button class=“SOCr7n1uoqI-”,我们用WebDriverWait等待这个页面的刷新,由于这个页面主要是视频文件输入框,所以我们的脚本并不需要去button.click(),而是找到type为file的input元素,向它输入我们需要上传的视频文件即可:
button = WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"button[class='SOCr7n1uoqI-']")))
search = driver.find_element(By.CSS_SELECTOR, "input[type='file']")
search.send_keys("<文件路径>/v1080p_audio_output.mp4")
运行后,你就能看到下一个页面,并且文件正在上传中,等待一会,就看到了文件上传成功:
那么如何用代码判断上传成功呢?同样的我们找到这个“上传成功”文字的HTML标记,得到它是span类型的类名为"DqNkLCyIyfQ-"。
所以我们就用一个while循环来等待这个span标记的出现,从而第一时刻获知视频文件的上传成功。
while "上传成功" != driver.find_element(By.CSS_SELECTOR, "span[class='DqNkLCyIyfQ-']").get_attribute("innerHTML"):
time.sleep(3)
封面上传
视频上传成功后,我们就可以开始上传在上一篇文章中制作好的封面图片了。首先找到“编辑封面”的HTML元素,<button class="ant-btn ant-btn-primary"...>:
点击操作,代码:
WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"button[class='ant-btn ant-btn-primary']"))).click()
在下个页面找到“上传封面”标签的HTML元素,<div id="rc-tabs-1-tab-2"...>:
点击操作,代码:
WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"div[id='rc-tabs-1-tab-2']"))).click()
再进入到下一个页面后,又来到了熟悉的文件输入页了,对于<button class="ant-btn ant-btn-primary xJ4CDDyNkAs-"...>我们就无需再点击了,只要等它的出现,然后直接对<input type="file"...>进行文件路径输入即可:
我们就使用上一篇制作的封面“v1080p_audio_output_with_title.jpeg”进行上传,代码部分:
WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[class='ant-btn ant-btn-primary xJ4CDDyNkAs-']")))
search_list = driver.find_elements(By.CSS_SELECTOR, "input[type='file']")
search_list[1].send_keys("<文件路径>/v1080p_audio_output_with_title.jpeg")
最后,在下一个页面中,点击“确定”按钮:
代码:
WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"button[class='ant-btn ant-btn-primary TyKzAKboOM0-']"))).click()
填写描述
上传完了视频和封面图片,最后一个需要输入的就是视频描述了,这个就很简单了。找到输入框的HTML元素标记 <div class="clGhv3UpdEo-"...>:
调用send_keys()输入text即可,这里我们就用封面使用的文字“一段不错的舞蹈!”。假设你搬运的都是同一个主题,另外还可以加入一些固定的标签,这里举例如“#小哥哥跳舞 #国外舞蹈达人”:
title_input = WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div[class='clGhv3UpdEo-']")))
title_input.send_keys("一段不错的舞蹈!")
title_input.send_keys("#小哥哥跳舞 #国外舞蹈达人")
此外,在实际代码开发中,还应该考虑到文本框的字数或视频大小限制等其他边界处理,这里就不展开了。
发布
所有必要的内容都输入完毕,页面上还有一些其他的选项,暂时不必去改动,让我们直接点击发布按钮,
最终发布前我们可以做一个一分钟的延时,避免由于网络问题导致封面没有来得及上传成功,上代码:
import time
time.sleep(60)
WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"button[class='ant-btn ant-btn-primary GncXo-rrppc-']"))).click()
如果看到跳转到以下的页面,说明脚本自动化发布视频到快手平台已经完成!
总结
至此,我们的整个自动化搬运流程走完了,通过这个系列的文章,整个过程包括频道监测->影片下载->影片处理->自媒体发布,都做了比较详细的介绍和源代码提供。后面还会为大家添加抖音等其他热门平台的自动化脚本,不过如果按照这篇文章走完了快手流程,相信你自己也可以很快复制出其他平台的上传脚本的。此外,后续还会提供从其他国外平台进行搬运的教程,敬请期待吧!
本篇用到的代码:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
import time
profileDir = '<修改为你的Firefox的profile目录>'
profile = webdriver.FirefoxProfile(profileDir)
driver = webdriver.Firefox(executable_path = '<修改为你的webdriver文件路径>/geckodriver', firefox_profile = profile)
driver.get("https://cp.kuaishou.com/article/publish/video")
#视频上传
button = WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"button[class='SOCr7n1uoqI-']")))
file_input = driver.find_element(By.CSS_SELECTOR, "input[type='file']")
file_input.send_keys("<修改为你的视频文件路径>/v1080p_audio_output.mp4")
while "上传成功" != driver.find_element(By.CSS_SELECTOR, "span[class='DqNkLCyIyfQ-']").get_attribute("innerHTML"):
time.sleep(3)
#封面上传
WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"button[class='ant-btn ant-btn-primary']"))).click()
WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"div[id='rc-tabs-1-tab-2']"))).click()
WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"button[class='ant-btn ant-btn-primary xJ4CDDyNkAs-']")))
search_list = driver.find_elements(By.CSS_SELECTOR, "input[type='file']")
search_list[1].send_keys("<修改为你的封面图片文件路径>/v1080p_audio_output_with_title.jpeg")
WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"button[class='ant-btn ant-btn-primary TyKzAKboOM0-']"))).click()
#填写描述
title_input = WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div[class='clGhv3UpdEo-']")))
title_input.send_keys("一段不错的舞蹈!")
title_input.send_keys("#小哥哥跳舞 #国外舞蹈达人")
#发布
time.sleep(60)
WebDriverWait(driver, 150).until(EC.element_to_be_clickable((By.CSS_SELECTOR, \
"button[class='ant-btn ant-btn-primary GncXo-rrppc-']"))).click()
publish.py