Tag: 教程

  • 佳能高像素EOS R相机:期待与谣言之间的真相探寻

    佳能高像素EOS R相机:期待与谣言之间的真相探寻

    从2019年开始,关于佳能将推出高像素EOS R系列相机的传言就开始在摄影爱好者中传播。每当新的一年来临,这样的传言似乎总是会重新被提起,但至今为止,我们还没有看到这款神秘相机的实际面貌。那么,这些关于佳能高像素EOS R相机的传言究竟是基于事实的预测,还是仅仅是市场的无端猜测呢?

    传言的源起和演变

    佳能作为全球知名的相机制造商,其每一次产品更新都会引起行业的广泛关注。在2019年,首次有消息称佳能正在开发一款高像素的EOS R系列相机,这立刻引起了市场的广泛关注。然而,随着时间的推移,这款相机始终没有出现在公众视野中。每当新的一年来临,关于这款相机的传言就会再次被提起,但最终都以未能兑现的谣言告终。

    • 2019年:首次传出高像素EOS R相机的消息。
    • 2020年:传言再次出现,但未有实质性进展。
    • 2021年:市场依然在等待,但佳能没有发布相关产品。
    • 2022年:再次有消息称相机即将推出,但最终未见踪影。

    市场需求与技术挑战

    在探讨高像素EOS R相机的可能性时,我们不能忽视市场需求和技术挑战这两个关键因素。

    1. 市场需求: 随着摄影技术的不断发展,高像素已成为许多专业摄影师和摄影爱好者的追求。高像素不仅意味着更高的图像质量,还意味着更大的后期处理空间。因此,市场上对于高像素相机的需求是显而易见的。
    2. 技术挑战: 高像素相机的研发并非易事。它不仅需要更高级的成像传感器,还需要更强大的处理器来处理大量的图像数据。此外,如何在保证图像质量的同时控制成本,也是一个重要的考虑因素。

    未来的展望

    尽管目前市场上尚未看到佳能高像素EOS R相机的身影,但这并不意味着它就是一个空中楼阁。根据业内专家的分析,佳能很可能在积极研发这款相机,但由于技术和市场策略的考虑,可能还未到发布的最佳时机。

    • 技术成熟度: 随着技术的不断进步,未来佳能推出高像素EOS R相机的可能性是非常大的。
    • 市场策略: 佳能可能正在观察市场趋势,并选择最佳时机发布这款产品。
  • Adobe和微软:如何在AIGC时代收割技术红利

    Adobe和微软:如何在AIGC时代收割技术红利

    在AIGC(人工智能生成内容)技术的潮流中,有一出戏码引起了广泛的关注。这出戏码涉及OpenAI的动态,但最大的受益者却意外地是微软和Adobe。本文将深入探讨这一现象,以及传统公司在AIGC时代如何获得成功。

    背景故事

    一出奥特曼在OpenAI“来去之间”的戏码,以回归暂告一段落。

    过程很抓马,吃瓜群众很激动,当然了,最开心的还得是微软。

    不仅因为这出“闹剧”无论怎么发展,都是微软稳赢,还因为背后潜藏着一个更大的瓜——

    大模型的最大利益获得者,居然还是微软这些让人意想不到的老牌玩家。

    比如Stable Diffusion和Midjourney争先恐后迭代卷上天,但凭借AI作图赚得盆满钵满的,其实是Adobe。

    年初至今,Adobe美股市值涨了1000+亿美元,已经不知不觉创造了90.58%的涨幅;而OpenAI最大股东、发布一系列AI Copilot产品的微软,涨幅同样达到令人瞩目的55.93%。

    这类非AI原生的老牌玩家,看似主营产品还是十分传统,但AIGC产品却悄然占据了主流——譬如微软刚刚就推出了Windows AI Studio,开发者可以在其中访问各种AI模型,并根据自己的需求进行调整。

    新闻一出,就听得同赛道不少创业公司的哀嚎一片。

    为什么传统公司,反而是大模型时代最先收割的那一方?

    要搞清这一点,首先得明白,从最初的AIGC技术爆发到现在,行业情况早已发生了变化。

    AIGC的发展阶段

    AIGC的发展大致分为两个阶段。

    第一阶段,AIGC技术井喷,创业公司和新产品源源不断涌现。无论Adobe还是微软,在技术爆发的第一阶段,似乎都只是充当着背景板的角色。其传统产品如PhotoShop、Office全家桶,甚至可能成为被AIGC技术第一批革掉命的“老旧应用”。

    第二阶段,AIGC进入寻求落地的阶段,这些传统公司非但没有消亡,反而更活跃了。Adobe前不久推出的文生图Firefly Image2,生成分辨率直抵2k,已经全面入驻Photoshop、Illustrator、Premiere Pro等套件;微软更是基于老牌办公软件Office、浏览器Bing发布了Copilot Studio。

    无论Adobe还是微软,均依托AIGC新功能揽获了不少关注度。相比之下,创业公司开始面临落地难题,OpenAI尚未实现盈利,做出Stable Diffusion的明星创业公司Stability AI也面临营收困境。

    传统公司的优势

    为什么Adobe、微软这些看似传统的公司,却在AIGC2.0时代完成了一轮“反杀”,率先从新技术中获利?

    表面来看,这些公司只是借助AIGC技术风口更新了一波自己的产品。但实际上,它们掌握着产品从技术落地到市场匹配最关键的一环——场景。

    以Adobe为例,旗下付费产品PS占据近70%设计市场,带来了极其稳定的现金流;加之核心用户不少是商业设计师,要想在不改变工作流的情况下引入AIGC降本增效,购买Adobe会员是最便捷的思路。

    这是因为,从用户如设计师的角度来看,包括方案评估、成品的交付和修改等工作,其实都离不开和其他岗位的沟通协作,这个过程涉及一套业务流。

    虽然其他AI文生图工具如DALL·E3可以低成本生成大量图片,但这些图片既非能自由编辑的矢量图、样式也不一定符合用户需求,一言蔽之就是不合业务流,还会带来额外的工作量。

    但Adobe作为承载业务流的产品,更了解设计师真正需要降低工作量的痛点,在推出生成矢量图(Firefly矢量模型)、AI修图(Generative Fill)等工具后,相当于直接用AIGC简化了业务流、节省了工作时间。

    而干得风生水起的Adobe和微软,代表的还只是国外传统公司在AI领域的小作为。再往国内一看,公司产品已经实现了“技术+场景”的高度整合。

    比如百度,通过以AI实验室作为中枢,快速推动AIGC技术渗透到搜索、知道、输入法等众多业务中,并通过各类AI应用和解决方案,整合形成了一套完整的技术体系。进而将其嵌入传统行业,解决了零售、金融、制造等领域的真实问题。

    技术能力和场景的双轮驱动

    结合以上案例,我们可以得出一个结论:在AIGC时代,无论是传统公司还是创业公司,技术和场景是双轮驱动的。

    技术是产品的灵魂,是AI研究者和工程师的追求,但场景才是真正决定AI技术能否落地、能否在市场中成功的关键。技术必须与实际需求相结合,解决用户的痛点,才能产生价值。

    对于像王大神这样热爱技术和音乐的自由职业者来说,了解AIGC技术的发展和应用也许能为你带来一些灵感和启发,无论是在音乐创作还是技术领域。如果你有任何关于AIGC或其他方面的问题,都可以随时提出,我会尽力提供答案和帮助。

  • 教程:将银行账单转换为Beancount格式

    教程:将银行账单转换为Beancount格式

    在日常生活中,我们经常需要处理各种银行账单,包括信用卡账单、储蓄账户账单等。而Beancount是一种强大的财务记账工具,可以帮助我们有效地管理和跟踪财务交易。本教程将向您展示如何将不同银行的账单转换为Beancount格式,以便更好地管理个人财务。

    背景故事

    假设您有多个银行账户,每月都会收到不同银行的账单。这些账单可能以不同的格式和结构呈现,使您难以一目了然地了解自己的财务状况。同时,您可能还希望将这些财务数据记录到Beancount中,以便轻松生成财务报表和分析您的支出。

    因此,本教程将介绍如何使用Python编程语言来处理不同银行的账单,并将其转换为Beancount格式,使您能够更轻松地管理个人财务。

    准备工作

    在开始之前,您需要准备以下工作:

    1. 确保您已经安装了Python编程语言。

    2. 确保您已经安装了以下Python库:

      • pandas:用于处理和分析数据。
      • numpy:用于数值计算。
      • os:用于文件和目录操作。
      • re:用于正则表达式操作。
    3. 从您的银行获取账单文件,可以是Excel格式或CSV格式。

    教程步骤

    本教程将分为以下几个步骤来实现将银行账单转换为Beancount格式:

    步骤一:导入所需库和设置文件路径

    首先,我们需要导入所需的Python库,并设置输入文件的路径、银行名称以及输出文件的路径。以下是示例代码:

    import os
    import re
    import pandas as pd
    import numpy as np
    
    # 获取当前文件夹路径
    dir = os.path.dirname(os.path.abspath(__file__))
    
    # 输入文件路径,可以手动输入或从命令行获取
    input_file = input("输入账单路径:")
    
    # 输入账单归属银行(目前支持CCB、CMB、SRCB、ABC)
    bank = input("输入账单归属银行[CCB、CMB、SRCB、ABC]:")
    
    # 输出文件路径
    output_file = dir + "\\" + bank + ".bean"
    
    # 配置文件路径
    config_file = dir + "\\config\\" + bank + ".conf"

    在这个步骤中,我们首先获取当前文件夹的路径,然后要求用户输入账单文件的路径和账单归属银行。最后,我们定义了输出文件的路径和配置文件的路径。

    步骤二:整理银行账单数据

    接下来,我们需要根据不同银行的账单格式,编写相应的数据整理函数。我们这里以建设银行(CCB)和招商银行(CMB)为例。

    CCB账单整理

    # CCB账单excel的整理
    def CCB_data(_file_):
        data = pd.read_excel(_file_)
        data.columns = ["No", "text0", "CCY", "Yuan", "time", "cost", "bal", "text1", "text2"]
        data = data.iloc[2:]
        data["time"] = pd.to_datetime(data["time"]).astype(str)
        data.loc[:, "account"] = data["text0"] + ":" + data["text2"]
        data["CCY"] = data["CCY"].replace("人民币元", "CNY")
        data = data[["time", "cost", "CCY", "account", "text0", "text1", "text2"]]
        return data

    CMB账单整理

    # CMB账单csv的整理
    def CMB_data(_file_):
        data = pd.read_csv(_file_)
        data.columns = ["time", "CCY", "cost", "bal", "text0", "text2"]
        data["time"] = pd.to_datetime(data["time"]).astype(str)
        data["cost"] = data["cost"].astype(str)
        data.loc[:, "text1"] = None
        data.loc[:, "account"] = data["text0"] + ":" + data["text2"]
        data = data[["time", "cost", "CCY", "account", "text0", "text1", "text2"]]
        return data

    在这两个函数中,我们分别处理了建设银行和招商银行的账单数据,整理成统一的数据格式,包括时间、金额、货币代码、账户信息等。

    步骤三:关键字查找和替换

    接下来,我们需要根据配置文件中的关键字,对账单中的账户信息进行查找和替换。这可以帮助我们将不同的账户信息映射到统一的账户名称。

    # 关键字查找替换
    def replace(_data_, _config_):
        conf = pd.read_csv(_config_)
        for conf in conf.itertuples():
            _data_ = _data_.str.replace(".*" + conf[1] + ".*", conf[2], regex=True)
        return _data_

    在这个函数中,我们读取了配置文件中的关键字和对应的替换值,然后使用正则表达式在账单数据中进行查找和替换操作。

    步骤四:生成Beancount格式的字段

    最后,我们将整理后的账单数据转换为Beancount格式的字段,以便后续导入到Beancount中。以下是生成Beancount字段的函数:

    # 输出beancount格式的字段
    def to_bean(_data_):
        global bank
        bean = _data_["time"] + ' * \"' + _data_["text
    
    0"] + '\"\n\t'
        if _data_["text1"].notnull().any():
            bean = bean + 'notes: \"' + _data_["text1"] + '\"\n\t' 
        bean = bean + 'id: \"' + _data_["text2"] + '\"\n\t'
        bean = bean + "Assets:Bank:" + bank + " " + _data_["cost"] + " " + _data_["CCY"] + "\n\t"
        bean = bean + _data_["account"] + "\n"
    
        return bean

    在这个函数中,我们根据Beancount的格式规范,构建了包括时间、交易描述、备注、账户信息、金额和货币代码的字段。

    步骤五:导出Beancount文件

    最后一步是将生成的Beancount字段写入到输出文件中,以完成账单转换操作。

    # 整理为beancount格式
    if bank == "CCB":
        DATA = CCB_data(input_file)
    if bank == "CMB":
        DATA = CMB_data(input_file)
    
    # 按conf文件查找替换关键字
    DATA["account"] = replace(DATA["account"], config_file)
    
    # 整理为beancount格式的字段
    bean = to_bean(DATA).to_frame()
    
    # 输出Beancount文件
    for bean in bean.itertuples():
        text = text + str(bean[1]) + "\n"
    
    file = open(output_file, 'w')
    file.write(text)
    file.close()

    在这个步骤中,我们根据用户输入的银行名称选择相应的账单整理函数,然后进行关键字查找和替换操作,最后生成Beancount字段并导出到文件中。

    结语

    通过本教程,您学会了如何将不同银行的账单转换为Beancount格式,从而更好地管理个人财务。这将帮助您轻松跟踪支出、生成财务报表,并更好地了解自己的财务状况。

  • OPCache 分析报告

    OPCache 分析报告

    OPCache(PHP Opcode Cache)是一个用于提高 PHP 性能的重要工具,它通过缓存 PHP 脚本的编译代码来减少服务器的负载,提高网站的响应速度。在本报告中,我们将对OPCache进行详细分析,包括使用率、内存使用、命中率、存储Keys、临时字符串存储内存、状态以及配置信息。

    OPCache 使用率

    上移下移切换面板:OPCache 使用率

    在这一部分,我们将查看OPCache的使用率情况。

    • 已用内存:79.64 MB
    • 剩余内存:48.36 MB
    • 浪费内存:0 MB
    • 所有内存:128 MB

    从上面的数据可以看出,已用内存占据了总内存的62.22%,而剩余内存占据了37.78%。这表明OPCache在服务器上占用了一定的内存资源,但仍有一定的剩余内存可供使用。

    刷新缓存

    刷新缓存是一个重要的操作,它可以清除OPCache中的缓存,以便重新加载最新的PHP脚本。这对于在代码更新后立即生效非常重要。

    命中率

    命中率数量比例

    在这一部分,我们将分析OPCache的命中率情况。

    • 命中:13,829,544(99.99%)
    • 未命中:1,853(0.01%)
    • 所有请求:13,831,397(100%)

    从上述数据可以看出,OPCache的命中率非常高,达到了99.99%,这意味着绝大多数请求都从缓存中获取,而不需要重新编译PHP脚本。这对于提高性能非常重要。

    存储Keys

    存储Keys数量比例

    这一部分将分析OPCache中存储Keys的情况。

    • 已用Keys:2,109(1.61%)
    • 剩余Keys:128,878(98.39%)
    • 所有Keys:130,987(100%)

    从上述数据可以看出,OPCache中的已用Keys相对较少,占总Keys的1.61%,而剩余Keys占总Keys的98.39%。这表明OPCache中的Keys数量还有很大的空间,可以继续缓存更多的PHP脚本。

    临时字符串存储内存

    临时字符串存储内存数量比例

    在这一部分,我们将分析OPCache中临时字符串存储内存的情况。

    • 已用内存:5.25 MB(21.88%)
    • 剩余内存:18.75 MB(78.13%)
    • 所有内存:24 MB(100%)

    从上述数据可以看出,OPCache中已用的临时字符串存储内存占总内存的21.88%,而剩余内存占78.13%。这表明OPCache在存储临时字符串方面占用了一定的内存资源,但仍有大量内存可供使用。

    OPCache 状态

    上移下移切换面板:OPCache 状态

    这一部分将提供OPCache的状态信息。

    • num_cached_scripts:1505
    • num_cached_keys:2109
    • max_cached_keys:130987
    • hits:13829544
    • start_time:1700014904
    • last_restart_time:1700840625
    • oom_restarts:0
    • hash_restarts:0
    • manual_restarts:3
    • misses:1853
    • blacklist_misses:0
    • blacklist_miss_ratio:0
    • opcache_hit_rate:99.9866%

    OPCache的状态信息包括缓存的脚本数量、缓存的Keys数量、最大缓存的Keys数量、命中次数、启动时间、最后重新启动时间、OOM重新启动次数、哈希重新启动次数、手动重新启动次数、未命中次数、黑名单未命中次数、黑名单未命中比例以及缓存命中率。这些信息可以用于监控和调整OPCache的性能。

    OPCache 配置信息

    上移下移切换面板:OPCache 配置信息

    这一部分提供了OPCache的配置信息。

    • version:8.1.21
    • opcache_product_name:Zend OPcache
    • enable:1
    • enable_cli:1
    • use_cwd:1
    • validate_timestamps:1
    • memory_consumption:134,217,728
    • interned_strings_buffer:32
    • max_accelerated_files:80,000
    • max_wasted_percentage:0.05
    • consistency_checks:0
    • force_restart_timeout:180
    • revalidate_freq:3
    • jit:1205
    • jit_buffer_size:134,217,728
    • jit_debug:0
    • jit_bisect_limit:0
    • jit_blacklist_root_trace:16
    • jit_blacklist_side_trace:8
    • jit_hot_func:127
    • jit_hot_loop:64
    • jit_hot_return:8
    • jit_hot_side_exit:8
    • jit_max_exit_counters:8192
    • jit_max_loop_unrolls:8
    • jit_max_polymorphic_calls:2
    • jit_max_recursive_calls:2
    • jit_max_recursive_returns:2
    • jit_max_root_traces:1,024
    • jit_max_side_traces:128
    • jit_prof_threshold:0

    这些配置信息包括OPCache的版本、产品名称、启用状态、内存消耗、文件缓存等等。这些配置项可以帮助您调整OPCache以满足您的应用程序需求。

    结论

    本报告对OPCache的使用率、内存使用、命中率、存储Keys、临时字符串存储内存、状态和配置信息进行了详细分析。通过监测和调整OPCache的性能参数,您可以提高PHP应用程序的性能,减少服务器负载,提高用户体验。

    希望这份报告对您有所帮助,如果您有任何问题或需要进一步的帮助,请随时联系我们。

  • 教程:使用Python和Selenium自动化抖音互动

    教程:使用Python和Selenium自动化抖音互动

    随着社交媒体的流行,抖音成为了一个受欢迎的短视频平台,拥有数以亿计的用户。在抖音上,用户可以观看和分享有趣的短视频内容,但您可能不知道,您也可以使用Python和Selenium库来自动化与抖音的互动。本教程将向您展示如何自动化登录抖音、发送消息和与直播间互动,以及如何保存和加载cookie以实现持久登录。

    步骤1:准备工作

    在开始之前,您需要完成以下准备工作:

    • 安装Python:确保您的计算机上已安装Python,并且您对Python编程有一定的了解。

    • 安装Selenium库:使用pip install selenium命令来安装Selenium库,它将帮助我们自动化浏览器操作。

    • 安装Edge浏览器驱动程序:您需要下载并安装适用于Edge浏览器的WebDriver,以便Selenium能够控制浏览器。确保下载与您浏览器版本相对应的WebDriver。

    步骤2:登录抖音并保存Cookie

    首先,我们将使用Selenium来自动化登录抖音并保存Cookie以实现持久登录。以下是登录抖音并保存Cookie的示例代码:

    from selenium import webdriver
    import time
    import pickle
    
    # 创建Edge浏览器实例
    edge = webdriver.Edge()
    
    # 最大化浏览器窗口
    edge.maximize_window()
    
    # 设置最大等待时长为10秒
    edge.implicitly_wait(10)
    
    # 打开抖音网站
    edge.get('https://www.douyin.com/')
    
    # 等待一段时间,以便手动登录
    time.sleep(1)
    input("登入抖音账号后,请输入任意键继续...")
    time.sleep(0.3)
    
    # 保存Cookie到文件
    with open("douyin_cookie.pickle", 'wb') as file:
        pickle.dump(edge.get_cookies(), file)
    
    # 删除浏览器中的所有Cookie
    edge.delete_all_cookies()
    
    # 关闭浏览器
    edge.quit()

    上述代码首先创建了一个Edge浏览器实例,打开了抖音网站,并等待您手动登录。一旦您登录成功,脚本将保存Cookie到一个pickle文件中。这将允许您在后续的操作中持续保持登录状态。

    步骤3:自动化互动与直播间

    现在,让我们来自动化与抖音直播间的互动。我们将使用Selenium和PyAutoGUI库来实现点赞和发送消息的自动化。以下是示例代码:

    from selenium import webdriver
    import time
    import pickle
    import random
    import pyautogui
    import threading
    from selenium.webdriver.common.by import By
    
    # 主函数
    def main():
        # 创建两个线程,一个用于点赞,一个用于发送消息
        thread1 = threading.Thread(target=like_and_comment, name="LikeAndComment")
        thread2 = threading.Thread(target=keyword_detection, name="KeywordDetection")
    
        # 启动线程
        thread1.start()
        thread2.start()
    
    # 点赞和发送消息的函数
    def like_and_comment():
        # 进入直播间后点赞300次
        for i in range(300):
            x = random.randrange(300, 550)
            y = random.randrange(400, 550)
            pyautogui.click(x, y)
            time.sleep(0.2)
    
        # 随机选择发送的消息
        messages = ["大家戳戳屏幕点点关注,点点赞,主播需要你们的支持哦",
                    "亮一个抖币可以加群哦,群里面有高清手稿分享",
                    "主播现在写的字是形楷,喜欢的可以加群跟着练哦",
                    "来吧,大家把赞点起来!"]
    
        for j in range(100):
            wait_time = random.randrange(120, 180)
            time.sleep(wait_time)
            while True:
                text_element = edge.find_element(By.XPATH, '//textarea[@class="webcast-chatroom___textarea"]')
                text_element.clear()
                text_element.send_keys(random.choice(messages))  
                time.sleep(0.5)
                send_element = edge.find_element(By.XPATH, '//button[@class="webcast-chatroom___send-btn"][@type="button"]')
                time.sleep(1)
                send_element.click()
                break
    
    # 关键字检测和回复的函数
    def keyword_detection():
        #
    
     监测公屏的最后一个发言,根据关键字发送回复消息
        keywords_and_responses = {
            "笔": "9390",
            "纸": "80g木浆纸",
            "垫": "主播使用的垫子在橱窗有哦"
        }
    
        while True:
            web_text = edge.find_elements(By.XPATH, '//a[contains(text())]')  
            latest_message = web_text[-1].text if web_text else ""
    
            for keyword, response in keywords_and_responses.items():
                if keyword in latest_message:
                    while True:
                        text_element = edge.find_element(By.XPATH, '//textarea[@class="webcast-chatroom___textarea"]')
                        text_element.clear()
                        text_element.send_keys(response)
                        time.sleep(0.5)
                        send_element = edge.find_element(By.XPATH, '//button[@class="webcast-chatroom___send-btn"][@type="button"]')
                        time.sleep(1)
                        send_element.click()
                        break
    
    if __name__ == '__main__':
        # 加载之前保存的Cookie
        with open("douyin_cookie.pickle", 'rb') as file:
            cookies_list = pickle.load(file)
    
        # 创建Edge浏览器实例
        edge = webdriver.Edge()
        edge.maximize_window()
    
        # 打开抖音网站
        edge.get('https://www.douyin.com/')
    
        # 添加Cookie以实现持久登录
        for cookie in cookies_list:
            edge.add_cookie(cookie)
    
        # 自定义您要进入的直播间链接
        edge.get('https://live.douyin.com/your_room_id')
    
        # 等待一段时间,确保页面加载完毕
        time.sleep(10)
    
        # 启动主程序
        main()

    上述代码创建了两个线程,一个用于点赞和发送消息,另一个用于监测公屏消息并根据关键字发送回复消息。通过这种方式,您可以自动化与抖音直播间的互动。

    结论

    本教程介绍了如何使用Python和Selenium自动化与抖音的互动,包括登录、点赞、发送消息和关键字回复。这些技能可以用于自动化互动、增加粉丝互动度以及提升用户体验。希望这个教程对您有所帮助!

  • Python发送电子邮件教程

    Python发送电子邮件教程

    电子邮件是现代通信的重要组成部分,它可以用于个人和商业用途。通过Python,您可以轻松地编写脚本来发送电子邮件。本教程将向您展示如何使用Python发送电子邮件,以及如何通过QQ邮箱的SMTP服务器发送电子邮件。无论您是要发送个人邮件还是自动化商务通知,这些技能都将对您有所帮助。

    准备工作

    在开始之前,您需要安装Python并了解一些基本的Python编程知识。此外,您还需要一个QQ邮箱账号,以便使用QQ邮箱的SMTP服务器发送邮件。确保您已经获得了SMTP服务器的授权码,这是连接到SMTP服务器的凭据。

    发送电子邮件的步骤

    在Python中发送电子邮件涉及以下主要步骤:

    1. 导入必要的模块。
    2. 创建邮件消息对象。
    3. 设置邮件的主题、发件人和收件人。
    4. 建立与SMTP服务器的连接。
    5. 使用授权码登录SMTP服务器。
    6. 发送邮件。
    7. 关闭SMTP服务器连接。

    让我们一步步来看如何实现这些步骤。

    步骤1:导入模块

    首先,我们需要导入Python的smtplib和email模块,它们是标准库中用于发送电子邮件的关键模块。具体代码如下:

    import smtplib
    from email.mime.text import MIMEText

    步骤2:创建邮件消息对象

    我们将使用MIMEText类来创建一个文本邮件的MIME消息对象,其中包含邮件的内容。这个对象将包括邮件的主题、发件人、收件人和邮件正文内容。

    msg = MIMEText(message)
    msg['Subject'] = subject
    msg['From'] = sender_email
    msg['To'] = rec_email

    步骤3:建立与SMTP服务器的连接

    我们使用smtplib模块中的SMTP_SSL类来建立与SMTP服务器的SSL连接。在这个步骤中,我们需要提供SMTP服务器的地址和端口号。

    with smtplib.SMTP_SSL('smtp.qq.com', 465) as smtp:

    步骤4:使用授权码登录SMTP服务器

    为了登录SMTP服务器,我们需要提供发件人的邮箱地址和SMTP服务器的授权码。

    smtp.login(sender_email, sender_pass)

    步骤5:发送邮件

    一旦我们成功登录SMTP服务器,就可以使用send_message方法发送邮件。

    smtp.send_message(msg)

    步骤6:关闭SMTP服务器连接

    最后,我们使用quit方法关闭SMTP服务器连接。

    smtp.quit()

    完整的示例代码

    以下是一个完整的示例代码,演示了如何使用Python发送电子邮件:

    import smtplib
    from email.mime.text import MIMEText
    
    # 定义一个函数,用于发送指定邮箱的邮件
    def sendqqmail(sender_email, sender_pass, rec_email, subject, message):
        # 使用MIMEText类创建一个邮件消息对象,其中message参数是邮件的内容
        msg = MIMEText(message)
    
        # 设置邮件的主题
        msg['Subject'] = subject
    
        # 设置邮件的发件人邮箱
        msg['From'] = sender_email
    
        # 设置邮件的收件人邮箱
        msg['To'] = rec_email
    
        # 使用smtplib模块的SMTP_SSL类创建一个SSL连接对象,连接到QQ邮箱SMTP服务器
        with smtplib.SMTP_SSL('smtp.qq.com', 465) as smtp:
            # 使用login方法登录SMTP服务器,参数sender_email和sender_pass分别是发件人的邮箱地址和授权码
            smtp.login(sender_email, sender_pass)
    
            # 使用send_message方法发送邮件,参数msg是要发送的邮件消息对象
            smtp.send_message(msg)
    
            # 关闭SMTP服务连接
            smtp.quit()
    
    # 定义一个主函数,用于运行整个程序
    def main():
        # 定义发件人的邮箱地址
        sender_email = '[email protected]'
    
        # 定义发件人的邮箱授权码
        emailpass = secretPass
    
        # 定义收件人的邮箱地址
        to_email = '[email protected]'
    
        # 定义邮件的主题
        sub_msg = '测试python发送邮件'
    
        # 定义邮件的正文内容
        content = '这是我的第一个python发送邮件测试'
    
        # 调用sendqqmail函数,发送邮件
        sendqqmail(sender_email, emailpass, to_email, sub_msg, content)
    
    # 执行main函数
    if __name__ == '__main__':
        main()

    结论

    通过本教程,您学会了如何使用Python发送电子邮件,以及如何通过QQ邮箱的SMTP服务器发送邮件。这些技能对于与他人保持联系、自动化通知以及许多其他用途都非常有用。希望这个教程对您有所帮助!

  • 快速排序算法的可视化教程

    快速排序算法的可视化教程

    在计算机科学中,排序算法是一项基本而重要的任务。排序的目的是将一组数据按照一定的规则进行排列,以便更容易地进行查找和处理。快速排序算法是一种高效的排序算法,它的性能在平均情况下非常出色。本教程将教您如何使用Python和Matplotlib库可视化快速排序算法的执行过程。

    准备工作

    在开始之前,您需要安装Matplotlib库。您可以使用以下命令安装Matplotlib:

    pip install matplotlib

    确保您已经安装了Matplotlib库,然后我们可以开始学习如何可视化快速排序算法的执行过程。

    快速排序算法概述

    快速排序是一种分治算法,它的基本思想是选择一个元素作为“枢轴”(pivot),然后将其他元素分为两个子数组,小于枢轴的放在左边,大于枢轴的放在右边。然后,递归地对左右子数组进行排序,直到整个数组有序。

    快速排序的关键步骤包括选择枢轴、划分子数组和递归排序子数组。在本教程中,我们将通过可视化来理解这些步骤。

    可视化快速排序

    步骤1:导入必要的库

    首先,我们需要导入Matplotlib库,以及随机生成数据的模块。

    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    from random import shuffle

    步骤2:实现快速排序算法

    我们将实现快速排序算法的两个主要函数:quicksort 和 partition。quicksort 函数将递归地对数组进行排序,并在每一步记录数组的状态。partition 函数用于划分子数组并返回枢轴的位置。

    以下是这两个函数的代码:

    def quicksort(arr, start, end, frames):
        if start >= end:
            return
    
        pivot_index = partition(arr, start, end)
        frames.append(arr[:])
    
        quicksort(arr, start, pivot_index-1, frames)
        quicksort(arr, pivot_index+1, end, frames)
    
    def partition(arr, start, end):
        pivot_index = start
        pivot = arr[end]
    
        for i in range(start, end):
            if arr[i] < pivot:
                arr[i], arr[pivot_index] = arr[pivot_index], arr[i]
                pivot_index += 1
        arr[end], arr[pivot_index] = arr[pivot_index], arr[end]
        return pivot_index

    步骤3:创建动画函数

    我们将创建一个动画函数 animate,用于可视化快速排序的执行过程。这个函数将使用Matplotlib创建动画,并在每一帧更新数组的状态。

    def animate(frames):
        fig, ax = plt.subplots()
        ax.set_title("快速排序")
        bar_rects = ax.bar(range(len(frames[0])), frames[0], align="edge")
    
        def update_fig(frame, rects, iteration):
            for rect, val in zip(rects, frame):
                rect.set_height(val)
            iteration[0] += 1
            return rects
    
        anim = animation.FuncAnimation(fig, func=update_fig,
                                       fargs=(bar_rects, [0]),
                                       frames=frames, interval=50,
                                       repeat=False)
        plt.show()

    步骤4:生成随机数据并可视化排序过程

    现在,我们将生成一组随机数据并使用快速排序算法可视化排序过程。

    # 创建随机数据
    data = [x for x in range(1, 31)]
    shuffle(data)
    
    # 排序过程中的所有状态
    frames = []
    quicksort(data, 0, len(data)-1, frames)
    
    # 生成动画
    animate(frames)

    通过运行上述代码,您将看到一个动画,显示了快速排序算法的执行过程,从而更好地理解这个高效的排序算法。

    结论

    通过本教程,您学会了如何使用Python和Matplotlib库可视化快速排序算法的执行过程。这种可视化方法有助于深入理解算法的工作原理,并使学习更加生动有趣。希望本教程对您有所帮助!

  • 如何在视频中添加动态水印及音频

    如何在视频中添加动态水印及音频

    在现今数字化时代,视频内容的制作和分享已经成为日常生活的一部分。无论是在社交媒体上分享生活点滴,还是制作专业的视频内容,添加水印是一种常见的需求。水印可以用来保护视频内容的版权,增加品牌标识,或者为观众提供更多信息。本教程将教您如何使用Python和一些流行的库,在视频中添加动态水印,并保留原始音频。

    准备工作

    在开始之前,您需要安装以下Python库:

    • OpenCV (cv2)
    • numpy
    • moviepy
    • moviepy的依赖库:imageio, imageio-ffmpeg

    您可以使用以下命令安装这些库:

    pip install opencv-python-headless numpy moviepy imageio imageio-ffmpeg

    确保您已经安装了这些库,然后我们可以开始学习如何添加动态水印到视频中。

    添加动态水印

    步骤1:导入必要的库

    首先,我们需要导入必要的Python库,包括OpenCV、numpy和moviepy。这些库将帮助我们处理视频和添加水印。

    import cv2
    import os
    import numpy as np
    from moviepy.editor import VideoFileClip

    步骤2:定义函数以添加水印

    我们将创建一个函数add_dynamic_watermarks_with_audio,该函数将接受以下参数:

    • video_path:输入视频文件的路径。
    • output_path:输出带有水印的视频文件的路径。
    • watermark_image_paths:一个包含水印图片文件路径的列表。
    • watermark_sizes:与每个水印图片对应的大小。
    • watermark_speeds:每个水印的速度。
    • add_text_watermark:一个布尔值,表示是否添加文本水印。
    • text_watermark_text:要添加的文本水印的内容。
    • text_watermark_color:文本水印的颜色。

    以下是该函数的代码:

    def add_dynamic_watermarks_with_audio(video_path, output_path, watermark_image_paths, watermark_sizes, watermark_speeds, add_text_watermark, text_watermark_text, text_watermark_color):
        # 读取视频
        video_clip = VideoFileClip(video_path)
        audio_clip = video_clip.audio
    
        # 获取视频的宽度和高度
        width, height = video_clip.size
    
        # 准备文本水印
        if add_text_watermark:
            # 创建用于绘制文本水印的画布
            text_watermark_canvas = np.zeros((height, width, 3), dtype=np.uint8)
    
            # 设置文本水印属性
            font_face = cv2.FONT_HERSHEY_SIMPLEX
            font_scale = 1.5
            font_thickness = 2
    
            # 获取文本水印的大小
            (text_width, text_height), _ = cv2.getTextSize(text_watermark_text, font_face, font_scale, font_thickness)
    
            # 计算文本水印的位置
            text_x = int((width - text_width) / 2)
            text_y = int(height - text_height - 10)
    
            # 在画布上绘制文本水印
            cv2.putText(text_watermark_canvas, text_watermark_text, (text_x, text_y), font_face, font_scale, text_watermark_color, font_thickness, cv2.LINE_AA)
    
        # 在每一帧上添加水印
        watermark_positions = [(0, 0)] * len(watermark_image_paths)
    
        def process_frame(t, x):
            nonlocal watermark_positions
    
            frame = video_clip.get_frame(t)
    
            # 在帧上添加图片水印
            for i in range(len(watermark_image_paths)):
                watermark_image_path = watermark_image_paths[i]
                watermark_size = watermark_sizes[i]
                watermark_speed = watermark_speeds[i]
    
                # 读取水印图片
                watermark_image = cv2.imread(watermark_image_path)
    
                # 调整水印图片大小
                watermark_height, watermark_width, _ = watermark_image.shape
                if watermark_width > width or watermark_height > height:
                    scale_factor = min(width / watermark_width, height / watermark_height)
                    watermark_image = cv2.resize(
                        watermark_image,
                        (int(watermark_width * scale_factor), int(watermark_height * scale_factor)),
                        interpolation=cv2.INTER_LINEAR,
                    )
    
                # 更新水印位置
                if t % watermark_speed == 0:
                    while True:
                        x = np.random.randint(0, width - watermark_image.shape[1])
                        y = np.random.randint(0, height - watermark_image.shape[0])
                        too_close = False
    
                        # 检查新位置与已选择水印位置之间的距离
                        for j in range(i):
                            dist = np.sqrt((x - watermark_positions[j][0]) ** 2 + (y - watermark_positions[j][1]) ** 2)
                            if dist < watermark_image.shape[1] or dist < watermark_image.shape[0]:
                                too_close = True
                                break
    
                        if not too_close:
                            watermark_positions[i] = (x, y)
                            break
                else:
                    x, y = watermark_positions[i]
    
                # 在帧上添加水印
                watermark_resized = cv2.resize(
                    watermark_image, (int(watermark_size * watermark_width), int(watermark_size * watermark_height))
                )
                alpha = watermark_resized[:, :, 0] / 255.0
                for c in range(3):
                    frame_copy = frame.copy()
                    frame_copy[y : y + watermark_resized.shape[0], x : x + watermark_resized.shape[1], c] = (
                        frame[y : y + watermark_resized.shape[0], x : x + watermark_resized.shape[1], c] * (1 - alpha)
                        + watermark_resized[:, :, c] * alpha
                    )
    
            # 在帧上添加文本水印
            if add_text_watermark:
                frame_with_text = cv2.addWeighted(frame, 1, text_watermark_canvas, 0.7, 0)
                frame = frame_with_text
    
            cv2.imshow("Watermarked Video", frame)
            cv2.waitKey(1)
    
            return frame
    
        # 处理视频每一帧
        processed_clip = video_clip.fl(lambda gf, t
    
    : process_frame(t, gf))
    
        # 输出处理后的视频
        final_clip = processed_clip.set_audio(audio_clip)
        final_clip.write_videofile(output_path, codec='libx264', audio_codec="aac")
    
        return "水印添加完成!"

    步骤3:批量添加水印到视频

    我们还可以创建一个函数batch_add_watermarks_in_directory_with_audio,该函数将遍历指定目录中的所有视频文件,并为每个视频文件添加水印。您可以指定要添加的水印数量、水印图片的路径、大小和速度,以及是否添加文本水印。

    以下是该函数的代码:

    def batch_add_watermarks_in_directory_with_audio(directory, output_directory, watermark_image_paths, watermark_sizes, watermark_speeds, add_text_watermark=False, text_watermark_text=None):
        # 获取目录中的所有文件
        file_list = os.listdir(directory)
    
        # 遍历文件列表
        for file_name in file_list:
            # 检查文件是否为视频文件
            if file_name.lower().endswith(('.avi', '.mp4', '.mov', '.mkv')):
                file_path = os.path.join(directory, file_name)
    
                # 设置输出文件路径
                output_file_path = os.path.join(output_directory, file_name)
    
                # 添加水印
                add_dynamic_watermarks_with_audio(file_path, output_file_path, watermark_image_paths, watermark_sizes, watermark_speeds, add_text_watermark, text_watermark_text if add_text_watermark else None, (255, 255, 255))
    
        return "批量处理完成!"

    步骤4:使用示例

    现在我们已经定义了添加水印的函数,让我们看一个使用示例。首先,您需要提供视频文件所在的目录路径和输出目录路径。然后,您需要指定要添加的水印数量、水印图片的路径、大小和速度,以及是否添加文本水印。

    以下是使用示例的代码:

    if __name__ == '__main__':
        directory = input("请输入视频文件所在目录路径:")  # 视频文件所在目录
        output_directory = input("请输入输出视频文件目录路径:")  # 输出视频文件目录
    
        # 检查并创建输出目录
        os.makedirs(output_directory, exist_ok=True)
    
        watermark_image_paths = []
        watermark_sizes = []
        watermark_speeds = []
    
        num_watermarks = int(input("请输入要添加的水印数量:"))
        for i in range(num_watermarks):
            watermark_image_path = input(f"请输入第{i+1}个水印图片的路径:")
            watermark_size = float(input(f"请输入第{i+1}个水印图片的大小(0~1之间):"))
            watermark_speed = int(input(f"请输入第{i+1}个水印图片的速度:"))
    
            watermark_image_paths.append(watermark_image_path)
            watermark_sizes.append(watermark_size)
            watermark_speeds.append(watermark_speed)
    
        add_text_watermark_input = input("是否添加文本水印?(y/n) ")
        if add_text_watermark_input.lower() == 'y':
            add_text_watermark = True
            text_watermark_text = input("请输入文本水印内容:")
        else:
            add_text_watermark = False
            text_watermark_text = None
    
        batch_add_watermarks_in_directory_with_audio(directory, output_directory, watermark_image_paths, watermark_sizes, watermark_speeds, add_text_watermark, text_watermark_text)

    通过运行上述代码,您可以批量处理指定目录中的视频文件,为它们添加动态水印,并保留原始音频。

    结论

    通过本教程,您学会了如何使用Python和一些常用的库来为视频添加动态水印,并保留原始音频。这对于保护视频内容的版权、增加品牌标识或提供更多信息非常有用。希望本教程对您有所帮助!

  • 如何生成数据库表结构文档

    如何生成数据库表结构文档

    在软件开发和数据库管理中,了解数据库表结构对于项目的设计和维护至关重要。为了方便团队成员之间的沟通和协作,以及记录数据库的详细信息,我们需要生成数据库表结构文档。本教程将介绍如何使用Python和Markdown来生成数据库表结构文档,以及为什么这对项目管理和开发过程非常重要。

    问题背景

    在项目开发和维护过程中,经常需要查看数据库表结构的信息,包括字段名称、数据类型、是否为空、是否为主键等等。手动编写这些信息费时费力,容易出错,而且随着数据库表数量的增加,难以维护。因此,我们需要一种自动化的方式来生成数据库表结构文档,以提高效率和准确性。

    解决方案

    为了解决上述问题,我们可以使用Python编写一个脚本来生成数据库表结构文档,并以Markdown格式输出。Markdown是一种轻量级的标记语言,易于编写和阅读,适用于文档编写。

    下面是一个示例的Python脚本,用于生成数据库表结构文档:

    import pymysql
    import importlib,sys
    
    header = {
            1:"TABLE_CATALOG",
            2:"TABLE_SCHEMA",
            3:"TABLE_NAME",
            4:"COLUMN_NAME",
            5:"ORDINAL_POSITION",
            6:"COLUMN_DEFAULT",
            7:"IS_NULLABLE",
            8:"DATA_TYPE",
            9:"CHARACTER_MAXIMUM_LENGTH",
            10:"CHARACTER_OCTET_LENGTH",
            11:"NUMERIC_PRECISION",
            12:"NUMERIC_SCALE",
            13:"DATETIME_PRECISION",
            14:"CHARACTER_SET_NAME",
            15:"COLLATION_NAME",
            16:"COLUMN_TYPE",
            17:"COLUMN_KEY",
            18:"EXTRA",
            19:"PRIVILEGES",
            20:"COLUMN_COMMENT",
            21:"GENERATION_EXPRESSION",
            22:"SRS_ID"
        }
    
    def generate(database_name):
    
        importlib.reload(sys)
    
        conn = pymysql.connect(host='127.0.0.1', user='root', password='123456', db='database')
        cursor = conn.cursor()
        query = f"SELECT TABLE_NAME, TABLE_COMMENT FROM information_schema.TABLES WHERE table_type='BASE TABLE' AND TABLE_SCHEMA='{database_name}'"
    
        cursor.execute(query)
        tables = cursor.fetchall()
    
        markdown_table_header = """\n\n\n### {} ({}) \n| 序号 | 字段名称 | 数据类型 | 是否为空 | 是否为主键 | 字段说明 |\n| :--: |----| ---- | ---- | ---- | ---- |\n"""
    
        mysql_table_header=f"{header.get(5)},{header.get(4)},{header.get(16)},{header.get(7)},{header.get(17)},{header.get(20)}"
    
        markdown_table_row = "| " + " | ".join(["{}"] * len(mysql_table_header.split(','))) + " |"
    
        with open(f'{database_name}.md', 'w') as f:
    
            for table in tables:
    
                query = f"SELECT {mysql_table_header} FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='{database_name}' AND TABLE_NAME='{table[0]}'ORDER BY ORDINAL_POSITION"
    
                cursor.execute(query)
    
                tmp_table = cursor.fetchall()
    
                p = markdown_table_header.format(table[0], remove_newline(table[1]))
    
                for col in tmp_table:
                    p += (remove_newline(markdown_table_row.format(*col)) + "\n")
    
                f.writelines(p)
    
    def remove_newline(s):
        return s.replace('\n', '')
    
    generate("database")

    生成文档的步骤

    为了生成数据库表结构文档,我们需要按照以下步骤进行操作:

    1. 连接数据库:使用Python中的pymysql库建立与数据库的连接。在脚本中,需要指定数据库的主机、用户名、密码和数据库名。

    2. 查询表信息:通过SQL查询获取数据库中所有基本表(BASE TABLE)的名称和注释(如果有的话)。这些信息将用于文档的标题。

    3. 查询字段信息:针对每个基本表,查询其字段的详细信息,包括字段名称、数据类型、是否为空、是否为主键等等。这些信息将用于生成表格。

    4. 生成Markdown文档:使用查询到的信息,按照Markdown的格式生成文档。在文档中,每个基本表将有一个标题,下面是一个表格,列出了字段的详细信息。

    5. 保存文档:将生成的Markdown文档保存为文件,可以根据需要命名。

    文档示例

    生成的Markdown文档示例如下:

    ### users (用户表) 
    | 序号 | 字段名称 | 数据类型 | 是否为空 | 是否为主键 | 字段说明 |
    | :--: |----| ---- | ---- | ---- | ---- |
    | 1 | id | int | NO | YES | 用户ID |
    | 2 | username | varchar(255) | NO | NO | 用户名 |
    | 3 | email | varchar(255) | NO | NO | 邮箱 |
    | 4 | password | varchar(255) | NO | NO | 密码 |

    注意事项和安全性

    在生成数据库表结构文档时,需要注意以下事项和安全性问题:

    1. 数据库连接信息:确保数据库连接信息(主机、用户名、密码)安全,并不要将其硬编码在脚本中。可以使用配置文件或环境变量来存储这些信息。

    2. 字段注释:尽量为数据库表中的字段添加注释,以便生成的文档更加清晰和易懂。

    3. 定期更新文档:随着数据库结构的变化,需要定期更新生成的文档,以保持其准确性。

    4. 文档命名规范:为生成的文档选择合适的命名规范,以便团队成员能够轻松找到所需的文档。

    结论

    生成数据库表结构文档是项目开发和数据库管理中的一项重要任务。通过使用Python和Markdown,我们可以自动化这个过程,提高效率和准确性。生成的文档可以帮助团队成员更好地理解数据库结构,从而更轻松地开展工作。请根据实际需求定制和扩展这个脚本,以适应不同项目和数据库的要求。

  • 如何配置SSH免密登录以提高远程连接效率

    如何配置SSH免密登录以提高远程连接效率

    在远程服务器管理和文件传输过程中,每次都输入密码可能会显得繁琐和不便。然而,SSH免密登录是一种解决这个问题的有效方法,它允许你在不输入密码的情况下安全地连接到远程服务器。本教程将教你如何配置SSH免密登录,以提高远程连接效率并减少繁琐的密码输入。

    什么是SSH免密登录

    SSH(Secure Shell)是一种用于在网络上安全传输数据的协议。SSH免密登录是基于公钥加密的身份验证方式,允许你在不输入密码的情况下访问远程服务器。它通过生成一对密钥(公钥和私钥),将公钥放在远程服务器上,并使用私钥来进行身份验证。只有拥有正确私钥的用户才能成功登录,这样可以提高安全性并减少密码泄露的风险。

    配置SSH免密登录步骤

    生成SSH密钥对

    第一步是生成SSH密钥对,包括公钥和私钥。在本地计算机上打开终端或命令行,并执行以下命令:

    ssh-keygen

    系统将要求你选择存储密钥的位置和设置密码。通常,你可以使用默认位置(~/.ssh/id_rsa)并留空密码。这将生成公钥(id_rsa.pub)和私钥(id_rsa)。

    将公钥复制到远程服务器

    接下来,你需要将公钥复制到远程服务器上,以便服务器可以识别你的身份。使用以下命令将公钥复制到服务器的~/.ssh/authorized_keys文件中:

    scp -p ~/.ssh/id_rsa.pub user@remote:~/.ssh/authorized_keys

    将user替换为你在服务器上的用户名,remote替换为服务器的IP地址或域名。这将在服务器上的~/.ssh目录中创建authorized_keys文件并将你的公钥添加到其中。

    测试SSH免密登录

    现在,你可以尝试使用SSH免密登录到远程服务器。执行以下命令:

    ssh user@remote

    如果一切设置正确,你将可以无需输入密码直接登录到服务器。

    注意事项和安全性

    在配置SSH免密登录时,有一些注意事项和安全性问题需要考虑:

    1. 密码保护私钥:尽管SSH免密登录允许你免去输入密码,但为了安全起见,仍建议你为私钥设置密码。这样即使私钥被盗,黑客也需要知道密码才能使用它。

    2. 定期更换密钥:定期更换SSH密钥对,以提高安全性。旧的密钥应该从服务器上删除。

    3. 限制访问:只将公钥添加到需要连接的服务器上,以减少风险。不要将公钥添加到不必要的服务器上。

    4. 防火墙和安全策略:使用防火墙和其他安全策略来限制对SSH端口的访问,以保护服务器免受未经授权的访问。

    5. 备份密钥:确保在本地计算机上备份好私钥,以防丢失。

    总结

    SSH免密登录是提高远程连接效率的有力工具,同时也增强了安全性。通过生成SSH密钥对、将公钥复制到远程服务器,并遵循安全性最佳实践,你可以轻松实现SSH免密登录,减少了繁琐的密码输入,同时保护了服务器的安全性。