init commit
This commit is contained in:
BIN
__pycache__/scan_qrcode.cpython-310.pyc
Normal file
BIN
__pycache__/scan_qrcode.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/scan_qrcode.cpython-312.pyc
Normal file
BIN
__pycache__/scan_qrcode.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/use_cookie.cpython-310.pyc
Normal file
BIN
__pycache__/use_cookie.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/use_cookie.cpython-312.pyc
Normal file
BIN
__pycache__/use_cookie.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/util.cpython-310.pyc
Normal file
BIN
__pycache__/util.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/util.cpython-312.pyc
Normal file
BIN
__pycache__/util.cpython-312.pyc
Normal file
Binary file not shown.
55
app.py
Normal file
55
app.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from flask import Flask, render_template, request, session
|
||||
from playwright.sync_api import sync_playwright
|
||||
import os
|
||||
import time
|
||||
app = Flask(__name__)
|
||||
app.secret_key = 'your_secret_key'
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/send_code', methods=['POST'])
|
||||
def send_code():
|
||||
phone = request.form['phone']
|
||||
session['phone'] = phone
|
||||
# 用 Playwright 打开页面并点击发送验证码
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=False)
|
||||
context = browser.new_context()
|
||||
page = context.new_page()
|
||||
page.goto("https://creator.xiaohongshu.com/login?source=&redirectReason=401&lastUrl=%252Fnew%252Fhome")
|
||||
page.get_by_role("textbox", name="手机号").click()
|
||||
page.get_by_role("textbox", name="手机号").fill(phone)
|
||||
page.wait_for_timeout(15000) # 等待用户手动输入验证码
|
||||
page.get_by_text("发送验证码").click()
|
||||
# 保存 context 到磁盘,后续用
|
||||
time.sleep(20) # 确保操作完成
|
||||
context.storage_state(path="state.json")
|
||||
# browser.close()
|
||||
return "验证码已发送,请查收手机短信!<a href='/'>返回</a>"
|
||||
|
||||
@app.route('/login', methods=['POST'])
|
||||
def login():
|
||||
code = request.form['code']
|
||||
phone = session.get('phone')
|
||||
# 用 Playwright继续登录
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True)
|
||||
context = p.chromium.launch_persistent_context(user_data_dir=".", storage_state="state.json")
|
||||
page = context.pages[0] if context.pages else context.new_page()
|
||||
page.goto("https://creator.xiaohongshu.com/login?source=&redirectReason=401&lastUrl=%252Fnew%252Fhome")
|
||||
# 填写验证码并登录
|
||||
page.wait_for_selector('input[placeholder*="验证码"]', timeout=15000)
|
||||
vcode_box = page.locator('input[placeholder*="验证码"]')
|
||||
vcode_box.fill(code)
|
||||
page.get_by_role("button", name="登 录").click()
|
||||
# 等待跳转
|
||||
page.wait_for_url("**/new/home", timeout=20000)
|
||||
# 保存 cookies
|
||||
context.storage_state(path="cookies.json")
|
||||
browser.close()
|
||||
return "登录成功,cookie已保存!<a href='/'>返回</a>"
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000)
|
||||
162
cookies.json
Normal file
162
cookies.json
Normal file
@@ -0,0 +1,162 @@
|
||||
[
|
||||
{
|
||||
"name": "acw_tc",
|
||||
"value": "0a0d0ad817647684166947926e745d544464c3eaa8c03b5c1daaf7727ce7ff",
|
||||
"domain": "creator.xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1764770219.611397,
|
||||
"httpOnly": true,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "xsecappid",
|
||||
"value": "ugc",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1796304431,
|
||||
"httpOnly": false,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "a1",
|
||||
"value": "19ae46510a243heinye4i1n8fbktbb3sjw9vp3uz950000223112",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1796304420,
|
||||
"httpOnly": false,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "webId",
|
||||
"value": "374bfec4808d4b49918e4f704bf9fde6",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1796304420,
|
||||
"httpOnly": false,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "acw_tc",
|
||||
"value": "0a4a65a917647684172526611e22e34304a2cc5b000e71927ba9fa334c4ca7",
|
||||
"domain": "edith.xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1764770220.163317,
|
||||
"httpOnly": true,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "acw_tc",
|
||||
"value": "0a00074117647684176224664e82e886141fe61870fff4ae1ea35c0940faa6",
|
||||
"domain": "customer.xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1764770220.557782,
|
||||
"httpOnly": true,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "websectiga",
|
||||
"value": "59d3ef1e60c4aa37a7df3c23467bd46d7f1da0c1918cf335ee7f2e9e52ac04cf",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1765027620,
|
||||
"httpOnly": false,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "sec_poison_id",
|
||||
"value": "b47ab11d-6876-4ed6-a53d-2192a935782b",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1764769025,
|
||||
"httpOnly": false,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "gid",
|
||||
"value": "yj0d4K2yJSVSyj0d4K2y8Eq70J4qxd3Ivd43jujyIYiDTh289DDqMU888JJqyyJ880ySdS84",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1799328434.79468,
|
||||
"httpOnly": false,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "customer-sso-sid",
|
||||
"value": "68c517579622683438383105kvqpbrqnjhxcuozw",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1765373230.597087,
|
||||
"httpOnly": true,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "x-user-id-creator.xiaohongshu.com",
|
||||
"value": "68763bd9000000001b019a5c",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1799328431.597156,
|
||||
"httpOnly": true,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "customerClientId",
|
||||
"value": "091245717510800",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1799328431.597244,
|
||||
"httpOnly": true,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "access-token-creator.xiaohongshu.com",
|
||||
"value": "customer.creator.AT-68c517579622683438383106wtbhklklncen9nms",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1767360430.59727,
|
||||
"httpOnly": true,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "galaxy_creator_session_id",
|
||||
"value": "jxq51QjFn05XJyW41Uz1ASvOSreHs5r2ImYX",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1767360431.597285,
|
||||
"httpOnly": true,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "galaxy.creator.beaker.session.id",
|
||||
"value": "1764768428620029392110",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1767360431.597294,
|
||||
"httpOnly": true,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
},
|
||||
{
|
||||
"name": "loadts",
|
||||
"value": "1764768431906",
|
||||
"domain": ".xiaohongshu.com",
|
||||
"path": "/",
|
||||
"expires": 1796304431,
|
||||
"httpOnly": false,
|
||||
"secure": false,
|
||||
"sameSite": "Lax"
|
||||
}
|
||||
]
|
||||
45
demo_ip.py
Normal file
45
demo_ip.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import requests
|
||||
import time
|
||||
if __name__ == '__main__':
|
||||
# 客户ip提取链接,每次提取1个,提取链接可以换成自己购买的
|
||||
url = 'http://api.tianqiip.com/getip?secret=6o09i8io&num=2&type=txt&port=1&mr=1&sign=5451e454a54b9f1f06222606c418e12f'
|
||||
# 访问的目标地址
|
||||
targeturl = 'http://47.99.184.230/info.php'
|
||||
response = requests.get(url)
|
||||
content =response.content.decode("utf-8").strip()
|
||||
print('提取IP:' + content)
|
||||
nowtime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
print('提取IP时间:' + nowtime)
|
||||
sj = content.strip().split(":", 1)
|
||||
sj1 = sj[0]
|
||||
print("IP:", sj1)
|
||||
sj2 = sj[1]
|
||||
print("端口:", sj2)
|
||||
try:
|
||||
#proxyMeta = "http://nfd0p2:bHQAp5iW@%(host)s:%(port)s" % { # 账密验证,需要购买的代理套餐开通才可使用账密验证,此种情况无需加白名单
|
||||
proxyMeta = "http://%(host)s:%(port)s" % {#白名单验证
|
||||
"host": sj1,
|
||||
"port": sj2,
|
||||
}
|
||||
print("代理1:", proxyMeta)
|
||||
proxysdata = {
|
||||
'http': proxyMeta,
|
||||
'https': proxyMeta
|
||||
}
|
||||
print("代理2:", proxysdata)
|
||||
headers = {
|
||||
"user-agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.'
|
||||
}
|
||||
start = int(round(time.time() * 1000))
|
||||
resp = requests.get(targeturl, proxies=proxysdata, headers=headers, timeout=20)
|
||||
costTime = int(round(time.time() * 1000)) - start
|
||||
print("耗时:" + str(costTime) + "ms")
|
||||
print("返回:",resp.text)
|
||||
s = requests.session()
|
||||
s.keep_alive = False
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
|
||||
|
||||
30
main.py
Normal file
30
main.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
main.py - 支持扫码登录和 cookie 登录模式选择
|
||||
"""
|
||||
import os
|
||||
|
||||
def main():
|
||||
print("请选择登录模式:")
|
||||
print("1. 扫码登录(首次登录/刷新 cookie)")
|
||||
print("2. 使用已保存 cookie 自动发文")
|
||||
choice = input("请输入模式编号(1或2):").strip()
|
||||
if choice == '1':
|
||||
from scan_qrcode import scan_qrcode
|
||||
scan_qrcode()
|
||||
elif choice == '2':
|
||||
cookie_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'cookies.json')
|
||||
if not os.path.exists(cookie_path):
|
||||
print("未检测到 cookie 文件,请先扫码登录!")
|
||||
return
|
||||
from use_cookie import get_test_content, auto_publish_xhs
|
||||
title, desc, image_list = get_test_content()
|
||||
if not image_list:
|
||||
print('未找到图片文件,请将图片放入test文件夹')
|
||||
else:
|
||||
auto_publish_xhs(title, desc, image_list)
|
||||
else:
|
||||
print("输入无效,请输入 1 或 2!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
103
phone_num.py
Normal file
103
phone_num.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
手机号登录小红书创作者中心
|
||||
功能:
|
||||
- 自动打开登录页,等待用户手机号登录
|
||||
- 登录成功后可获取 cookies 或提示
|
||||
"""
|
||||
import time
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def scan_qrcode():
|
||||
with sync_playwright() as p:
|
||||
import getpass
|
||||
browser = p.chromium.launch(headless=False)
|
||||
context = browser.new_context(viewport={"width": 1600, "height": 900})
|
||||
page = context.new_page()
|
||||
# 1. 让用户输入手机号和验证码
|
||||
phone = input("请输入手机号: ")
|
||||
page.goto("https://creator.xiaohongshu.com/login?source=&redirectReason=401&lastUrl=%252Fnew%252Fhome")
|
||||
page.get_by_role("textbox", name="手机号").click()
|
||||
page.get_by_role("textbox", name="手机号").fill(phone)
|
||||
page.get_by_text("发送验证码").click()
|
||||
code = input("请输入收到的验证码: ")
|
||||
# 等待验证码输入框出现,兼容多种写法
|
||||
try:
|
||||
# 先尝试常规定位
|
||||
page.wait_for_selector('input[placeholder*="验证码"], input[name*="code"], input[type="text"]', timeout=15000)
|
||||
# 优先用 placeholder 包含“验证码”
|
||||
if page.query_selector('input[placeholder*="验证码"]'):
|
||||
vcode_box = page.locator('input[placeholder*="验证码"]')
|
||||
elif page.query_selector('input[name*="code"]'):
|
||||
vcode_box = page.locator('input[name*="code"]')
|
||||
else:
|
||||
# 兜底用第2个文本框
|
||||
vcode_box = page.locator('input[type="text"]').nth(1)
|
||||
vcode_box.click()
|
||||
vcode_box.fill(code)
|
||||
except Exception as e:
|
||||
print("验证码输入框定位失败,请检查页面结构或手动调整定位方式。", e)
|
||||
browser.close()
|
||||
return
|
||||
page.get_by_role("button", name="登 录").click()
|
||||
# 等待跳转到主页
|
||||
for i in range(30):
|
||||
time.sleep(2)
|
||||
if "new/home" in page.url:
|
||||
print("手机号登录成功!")
|
||||
break
|
||||
else:
|
||||
print("登录失败,请检查手机号和验证码。")
|
||||
browser.close()
|
||||
return
|
||||
# 只有手机号登录成功后才执行发文流程
|
||||
try:
|
||||
import os
|
||||
# 读取 test 文件夹内容
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
test_dir = os.path.join(base_dir, 'test')
|
||||
title_path = os.path.join(test_dir, 'title.txt')
|
||||
desc_path = os.path.join(test_dir, 'desc.txt')
|
||||
# 查找所有图片,支持多图上传
|
||||
exts = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']
|
||||
image_list = []
|
||||
for fname in os.listdir(test_dir):
|
||||
if any(fname.lower().endswith(ext) for ext in exts):
|
||||
image_list.append(os.path.join(test_dir, fname))
|
||||
if not image_list:
|
||||
raise Exception("未找到图片文件,请在 test 文件夹中放入图片。")
|
||||
with open(title_path, 'r', encoding='utf-8') as f:
|
||||
title = f.read().strip()
|
||||
with open(desc_path, 'r', encoding='utf-8') as f:
|
||||
desc = f.read().strip()
|
||||
page.get_by_text("发布图文笔记").click()
|
||||
time.sleep(1)
|
||||
# 上传多张图片,定位 input[type='file']
|
||||
file_input = page.locator("input[type='file']")
|
||||
file_input.set_input_files(image_list)
|
||||
time.sleep(1)
|
||||
# 填写标题
|
||||
page.get_by_role("textbox", name="填写标题会有更多赞哦~").click()
|
||||
page.get_by_role("textbox", name="填写标题会有更多赞哦~").fill(title)
|
||||
time.sleep(1)
|
||||
# 填写正文
|
||||
page.get_by_role("textbox").nth(1).click()
|
||||
page.get_by_role("textbox").nth(1).fill(desc)
|
||||
time.sleep(1)
|
||||
# 点击发布
|
||||
page.get_by_role("button", name="发布").click()
|
||||
print("已点击发布(多图模式)")
|
||||
# 发完图文后保存 cookie
|
||||
try:
|
||||
from util import save_cookies
|
||||
cookie_path = os.path.join(base_dir, "cookies.json")
|
||||
save_cookies(context, cookie_path)
|
||||
print(f"登录 cookie 已保存到 {cookie_path}")
|
||||
except Exception as e:
|
||||
print("保存 cookie 失败:", e)
|
||||
except Exception as e:
|
||||
print("自动发文流程异常:", e)
|
||||
browser.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
scan_qrcode()
|
||||
30
readme.md
Normal file
30
readme.md
Normal file
@@ -0,0 +1,30 @@
|
||||
1. 安装依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
2. 安装 Playwright 浏览器驱动
|
||||
playwright install chromium
|
||||
|
||||
3. 文件结构说明
|
||||
├── main.py # 主入口,选择登录模式
|
||||
├── scan_qrcode.py # 扫码登录并自动发文,首次登录/刷新 cookie 用
|
||||
├── use_cookie.py # 使用已保存 cookie 自动发文
|
||||
├── util.py # cookie 保存与加载 工具
|
||||
├── requirements.txt # 依赖包列表
|
||||
├── test/ # 发文内容文件夹
|
||||
│ ├── title.txt # 标题
|
||||
│ ├── desc.txt # 正文
|
||||
│ └── *.jpg/png/... # 图片(支持多图)
|
||||
|
||||
4. 使用方法
|
||||
- 首次登录或 cookie 失效时,运行 main.py,选择【1 扫码登录】,扫码后自动保存 cookie 并发文。
|
||||
- 后续可直接选择【2 使用已保存 cookie 自动发文】,无需再次扫码。
|
||||
- 发文内容请放在 test 文件夹,支持多张图片。
|
||||
|
||||
5. 常见问题
|
||||
- cookies.json 文件丢失或无效时,必须重新扫码登录。
|
||||
- Playwright 导入失败请检查依赖安装。
|
||||
|
||||
6. 运行示例
|
||||
python main.py
|
||||
|
||||
|
||||
BIN
requirements.txt
Normal file
BIN
requirements.txt
Normal file
Binary file not shown.
80
scan_qrcode.py
Normal file
80
scan_qrcode.py
Normal file
@@ -0,0 +1,80 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
扫码登录小红书创作者中心
|
||||
功能:
|
||||
- 自动打开登录页,展示二维码,等待用户扫码登录
|
||||
- 登录成功后可获取 cookies 或提示
|
||||
"""
|
||||
import time
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def scan_qrcode():
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=False)
|
||||
context = browser.new_context(viewport={"width": 1600, "height": 900})
|
||||
page = context.new_page()
|
||||
|
||||
login_success = False
|
||||
for i in range(120):
|
||||
page.goto("https://creator.xiaohongshu.com/new/home")
|
||||
if "new/home" in page.url:
|
||||
print("扫码登录成功!")
|
||||
login_success = True
|
||||
break
|
||||
## 等待10s的扫码时间
|
||||
time.sleep(10)
|
||||
if not login_success:
|
||||
print("扫码超时,请重试。")
|
||||
browser.close()
|
||||
return
|
||||
# 只有扫码登录成功后才执行发文流程
|
||||
try:
|
||||
import os
|
||||
# 读取 test 文件夹内容
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
test_dir = os.path.join(base_dir, 'test')
|
||||
title_path = os.path.join(test_dir, 'title.txt')
|
||||
desc_path = os.path.join(test_dir, 'desc.txt')
|
||||
# 查找所有图片,支持多图上传
|
||||
exts = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']
|
||||
image_list = []
|
||||
for fname in os.listdir(test_dir):
|
||||
if any(fname.lower().endswith(ext) for ext in exts):
|
||||
image_list.append(os.path.join(test_dir, fname))
|
||||
if not image_list:
|
||||
raise Exception("未找到图片文件,请在 test 文件夹中放入图片。")
|
||||
with open(title_path, 'r', encoding='utf-8') as f:
|
||||
title = f.read().strip()
|
||||
with open(desc_path, 'r', encoding='utf-8') as f:
|
||||
desc = f.read().strip()
|
||||
page.get_by_text("发布图文笔记").click()
|
||||
time.sleep(1)
|
||||
# 上传多张图片,定位 input[type='file']
|
||||
file_input = page.locator("input[type='file']")
|
||||
file_input.set_input_files(image_list)
|
||||
time.sleep(1)
|
||||
# 填写标题
|
||||
page.get_by_role("textbox", name="填写标题会有更多赞哦~").click()
|
||||
page.get_by_role("textbox", name="填写标题会有更多赞哦~").fill(title)
|
||||
time.sleep(1)
|
||||
# 填写正文
|
||||
page.get_by_role("textbox").nth(1).click()
|
||||
page.get_by_role("textbox").nth(1).fill(desc)
|
||||
time.sleep(1)
|
||||
# 点击发布
|
||||
page.get_by_role("button", name="发布").click()
|
||||
print("已点击发布(多图模式)")
|
||||
# 发完图文后保存 cookie
|
||||
try:
|
||||
from util import save_cookies
|
||||
cookie_path = os.path.join(base_dir, "cookies.json")
|
||||
save_cookies(context, cookie_path)
|
||||
print(f"登录 cookie 已保存到 {cookie_path}")
|
||||
except Exception as e:
|
||||
print("保存 cookie 失败:", e)
|
||||
except Exception as e:
|
||||
print("自动发文流程异常:", e)
|
||||
browser.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
scan_qrcode()
|
||||
32
scrapy_proxy.py
Normal file
32
scrapy_proxy.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import scrapy
|
||||
|
||||
class MimvpSpider(scrapy.spiders.Spider):
|
||||
name = "mimvp"
|
||||
allowed_domains = ["mimvp.com"]
|
||||
start_urls = [
|
||||
"http://proxy.mimvp.com/exist.php",
|
||||
"https://proxy.mimvp.com/exist.php",
|
||||
]
|
||||
|
||||
## 代理设置方式1:直接在代理里设置
|
||||
def start_requests(self):
|
||||
urls = [
|
||||
"http://proxy.mimvp.com/exist.php",
|
||||
"https://proxy.mimvp.com/exist.php",
|
||||
]
|
||||
for url in urls:
|
||||
meta_proxy = ""
|
||||
if url.startswith("http://"):
|
||||
meta_proxy = "http://180.96.27.12:88" # http代理
|
||||
elif url.startswith("https://"):
|
||||
meta_proxy = "http://109.108.87.136:53281" # https代理
|
||||
|
||||
yield scrapy.Request(url=url, callback=self.parse, meta={'proxy': meta_proxy})
|
||||
|
||||
|
||||
def parse(self, response):
|
||||
mimvp_url = response.url # 爬取时请求的url
|
||||
body = response.body # 返回网页内容
|
||||
|
||||
print("mimvp_url : " + str(mimvp_url))
|
||||
print("body : " + str(body))
|
||||
1
state.json
Normal file
1
state.json
Normal file
@@ -0,0 +1 @@
|
||||
{"cookies": [{"name": "acw_tc", "value": "0a00073f17648186622332701e43566b95b602a93cd549a88b024eee608633", "domain": "creator.xiaohongshu.com", "path": "/", "expires": 1764820461.74781, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "xsecappid", "value": "ugc", "domain": ".xiaohongshu.com", "path": "/", "expires": 1796354663, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "loadts", "value": "1764818663108", "domain": ".xiaohongshu.com", "path": "/", "expires": 1796354663, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "a1", "value": "19ae763b6c588mpg6z68nwm5jhc3ndw0r6snpywrx50000382215", "domain": ".xiaohongshu.com", "path": "/", "expires": 1796354663, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "webId", "value": "634abc526217352bc577c6c427da9e75", "domain": ".xiaohongshu.com", "path": "/", "expires": 1796354663, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "acw_tc", "value": "0a50896717648186651941329eb01af570a20066b520e2ff1034dffa32d0b3", "domain": "edith.xiaohongshu.com", "path": "/", "expires": 1764820464.69803, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "websectiga", "value": "88%3B6be45f388a1ee7bf611a69f3e174cae48f1ea02c0f8ec3256031b8be9c7ee", "domain": ".xiaohongshu.com", "path": "/", "expires": 1765077866, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "sec_poison_id", "value": "ecf5ae6b-e74a-438b-8798-f95c3add5334", "domain": ".xiaohongshu.com", "path": "/", "expires": 1764819271, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "acw_tc", "value": "0a00071517648186671184918e7d081a899a9aa951214f73a355f65e3832f0", "domain": "customer.xiaohongshu.com", "path": "/", "expires": 1764820466.772548, "httpOnly": true, "secure": false, "sameSite": "Lax"}], "origins": [{"origin": "https://creator.xiaohongshu.com", "localStorage": [{"name": "last_tiga_update_time", "value": "1764818666580"}, {"name": "sdt_source_storage_key", "value": "{\"extraInfo\":{},\"reportUrl\":\"/api/sec/v1/shield/webprofile\",\"xhsTokenUrl\":\"https://fe-static.xhscdn.com/as/v1/3e44/public/bf7d4e32677698655a5cadc581fd09b3.js\",\"validate\":false,\"commonPatch\":[\"/fe_api/burdock/v2/note/post\",\"/api/sns/web/v1/comment/post\",\"/api/sns/web/v1/note/like\",\"/api/sns/web/v1/note/collect\",\"/api/sns/web/v1/user/follow\",\"/api/sns/web/v1/feed\",\"/api/sns/web/v1/login/activate\",\"/api/sns/web/v1/note/metrics_report\",\"/api/redcaptcha\",\"/api/store/jpd/main\",\"/phoenix/api/strategy/getAppStrategy\",\"/web_api/sns/v2/note\"],\"signUrl\":\"https://fe-static.xhscdn.com/as/v1/f218/a15/public/04b29480233f4def5c875875b6bdc3b1.js\",\"signVersion\":\"1\",\"url\":\"https://fe-static.xhscdn.com/as/v2/fp/962356ead351e7f2422eb57edff6982d.js\",\"desVersion\":\"2\"}"}]}]}
|
||||
37
templates/login.html
Normal file
37
templates/login.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>小红书创作者中心登录</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; background: #f7f7f7; }
|
||||
.container { max-width: 400px; margin: 80px auto; background: #fff; padding: 32px 24px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
|
||||
h2 { text-align: center; margin-bottom: 24px; }
|
||||
label { display: block; margin-bottom: 8px; }
|
||||
input[type="text"] { width: 100%; padding: 8px; margin-bottom: 16px; border: 1px solid #ddd; border-radius: 4px; }
|
||||
button { width: 100%; padding: 10px; background: #e34f4f; color: #fff; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; }
|
||||
.msg { color: #e34f4f; text-align: center; margin-bottom: 16px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2>小红书创作者中心登录</h2>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div class="msg">{{ messages[0] }}</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<form method="post" action="/send_code">
|
||||
<label for="phone">手机号:</label>
|
||||
<input type="text" id="phone" name="phone" placeholder="请输入手机号" required>
|
||||
<button type="submit">发送验证码</button>
|
||||
</form>
|
||||
<form method="post" action="/login">
|
||||
<label for="code">验证码:</label>
|
||||
<input type="text" id="code" name="code" placeholder="请输入验证码" required>
|
||||
<button type="submit">登录</button>
|
||||
</form>
|
||||
<p style="color:#888;font-size:13px;text-align:center;margin-top:18px;">请先在小红书页面点击“发送验证码”后再输入收到的验证码</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
103
test.py
Normal file
103
test.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
手机号登录小红书创作者中心
|
||||
功能:
|
||||
- 自动打开登录页,等待用户手机号登录
|
||||
- 登录成功后可获取 cookies 或提示
|
||||
"""
|
||||
import time
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def scan_qrcode():
|
||||
with sync_playwright() as p:
|
||||
import getpass
|
||||
browser = p.chromium.launch(headless=False)
|
||||
context = browser.new_context(viewport={"width": 1600, "height": 900})
|
||||
page = context.new_page()
|
||||
# 1. 让用户输入手机号和验证码
|
||||
phone = input("请输入手机号: ")
|
||||
page.goto("https://creator.xiaohongshu.com/login?source=&redirectReason=401&lastUrl=%252Fnew%252Fhome")
|
||||
page.get_by_role("textbox", name="手机号").click()
|
||||
page.get_by_role("textbox", name="手机号").fill(phone)
|
||||
page.get_by_text("发送验证码").click()
|
||||
code = input("请输入收到的验证码: ")
|
||||
# 等待验证码输入框出现,兼容多种写法
|
||||
try:
|
||||
# 先尝试常规定位
|
||||
page.wait_for_selector('input[placeholder*="验证码"], input[name*="code"], input[type="text"]', timeout=15000)
|
||||
# 优先用 placeholder 包含“验证码”
|
||||
if page.query_selector('input[placeholder*="验证码"]'):
|
||||
vcode_box = page.locator('input[placeholder*="验证码"]')
|
||||
elif page.query_selector('input[name*="code"]'):
|
||||
vcode_box = page.locator('input[name*="code"]')
|
||||
else:
|
||||
# 兜底用第2个文本框
|
||||
vcode_box = page.locator('input[type="text"]').nth(1)
|
||||
vcode_box.click()
|
||||
vcode_box.fill(code)
|
||||
except Exception as e:
|
||||
print("验证码输入框定位失败,请检查页面结构或手动调整定位方式。", e)
|
||||
browser.close()
|
||||
return
|
||||
page.get_by_role("button", name="登 录").click()
|
||||
# 等待跳转到主页
|
||||
for i in range(30):
|
||||
time.sleep(2)
|
||||
if "new/home" in page.url:
|
||||
print("手机号登录成功!")
|
||||
break
|
||||
else:
|
||||
print("登录失败,请检查手机号和验证码。")
|
||||
browser.close()
|
||||
return
|
||||
# 只有手机号登录成功后才执行发文流程
|
||||
try:
|
||||
import os
|
||||
# 读取 test 文件夹内容
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
test_dir = os.path.join(base_dir, 'test')
|
||||
title_path = os.path.join(test_dir, 'title.txt')
|
||||
desc_path = os.path.join(test_dir, 'desc.txt')
|
||||
# 查找所有图片,支持多图上传
|
||||
exts = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']
|
||||
image_list = []
|
||||
for fname in os.listdir(test_dir):
|
||||
if any(fname.lower().endswith(ext) for ext in exts):
|
||||
image_list.append(os.path.join(test_dir, fname))
|
||||
if not image_list:
|
||||
raise Exception("未找到图片文件,请在 test 文件夹中放入图片。")
|
||||
with open(title_path, 'r', encoding='utf-8') as f:
|
||||
title = f.read().strip()
|
||||
with open(desc_path, 'r', encoding='utf-8') as f:
|
||||
desc = f.read().strip()
|
||||
page.get_by_text("发布图文笔记").click()
|
||||
time.sleep(1)
|
||||
# 上传多张图片,定位 input[type='file']
|
||||
file_input = page.locator("input[type='file']")
|
||||
file_input.set_input_files(image_list)
|
||||
time.sleep(1)
|
||||
# 填写标题
|
||||
page.get_by_role("textbox", name="填写标题会有更多赞哦~").click()
|
||||
page.get_by_role("textbox", name="填写标题会有更多赞哦~").fill(title)
|
||||
time.sleep(1)
|
||||
# 填写正文
|
||||
page.get_by_role("textbox").nth(1).click()
|
||||
page.get_by_role("textbox").nth(1).fill(desc)
|
||||
time.sleep(1)
|
||||
# 点击发布
|
||||
page.get_by_role("button", name="发布").click()
|
||||
print("已点击发布(多图模式)")
|
||||
# 发完图文后保存 cookie
|
||||
try:
|
||||
from util import save_cookies
|
||||
cookie_path = os.path.join(base_dir, "cookies.json")
|
||||
save_cookies(context, cookie_path)
|
||||
print(f"登录 cookie 已保存到 {cookie_path}")
|
||||
except Exception as e:
|
||||
print("保存 cookie 失败:", e)
|
||||
except Exception as e:
|
||||
print("自动发文流程异常:", e)
|
||||
browser.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
scan_qrcode()
|
||||
11
test/desc.txt
Normal file
11
test/desc.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
从 17 岁的天才少年到 29 岁的六冠王,Faker 的名字早就刻在了《英雄联盟》的神坛上🏆
|
||||
谁能想到,这个 1996 年出生的韩国少年,从韩服路人王出道,一脚踏入职业赛场就打出了 “双劫大战” 的封神操作,残血反杀 Ryu 的画面至今仍是电竞史上的名场面。2013 年第一次站在 S 赛舞台,他就带着 SKT 拿下 S3 冠军,出道即巅峰说的就是他吧!
|
||||
✨三冠王的统治时代
|
||||
2015-2016 年是 Faker 的绝对统治期,连续拿下 S5、S6 全球总决赛冠军,成为 LOL 史上第一个三冠王。那时候的他就像中路的 “大魔王”,瑞兹、发条、阿狸这些英雄在他手里仿佛有了灵魂,对手连兵线都不敢轻易靠近。
|
||||
🌧️低谷时的坚守最动人
|
||||
2017 年后 SKT 经历重组,Faker 也曾跌入低谷,甚至坐过替补席。但他从没离开 T1,这个从出道就效力的战队,成了他唯一的执念。2022 年重返 S 赛决赛,2023 年 27 岁的他带着新生代队友拿下 S13 冠军,四冠王的成就已经前无古人。
|
||||
🏆六冠王!传奇还在继续
|
||||
2024 年拿下 S14 冠军成为五冠王,2025 年在成都东安湖体育馆,他又带领 T1 打满五局战胜 KT,拿下史无前例的六冠王和三连冠!当《Silver Scrapes》的战歌响起,看着他站在领奖台上举起奖杯,真的忍不住泪目 ——12 年了,他还在守着中路的塔,守着属于自己的传奇。
|
||||
从 S3 到 S15,队友换了一批又一批,挑战者来了又走,只有 Faker 还在赛场。他是首位入选英雄联盟名人堂的选手,是拿下 10 次 LCK 冠军、500 次 S 赛击杀的传奇,更是把 “电竞精神” 刻进骨子里的李相赫。
|
||||
有人说他是电竞乔丹,可我觉得,Faker 就是 Faker,是独一份的英雄联盟之神🙌
|
||||
#Faker #T1 #英雄联盟 S15 #电竞传奇 #六冠王 #李相赫
|
||||
BIN
test/faker.png
Normal file
BIN
test/faker.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 310 KiB |
1
test/title.txt
Normal file
1
test/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
Faker 六冠王封神!12 年守塔的电竞传奇✨
|
||||
86
use_cookie.py
Normal file
86
use_cookie.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
自动发文脚本:test.py
|
||||
功能:
|
||||
"""
|
||||
import os
|
||||
import json
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def get_test_content():
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
test_dir = os.path.join(base_dir, 'test')
|
||||
title_path = os.path.join(test_dir, 'title.txt')
|
||||
desc_path = os.path.join(test_dir, 'desc.txt')
|
||||
with open(title_path, 'r', encoding='utf-8') as f:
|
||||
title = f.read().strip()
|
||||
with open(desc_path, 'r', encoding='utf-8') as f:
|
||||
desc = f.read().strip()
|
||||
# 查找所有图片,支持多图上传
|
||||
exts = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']
|
||||
image_list = []
|
||||
for fname in os.listdir(test_dir):
|
||||
if any(fname.lower().endswith(ext) for ext in exts):
|
||||
image_list.append(os.path.join(test_dir, fname))
|
||||
return title, desc, image_list
|
||||
|
||||
def auto_publish_xhs(title, desc, image_path):
|
||||
# 自动读取扫码登录保存的 cookie
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
cookie_path = os.path.join(base_dir, "cookies.json")
|
||||
if not os.path.exists(cookie_path):
|
||||
print("未找到 cookies.json,请先扫码登录生成 cookie")
|
||||
return
|
||||
with open(cookie_path, 'r', encoding='utf-8') as f:
|
||||
cookies = json.load(f)
|
||||
|
||||
import time
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=False)
|
||||
context = browser.new_context(viewport={"width": 1600, "height": 900})
|
||||
context.add_cookies(cookies)
|
||||
page = context.new_page()
|
||||
# 进入主页,cookie生效后自动登录
|
||||
page.goto("https://creator.xiaohongshu.com/new/home")
|
||||
time.sleep(2)
|
||||
# 进入图文发布页面
|
||||
page.get_by_text("发布图文笔记").click()
|
||||
time.sleep(1)
|
||||
# 上传多张图片,定位 input[type='file']
|
||||
try:
|
||||
file_input = page.locator("input[type='file']")
|
||||
file_input.set_input_files(image_path)
|
||||
print('图片已上传')
|
||||
except Exception as e:
|
||||
print('图片上传控件异常:', e)
|
||||
time.sleep(1)
|
||||
# 填写标题
|
||||
try:
|
||||
page.get_by_role("textbox", name="填写标题会有更多赞哦~").click()
|
||||
page.get_by_role("textbox", name="填写标题会有更多赞哦~").fill(title)
|
||||
except Exception as e:
|
||||
print('标题控件异常:', e)
|
||||
time.sleep(1)
|
||||
# 填写正文
|
||||
try:
|
||||
page.get_by_role("textbox").nth(1).click()
|
||||
page.get_by_role("textbox").nth(1).fill(desc)
|
||||
except Exception as e:
|
||||
print('正文控件异常:', e)
|
||||
time.sleep(1)
|
||||
# 点击发布
|
||||
try:
|
||||
page.get_by_role("button", name="发布").click()
|
||||
print('已点击发布(多图模式)')
|
||||
except Exception as e:
|
||||
print('发布按钮异常:', e)
|
||||
time.sleep(2)
|
||||
print('发文流程结束,请检查小红书后台是否发布成功')
|
||||
browser.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
title, desc, image_list = get_test_content()
|
||||
if not image_list:
|
||||
print('未找到图片文件,请将图片放入test文件夹')
|
||||
else:
|
||||
auto_publish_xhs(title, desc, image_list)
|
||||
28
util.py
Normal file
28
util.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
util.py - 用于保存和读取 cookie 的工具函数
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
|
||||
def save_cookies(context, save_path):
|
||||
"""
|
||||
保存 Playwright 浏览器上下文中的 cookies 到指定路径
|
||||
:param context: Playwright browser context
|
||||
:param save_path: 保存 cookie 的文件路径
|
||||
"""
|
||||
cookies = context.cookies()
|
||||
with open(save_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(cookies, f, ensure_ascii=False, indent=2)
|
||||
|
||||
def load_cookies(context, load_path):
|
||||
"""
|
||||
从指定路径读取 cookies 并设置到 Playwright 浏览器上下文
|
||||
:param context: Playwright browser context
|
||||
:param load_path: cookie 文件路径
|
||||
"""
|
||||
if not os.path.exists(load_path):
|
||||
return
|
||||
with open(load_path, 'r', encoding='utf-8') as f:
|
||||
cookies = json.load(f)
|
||||
context.add_cookies(cookies)
|
||||
117
web_login.py
Normal file
117
web_login.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Flask Web 登录小红书创作者中心
|
||||
- 前端输入手机号和验证码
|
||||
- 后端用 Playwright 自动登录
|
||||
"""
|
||||
|
||||
from flask import Flask, render_template, request, redirect, url_for, flash, session
|
||||
|
||||
|
||||
import time
|
||||
from playwright.sync_api import sync_playwright
|
||||
import requests
|
||||
|
||||
def get_working_proxy(test_url="https://creator.xiaohongshu.com"):
|
||||
proxy_url = 'http://api.tianqiip.com/getip?secret=ew9mj7j3yplbk3xb&num=1&type=txt&port=1&time=3&mr=1&sign=5451e454a54b9f1f06222606c418e12f'
|
||||
try:
|
||||
resp = requests.get(proxy_url, timeout=10)
|
||||
proxy_content = resp.content.decode("utf-8").strip()
|
||||
proxy_ip, proxy_port = proxy_content.split(":", 1)
|
||||
proxy_server = f"http://{proxy_ip}:{proxy_port}"
|
||||
proxies = {"http": proxy_server, "https": proxy_server}
|
||||
# 检查代理可用性
|
||||
try:
|
||||
test = requests.get(test_url, proxies=proxies, timeout=8)
|
||||
if test.status_code == 200:
|
||||
print(f"代理可用: {proxy_server}")
|
||||
return proxy_server
|
||||
else:
|
||||
print(f"代理返回非200: {proxy_server}")
|
||||
except Exception as e:
|
||||
print(f"代理不可用: {proxy_server}, {e}")
|
||||
except Exception as e:
|
||||
print("获取代理失败", e)
|
||||
return None
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = 'xhs_secret_key'
|
||||
|
||||
# 步骤1:输入手机号,自动打开浏览器并填手机号,等待用户手动点击发送验证码
|
||||
@app.route('/', methods=['GET'])
|
||||
def index():
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/send_code', methods=['POST'])
|
||||
def send_code():
|
||||
phone = request.form.get('phone')
|
||||
if not phone:
|
||||
flash('请输入手机号')
|
||||
return redirect(url_for('index'))
|
||||
session['phone'] = phone
|
||||
# 获取可用代理
|
||||
proxy_server = get_working_proxy()
|
||||
if proxy_server:
|
||||
print(f"使用代理: {proxy_server}")
|
||||
else:
|
||||
print("未获取到可用代理,使用本地IP")
|
||||
|
||||
with sync_playwright() as p:
|
||||
launch_args = {"headless": False}
|
||||
if proxy_server:
|
||||
launch_args["proxy"] = {"server": proxy_server}
|
||||
browser = p.chromium.launch(**launch_args)
|
||||
context = browser.new_context()
|
||||
page = context.new_page()
|
||||
page.goto("https://creator.xiaohongshu.com/login?source=&redirectReason=401&lastUrl=%252Fnew%252Fhome")
|
||||
page.get_by_role("textbox", name="手机号").click()
|
||||
page.get_by_role("textbox", name="手机号").fill(phone)
|
||||
page.get_by_text("发送验证码").click()
|
||||
# 等待用户收到验证码
|
||||
input("验证码已自动发送,请查收手机短信并输入验证码,操作完成后回到命令行按回车...")
|
||||
# 保存 context 到磁盘,后续用
|
||||
context.storage_state(path="state.json")
|
||||
browser.close()
|
||||
flash('验证码已发送,请查收手机短信并输入验证码')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
# 步骤2:输入验证码,自动登录
|
||||
@app.route('/login', methods=['POST'])
|
||||
def login():
|
||||
code = request.form.get('code')
|
||||
phone = session.get('phone')
|
||||
if not code or not phone:
|
||||
flash('请先输入手机号并发送验证码')
|
||||
return redirect(url_for('index'))
|
||||
# 获取可用代理
|
||||
proxy_server = get_working_proxy()
|
||||
if proxy_server:
|
||||
print(f"使用代理: {proxy_server}")
|
||||
else:
|
||||
print("未获取到可用代理,使用本地IP")
|
||||
|
||||
with sync_playwright() as p:
|
||||
launch_args = {"headless": False}
|
||||
if proxy_server:
|
||||
launch_args["proxy"] = {"server": proxy_server}
|
||||
context = p.chromium.launch_persistent_context(user_data_dir=".", storage_state="state.json", **launch_args)
|
||||
page = context.pages[0] if context.pages else context.new_page()
|
||||
page.goto("https://creator.xiaohongshu.com/login?source=&redirectReason=401&lastUrl=%252Fnew%252Fhome")
|
||||
# 填写验证码并登录
|
||||
page.wait_for_selector('input[placeholder*="验证码"]', timeout=15000)
|
||||
vcode_box = page.locator('input[placeholder*="验证码"]')
|
||||
vcode_box.fill(code)
|
||||
page.get_by_role("button", name="登 录").click()
|
||||
# 等待跳转
|
||||
try:
|
||||
page.wait_for_url("**/new/home", timeout=20000)
|
||||
flash('登录成功!')
|
||||
# 保存 cookies
|
||||
context.storage_state(path="cookies.json")
|
||||
except Exception as e:
|
||||
flash('登录失败,请检查验证码')
|
||||
context.close()
|
||||
return redirect(url_for('index'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
Reference in New Issue
Block a user