hahaa
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
"""
|
||||
兼容入口:使用统一的 GUI 实现。
|
||||
"""
|
||||
|
||||
from 自动化_gui import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,202 +0,0 @@
|
||||
"""
|
||||
微信小程序 HTTPS 后端服务器示例
|
||||
支持 Flask 和 FastAPI 两种框架
|
||||
"""
|
||||
|
||||
# ==================== Flask 版本 ====================
|
||||
from flask import Flask, jsonify, request
|
||||
from flask_cors import CORS
|
||||
import ssl
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
# 配置 CORS,允许微信小程序访问
|
||||
CORS(app, resources={
|
||||
r"/api/*": {
|
||||
"origins": "*", # 生产环境建议指定具体域名
|
||||
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||
"allow_headers": ["Content-Type", "Authorization"]
|
||||
}
|
||||
})
|
||||
|
||||
@app.route('/api/test', methods=['GET'])
|
||||
def test():
|
||||
"""测试接口"""
|
||||
return jsonify({
|
||||
'code': 200,
|
||||
'message': 'HTTPS 接口测试成功',
|
||||
'data': {
|
||||
'timestamp': request.headers.get('X-Request-Time', ''),
|
||||
'user_agent': request.headers.get('User-Agent', '')
|
||||
}
|
||||
})
|
||||
|
||||
@app.route('/api/user/login', methods=['POST'])
|
||||
def login():
|
||||
"""登录接口示例"""
|
||||
data = request.get_json()
|
||||
username = data.get('username', '')
|
||||
password = data.get('password', '')
|
||||
|
||||
# 这里添加您的登录逻辑
|
||||
if username and password:
|
||||
return jsonify({
|
||||
'code': 200,
|
||||
'message': '登录成功',
|
||||
'data': {
|
||||
'token': 'example_token_12345',
|
||||
'user_id': 1
|
||||
}
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'code': 400,
|
||||
'message': '用户名或密码不能为空'
|
||||
}), 400
|
||||
|
||||
@app.route('/api/health', methods=['GET'])
|
||||
def health():
|
||||
"""健康检查接口"""
|
||||
return jsonify({
|
||||
'status': 'healthy',
|
||||
'service': 'wechat-miniprogram-api'
|
||||
})
|
||||
|
||||
def run_flask_server():
|
||||
"""运行 Flask HTTPS 服务器"""
|
||||
# SSL 证书路径(根据您的实际情况修改)
|
||||
cert_file = os.getenv('SSL_CERT', '/etc/letsencrypt/live/yourdomain.com/fullchain.pem')
|
||||
key_file = os.getenv('SSL_KEY', '/etc/letsencrypt/live/yourdomain.com/privkey.pem')
|
||||
|
||||
# 检查证书文件是否存在
|
||||
if not os.path.exists(cert_file) or not os.path.exists(key_file):
|
||||
print(f"警告:证书文件不存在!")
|
||||
print(f"证书路径: {cert_file}")
|
||||
print(f"私钥路径: {key_file}")
|
||||
print("请先配置 SSL 证书,或使用 Nginx 反向代理")
|
||||
# 开发环境可以运行 HTTP(仅用于测试)
|
||||
app.run(host='0.0.0.0', port=8000, debug=True)
|
||||
return
|
||||
|
||||
# 配置 SSL
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
context.load_cert_chain(cert_file, key_file)
|
||||
|
||||
print("启动 Flask HTTPS 服务器...")
|
||||
print(f"访问地址: https://yourdomain.com/api/test")
|
||||
|
||||
app.run(
|
||||
host='0.0.0.0',
|
||||
port=443,
|
||||
ssl_context=context,
|
||||
debug=False # 生产环境设为 False
|
||||
)
|
||||
|
||||
|
||||
# ==================== FastAPI 版本 ====================
|
||||
try:
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
import uvicorn
|
||||
|
||||
fastapi_app = FastAPI(title="微信小程序 API", version="1.0.0")
|
||||
|
||||
# 配置 CORS
|
||||
fastapi_app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # 生产环境建议指定具体域名
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@fastapi_app.get("/api/test")
|
||||
async def test_api(request: Request):
|
||||
"""测试接口"""
|
||||
return {
|
||||
'code': 200,
|
||||
'message': 'HTTPS 接口测试成功',
|
||||
'data': {
|
||||
'timestamp': request.headers.get('x-request-time', ''),
|
||||
'user_agent': request.headers.get('user-agent', '')
|
||||
}
|
||||
}
|
||||
|
||||
@fastapi_app.post("/api/user/login")
|
||||
async def login_api(data: dict):
|
||||
"""登录接口示例"""
|
||||
username = data.get('username', '')
|
||||
password = data.get('password', '')
|
||||
|
||||
if username and password:
|
||||
return {
|
||||
'code': 200,
|
||||
'message': '登录成功',
|
||||
'data': {
|
||||
'token': 'example_token_12345',
|
||||
'user_id': 1
|
||||
}
|
||||
}
|
||||
else:
|
||||
return JSONResponse(
|
||||
status_code=400,
|
||||
content={
|
||||
'code': 400,
|
||||
'message': '用户名或密码不能为空'
|
||||
}
|
||||
)
|
||||
|
||||
@fastapi_app.get("/api/health")
|
||||
async def health_check():
|
||||
"""健康检查接口"""
|
||||
return {
|
||||
'status': 'healthy',
|
||||
'service': 'wechat-miniprogram-api'
|
||||
}
|
||||
|
||||
def run_fastapi_server():
|
||||
"""运行 FastAPI HTTPS 服务器"""
|
||||
cert_file = os.getenv('SSL_CERT', '/etc/letsencrypt/live/yourdomain.com/fullchain.pem')
|
||||
key_file = os.getenv('SSL_KEY', '/etc/letsencrypt/live/yourdomain.com/privkey.pem')
|
||||
|
||||
if not os.path.exists(cert_file) or not os.path.exists(key_file):
|
||||
print(f"警告:证书文件不存在!")
|
||||
print(f"证书路径: {cert_file}")
|
||||
print(f"私钥路径: {key_file}")
|
||||
print("请先配置 SSL 证书,或使用 Nginx 反向代理")
|
||||
# 开发环境可以运行 HTTP(仅用于测试)
|
||||
uvicorn.run(fastapi_app, host="0.0.0.0", port=8000)
|
||||
return
|
||||
|
||||
print("启动 FastAPI HTTPS 服务器...")
|
||||
print(f"访问地址: https://yourdomain.com/api/test")
|
||||
|
||||
uvicorn.run(
|
||||
fastapi_app,
|
||||
host="0.0.0.0",
|
||||
port=443,
|
||||
ssl_keyfile=key_file,
|
||||
ssl_certfile=cert_file
|
||||
)
|
||||
|
||||
except ImportError:
|
||||
print("FastAPI 未安装,跳过 FastAPI 示例")
|
||||
print("安装命令: pip install fastapi uvicorn")
|
||||
|
||||
|
||||
# ==================== 主程序 ====================
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
# 选择框架:'flask' 或 'fastapi'
|
||||
framework = os.getenv('FRAMEWORK', 'flask').lower()
|
||||
|
||||
if framework == 'fastapi':
|
||||
try:
|
||||
run_fastapi_server()
|
||||
except NameError:
|
||||
print("FastAPI 未安装,使用 Flask")
|
||||
run_flask_server()
|
||||
else:
|
||||
run_flask_server()
|
||||
48
main.py
48
main.py
@@ -1,48 +0,0 @@
|
||||
import requests
|
||||
|
||||
|
||||
cookies = {
|
||||
'abRequestId': '343dbd09-707e-59a3-9315-3b6fa1c3ff34',
|
||||
'webBuild': '5.7.0',
|
||||
'xsecappid': 'xhs-pc-web',
|
||||
'a1': '19bbae7730ahxzrs5lm6s46vde8fq350g5klg6uij50000427550',
|
||||
'webId': '17c9fe1d1bc556b2837a35fb01b770b5',
|
||||
'gid': 'yjDD0dWYyWk8yjDD0dWWq2K780xCuFM216KMT3V4KhfdY128iAq28k8884JW2288YJWqyYjD',
|
||||
'web_session': '0400698f1bdf69567ccb126e523b4ba45d4326',
|
||||
'id_token': 'VjEAANiXRALq8n+D/Uh5zBxUZgZQea2cBzD/+4ZtKQHrx2FPtJYJfV+n5N7LJDdNZmVmMugQNdlm0mg6Dy78u0wHOnF2RDB4ZB7i2whxyVT8v97Yrbwz4hbQM3EtVEyNMgzIvZnR',
|
||||
'websectiga': '29098a4cf41f76ee3f8db19051aaa60c0fc7c5e305572fec762da32d457d76ae',
|
||||
'sec_poison_id': '36102520-3a09-4447-a36f-a1c31b15b950',
|
||||
'acw_tc': '0a00d49317683703864606878eb20c852cedbeb990c4ffae205ebc083fcc93',
|
||||
'loadts': '1768370387610',
|
||||
}
|
||||
|
||||
headers = {
|
||||
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
|
||||
'cache-control': 'no-cache',
|
||||
'dnt': '1',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=0, i',
|
||||
'sec-ch-ua': '"Microsoft Edge";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'document',
|
||||
'sec-fetch-mode': 'navigate',
|
||||
'sec-fetch-site': 'same-origin',
|
||||
'sec-fetch-user': '?1',
|
||||
'upgrade-insecure-requests': '1',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0',
|
||||
# 'cookie': 'abRequestId=343dbd09-707e-59a3-9315-3b6fa1c3ff34; webBuild=5.7.0; xsecappid=xhs-pc-web; a1=19bbae7730ahxzrs5lm6s46vde8fq350g5klg6uij50000427550; webId=17c9fe1d1bc556b2837a35fb01b770b5; gid=yjDD0dWYyWk8yjDD0dWWq2K780xCuFM216KMT3V4KhfdY128iAq28k8884JW2288YJWqyYjD; web_session=0400698f1bdf69567ccb126e523b4ba45d4326; id_token=VjEAANiXRALq8n+D/Uh5zBxUZgZQea2cBzD/+4ZtKQHrx2FPtJYJfV+n5N7LJDdNZmVmMugQNdlm0mg6Dy78u0wHOnF2RDB4ZB7i2whxyVT8v97Yrbwz4hbQM3EtVEyNMgzIvZnR; websectiga=29098a4cf41f76ee3f8db19051aaa60c0fc7c5e305572fec762da32d457d76ae; sec_poison_id=36102520-3a09-4447-a36f-a1c31b15b950; acw_tc=0a00d49317683703864606878eb20c852cedbeb990c4ffae205ebc083fcc93; loadts=1768370387610',
|
||||
}
|
||||
|
||||
response = requests.get(
|
||||
'https://www.xiaohongshu.com/explore/68eab3870000000004004112?xsec_token=ABhDIDWci_QiY0PVUtlkViv01hH_8c3AiHfagOPVFNVms=&xsec_source=pc_search&source=unknown',
|
||||
# cookies=cookies,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
# 保存HTML到文件
|
||||
with open('1.html', 'w', encoding='utf-8') as f:
|
||||
f.write(response.text)
|
||||
|
||||
print("HTML已保存到 1.html")
|
||||
281
parse_html.py
281
parse_html.py
@@ -1,281 +0,0 @@
|
||||
import re
|
||||
import json
|
||||
import sys
|
||||
import io
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
# 设置标准输出编码为UTF-8,避免Windows控制台编码问题
|
||||
if sys.platform == 'win32':
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
|
||||
def extract_video_from_meta(html_content):
|
||||
"""
|
||||
从HTML的meta标签中提取视频信息
|
||||
|
||||
Args:
|
||||
html_content: HTML内容字符串
|
||||
|
||||
Returns:
|
||||
dict: 视频信息字典,如果没有找到则返回None
|
||||
"""
|
||||
try:
|
||||
soup = BeautifulSoup(html_content, 'html.parser')
|
||||
video_info = {}
|
||||
|
||||
# 提取og:video标签
|
||||
og_video = soup.find('meta', {'name': 'og:video'})
|
||||
if og_video and og_video.get('content'):
|
||||
video_info['url'] = og_video.get('content')
|
||||
|
||||
# 提取视频时长
|
||||
og_videotime = soup.find('meta', {'name': 'og:videotime'})
|
||||
if og_videotime and og_videotime.get('content'):
|
||||
video_info['time'] = og_videotime.get('content')
|
||||
|
||||
# 提取视频质量
|
||||
og_videoquality = soup.find('meta', {'name': 'og:videoquality'})
|
||||
if og_videoquality and og_videoquality.get('content'):
|
||||
video_info['quality'] = og_videoquality.get('content')
|
||||
|
||||
# 如果找到了视频URL,返回视频信息
|
||||
if video_info.get('url'):
|
||||
return video_info
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"从meta标签提取视频信息时出错:{e}")
|
||||
return None
|
||||
|
||||
|
||||
def parse_html_file(html_file='1.html'):
|
||||
"""
|
||||
解析HTML文件,提取标题、描述、图片列表、视频列表和话题
|
||||
|
||||
Args:
|
||||
html_file: HTML文件路径
|
||||
|
||||
Returns:
|
||||
dict: 解析出的笔记数据
|
||||
"""
|
||||
try:
|
||||
# 读取HTML文件
|
||||
with open(html_file, 'r', encoding='utf-8') as f:
|
||||
html_content = f.read()
|
||||
|
||||
# 提取meta标签中的视频信息
|
||||
video_info = extract_video_from_meta(html_content)
|
||||
|
||||
# 使用正则表达式提取window.__INITIAL_STATE__的内容
|
||||
pattern = r'<script>window\.__INITIAL_STATE__\s*=\s*({.*?});?\s*</script>'
|
||||
match = re.search(pattern, html_content, re.DOTALL)
|
||||
|
||||
if not match:
|
||||
print("未找到 window.__INITIAL_STATE__ 数据")
|
||||
# 如果只有视频信息,返回视频信息
|
||||
if video_info:
|
||||
return {'videos': [video_info]}
|
||||
return None
|
||||
|
||||
# 提取JSON字符串
|
||||
json_str = match.group(1)
|
||||
|
||||
# 处理JavaScript中的undefined值(Python JSON不支持undefined)
|
||||
json_str = re.sub(r'\bundefined\b', 'null', json_str)
|
||||
|
||||
# 解析JSON
|
||||
initial_state = json.loads(json_str)
|
||||
|
||||
# 提取笔记数据
|
||||
note_data = extract_note_data(initial_state)
|
||||
|
||||
# 如果提取到视频信息,添加到笔记数据中
|
||||
if video_info and note_data:
|
||||
if 'videos' not in note_data or not note_data['videos']:
|
||||
note_data['videos'] = []
|
||||
note_data['videos'].append(video_info)
|
||||
|
||||
return note_data
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"错误:找不到文件 {html_file}")
|
||||
return None
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"JSON解析错误:{e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"解析错误:{e}")
|
||||
return None
|
||||
|
||||
|
||||
def extract_note_data(initial_state):
|
||||
"""
|
||||
从初始状态中提取笔记数据(只提取标题、描述、图片列表、视频列表和话题)
|
||||
|
||||
Args:
|
||||
initial_state: window.__INITIAL_STATE__ 解析后的字典
|
||||
|
||||
Returns:
|
||||
dict: 提取的笔记数据
|
||||
"""
|
||||
try:
|
||||
# 获取笔记详情
|
||||
note_store = initial_state.get('note', {})
|
||||
note_detail_map = note_store.get('noteDetailMap', {})
|
||||
|
||||
# 获取第一个笔记ID
|
||||
first_note_id = note_store.get('firstNoteId')
|
||||
if not first_note_id:
|
||||
# 如果没有firstNoteId,尝试获取noteDetailMap中的第一个key
|
||||
if note_detail_map:
|
||||
first_note_id = list(note_detail_map.keys())[0]
|
||||
else:
|
||||
print("未找到笔记ID")
|
||||
return None
|
||||
|
||||
# 获取笔记详情
|
||||
note_detail = note_detail_map.get(first_note_id, {})
|
||||
note_info = note_detail.get('note', {})
|
||||
|
||||
if not note_info:
|
||||
print("未找到笔记信息")
|
||||
return None
|
||||
|
||||
# 只提取需要的字段
|
||||
extracted_data = {
|
||||
'title': note_info.get('title'),
|
||||
'desc': note_info.get('desc'),
|
||||
'images': [],
|
||||
'videos': [],
|
||||
'topics': []
|
||||
}
|
||||
|
||||
# 提取图片信息
|
||||
image_list = note_info.get('imageList', [])
|
||||
for img in image_list:
|
||||
image_data = {
|
||||
'url': img.get('urlDefault') or img.get('url'),
|
||||
'urlPre': img.get('urlPre'),
|
||||
'width': img.get('width'),
|
||||
'height': img.get('height'),
|
||||
}
|
||||
extracted_data['images'].append(image_data)
|
||||
|
||||
# 提取视频信息(如果存在)
|
||||
video_info = note_info.get('video', {})
|
||||
if video_info:
|
||||
video_data = {}
|
||||
|
||||
# 尝试提取视频URL
|
||||
media = video_info.get('media', {})
|
||||
if media:
|
||||
stream = media.get('stream', {})
|
||||
if stream:
|
||||
hls = stream.get('hls', {})
|
||||
if hls:
|
||||
video_data['url'] = hls.get('masterUrl') or hls.get('url')
|
||||
# 如果没有hls,尝试其他字段
|
||||
if not video_data.get('url'):
|
||||
video_data['url'] = media.get('url') or media.get('videoUrl')
|
||||
|
||||
# 提取视频封面
|
||||
if video_info.get('cover'):
|
||||
video_data['cover'] = video_info.get('cover')
|
||||
|
||||
# 提取视频时长
|
||||
if video_info.get('time'):
|
||||
video_data['time'] = video_info.get('time')
|
||||
|
||||
if video_data.get('url'):
|
||||
extracted_data['videos'].append(video_data)
|
||||
|
||||
# 提取话题信息
|
||||
# 话题可能在多个位置,尝试不同的字段名
|
||||
topic_list = note_info.get('topicList', []) or note_info.get('tagList', []) or note_info.get('hashtagList', [])
|
||||
if topic_list:
|
||||
for topic in topic_list:
|
||||
topic_data = {
|
||||
'name': topic.get('name') or topic.get('title') or topic.get('tagName'),
|
||||
'id': topic.get('id') or topic.get('topicId') or topic.get('tagId'),
|
||||
}
|
||||
if topic_data.get('name'):
|
||||
extracted_data['topics'].append(topic_data)
|
||||
|
||||
# 如果描述中包含话题(#话题#格式),也提取出来
|
||||
desc = note_info.get('desc', '')
|
||||
if desc:
|
||||
# 使用正则表达式提取 #话题# 格式
|
||||
topic_pattern = r'#([^#]+)#'
|
||||
matches = re.findall(topic_pattern, desc)
|
||||
for match in matches:
|
||||
# 避免重复添加
|
||||
if not any(t.get('name') == match for t in extracted_data['topics']):
|
||||
extracted_data['topics'].append({'name': match})
|
||||
|
||||
return extracted_data
|
||||
|
||||
except Exception as e:
|
||||
print(f"提取笔记数据时出错:{e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
|
||||
def print_note_data(note_data):
|
||||
"""
|
||||
格式化打印笔记数据
|
||||
|
||||
Args:
|
||||
note_data: 笔记数据字典
|
||||
"""
|
||||
if not note_data:
|
||||
print("没有可显示的数据")
|
||||
return
|
||||
|
||||
print("=" * 60)
|
||||
print("笔记信息")
|
||||
print("=" * 60)
|
||||
print(f"标题: {note_data.get('title')}")
|
||||
print()
|
||||
|
||||
print("描述:")
|
||||
desc = note_data.get('desc', '')
|
||||
if desc:
|
||||
print(desc)
|
||||
|
||||
print("图片列表:")
|
||||
images = note_data.get('images', [])
|
||||
print(f" 共 {len(images)} 张图片")
|
||||
print(images)
|
||||
|
||||
print("视频列表:")
|
||||
videos = note_data.get('videos', [])
|
||||
print(f" 共 {len(videos)} 个视频")
|
||||
print(videos)
|
||||
|
||||
print("话题列表:")
|
||||
topics = note_data.get('topics', [])
|
||||
print(f" 共 {len(topics)} 个话题")
|
||||
print(topics)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 解析HTML文件
|
||||
print("正在解析HTML文件...")
|
||||
note_data = parse_html_file('1.html')
|
||||
|
||||
if note_data:
|
||||
# 保存为JSON文件
|
||||
with open('note_data.json', 'w', encoding='utf-8') as f:
|
||||
json.dump(note_data, f, ensure_ascii=False, indent=2)
|
||||
print("数据已保存到 note_data.json")
|
||||
print()
|
||||
|
||||
# 打印数据(尝试处理编码问题)
|
||||
try:
|
||||
print_note_data(note_data)
|
||||
except Exception as e:
|
||||
print(f"打印数据时出错(但数据已保存到JSON文件): {e}")
|
||||
print("请查看 note_data.json 文件获取完整数据")
|
||||
else:
|
||||
print("解析失败,请检查HTML文件")
|
||||
24
pdd_gui.log
24
pdd_gui.log
@@ -1,24 +0,0 @@
|
||||
从Excel文件加载了 0 个任务
|
||||
已添加任务: C:/Users/27942/Desktop/image
|
||||
配置已保存,开始运行任务...
|
||||
开始任务:C:/Users/27942/Desktop/image
|
||||
程序关闭,配置已保存
|
||||
任务已停止:C:/Users/27942/Desktop/image
|
||||
任务队列已停止
|
||||
从Excel文件加载了 0 个任务
|
||||
已添加任务: C:/Users/27942/Desktop/image
|
||||
配置已保存,开始运行任务...
|
||||
开始任务:C:/Users/27942/Desktop/image
|
||||
正在停止任务队列...
|
||||
强制终止任务线程...
|
||||
任务线程已强制终止
|
||||
任务队列已停止
|
||||
从Excel文件加载了 0 个任务
|
||||
已添加任务: C:/Users/27942/Desktop/image
|
||||
配置已保存,开始运行任务...
|
||||
开始任务:C:/Users/27942/Desktop/image
|
||||
正在停止任务队列...
|
||||
强制终止任务线程...
|
||||
任务线程已强制终止
|
||||
任务队列已停止
|
||||
程序关闭,配置已保存
|
||||
@@ -1,15 +0,0 @@
|
||||
[
|
||||
{
|
||||
"user_id": "1050100241",
|
||||
"file_path": "C:/Users/27942/Desktop/image",
|
||||
"topics": "python-haha",
|
||||
"interval": "5",
|
||||
"creator_link": "https://www.xiaohongshu.com/explore/694d4fb6000000001e014195?xsec_token=ABdCdLrjMkmGbv623XZvEingO82fryJDFzzAXLgmYmark=&xsec_source=pc_user",
|
||||
"count": "1",
|
||||
"note": "",
|
||||
"time_start": null,
|
||||
"enabled": true,
|
||||
"status": "运行中",
|
||||
"last_run": "2026-01-16 11:18:35"
|
||||
}
|
||||
]
|
||||
@@ -1,15 +0,0 @@
|
||||
[
|
||||
{
|
||||
"user_id": "1050100241",
|
||||
"file_path": "C:/Users/27942/Desktop/image",
|
||||
"topics": "python-haha",
|
||||
"interval": "5",
|
||||
"creator_link": "https://www.xiaohongshu.com/explore/694d4fb6000000001e014195?xsec_token=ABdCdLrjMkmGbv623XZvEingO82fryJDFzzAXLgmYmark=&xsec_source=pc_user",
|
||||
"count": "1",
|
||||
"note": "",
|
||||
"time_start": null,
|
||||
"enabled": true,
|
||||
"status": "运行中",
|
||||
"last_run": "2026-01-16 11:18:35"
|
||||
}
|
||||
]
|
||||
218
test.py
218
test.py
@@ -1,218 +0,0 @@
|
||||
"""
|
||||
Django settings for django_lanyu project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 4.1.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/4.1/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = "django-insecure--+*v2+o0c*d7z3&+!z%&k^!b8w%1myflhkf4doh!12#$xo8)o="
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"corsheaders",
|
||||
"agent", # 经济人应用
|
||||
"user",
|
||||
"operate", # 运营应用
|
||||
"risk", # 风控
|
||||
'agent_manage' # 经纪人管理员
|
||||
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
# "django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"django_lanyu.middleware.JWTAuthenticationMiddleware",
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "django_lanyu.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
# "DIRS": [BASE_DIR / 'templates']
|
||||
# ,
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = "django_lanyu.wsgi.application"
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_RENDERER_CLASSES': [
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
],
|
||||
}
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases
|
||||
|
||||
# DATABASES = {
|
||||
# 'default': {
|
||||
# 'ENGINE': 'django.db.backends.mysql', # 替换为你的数据库引擎,比如 'django.db.engine.mysql'
|
||||
# 'NAME': 'lanyu', # 你的数据库名称
|
||||
# 'USER': 'root', # 数据库用户名
|
||||
# 'PASSWORD': '123456', # 数据库密码
|
||||
# 'HOST': '127.0.0.1', # 数据库主机地址
|
||||
# 'PORT': '3306', # 数据库端口
|
||||
# 'OPTIONS': {
|
||||
# 'charset': 'utf8mb4',
|
||||
# },
|
||||
# }
|
||||
# }
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql', # 替换为你的数据库引擎,比如 'django.db.engine.mysql'
|
||||
'NAME': 'lanyu', # 你的数据库名称
|
||||
'USER': 'lanyu', # 数据库用户名
|
||||
'PASSWORD': 'Ly123456.', # 数据库密码
|
||||
'HOST': '127.0.0.1', # 数据库主机地址
|
||||
'PORT': '3306', # 数据库端口
|
||||
'OPTIONS': {
|
||||
'charset': 'utf8mb4',
|
||||
},
|
||||
'POOL_OPTIONS': {
|
||||
'POOL_SIZE': 5, # 连接池的初始大小
|
||||
'MAX_OVERFLOW': 10, # 连接池允许的最大额外连接数
|
||||
'RECYCLE': 3600, # 连接的最大存活时间(秒),超过该时间连接将被回收
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': 'unique-snowflake',
|
||||
}
|
||||
}
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.1/howto/static-files/
|
||||
|
||||
STATIC_URL = "static/"
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
|
||||
CORS_ALLOWED_ORIGINS = [
|
||||
"http://192.168.0.221",
|
||||
"http://192.168.0.228",
|
||||
'http://your-frontend-url',
|
||||
|
||||
]
|
||||
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
CORS_ALLOW_ALL_ORIGINS = True
|
||||
|
||||
CORS_ALLOW_METHODS = [
|
||||
'DELETE',
|
||||
'GET',
|
||||
'OPTIONS',
|
||||
'PATCH',
|
||||
'POST',
|
||||
'PUT',
|
||||
]
|
||||
|
||||
CORS_ALLOW_HEADERS = [
|
||||
'accept',
|
||||
'accept-encoding',
|
||||
'authorization',
|
||||
'content-type',
|
||||
'dnt',
|
||||
'origin',
|
||||
'user-agent',
|
||||
'x-csrftoken',
|
||||
'x-requested-with',
|
||||
]
|
||||
|
||||
APPEND_SLASH = True
|
||||
|
||||
# 七牛云存储
|
||||
QINIU_ACCESS_KEY = 'CB8j8D9voknWUVendxZi4h-LERDfD0XU3IXtSeEu'
|
||||
QINIU_SECRET_KEY = 'I3uaom2fiWMBNZQpOIQCdi0N7x1V13hNJBfSmO0C' # 待修改
|
||||
QINIU_BUCKET_NAME = 'shuju9'
|
||||
QINIU_DOMAIN = 'lyamcn.com'
|
||||
# 使用七牛云作为默认文件存储后端
|
||||
DEFAULT_FILE_STORAGE = 'qiniu_storage.storage.QiniuStorage'
|
||||
|
||||
QINIU_STORAGE_OPTIONS = {
|
||||
'access_key': QINIU_ACCESS_KEY,
|
||||
'secret_key': QINIU_SECRET_KEY,
|
||||
'bucket_name': QINIU_BUCKET_NAME,
|
||||
'bucket_domain': QINIU_DOMAIN,
|
||||
}
|
||||
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
56
下载图片.py
56
下载图片.py
@@ -1,56 +0,0 @@
|
||||
import requests
|
||||
import os
|
||||
from urllib.parse import urlparse
|
||||
|
||||
def download_image(url, save_path=None):
|
||||
"""
|
||||
下载图片文件
|
||||
|
||||
Args:
|
||||
url: 图片URL
|
||||
save_path: 保存路径,如果为None则使用URL中的文件名
|
||||
"""
|
||||
# 设置请求头
|
||||
headers = {
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
|
||||
'Cache-Control': 'no-cache',
|
||||
'DNT': '1',
|
||||
'Pragma': 'no-cache',
|
||||
'Proxy-Connection': 'keep-alive',
|
||||
'Upgrade-Insecure-Requests': '1',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0'
|
||||
}
|
||||
|
||||
try:
|
||||
# 发送请求,verify=False 相当于 curl 的 --insecure
|
||||
response = requests.get(url, headers=headers, verify=False, timeout=30)
|
||||
response.raise_for_status() # 检查HTTP错误
|
||||
|
||||
# 如果没有指定保存路径,从URL中提取文件名
|
||||
if save_path is None:
|
||||
parsed_url = urlparse(url)
|
||||
filename = os.path.basename(parsed_url.path)
|
||||
# 如果URL中没有明确的文件名,使用默认名称
|
||||
if not filename or '.' not in filename:
|
||||
filename = 'downloaded_image.webp'
|
||||
save_path = filename
|
||||
|
||||
# 保存文件
|
||||
with open(save_path, 'wb') as f:
|
||||
f.write(response.content)
|
||||
|
||||
print(f"图片下载成功!保存路径: {save_path}")
|
||||
print(f"文件大小: {len(response.content)} 字节")
|
||||
return save_path
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"下载失败: {e}")
|
||||
return None
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 图片URL
|
||||
image_url = 'http://sns-webpic-qc.xhscdn.com/202601141654/fea93045d0792c459a13310ffd9e0cfb/notes_pre_post/1040g3k031r0eua5ng0104a0mknaoroirqoi9ju8!nd_dft_wlteh_webp_3'
|
||||
|
||||
# 下载图片
|
||||
download_image(image_url)
|
||||
4
下载文件.py
4
下载文件.py
@@ -1,4 +0,0 @@
|
||||
from DrissionPage import SessionPage
|
||||
|
||||
page = SessionPage()
|
||||
page.download('https://sns-video-hw.xhscdn.com/stream/110/258/01e6cd08be6e36ad010370019190eceaac_258.mp4')
|
||||
1109
自动化_gui.py
1109
自动化_gui.py
File diff suppressed because it is too large
Load Diff
39
自动化_gui.spec
39
自动化_gui.spec
@@ -1,39 +0,0 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['自动化_gui.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=['自动化_wrapper'],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name='自动化_gui',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=['0.webp'],
|
||||
)
|
||||
102
自动化_wrapper.py
102
自动化_wrapper.py
@@ -1,102 +0,0 @@
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from 自动化 import Pdd
|
||||
|
||||
|
||||
class PddRunner:
|
||||
def __init__(self, retries=1, retry_delay=5):
|
||||
self.retries = max(0, int(retries))
|
||||
self.retry_delay = max(0, int(retry_delay))
|
||||
self._stop_requested = False
|
||||
|
||||
def request_stop(self):
|
||||
"""请求停止任务"""
|
||||
self._stop_requested = True
|
||||
|
||||
def _normalize_time_start(self, time_start):
|
||||
if not time_start:
|
||||
return None
|
||||
if isinstance(time_start, datetime):
|
||||
return time_start.strftime("%Y-%m-%d %H:%M:%S")
|
||||
return str(time_start).strip() or None
|
||||
|
||||
def _cleanup(self, pdd):
|
||||
page = getattr(pdd, "page", None)
|
||||
if not page:
|
||||
return
|
||||
for method in ("quit", "close", "stop", "terminate"):
|
||||
func = getattr(page, method, None)
|
||||
if func:
|
||||
try:
|
||||
func()
|
||||
except Exception:
|
||||
pass
|
||||
break
|
||||
|
||||
def run(
|
||||
self,
|
||||
user_id,
|
||||
file_path,
|
||||
topics="",
|
||||
time_start=None,
|
||||
interval=None,
|
||||
creator_link=None,
|
||||
count=None,
|
||||
stop_callback=None,
|
||||
):
|
||||
self._stop_requested = False
|
||||
normalized_time = self._normalize_time_start(time_start)
|
||||
last_exc = None
|
||||
pdd = None
|
||||
def combined_stop():
|
||||
if self._stop_requested:
|
||||
return True
|
||||
if stop_callback:
|
||||
try:
|
||||
return bool(stop_callback())
|
||||
except Exception:
|
||||
return False
|
||||
return False
|
||||
for attempt in range(self.retries + 1):
|
||||
# 检查停止请求
|
||||
if combined_stop():
|
||||
return False, "已停止"
|
||||
|
||||
try:
|
||||
pdd = Pdd(
|
||||
user_id=user_id,
|
||||
file_path=file_path,
|
||||
topics=topics,
|
||||
time_start=normalized_time,
|
||||
interval=interval,
|
||||
creator_link=creator_link,
|
||||
count=count,
|
||||
)
|
||||
pdd.action(stop_callback=combined_stop)
|
||||
return True, "完成"
|
||||
except Exception as exc:
|
||||
# 检查是否是因为停止请求导致的异常
|
||||
if combined_stop():
|
||||
return False, "已停止"
|
||||
|
||||
last_exc = exc
|
||||
import traceback
|
||||
error_msg = f"{type(exc).__name__}: {str(exc)}"
|
||||
if attempt < self.retries:
|
||||
print(f"任务失败 (尝试 {attempt + 1}/{self.retries + 1}),{self.retry_delay}秒后重试: {error_msg}")
|
||||
# 在等待期间也检查停止请求
|
||||
for _ in range(self.retry_delay):
|
||||
if self._stop_requested or (stop_callback and stop_callback()):
|
||||
return False, "已停止"
|
||||
time.sleep(1)
|
||||
else:
|
||||
print(f"任务最终失败: {error_msg}")
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
if pdd:
|
||||
try:
|
||||
self._cleanup(pdd)
|
||||
except Exception as cleanup_exc:
|
||||
print(f"清理资源时出错: {cleanup_exc}")
|
||||
return False, f"失败:{last_exc}"
|
||||
Reference in New Issue
Block a user