1. add xhs

2. change douyin third-pard(checkbox)
3. add douyin demo
4. add group-qr
This commit is contained in:
pookz@stme
2023-12-08 13:28:33 +08:00
parent 3d9ace9fa8
commit eec0a68e3a
13 changed files with 216 additions and 7 deletions
+48 -3
View File
@@ -1,6 +1,7 @@
# social-auto-upload # social-auto-upload
social-auto-upload 该项目旨在自动化发布视频到各个社交媒体平台 social-auto-upload 该项目旨在自动化发布视频到各个社交媒体平台
## 💡Feature ## 💡Feature
- 中国主流社交媒体平台: - 中国主流社交媒体平台:
- 抖音 - 抖音
@@ -25,9 +26,15 @@ pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
playwright install chromium playwright install chromium
``` ```
# 🐇 About
该项目为我自用项目抽离出来,我的发布策略是定时发布(提前一天发布),故发布部分采用的事件均为第二天的时间
如果你有需求立即发布,可自行研究源码或者向我提问
# 核心模块解释 # 核心模块解释
## 视频文件准备 ## 1. 视频文件准备
filepath 本地视频目录,目录包含 filepath 本地视频目录,目录包含
- 视频文件 - 视频文件
- 视频meta信息txt文件 - 视频meta信息txt文件
@@ -43,7 +50,9 @@ meta_file 内容:
#坚持不懈 #爱情执着 #奋斗使者 #短视频 #坚持不懈 #爱情执着 #奋斗使者 #短视频
``` ```
### 抖音 ### 2. 抖音
<img src="media/show/pdf3.gif" alt="douyin show" width="500"/>
使用playwright模拟浏览器行为 使用playwright模拟浏览器行为
> 抖音前端实现,诸多css class id 均为随机数,故项目中locator多采用相对定位,而非固定定位 > 抖音前端实现,诸多css class id 均为随机数,故项目中locator多采用相对定位,而非固定定位
1. 准备视频目录结构 1. 准备视频目录结构
@@ -71,9 +80,45 @@ generate_schedule_time_next_day 默认从第二天开始(此举为避免选择
- https://github.com/lishang520/DouYin-Auto-Upload.git - https://github.com/lishang520/DouYin-Auto-Upload.git
### 3. 小红书
该实现,借助ReaJason的[xhs](https://github.com/ReaJason/xhs),再次感谢。
1. 目录结构同上
2. cookie获取,可使用chrome插件:EditThisCookie
- 设置导出格式
![Alt text](media/20231009111131.png)
- 导出
![Alt text](media/20231009111214.png)
3. 黏贴至 accounts.ini文件中
#### 解释与注意事项:
##### 上传方式
- 本地签名
- 自建签名服务
测试下来发现本地签名,在实际多账号情况下会存在问题
故如果你有多账号分发,建议采用自建签名服务(todo 上传docker配置)
##### 疑难杂症
遇到签名问题,可尝试更新cdn.jsdelivr.net_gh_requireCool_stealth.min.js_stealth.min.js文件
https://github.com/requireCool/stealth.min.js
参考: https://reajason.github.io/xhs/basic
##### todo
- 扫码登录方式(实验下来发现与浏览器获取的存在区别,会有问题,未来再研究)
参考项目:
- https://github.com/ReaJason/xhs
### 其余部分(todo) ### 其余部分(todo)
整理后上传 整理后上传
# 联系我 # 联系我
探讨自动化上传、自动制作视频 探讨自动化上传、自动制作视频
- 微信:rstar019
<img src="media/group-qr.png" alt="group-qr" width="300"/>
+1
View File
@@ -1,3 +1,4 @@
from pathlib import Path from pathlib import Path
BASE_DIR = Path(__file__).parent.resolve() BASE_DIR = Path(__file__).parent.resolve()
XHS_SERVER = "http://127.0.0.1:11901"
+7 -4
View File
@@ -162,10 +162,13 @@ class DouYinVideo(object):
await asyncio.sleep(1) await asyncio.sleep(1)
await page.locator('div[role="listbox"] [role="option"]').first.click() await page.locator('div[role="listbox"] [role="option"]').first.click()
# 頭條 # 頭條/西瓜
if await page.locator('text="今日头条" >> xpath=../following-sibling::div/div[contains(@class, "semi-switch")]').count(): third_part_element = '[class^="info"] > [class^="first-part"] div div.semi-switch'
if 'semi-switch-checked' not in await page.eval_on_selector('text="今日头条" >> xpath=../following-sibling::div/div[contains(@class, "semi-switch")]', 'div => div.className'): # 定位是否有第三方平台
await page.locator('text="今日头条" >> xpath=../following-sibling::div//input[contains(@class, "semi-switch-native-control")]').click() if await page.locator(third_part_element).count():
# 检测是否是已选中状态
if 'semi-switch-checked' not in await page.eval_on_selector(third_part_element, 'div => div.className'):
await page.locator(third_part_element).locator('input.semi-switch-native-control').click()
if self.publish_date != 0: if self.publish_date != 0:
await self.set_schedule_time_douyin(page, self.publish_date) await self.set_schedule_time_douyin(page, self.publish_date)
+57
View File
@@ -0,0 +1,57 @@
import configparser
from pathlib import Path
from time import sleep
from xhs import XhsClient
from conf import BASE_DIR
from utils.files_times import generate_schedule_time_next_day, get_title_and_hashtags
from xhs_uploader.main import sign_local, beauty_print
config = configparser.RawConfigParser()
config.read(Path(BASE_DIR / "xhs_uploader" / "accounts.ini"))
if __name__ == '__main__':
filepath = Path(BASE_DIR) / "videos"
# 获取视频目录
folder_path = Path(filepath)
# 获取文件夹中的所有文件
files = list(folder_path.glob("*.mp4"))
file_num = len(files)
cookies = config['account1']['cookies']
xhs_client = XhsClient(cookies, sign=sign_local, timeout=60)
# auth cookie
# 注意:该校验cookie方式可能并没那么准确
try:
xhs_client.get_video_first_frame_image_id("3214")
except:
print("cookie 失效")
exit()
publish_datetimes = generate_schedule_time_next_day(file_num, 1, daily_times=[16])
for index, file in enumerate(files):
title, tags = get_title_and_hashtags(str(file))
tags_str = ' '.join(['#' + tag for tag in tags])
# 打印视频文件名、标题和 hashtag
print(f"视频文件名:{file}")
print(f"标题:{title}")
print(f"Hashtag:{tags}")
topics = []
# 获取hashtag
for i in tags[:3]:
topic_official = xhs_client.get_suggest_topic(i)
if topic_official:
topics.append(topic_official[0])
note = xhs_client.create_video_note(title=title[:20], video_path=str(file), desc=title + tags_str,
topics=topics,
is_private=False,
post_time=publish_datetimes[index].strftime("%Y-%m-%d %H:%M:%S"))
beauty_print(note)
# 强制休眠30s,避免风控(必要)
sleep(30)
Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

View File
+2
View File
@@ -0,0 +1,2 @@
[account1]
cookies = changeme
File diff suppressed because one or more lines are too long
+59
View File
@@ -0,0 +1,59 @@
import configparser
import json
import pathlib
from time import sleep
import requests
from playwright.sync_api import sync_playwright
from conf import BASE_DIR, XHS_SERVER
config = configparser.RawConfigParser()
config.read('accounts.ini')
def sign_local(uri, data=None, a1="", web_session=""):
for _ in range(10):
try:
with sync_playwright() as playwright:
stealth_js_path = pathlib.Path(
BASE_DIR) / "xhs_uploader" / "cdn.jsdelivr.net_gh_requireCool_stealth.min.js_stealth.min.js"
chromium = playwright.chromium
# 如果一直失败可尝试设置成 False 让其打开浏览器,适当添加 sleep 可查看浏览器状态
browser = chromium.launch(headless=True)
browser_context = browser.new_context()
browser_context.add_init_script(path=stealth_js_path)
context_page = browser_context.new_page()
context_page.goto("https://www.xiaohongshu.com")
browser_context.add_cookies([
{'name': 'a1', 'value': a1, 'domain': ".xiaohongshu.com", 'path': "/"}]
)
context_page.reload()
# 这个地方设置完浏览器 cookie 之后,如果这儿不 sleep 一下签名获取就失败了,如果经常失败请设置长一点试试
sleep(2)
encrypt_params = context_page.evaluate("([url, data]) => window._webmsxyw(url, data)", [uri, data])
return {
"x-s": encrypt_params["X-s"],
"x-t": str(encrypt_params["X-t"])
}
except Exception:
# 这儿有时会出现 window._webmsxyw is not a function 或未知跳转错误,因此加一个失败重试趴
pass
raise Exception("重试了这么多次还是无法签名成功,寄寄寄")
def sign(uri, data=None, a1="", web_session=""):
# 填写自己的 flask 签名服务端口地址
res = requests.post(f"{XHS_SERVER}/sign",
json={"uri": uri, "data": data, "a1": a1, "web_session": web_session})
signs = res.json()
return {
"x-s": signs["x-s"],
"x-t": signs["x-t"]
}
def beauty_print(data: dict):
print(json.dumps(data, ensure_ascii=False, indent=2))
+35
View File
@@ -0,0 +1,35 @@
import datetime
import json
import qrcode
from time import sleep
from xhs import XhsClient
from xhs_uploader.main import sign
# pip install qrcode
if __name__ == '__main__':
xhs_client = XhsClient(sign=sign)
print(datetime.datetime.now())
qr_res = xhs_client.get_qrcode()
qr_id = qr_res["qr_id"]
qr_code = qr_res["code"]
qr = qrcode.QRCode(version=1, error_correction=qrcode.ERROR_CORRECT_L,
box_size=50,
border=1)
qr.add_data(qr_res["url"])
qr.make()
img = qr.make_image(fill_color="black", back_color="white")
img.save('qrcode.png')
while True:
check_qrcode = xhs_client.check_qrcode(qr_id, qr_code)
print(check_qrcode)
sleep(1)
if check_qrcode["code_status"] == 2:
print(json.dumps(check_qrcode["login_info"], indent=4))
print("当前 cookie:" + xhs_client.cookie)
break
print(json.dumps(xhs_client.get_self_info(), indent=4))