Toggle navigation
Toggle navigation
This project
Loading...
Sign in
LiuFuhua
/
xj-marketing
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
zoujie
2024-12-24 10:58:04 +0800
Browse Files
Options
Browse Files
Download
Plain Diff
Commit
4f612f74e00beeee537b3da17c26755b2a15811f
4f612f74
2 parents
3983863b
885894d5
Merge branch 'master' of
ssh://gitlab.rinal-li.cn:10022/liufuhua/xj-marketing
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
232 additions
and
55 deletions
cli_main.py
uploader/xhs_uploader/main.py
uploader/xhs_uploader/main_old.py
uploader/xhs_uploader/xhs_login_qrcode.py
utils/base_social_media.py
cli_main.py
View file @
4f612f7
...
...
@@ -9,6 +9,7 @@ from uploader.douyin_uploader.main import douyin_setup, DouYinVideo
from
uploader.ks_uploader.main
import
ks_setup
,
KSVideo
from
uploader.tencent_uploader.main
import
weixin_setup
,
TencentVideo
from
uploader.tk_uploader.main_chrome
import
tiktok_setup
,
TiktokVideo
from
uploader.xhs_uploader.main
import
xhs_setup
,
XHSVideo
from
uploader.wyh_uploader.main
import
wyh_setup
from
uploader.toutiao_uploader.main
import
toutiao_setup
from
utils.base_social_media
import
get_supported_social_media
,
get_cli_action
,
SOCIAL_MEDIA_DOUYIN
,
\
...
...
@@ -73,8 +74,8 @@ async def main():
await
ks_setup
(
str
(
account_file
),
handle
=
True
)
elif
args
.
platform
==
SOCIAL_MEDIA_WANGYIHAO
:
await
wyh_setup
(
str
(
account_file
),
handle
=
True
)
#
elif args.platform == SOCIAL_MEDIA_XHS:
#
await xhs_setup(str(account_file), handle=True)
elif
args
.
platform
==
SOCIAL_MEDIA_XHS
:
await
xhs_setup
(
str
(
account_file
),
handle
=
True
)
elif
args
.
platform
==
SOCIAL_MEDIA_TOUTIAO
:
await
toutiao_setup
(
str
(
account_file
),
handle
=
True
)
elif
args
.
platform
==
SOCIAL_MEDIA_BAIJIAHAO
:
...
...
@@ -104,6 +105,9 @@ async def main():
elif
args
.
platform
==
SOCIAL_MEDIA_KUAISHOU
:
await
ks_setup
(
account_file
,
handle
=
True
)
app
=
KSVideo
(
title
,
video_file
,
tags
,
publish_date
,
account_file
)
elif
args
.
platform
==
SOCIAL_MEDIA_XHS
:
await
xhs_setup
(
account_file
,
handle
=
True
)
app
=
XHSVideo
(
title
,
video_file
,
tags
,
publish_date
,
account_file
)
else
:
print
(
"Wrong platform, please check your input"
)
exit
()
...
...
uploader/xhs_uploader/main.py
View file @
4f612f7
import
configparser
# -*- coding: utf-8 -*-
from
datetime
import
datetime
from
playwright.async_api
import
Playwright
,
async_playwright
import
os
import
asyncio
import
json
import
pathlib
from
time
import
sleep
from
conf
import
LOCAL_CHROME_PATH
from
utils.base_social_media
import
set_init_script
from
utils.files_times
import
get_absolute_path
from
utils.log
import
xhs_logger
import
requests
from
playwright.sync_api
import
sync_playwright
async
def
write_cookies_to_file
(
cookies
,
file_path
):
from
conf
import
BASE_DIR
,
XHS_SERVER
with
open
(
file_path
,
'w'
)
as
f
:
json
.
dump
(
cookies
,
f
)
config
=
configparser
.
RawConfigParser
()
config
.
read
(
'accounts.ini'
)
async
def
read_cookies_from_file
(
file_path
):
try
:
with
open
(
file_path
,
'r'
)
as
f
:
return
json
.
load
(
f
)
except
FileNotFoundError
:
return
[]
def
sign_local
(
uri
,
data
=
None
,
a1
=
""
,
web_session
=
""
):
for
_
in
range
(
10
):
async
def
cookie_auth
(
account_file
):
print
(
account_file
)
async
with
async_playwright
()
as
playwright
:
browser
=
await
playwright
.
chromium
.
launch
(
headless
=
True
)
# context = await browser.new_context(storage_state=account_file)
context
=
await
browser
.
new_context
()
page
=
await
context
.
new_page
()
saved_cookies
=
await
read_cookies_from_file
(
account_file
)
await
context
.
add_cookies
(
saved_cookies
)
await
page
.
goto
(
"https://www.xiaohongshu.com"
)
try
:
with
sync_playwright
()
as
playwright
:
stealth_js_path
=
pathlib
.
Path
(
BASE_DIR
/
"utils/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
))
print
(
await
page
.
title
())
await
page
.
wait_for_selector
(
"div.link-wrapper a.link-wrapper span.channel"
,
timeout
=
5000
)
# 等待5秒
xhs_logger
.
success
(
"[+] cookie 有效"
)
return
True
except
:
xhs_logger
.
info
(
"[+] 等待5秒 cookie 失效"
)
return
False
async
def
xhs_setup
(
account_file
,
handle
=
False
):
account_file
=
get_absolute_path
(
account_file
,
"xhs_uploader"
)
if
not
os
.
path
.
exists
(
account_file
)
or
not
await
cookie_auth
(
account_file
):
if
not
handle
:
return
False
xhs_logger
.
info
(
'[+] cookie文件不存在或已失效,即将自动打开浏览器,请扫码登录,登陆后会自动生成cookie文件'
)
await
get_xhs_cookie
(
account_file
)
return
True
async
def
get_xhs_cookie
(
account_file
):
async
with
async_playwright
()
as
playwright
:
options
=
{
'args'
:
[
'--lang en-GB'
],
'headless'
:
False
,
# Set headless option here
}
# Make sure to run headed.
browser
=
await
playwright
.
chromium
.
launch
(
**
options
)
# Setup context however you like.
context
=
await
browser
.
new_context
()
# Pass any options
context
=
await
set_init_script
(
context
)
# Pause the page, and start recording manually.
page
=
await
context
.
new_page
()
await
page
.
goto
(
"https://www.xiaohongshu.com"
)
await
asyncio
.
sleep
(
1
)
await
page
.
wait_for_selector
(
"div.link-wrapper a.link-wrapper span.channel"
,
timeout
=
50000
)
print
(
"小红书登录成功"
)
await
asyncio
.
sleep
(
3
)
# await page.pause()
# 点击调试器的继续,保存cookie
await
asyncio
.
sleep
(
3
)
cookies
=
await
context
.
cookies
()
await
write_cookies_to_file
(
cookies
,
account_file
)
class
XHSVideo
(
object
):
def
__init__
(
self
,
title
,
file_path
,
tags
,
publish_date
:
datetime
,
account_file
):
self
.
title
=
title
# 视频标题
self
.
file_path
=
file_path
self
.
tags
=
tags
self
.
publish_date
=
publish_date
self
.
account_file
=
account_file
self
.
date_format
=
'
%
Y-
%
m-
%
d
%
H:
%
M'
self
.
local_executable_path
=
LOCAL_CHROME_PATH
async
def
handle_upload_error
(
self
,
page
):
xhs_logger
.
error
(
"视频出错了,重新上传中"
)
await
page
.
locator
(
'div.progress-div [class^="upload-btn-input"]'
)
.
set_input_files
(
self
.
file_path
)
async
def
upload
(
self
,
playwright
:
Playwright
)
->
None
:
# 使用 Chromium 浏览器启动一个浏览器实例
if
self
.
local_executable_path
:
browser
=
await
playwright
.
chromium
.
launch
(
headless
=
False
,
executable_path
=
self
.
local_executable_path
,
)
else
:
browser
=
await
playwright
.
chromium
.
launch
(
headless
=
True
)
# 创建一个浏览器上下文,使用指定的 cookie 文件
context
=
await
browser
.
new_context
()
page
=
await
context
.
new_page
()
saved_cookies
=
await
read_cookies_from_file
(
self
.
account_file
)
await
context
.
add_cookies
(
saved_cookies
)
# context = await set_init_script(context)
# context.on("close", lambda: )
# 创建一个新的页面
# 访问指定的 URL
await
page
.
goto
(
"https://creator.xiaohongshu.com/publish/publish?source=official"
)
xhs_logger
.
info
(
'正在上传-------{}.mp4'
.
format
(
self
.
title
))
# 等待页面跳转到指定的 URL,没进入,则自动等待到超时
# 点击 "上传视频" 按钮
await
page
.
locator
(
"input.upload-input"
)
.
set_input_files
(
self
.
file_path
)
upload_button
=
page
.
locator
(
'button div span:has-text("发布")'
)
await
upload_button
.
wait_for
(
state
=
'visible'
)
# 确保按钮可见
xhs_logger
.
info
(
"正在填充标题和话题..."
)
await
page
.
locator
(
"div.titleInput div div input"
)
.
fill
(
"花花"
)
inputTag
=
page
.
locator
(
'id=quillEditor'
)
.
locator
(
'div p'
)
await
inputTag
.
click
()
for
index
,
tag
in
enumerate
(
self
.
tags
,
start
=
1
):
await
page
.
keyboard
.
type
(
f
"#{tag} "
)
await
asyncio
.
sleep
(
2
)
xhs_logger
.
info
(
"已写完tag"
)
await
asyncio
.
sleep
(
1
)
# 定时任务
if
self
.
publish_date
!=
0
:
await
self
.
set_schedule_time
(
page
,
self
.
publish_date
)
upload_button
=
page
.
locator
(
'button div span:has-text("定时发布")'
)
await
upload_button
.
click
()
await
page
.
wait_for_selector
(
'p:has-text("发布成功")'
)
await
write_cookies_to_file
(
await
context
.
cookies
(),
self
.
account_file
)
xhs_logger
.
info
(
'cookie更新完毕!'
)
await
asyncio
.
sleep
(
2
)
# 这里延迟是为了方便眼睛直观的观看
# 关闭浏览器上下文和浏览器实例
await
context
.
close
()
await
browser
.
close
()
async
def
main
(
self
):
async
with
async_playwright
()
as
playwright
:
await
self
.
upload
(
playwright
)
async
def
set_schedule_time
(
self
,
page
,
publish_date
):
xhs_logger
.
info
(
"click schedule"
)
publish_date_hour
=
publish_date
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
)
await
page
.
locator
(
"span.el-radio__input"
)
.
nth
(
1
)
.
click
()
await
asyncio
.
sleep
(
1
)
await
page
.
locator
(
'div.date-picker div div input'
)
.
click
()
await
asyncio
.
sleep
(
1
)
await
page
.
keyboard
.
press
(
"Control+KeyA"
)
await
page
.
keyboard
.
type
(
str
(
publish_date_hour
))
await
page
.
keyboard
.
press
(
"Enter"
)
await
asyncio
.
sleep
(
1
)
...
...
uploader/xhs_uploader/main_old.py
0 → 100644
View file @
4f612f7
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
/
"utils/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
))
uploader/xhs_uploader/xhs_login_qrcode.py
View file @
4f612f7
...
...
@@ -5,7 +5,7 @@ from time import sleep
from
xhs
import
XhsClient
from
uploader.xhs_uploader.main
import
sign
from
uploader.xhs_uploader.main
_old
import
sign
# pip install qrcode
if
__name__
==
'__main__'
:
...
...
utils/base_social_media.py
View file @
4f612f7
...
...
@@ -16,7 +16,7 @@ SOCIAL_MEDIA_BAIJIAHAO = 'baijiahao'
def
get_supported_social_media
()
->
List
[
str
]:
return
[
SOCIAL_MEDIA_DOUYIN
,
SOCIAL_MEDIA_TENCENT
,
SOCIAL_MEDIA_TIKTOK
,
SOCIAL_MEDIA_KUAISHOU
,
SOCIAL_MEDIA_WANGYIHAO
,
SOCIAL_MEDIA_TOUTIAO
,
SOCIAL_MEDIA_BAIJIAHAO
]
SOCIAL_MEDIA_WANGYIHAO
,
SOCIAL_MEDIA_TOUTIAO
,
SOCIAL_MEDIA_BAIJIAHAO
,
SOCIAL_MEDIA_XHS
]
def
get_cli_action
()
->
List
[
str
]:
...
...
Please
register
or
login
to post a comment