使用DeepSeek+本地知识库,尝试从0到1搭建高度定制化工作流(自动化篇)

news2025/2/21 5:22:01
7.5. 配图生成
  • 目的:由于小红书发布文章要求图文格式,因此在生成文案的基础上,我们还需要生成图文搭配文案进行发布。

  • 原实现思路:

    • 起初我打算使用deepseek的文生图模型Janus进行本地部署生成,参考博客:Deepseek发布的Janus-Pro-1B初体验但后来尝试使用后发现Janus现阶段对于这类特定任务的生成图还不太能够胜任。以下是我尝试使用文案让Janus生成的图片:
    • 文案图
  • 现实现思路:

    • 当下普遍的方案是使用文案生成一段相关的html代码,再使用python中的自动化库来进行相应部分的截图,最后将截图与文案进行组合,形成图文格式。
  • 代码实现:

    • html生成:

      '''
      Author: yeffky
      Date: 2025-02-14 08:43:28
      LastEditTime: 2025-02-15 19:28:28
      '''
      import requests
      import json
      import os
      from datetime import datetime 
      
      def build_prompt(drafts):
          prompt = "根据下面的小红书文案,帮我生成一个html页面,包含小红书的封面(需要一个卡片状的封面,上面只需文案内容即可,需要吸引眼球),以及下方几个要点内容,要点内容和封面我希望制作成卡片形式,并且每一部分的div请为我附上属性id,id为'card1', 'card2', ...。要求符合小红书平台的图文要求规则以及平替风格,还要符合小红书平台的用户审美。回复只要给出代码即可,请不要添加多余表达" 
          return f"""{prompt} \n\n小红书文案:\n\n{drafts}"""
      
      
      def get_deepseek_response(prompt, api_key):
          url = "https://api.deepseek.com/chat/completions"
          headers = {
              "Authorization": f"Bearer {api_key}",
              'Content-Type': 'application/json',
              'Accept': 'application/json',
          }
          payload = json.dumps({
              "messages": [
                  {
                      "content": prompt,
                      "role": "user"
                  }
              ],
              "model": "deepseek-reasoner",
              "frequency_penalty": 0,
              "max_tokens": 2048,
              "presence_penalty": 0,
              "response_format": {
                  "type": "text"
              },
              "stop": None,
              "stream": False,
              "stream_options": None,
              "temperature": 1,
              "top_p": 1,
              "tools": None,
              "tool_choice": "none",
              "logprobs": False,
              "top_logprobs": None
          })
          response = None
          while not response:
              try:
                  print("发送请求")
                  response = requests.post(url, data=payload, headers=headers, timeout=200)
                  response.raise_for_status()
                  if not response.json():
                      response = None
              except requests.exceptions.RequestException as e:
                  print(f"请求失败:{str(e)},开始重试...")
                  response = None
          return response.json()['choices'][0]['message']['content']
      
      def generate_html():
          api_key = os.getenv("DEEPSEEK_API_KEY")
          today = datetime.now().strftime("%Y-%m-%d")
      
          file_path = "./xiaohongshu_drafts/小红书_推广文案_千战系列" + today +".txt"
          drafts = open(file_path, "r", encoding="utf-8").read()
          prompt = build_prompt(drafts=drafts)
      
          response = get_deepseek_response(prompt, api_key)
          print(response)
          with open('./pic_generate/pic.html', 'w', encoding='utf-8') as f:
              f.write(response)
      
    • 截图:

      '''
      Author: yeffky
      Date: 2025-02-14 09:41:09
      LastEditTime: 2025-02-15 10:44:51
      '''
      from playwright.sync_api import sync_playwright
      import time
      import re
      
      def generate_pic(url):
          # 启动浏览器
          player = sync_playwright().start()  # 初始化Playwright并启动
          chrome_driver = player.chromium  # 获取Chromium浏览器实例
          browser = chrome_driver.launch(headless=False)  # 启动浏览器,headless=False表示以非无头模式启动,即显示浏览器窗口
          context = browser.new_context()  # 创建一个新的浏览器上下文(类似于一个新的浏览器窗口)
          page = context.new_page()  # 在该上下文中创建一个新的页面(标签页)
          # 访问页面
          card_cnt = 0
          with(open('./pic_generate/pic.html', 'r', encoding='utf-8')) as f:
              page_content = f.read()
              card_cnt = len(re.findall(r'<div class="card" id="card\d+">', page_content))
          print(card_cnt)
          page.goto(url)  # 导航到指定的URL
          #  截取相关卡片的截图
          for i in range(1, card_cnt + 1):
              card_pic = page.query_selector(f"id=card{i}")  # 使用CSS选择器查找页面中的搜索按钮元素
              card_pic.screenshot(path=f"./pictures/card{i}.png")  # 对搜索按钮元素进行截图并保存为b.png
      
          # 停止访问
          context.close()  # 关闭浏览器上下文
          browser.close()  # 关闭浏览器
          player.stop()  # 停止Playwright
      
      if __name__ == '__main__':
          url = 'D:/Project/UUCrawl/Code/pic_generate/pic.html'
          generate_pic(url)
      
7.6. 自动化发布
  • 目的:将生成的图片和文案自动发布到小红书
  • 实现思路:
    • 1.使用python中的selenium库,模拟页面操作,登陆后需要将cookie保存下来,下次使用时直接读取cookie,避免重复登陆。同时保存一份token,每次调用登录时检查token是否过期,如未过期则无需登录操作。
    • 2.登录后,模拟页面操作前往发布页面,使用send_keys()方法输入标题和正文,使用click()方法点击发布按钮。
    • 参考开源项目:xhs_ai_publisher
  • 代码实现:
'''
Author: yeffky
Date: 2025-02-15 20:28:32
LastEditTime: 2025-02-17 14:08:45
'''
import sys
sys.path.append("./")
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from utils import line_process
import time
import json
import os

class XiaohongshuClient:
  
    def __init__(self):
        self.driver = webdriver.Chrome()
        self.wait = WebDriverWait(self.driver, 10)
        # 获取当前执行文件所在目录
        current_dir = os.path.dirname(os.path.abspath(__file__))
        self.token_file = os.path.join(current_dir, "xiaohongshu_token.json")
        self.cookies_file = os.path.join(current_dir, "xiaohongshu_cookies.json")
        self.token = self._load_token()
        self._load_cookies()
      
    def _load_token(self):
        """从文件加载token"""
        if os.path.exists(self.token_file):
            try:
                with open(self.token_file, 'r') as f:
                    token_data = json.load(f)
                    # 检查token是否过期
                    if token_data.get('expire_time', 0) > time.time():
                        return token_data.get('token')
            except:
                pass
        return None
      
    def _save_token(self, token):
        """保存token到文件"""
        token_data = {
            'token': token,
            # token有效期设为30天
            'expire_time': time.time() + 30 * 24 * 3600
        }
        with open(self.token_file, 'w') as f:
            json.dump(token_data, f)
  
    def _load_cookies(self):
        """从文件加载cookies"""
        if os.path.exists(self.cookies_file):
            try:
                with open(self.cookies_file, 'r') as f:
                    cookies = json.load(f)
                    self.driver.get("https://creator.xiaohongshu.com")
                    for cookie in cookies:
                        self.driver.add_cookie(cookie)
            except:
                pass
  
    def _save_cookies(self):
        """保存cookies到文件"""
        cookies = self.driver.get_cookies()
        with open(self.cookies_file, 'w') as f:
            json.dump(cookies, f)
          
    def login(self, phone, country_code="+86"):
        """登录小红书"""
        # 如果token有效则直接返回
        if self.token:
            return
      
        # 尝试加载cookies进行登录
        self.driver.get("https://creator.xiaohongshu.com/login")
        self._load_cookies()
        self.driver.refresh()
        time.sleep(3)
        # 检查是否已经登录
        if self.driver.current_url != "https://creator.xiaohongshu.com/login":
            print("使用cookies登录成功")
            self.token = self._load_token()
            self._save_cookies()
            time.sleep(2)
            return
        else:
            # 清理无效的cookies
            self.driver.delete_all_cookies()
            print("无效的cookies,已清理")
          
        # 如果cookies登录失败,则进行手动登录
        self.driver.get("https://creator.xiaohongshu.com/login")

        # 等待登录页面加载完成
        time.sleep(5)
        # 点击国家区号输入框
        country_input = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "input[placeholder='请选择选项']")))
        country_input.click()
        time.sleep(5)
      
        # 等待区号列表出现并点击+886
        # 等待区号列表出现并点击+86
        try:
            self.driver.find_element(By.XPATH, "/html/body/div[1]/div/div/div/div[2]/div[1]/div[2]/div/div/div/div/div/div[2]/div[1]/div[1]/div/div/div[1]/input").click()
            time.sleep(3)
            self.driver.find_element(By.XPATH, "/html/body/div[1]/div/div/div/div[2]/div[1]/div[2]/div/div/div/div/div/div[2]/div[1]/div[1]/div/div/div[1]/input").send_keys(country_code)
            time.sleep(3)
            # self.driver.find_element(By.XPATH, "/html/body/div[6]/div/div").click()
            # china_option = self.wait.until(EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'css-cqcgee')]//div[contains(text(), '+86')]")))
            time.sleep(5)
        except Exception as e:
            print("无法找到国家区号选项")
            print(e)
      
        # 定位手机号输入框
        phone_input = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "input[placeholder='手机号']")))
        phone_input.clear()
        phone_input.send_keys(phone)
      
        # 点击发送验证码按钮
        try:
            send_code_btn = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".css-uyobdj")))
            send_code_btn.click()
        except:
            # 尝试其他可能的选择器
            try:
                send_code_btn = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".css-1vfl29"))) 
                send_code_btn.click()
            except:
                try:
                    send_code_btn = self.wait.until(EC.element_to_be_clickable((By.XPATH, "//button[contains(text(),'发送验证码')]")))
                    send_code_btn.click()
                except:
                    print("无法找到发送验证码按钮")
      
        # 输入验证码
        verification_code = input("请输入验证码: ")
        code_input = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "input[placeholder='验证码']")))
        code_input.clear()
        code_input.send_keys(verification_code)
              
        # 点击登录按钮
        login_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".beer-login-btn")))
        login_button.click()
      
        # 等待登录成功,获取token
        time.sleep(3)
        # 保存cookies
        self._save_cookies()

        # 关闭浏览器
        # self.driver.quit()
          
        # print(f"获取到的token: {token}")
      
        # if token:
        #     self._save_token(token)
        #     self.token = token
        # else:
        #     print("未能获取到token")
      
    def post_article(self, title, content, images=None):
        """发布文章
        Args:
            title: 文章标题
            content: 文章内容
            images: 图片路径列表
        """
        # 如果token失效则重新登录
          
        # 设置token
        # self.driver.execute_script(f'localStorage.setItem("token", "{self.token}")')
        time.sleep(3)
        print("点击发布按钮")
        # 点击发布按钮
        publish_btn = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".btn.el-tooltip__trigger.el-tooltip__trigger")))
        publish_btn.click()

        # 如果是发布视频,则不操作这一步
        # 切换到上传图文
        time.sleep(3)
        tabs = self.driver.find_elements(By.CSS_SELECTOR, ".creator-tab")
        if len(tabs) > 1:
            tabs[1].click()
        time.sleep(3)
        # # 输入标题和内容
        # title_input = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".title-input")))
        # content_input = self.driver.find_element(By.CSS_SELECTOR, ".content-input")
      
        # title_input.send_keys(title)
        # content_input.send_keys(content)
      
        # 上传图片
        if images:
            upload_input = self.driver.find_element(By.CSS_SELECTOR,'input[type="file"]')
            # 将所有图片路径用\n连接成一个字符串一次性上传
            upload_input.send_keys('\n'.join(images))
            time.sleep(1)
        time.sleep(3)
      
        JS_ADD_TEXT_TO_INPUT = """
        var elm = arguments[0], txt = arguments[1];
        elm.value += txt;
        elm.dispatchEvent(new Event('change'));
        """
      
        title_input = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".d-text")))
        self.driver.execute_script(JS_ADD_TEXT_TO_INPUT, title_input, title)
        # title_input.send_keys(title)
      
        # Start of Selection
        # Start of Selection
        print(content)
        JS_ADD_TEXT_TO_P = """
        var elm = arguments[0], txt = arguments[1];
        elm.textContent = txt;
        """
        content_input = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".ql-editor")))
        p_element = content_input.find_element(By.CSS_SELECTOR, "p")
        print(p_element)
        self.driver.execute_script(JS_ADD_TEXT_TO_P, p_element, content)
        # content_input.send_keys(123)
        # 发布
        time.sleep(600)
        submit_btn = self.driver.find_element(By.CSS_SELECTOR, ".el-button.publishBtn")
        submit_btn.click()
      
    def close(self):
        """关闭浏览器"""
        self.driver.quit()
      
def post_article():
    poster = XiaohongshuClient()
    phone = open('./docs/phone.txt').read()
    poster.login(phone)
    print("登录成功")
    print("开始发布文章")
    print(os.getcwd())
    title = open('./xiaohongshu_drafts/小红书_推广文案_千战系列2025-02-15.txt', 'r', encoding='utf-8').readline()
    article = line_process.get_article('./xiaohongshu_drafts/小红书_推广文案_千战系列2025-02-15.txt')
    print(article)
    images = os.listdir('./pictures')
    images = map(lambda x: os.path.join(r"D:\Project\UUCrawl\Code\pictures", x), images)
    poster.post_article(title, article, images)
    poster.close()
7.7. 主程序
from crawler import ip_crawler, data_crawler
from analysis import data_analysis
from pic_generate import pic_generate, html_generate
from post import xiaohongshu_post
import file_handler

if __name__ == '__main__':
    url = 'D:/Project/UUCrawl/Code/pic_generate/pic.html'
    # 获取IP
    ip = ip_crawler.crawl_ip()
    # 获取数据
    data = data_crawler.crawl_data()
    
    # 数据分析
    data_analysis.analysis_data()
    
    file_handler.start_observer()
    
    # 生成html
    html_generate.generate_html()
    # 生成图片
    pic_generate.generate_pic(url)

    # 发布小红书
    xiaohongshu_post.post_article()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2300728.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

#渗透测试#批量漏洞挖掘#Apache Log4j反序列化命令执行漏洞

免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章读。 目录 Apache Log4j反序列化命令执行漏洞 一、…

HTTP FTP SMTP TELNET 应用协议

1. 标准和非标准的应用协议 标准应用协议&#xff1a; 由标准化组织&#xff08;如 IETF&#xff0c;Internet Engineering Task Force&#xff09;制定和维护&#xff0c;具有广泛的通用性和互操作性。这些协议遵循严格的规范和标准&#xff0c;不同的实现之间可以很好地进行…

百度搜索全面接入DeepSeek-R1满血版:AI与搜索的全新融合

不等了&#xff0c;就是现在&#xff01;百度搜索全量接入DeepSeek-R1满血版 百度搜索已正式全量接入DeepSeek-R1满血版&#xff0c;在宣布“将接入”仅过了24小时后。 就在宣布“将接入”仅24小时后&#xff0c;百度搜索 已正式全量接入 DeepSeek-R1满血版&#xff01;得益于…

nordic(nrf52832、nrf52840)如何使用SES(SEGGER Embedded Studio)编辑编译工程?

nordic官方例程中一般都会给出好几个不同的编译环境供用户选择&#xff0c;一般是 keil工程、armgcc工程、IAR工程、ses工程等。 一、segger embedded studio如何添加工程.h头文件&#xff1f; 1)首先打开options 2&#xff09;下拉选中common 3&#xff09;找到common下的Pre…

LabVIEW利用CANopen的Batch SDO写入

本示例展示了如何通过CANopen协议向设备写入Batch SDO&#xff08;批量服务数据对象&#xff09;。Batch SDO允许用户在一次操作中配置多个参数&#xff0c;适用于设备的批量配置和参数设置。此方法能够简化多个参数的写入过程&#xff0c;提高设备管理效率。 主要步骤&#xf…

python旅游推荐系统+爬虫+可视化(协同过滤算法)

✅️基于用户的协同过滤算法 ✅️有后台管理 ✅️2w多数据集 这个旅游数据分析推荐系统采用了Python语言、Django框架、MySQL数据库、requests库进行网络爬虫开发、机器学习中的协同过滤算法、ECharts数据可视化技术&#xff0c;以实现从网站抓取旅游数据、个性化推荐和直观展…

【弹性计算】IaaS 和 PaaS 类计算产品

《弹性计算产品》系列&#xff0c;共包含以下文章&#xff1a; 云服务器&#xff1a;实例、存储、网络、镜像、快照容器、裸金属云上运维IaaS 和 PaaS 类计算产品 &#x1f60a; 如果您觉得这篇文章有用 ✔️ 的话&#xff0c;请给博主一个一键三连 &#x1f680;&#x1f680…

视频转序列帧

视频转序列帧 介绍操作总结 介绍 这篇文章不是单独讲视频转序列帧所有的方法&#xff0c;这里是针对我后面要做序列帧动画优化的一个工具篇幅。这里我用的premiere Pro 2020下面会讲方法简称pr。 操作 打开pr点击新建项目 输入名称点击确认 将需要转换的视频导入到媒体浏览…

LLM有哪些可控超参数

LLM有哪些可控超参数 目录 LLM有哪些可控超参数生成控制类采样相关类推理优化类惩罚类其他类计算资源与批量处理类上下文与Token相关类内存相关类生成控制类 以流式返回对话响应:指模型在生成回复时,是否以逐字或逐句的流式方式返回给用户,而不是等全部生成完再返回,能提升…

算法分析—— 《归并排序》

《排序数组》 题目描述&#xff1a; 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 你必须在 不使用任何内置函数 的情况下解决问题&#xff0c;时间复杂度为 O(nlog(n))&#xff0c;并且空间复杂度尽可能小。 示例 1&#xff1a; 输入&#xff1a;nums [5,2…

SpringBoot启动时报错:cannot use an unresolved DNS server address: I:53

报错如下&#xff1a; 2025-02-17 13:59:41.374 [main] ERROR org.springframework.boot.SpringApplication:835 - Application run failed org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name mySwaggerResourceProvider def…

AI进展不止于基准:深度解析Grok 3的局限

基准测试长期以来一直是AI评估的基石,但任何认真的AI科学家都知道它们是可以被“游戏化”的。 我曾经详细写过这个问题,甚至LMsys也不得不调整其盲测格式——将Grok 3用不同的标签代替,而不仅仅是隐藏品牌——以减少品牌偏见。 高能力AI,尤其是像GPT-4级别的模型,或那些依…

Miniconda + VSCode 的Python环境搭建

目录&#xff1a; 安装 VScode 安装 miniconda 在VScode 使用conda虚拟环境 运行Python程序 1.安装 vscode 编辑器 官网链接&#xff1a;Visual Studio Code - Code Editing. Redefined 下载得到&#xff1a;&#xff0c;双击安装。 安装成功…

防御保护选路练习

拓扑 配置 IP的基本配置 r2 [R2]int g0/0/0 [R2-GigabitEthernet0/0/0]ip add 12.0.0.2 255.255.255.0 [R2]int g0/0/2 [R2-GigabitEthernet0/0/2]ip add 210.1.1.254 255.255.255.0 [R2-GigabitEthernet0/0/2]int g0/0/1 [R2-GigabitEthernet0/0/1]ip add 200.1.1.254 255.…

AI性能极致体验:通过阿里云平台高效调用满血版DeepSeek-R1模型

前言 解决方案链接&#xff1a; https://www.aliyun.com/solution/tech-solution/deepseek-r1-for-platforms?utm_contentg_1000401616 DeepSeek是近期爆火的开源大语言模型&#xff08;LLM&#xff09;&#xff0c;凭借其强大的模型训练和推理能力&#xff0c;受到越来越多…

Windows本地部署DeepSeek

文章目录 一、准备工作1、准备服务器2、准备APP 二、部署deepseek-r11、脚本部署2、脚本部署 三、ChatBox集成 一、准备工作 1、准备服务器 本案例使用Windows电脑 2、准备APP Download Ollama Download Chatbox 二、部署deepseek-r1 1、脚本部署 双击安装完Ollama,默认…

力扣高频sql 50题(基础版) :NULL, 表连接,子查询,case when和avg的结合

NULL的处理 nvl(字段,num) 和数字进行比较需要先使用nvl(字段,num)函数处理空值 思路: 没有被id 2 的客户推荐>> 过滤条件 referee_id !2 没有被id 2 的客户推荐>>被其他客户推荐, 但是也有可能没有被任何客户推荐>>NULL 考点: NULL是 不一个具体的数…

C#中File.Copy方法的参数overwrite取false和true的区别

当调用 System.IO.File.Copy 方法时&#xff0c;第三个参数 overwrite 控制着如果目标位置已经存在同名文件的情况下如何处理。 1、当 overwrite 设置为 true 在这种情况下&#xff0c;即使目标路径下已经有相同名称的文件&#xff0c;该方法也会无条件地覆盖现有的文件。这不…

用promptfoo做大模型安全性测评

1. 引入 promptfoo 是一款专为大模型安全测试打造的强大工具。它能通过红队测试、渗透测试以及漏洞扫描等方式&#xff0c;对各类大模型展开深度安全评估&#xff0c;全面检测模型在不同场景下的安全性。 2. 运行promptfoo的过程 安装nodejs 用npm安装promptfoo npm insta…

用STC-ISP写延时函数

若想写出自己可以定义时长的延时函数&#xff0c;需要重新生成一个1ms的延时函数并稍加修改。 STC-ISP生成的1ms的延时函数代码如下&#xff1a; void Delay1ms(void) //12.000MHz {unsigned char data i, j;i 2;j 239;do{while (--j);} while (--i); }将上述代码改为可自定…