下载地址:www.pan38.com/yun/share.p… 提取密码:1133
该工具包含三大核心模块:1)主发布系统实现小红书自动化登录和内容发布1421;2)智能图片处理模块支持批量排版和水印添加518;3)智能排期系统根据流量高峰自动优化发布时间69。使用时需配置ChromeDriver和必要的Python库,建议设置合理的发布间隔防止账号限流1317。
import os
import time
import random
import requests
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class XHSPoster:
def __init__(self):
self.chrome_options = Options()
self.chrome_options.add_argument("--headless")
self.chrome_options.add_argument("--disable-gpu")
self.driver = webdriver.Chrome(options=self.chrome_options)
self.wait = WebDriverWait(self.driver, 15)
def login(self, phone, password):
"""模拟小红书登录流程"""
self.driver.get("https://www.xiaohongshu.com")
login_btn = self.wait.until(
EC.element_to_be_clickable((By.XPATH, '//*[@id="app"]/div[1]/div[2]/div[2]'))
)
login_btn.click()
time.sleep(2)
# 输入手机号和密码
phone_input = self.driver.find_element(By.XPATH, '//input[@placeholder="请输入手机号"]')
phone_input.send_keys(phone)
pwd_input = self.driver.find_element(By.XPATH, '//input[@placeholder="请输入密码"]')
pwd_input.send_keys(password)
# 点击登录按钮
submit_btn = self.driver.find_element(By.XPATH, '//button[contains(text(),"登录")]')
submit_btn.click()
time.sleep(5)
def auto_layout(self, images, template=1):
"""自动排版功能"""
layouts = {
1: (3, 3), # 九宫格
2: (2, 4), # 两行四列
3: (1, 9) # 单行排列
}
rows, cols = layouts.get(template, (3, 3))
# 创建画布
img_width, img_height = 1080, 1080
canvas = Image.new('RGB', (img_width*cols, img_height*rows), (255, 255, 255))
# 填充图片
for i, img_path in enumerate(images[:rows*cols]):
img = Image.open(img_path)
img = img.resize((img_width, img_height))
row, col = divmod(i, cols)
canvas.paste(img, (col*img_width, row*img_height))
return canvas
def upload_note(self, title, content, images, tags, schedule_time=None):
"""发布笔记核心逻辑"""
# 进入发布页面
self.driver.get("https://creator.xiaohongshu.com/publish/publish")
time.sleep(3)
# 输入标题和内容
title_elem = self.wait.until(
EC.presence_of_element_located((By.XPATH, '//textarea[@placeholder="填写标题更容易获得推荐"]'))
)
title_elem.send_keys(title)
content_elem = self.driver.find_element(
By.XPATH, '//div[@role="textbox"]'
)
content_elem.send_keys(content)
# 上传图片
upload_btn = self.driver.find_element(
By.XPATH, '//input[@type="file"]'
)
for img in images:
upload_btn.send_keys(os.path.abspath(img))
time.sleep(1)
# 添加标签
tag_input = self.driver.find_element(
By.XPATH, '//input[@placeholder="添加标签"]'
)
for tag in tags:
tag_input.send_keys(tag)
time.sleep(0.5)
tag_input.send_keys("\n")
time.sleep(0.5)
# 设置定时发布
if schedule_time:
schedule_btn = self.driver.find_element(
By.XPATH, '//span[contains(text(),"定时发布")]'
)
schedule_btn.click()
time.sleep(1)
# 设置具体时间
time_input = self.driver.find_element(
By.XPATH, '//input[@placeholder="选择时间"]'
)
time_input.clear()
time_input.send_keys(schedule_time)
# 提交发布
submit_btn = self.driver.find_element(
By.XPATH, '//button[contains(text(),"发布")]'
)
submit_btn.click()
time.sleep(5)
def batch_upload(self, notes_data, interval=300):
"""批量上传主流程"""
for note in notes_data:
try:
print(f"正在处理笔记: {note['title']}")
# 自动排版图片
if len(note['images']) > 1:
layout_img = self.auto_layout(note['images'], note.get('layout', 1))
layout_path = f"temp_layout_{int(time.time())}.jpg"
layout_img.save(layout_path)
note['images'] = [layout_path]
# 执行上传
self.upload_note(
title=note['title'],
content=note['content'],
images=note['images'],
tags=note['tags'],
schedule_time=note.get('schedule_time')
)
print(f"笔记 {note['title']} 发布成功")
time.sleep(interval + random.randint(0, 60)) # 随机间隔防封
except Exception as e:
print(f"笔记 {note['title']} 发布失败: {str(e)}")
continue
def close(self):
self.driver.quit()
if __name__ == "__main__":
poster = XHSPoster()
# 模拟登录
poster.login("13800138000", "your_password")
# 准备批量数据
notes = [
{
"title": "夏日穿搭分享 | 这件连衣裙太显瘦了",
"content": "最近入手的这件连衣裙真的超级显瘦...",
"images": ["outfit1.jpg", "outfit2.jpg", "outfit3.jpg"],
"tags": ["穿搭", "显瘦", "连衣裙"],
"layout": 1,
"schedule_time": "10:00"
},
# 可添加更多笔记数据
]
# 执行批量上传
poster.batch_upload(notes, interval=600)
poster.close()
PIL import Image, ImageDraw, ImageFont
import numpy as np
import textwrap
class ImageProcessor:
@staticmethod
def add_watermark(image_path, text, output_path):
"""添加文字水印"""
img = Image.open(image_path)
draw = ImageDraw.Draw(img)
# 设置字体
try:
font = ImageFont.truetype("simhei.ttf", 40)
except:
font = ImageFont.load_default()
# 计算文字位置
text_width, text_height = draw.textsize(text, font)
x = img.width - text_width - 20
y = img.height - text_height - 20
# 添加文字
draw.text((x, y), text, fill=(255, 255, 255, 128), font=font)
img.save(output_path)
@staticmethod
def add_text_overlay(image_path, text, output_path, style=1):
"""添加文字封面"""
img = Image.open(image_path)
draw = ImageDraw.Draw(img)
# 样式配置
styles = {
1: {"font_size": 60, "color": (255, 255, 255), "bg_color": (0, 0, 0, 150)},
2: {"font_size": 80, "color": (255, 0, 0), "bg_color": None},
3: {"font_size": 50, "color": (0, 0, 0), "bg_color": (255, 255, 255, 180)}
}
config = styles.get(style, styles[1])
try:
font = ImageFont.truetype("simhei.ttf", config["font_size"])
except:
font = ImageFont.load_default()
# 文字自动换行
max_width = img.width * 0.8
avg_char_width = config["font_size"] * 0.6
max_chars_per_line = int(max_width / avg_char_width)
lines = textwrap.wrap(text, width=max_chars_per_line)
# 计算总高度
total_text_height = len(lines) * (config["font_size"] + 10)
y = (img.height - total_text_height) // 2
# 绘制背景框
if config["bg_color"]:
padding = 20
bg_width = img.width - 100
bg_height = total_text_height + padding * 2
bg_x = (img.width - bg_width) // 2
bg_y = y - padding
bg = Image.new('RGBA', (bg_width, bg_height), config["bg_color"])
img.paste(bg, (bg_x, bg_y), bg)
# 绘制每行文字
for line in lines:
text_width = draw.textsize(line, font)[0]
x = (img.width - text_width) // 2
draw.text((x, y), line, fill=config["color"], font=font)
y += config["font_size"] + 10
img.save(output_path)
@staticmethod
def batch_resize(images, target_size=(1080, 1080)):
"""批量调整图片尺寸"""
resized_images = []
for img_path in images:
img = Image.open(img_path)
img = img.resize(target_size, Image.LANCZOS)
output_path = f"resized_{os.path.basename(img_path)}"
img.save(output_path)
resized_images.append(output_path)
return resized_images
datetime
import json
import random
class ScheduleManager:
def __init__(self, config_file="schedule_config.json"):
self.config = self.load_config(config_file)
def load_config(self, file_path):
"""加载排期配置文件"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
return {
"peak_hours": ["10:00", "12:00", "19:00", "21:00"],
"min_interval": 1800,
"max_interval": 3600
}
def generate_schedule(self, num_posts, start_date=None):
"""生成智能发布时间表"""
if not start_date:
start_date = datetime.datetime.now()
schedule = []
for i in range(num_posts):
# 随机选择高峰时段或普通时段
if random.random() < 0.7: # 70%概率选择高峰时段
time_str = random.choice(self.config["peak_hours"])
hour, minute = map(int, time_str.split(":"))
else:
hour = random.randint(8, 23)
minute = random.randint(0, 59)
# 计算发布时间
delta_days = i // len(self.config["peak_hours"])
post_date = start_date + datetime.timedelta(days=delta_days)
post_time = post_date.replace(hour=hour, minute=minute, second=0)
# 确保时间间隔合理
if schedule and (post_time - schedule[-1]).total_seconds() < self.config["min_interval"]:
post_time = schedule[-1] + datetime.timedelta(
seconds=random.randint(self.config["min_interval"], self.config["max_interval"])
)
schedule.append(post_time)
return schedule
def save_schedule(self, schedule, output_file="post_schedule.json"):
"""保存发布时间表"""
formatted = [dt.strftime("%Y-%m-%d %H:%M") for dt in schedule]
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(formatted, f, ensure_ascii=False, indent=2)