<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Hoi3vel</title>
  
  
  <link href="https://www.hoi3vel.cn/atom.xml" rel="self"/>
  
  <link href="https://www.hoi3vel.cn/"/>
  <updated>2026-06-01T08:31:00.000Z</updated>
  <id>https://www.hoi3vel.cn/</id>
  
  <author>
    <name>Hoi3vel</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>判断三维坐标系旋转正方向的简单方法</title>
    <link href="https://www.hoi3vel.cn/2026/06/01/t-coordinates-recogonize/"/>
    <id>https://www.hoi3vel.cn/2026/06/01/t-coordinates-recogonize/</id>
    <published>2026-06-01T07:00:00.000Z</published>
    <updated>2026-06-01T08:31:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>最近在学习OpenGL，对其使用的坐标系有些不了解，后面查资料发现在3D空间中，有左右手坐标系的区别，为了弄清这两种坐标系的区别，于是写下这篇文章方便日后回顾查看。</p></blockquote><span id="more"></span><h2 id="一、三维坐标系"><a href="#一、三维坐标系" class="headerlink" title="一、三维坐标系"></a>一、三维坐标系</h2><p>做3D特效时，就要用到三维坐标系，这是后人在笛卡尔的平面坐标系的基础上发明的。三维坐标系分两种，左手坐标系和右手坐标系，为什么用左手和右手来区分呢？这是因为当确定了x轴，y轴方向之后,z轴的方向的两种，它可以通过左手或右手来确定。下面就是这两个坐标系的规则示意图（图中固定了x轴的正方向向右，y轴的正方向向上）：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:790/338;"><img class="lazy" src="https://img.hoi3vel.cn/tech/2026_06_01_01_01.webp" data-src="https://img.hoi3vel.cn/tech/2026_06_01_01_01.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div></p><p>相信大多数人对图中的右手坐标系很眼熟，没错，这就是初高中数学教材用到的三维坐标系，只是我们不一定知道它叫右手坐标系。左手坐标系我们之前很少接触，但是在计算机图形学中这种坐标系非常重要，比如iOS的UIView使用的坐标系就是左手坐标系。有人可能会说，不对吧，<code>UIView</code>的坐标系是原点在左上角，y轴正方向向下，图中的不是这样啊，其实没错啦，把图中的左手坐标系沿x轴旋转180度就是原点在左上角的左手坐标系，区别就是旋转的角度不同而已。这是因为左手坐标系或者右手坐标系整体旋转后性质是不变的。</p><p>对坐标系使用左手与右手的命名，有一个作用就是用来方便判断旋转的正方向，这就是左手法则和右手法则。例如对左手坐标系，确定一个旋转轴后，左手握住拳头，拇指指向旋转轴的正方向，四指弯曲的方向为旋转的正方向。相应地，右手坐标系就用右手来判定。确定了旋转的正方向后，在公式计算中就很容易知道是该使用正角度还是负角度了。下图就是右手的例子：<div class="tag-plugin image"><div class="image-bg"><img class="lazy" src="https://img.hoi3vel.cn/tech/2026_06_01_01_02.webp" data-src="https://img.hoi3vel.cn/tech/2026_06_01_01_02.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div><br>但是，这个判断旋转正方向的方法还是不够快。给定任意一个旋转角度的三维坐标系，如果按上面的方法判断旋转正方向，首先，你得确定这个坐标系是左手坐标系还是右手坐标系，这时你会先拿出一只手来，像上图一样摆好三根手指的姿势来比对给定坐标系的x、y、z轴正方向看是否一致。然后根据旋转轴的正方向，用相应的手来判断旋转正方向。</p><p>其实，完全没必要这么麻烦。先看第一个图的两个坐标系，左边的为左手坐标系，右边的为右手坐标系，两坐标系的x轴和y轴正方向保持一致，z轴正方向相反。分别用左手法则与右手法则去判断它们各自绕z轴旋转的正方向，那么从我们眼睛看屏幕的角度来看，它们绕z轴旋转的正方向都是逆时针，这当然不会是巧合。观察这两个坐标系，就会发现这个逆时针方向与x轴正方向箭头顶点指向y轴正方向箭头顶点的方向一致，这说明绕z轴旋转的正方向与x轴正方向箭头顶端指向y轴正方向箭头顶端的方向有关联吗？我想是的。然后再尝试判断两坐标系绕x轴旋转的正方向，它与y轴正方向顶端指向z轴正方向顶端的方向一致；而绕y轴旋转的正方向，与z轴正方向顶端指向x轴正方向顶端的方向一致。</p><h2 id="二、结论"><a href="#二、结论" class="headerlink" title="二、结论"></a>二、结论</h2><h3 id="1-结论1"><a href="#1-结论1" class="headerlink" title="1.结论1"></a>1.结论1</h3><p>根据上述分析，我们可以得出一个结论：<strong>对于任意旋转角度的三维坐标系，绕某一坐标轴旋转的正方向，与另外两个坐标轴的正方向顶端按X—&gt;Y—&gt;Z—&gt;X的顺序进行指向的方向一致。</strong></p><p>这就意味着，判断三维坐标系绕某一坐标轴旋转的正方向，不用事先知晓这个坐标系是左手坐标系还是右手坐标系，完全不需要你用手去比划.反过来，既然判断旋转正方向这么容易，我们也可以利用它来快速判断一个坐标系是左手坐标系和右手坐标系：使用上述结论确定坐标系绕某一某旋转的正方向，然后逆用左手法则与右手法则，大拇指指向该轴的正方向，如果左手四指弯曲的方向与旋转的正方向一致，该坐标系就是左手坐标系，反之就是右手坐标系。</p><p>不过这还是复杂，还是需要用手比划。有一个更好的方法是：想象y轴是一面墙，你面朝前方斜靠在墙上，可以假设你的头部为y轴正方向顶点，脚为x轴正方向顶点，那么z轴在你的左侧时就是左手坐标系，在右侧时就是右手坐标系。这个时候，人体的生长方向也刚好是绕z轴旋转的正方向。</p><h3 id="2-结论2"><a href="#2-结论2" class="headerlink" title="2.结论2"></a>2.结论2</h3><p>再扩展一下就是：对于任意旋转角度的三维坐标系，想象你的脚踩在一个坐标轴（如x轴）正方向的顶点，头倚靠在其邻高坐标轴（如y轴）的正方向顶点，面朝背离原点的方向，那么，第三轴正方向顶点在你的左手边时，这个坐标系就是左手坐标系，在右手边时就是右手坐标系，而人体此时的生长方向就是绕第三轴(如z轴)旋转的正方向。（注：这里的邻高坐标轴是我自己定义的一个概念，X轴的邻高坐标轴为Y轴，Y轴的邻高坐标轴为Z轴，Z轴的邻高坐标轴为X轴.）</p>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;最近在学习OpenGL，对其使用的坐标系有些不了解，后面查资料发现在3D空间中，有左右手坐标系的区别，为了弄清这两种坐标系的区别，于是写下这篇文章方便日后回顾查看。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="3D Coordinate" scheme="https://www.hoi3vel.cn/tags/3D-Coordinate/"/>
    
    <category term="OpenGL" scheme="https://www.hoi3vel.cn/tags/OpenGL/"/>
    
  </entry>
  
  <entry>
    <title>基于FRP的Mac-Windows端远程连接</title>
    <link href="https://www.hoi3vel.cn/2025/12/25/t-ssh-Mac2Windows/"/>
    <id>https://www.hoi3vel.cn/2025/12/25/t-ssh-Mac2Windows/</id>
    <published>2025-12-25T02:24:00.000Z</published>
    <updated>2025-12-25T04:24:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Mac如何连接寝室里充当远程服务器的Windows端？经过自己将近两天的捣鼓，终于把这个问题解决了。感谢Chat GPT、感谢千问，在搭建连接的过程中给予了我莫大的支持。由于过程颇为繁琐，为了以后遇到类似问题自己能够有所参考，于是趁着期末周仅有的休息时间，写下这篇文章，若有差错的地方，恳请海涵指正。</p></blockquote><span id="more"></span><h2 id="一、写在前面"><a href="#一、写在前面" class="headerlink" title="一、写在前面"></a>一、写在前面</h2><h3 id="1-部署方式"><a href="#1-部署方式" class="headerlink" title="1.部署方式"></a>1.部署方式</h3><p>大学的校园网通常是禁止路由器端口转发的，因此要想实现公网电脑远程连接内网服务端电脑就需要使用<strong>FRP</strong>技术进行内网穿透。基本原理为:</p><ul><li>云服务器作为中转；</li><li>Windows在内网运行SSH服务；</li><li>Mac通过SSH连接Windows；</li><li>FRP实现内网穿透。</li></ul><h3 id="2-相关环境"><a href="#2-相关环境" class="headerlink" title="2.相关环境"></a>2.相关环境</h3><ul><li>阿里云服务器：1.CPU&amp;内存-2核(vCPU) 2GiB; 2.操作系统: Ubuntu 20.04 64位; 3.公网带宽: 3Mbps</li><li>Windows系统：Win11</li><li>Mac系统：macOS 15.7.3</li><li>FRP版本：frp_0.54.0_linux_amd64(云服务器上)与frp_0.54.0_windows_amd64(Windows机上)</li></ul><h2 id="二、搭建步骤"><a href="#二、搭建步骤" class="headerlink" title="二、搭建步骤"></a>二、搭建步骤</h2><h3 id="1-云服务器端-FRPS服务端"><a href="#1-云服务器端-FRPS服务端" class="headerlink" title="1.云服务器端(FRPS服务端)"></a>1.云服务器端(FRPS服务端)</h3><p>在Mac终端或者其他SSH服务端(如Xshell)输入如下代码：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">登录云服务器端</span></span><br><span class="line">ssh root@云服务器公网IP</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装基础工具</span></span><br><span class="line">sudo apt update &amp;&amp; sudo apt install -y wget tar</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载并解压FRP</span></span><br><span class="line">wget https://github.com/fatedier/frp/releases/download/v0.54.0/frp_0.54.0_linux_amd64.tar.gz</span><br><span class="line">tar -zxvf frp_0.54.0_linux_amd64.tar.gz</span><br><span class="line">mv frp_0.54.0_linux_amd64 frp</span><br><span class="line">cd frp</span><br></pre></td></tr></table></figure><p>默认情况下，<strong>FRP</strong>保存在<code>/root/</code>目录下。</p><p>接着在终端输入指令<code>sudo nano frps.toml</code>创建<strong>frps.toml</strong>安全配置文件。文件内容如下：</p><figure class="highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># [server]</span></span><br><span class="line"><span class="attr">bindPort</span> = <span class="number">7000</span> <span class="comment"># 固定端口，无需修改</span></span><br><span class="line"><span class="attr">auth.method</span> = <span class="string">&quot;token&quot;</span></span><br><span class="line"><span class="attr">auth.token</span> = <span class="string">&quot;strong_password&quot;</span> <span class="comment"># 设置连接密码</span></span><br></pre></td></tr></table></figure><p>再在终端输入指令<code>sudo nano /etc/systemd/system/frps.service</code>配置开机自启文件，文件内容如下：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[Unit]</span></span><br><span class="line"><span class="attr">Description</span>=frp server</span><br><span class="line"><span class="attr">After</span>=network.target</span><br><span class="line"></span><br><span class="line"><span class="section">[Service]</span></span><br><span class="line"><span class="attr">Type</span>=simple</span><br><span class="line"><span class="attr">User</span>=root</span><br><span class="line"><span class="attr">WorkingDirectory</span>=/root/frp</span><br><span class="line"><span class="attr">ExecStart</span>=/root/frp/frps -c /root/frp/frps.toml</span><br><span class="line"><span class="attr">Restart</span>=<span class="literal">on</span>-failure</span><br><span class="line"><span class="attr">RestartSec</span>=<span class="number">5</span>s</span><br><span class="line"></span><br><span class="line"><span class="section">[Install]</span></span><br><span class="line"><span class="attr">WantedBy</span>=multi-user.target</span><br></pre></td></tr></table></figure><p>最后启动<strong>FRP</strong>服务，终端指令如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">启动服务</span></span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line">sudo systemctl start frps</span><br><span class="line">sudo systemctl enable frps</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">检查状态</span></span><br><span class="line">sudo systemctl status frps  # 看到 active (running) 即成功</span><br></pre></td></tr></table></figure><p>到此，云服务器上的相关配置文件就配好了。</p><p>不过云服务器还有最后一个操作，就是设置安全组允许<strong>6000端口、7000端口以及7500端口</strong>开放。具体路径为：控制台-&gt;云服务器ECS-&gt;安全组-&gt;管理规则。在<strong>入方向</strong>增加规则，其中<strong>授权策略</strong>选择“允许”，<strong>优先级</strong>选择“1”，<strong>协议</strong>选择“自定义TCP”，<strong>访问来源</strong>选择“任何位置”，<strong>访问目的</strong>设置为“6000”、“7000”以及“7500”。设置完成后可在终端输入命令<code>sudo netstat -tulpn | grep 7000</code>，若有“… Listen …&#x2F;frps”内容输出，则证明设置成功。</p><h3 id="2-Windows配置-FRPC客户端"><a href="#2-Windows配置-FRPC客户端" class="headerlink" title="2.Windows配置(FRPC客户端)"></a>2.Windows配置(FRPC客户端)</h3><p>下载<a href="https://github.com/fatedier/frp/releases/download/v0.54.0/frp_0.54.0_windows_amd64.zip?spm=a2ty_o01.29997173.0.0.4ebc5171G3oahi&file=frp_0.54.0_windows_amd64.zip">frp_0.54.0_windows_amd64.zip</a>并解压到指定文件夹，以<code>E:\frp</code>为例。<br>接着进入<code>E:\frp</code>文件夹中创建<code>frpc.toml</code>，该文件内容如下：</p><figure class="highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">serverAddr</span> = <span class="string">&quot;云服务器公网IP&quot;</span></span><br><span class="line"><span class="attr">serverPort</span> = <span class="number">7000</span> <span class="comment"># 固定端口，无需修改</span></span><br><span class="line"></span><br><span class="line"><span class="attr">auth.method</span> = <span class="string">&quot;token&quot;</span></span><br><span class="line"><span class="attr">auth.token</span> = <span class="string">&quot;strong_password&quot;</span> <span class="comment"># 必须同云服务器端的frps.toml文件一致</span></span><br><span class="line"></span><br><span class="line"><span class="section">[[proxies]]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">&quot;ssh_windows&quot;</span> <span class="comment"># 随意取</span></span><br><span class="line"><span class="attr">type</span> = <span class="string">&quot;tcp&quot;</span></span><br><span class="line"><span class="attr">localIP</span> = <span class="string">&quot;127.0.0.1&quot;</span> <span class="comment"># 固定ip，无需修改</span></span><br><span class="line"><span class="attr">localPort</span> = <span class="number">22</span></span><br><span class="line"><span class="attr">remotePort</span> = <span class="number">6000</span> <span class="comment"># 云服务器映射端口，无需修改</span></span><br></pre></td></tr></table></figure><p>完成上述文件配置后，以<strong>管理员身份</strong>启动终端输入指令<code>cd E:\frp</code>和<code>.\frpc.exe -c frpc.toml</code>，若出现“login to server success”和“start proxy success”则表示配置成功。</p><p>此外可能还需要配置OpenSSH服务的config文件信息，该文件路径为<code>C:\ProgramData\ssh\sshd_config</code>，其中“ProgramData”在Windows中是隐藏文件，需要手动开启。打开<code>sshd_config</code>文件后进行如下修改<code>PasswordAuthentication yes</code>以及<code>PubkeyAuthentication yes</code>，设置完成后再进行SSH连接时就可以支持Windows用户密码验证了。</p><h3 id="3-Mac端"><a href="#3-Mac端" class="headerlink" title="3.Mac端"></a>3.Mac端</h3><p>当云服务端和Windows端配置没有问题后，在Mac终端输入指令<code>ssh Windows用户名@云服务器公网IP</code>即可进行连接。如果想在vs code中进行远程连接，则需要在<code>.ssh\config</code>文件中配置如下信息：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Host 终端名称</span><br><span class="line">    HostName 云服务器公网IP</span><br><span class="line">    User Windows用户名</span><br><span class="line">    Port 6000</span><br></pre></td></tr></table></figure><p>其中<strong>Host</strong>名随意。设置好以后即可在vscode中进行远程终端连接，前提是vs code下载了SSH Remote插件。</p><h2 id="三、最后的话"><a href="#三、最后的话" class="headerlink" title="三、最后的话"></a>三、最后的话</h2><p>在实际配置过程中，不同人会遇到不同的问题，若出现了本文章之外的状况，可以借助AI辅助一下，我相信只要不放弃，总有解决的那一天。</p>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Mac如何连接寝室里充当远程服务器的Windows端？经过自己将近两天的捣鼓，终于把这个问题解决了。感谢Chat GPT、感谢千问，在搭建连接的过程中给予了我莫大的支持。由于过程颇为繁琐，为了以后遇到类似问题自己能够有所参考，于是趁着期末周仅有的休息时间，写下这篇文章，若有差错的地方，恳请海涵指正。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="Guidance" scheme="https://www.hoi3vel.cn/tags/Guidance/"/>
    
    <category term="Deployment" scheme="https://www.hoi3vel.cn/tags/Deployment/"/>
    
    <category term="Cloud Server" scheme="https://www.hoi3vel.cn/tags/Cloud-Server/"/>
    
    <category term="Frp" scheme="https://www.hoi3vel.cn/tags/Frp/"/>
    
    <category term="SSH" scheme="https://www.hoi3vel.cn/tags/SSH/"/>
    
  </entry>
  
  <entry>
    <title>Git知识总结</title>
    <link href="https://www.hoi3vel.cn/2025/12/05/t-git/"/>
    <id>https://www.hoi3vel.cn/2025/12/05/t-git/</id>
    <published>2025-12-05T12:31:00.000Z</published>
    <updated>2025-12-05T14:31:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>最近又开始使用Git工具部署项目了，太久没用很多基本的语法都忘记了，再加上自己博客也好久没更新文章了。于是乎，接着这个机会就对Git知识进行一次总结吧。</p></blockquote><span id="more"></span><h1 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h1><h2 id="1-Git工作流程"><a href="#1-Git工作流程" class="headerlink" title="1.Git工作流程"></a>1.Git工作流程</h2><p><strong>Git</strong>工作区域可以分为四个：①远程仓库；②本地仓库；③暂存区；④Workspace(即本地项目文件夹)。通过使用<code>Git</code>相关指令可以实现本地项目上传至远程仓库，也可以实现从远程仓库中拉项目到本地文件夹中，具体流程图如下：<img src="https://img.hoi3vel.cn/tech/git_01.webp" alt="Git工作流程图"></p><h2 id="2-Git文件状态"><a href="#2-Git文件状态" class="headerlink" title="2.Git文件状态"></a>2.Git文件状态</h2><p><strong>Git</strong>文件拥有4种状态：<br>1.<strong>Untracked</strong>: 这代表此文件在<strong>Workspace</strong>中但并未加到暂存区中；<br>2.<strong>Unmodified</strong>: 这代表该文件已加入到<strong>本地仓库</strong>且此文件版本与<strong>Workspace</strong>中文件版本一致。可使用<code>git rm --cached &lt;fileName&gt;</code>命令将其仅从本地仓库中删除，但是不会影响工作区间中的原文件。<br>3.<strong>Modified</strong>: 这代表该文件版本已改变但并未放入暂存区中。此时有两种操作：①使用<code>git add &lt;fileName&gt;</code>命令将其放入暂存区；②使用<code>git restore &lt;fileName&gt;</code>命令将本地仓库中前版本同名文件调出进而对<strong>Workspace</strong>中的同名文件进行覆盖(即丢弃当前修改)。<br>4.<strong>Staged</strong>: 这代表此文件已经放入暂存区中。此时仍有两种操作：①使用<code>git commit -m &quot;Tips&quot;</code>将其放入本地仓库中，此时本地仓库文件和工作区文件版本一致，此文件状态将转换为<strong>Unmodified</strong>；②使用<code>git reset &lt;fileName&gt;</code>命令取消暂存，此时<strong>Workspace</strong>同名文件状态转化为<strong>Modified</strong>。</p><h2 id="3-忽略文件"><a href="#3-忽略文件" class="headerlink" title="3.忽略文件"></a>3.忽略文件</h2><p><strong>Git</strong>中忽略文件名为<code>.gitignore</code>，位于<strong>Workspace</strong>中。其用途简单来说就是避免不必要的文件上传至远程仓库中。<code>.gitignore</code>文件规则如下：</p><ul><li>忽略文件中的空行或以“#”开始的行会被忽略；</li><li>可以使用Linux通配符。例如，“*”代表任意多字符、“？”代表一个字符、“[]”代表可选字符范围、“{}”代表可选字符串等；</li><li>若名称的最前面有“!”，表示例外规则，将不被忽略；</li><li>若名称的最前面有“&#x2F;”，表示要忽略的文件或文件夹在此目录下，而其它子目录下的同名文件或文件夹则不会被忽略；</li><li>若名称的最后有“&#x2F;”，表示要忽略所有目录下的同名目录，同名文件则不会忽略。</li></ul><p><strong>注意:</strong> “temp&#x2F;*.txt”则代表忽略所有”temp&#x2F;“目录下直属txt文件，但是”temp&#x2F;sub&#x2F;“中的txt文件则不会被忽略。</p><h1 id="二、使用流程"><a href="#二、使用流程" class="headerlink" title="二、使用流程"></a>二、使用流程</h1><h2 id="1-初始化工作间-Workspace"><a href="#1-初始化工作间-Workspace" class="headerlink" title="1.初始化工作间(Workspace)"></a>1.初始化工作间(Workspace)</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">在当前目录新建一个Git代码库</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git init</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">新建一个目录，将其初始化为Git代码库</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git init [project-name]</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载一个项目和它的整个代码历史</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git <span class="built_in">clone</span> [url]</span></span><br></pre></td></tr></table></figure><h2 id="2-全局配置"><a href="#2-全局配置" class="headerlink" title="2.全局配置"></a>2.全局配置</h2><p>首先要进行<strong>SSH Key</strong>配置。只有在远端仓库添加了本地生成的<strong>SSH Key</strong>才能实现在本地与远端仓库的文件拉取。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">1.生成本地SSH Key</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">ssh-keygen -t rsa -C <span class="string">&quot;邮箱地址&quot;</span></span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">2.查看本地SSH Key， 通常在<span class="string">&quot;C/users/用户名/.ssh/&quot;</span>文件夹中，之后再把此文件内容粘贴到远程仓库中</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">cat</span> ~/.ssh/id_rsa.pub</span></span><br></pre></td></tr></table></figure><p><strong>Git</strong>的设置文件为<code>.gitconfig</code>，它可以在用户主目录下(全局配置)，也可以在项目目录下(项目配置)。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">显示当前的Git配置</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git config --list</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">编辑Git配置文件</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git config -e [--global]</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置提交代码时的用户信息</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git config [--global] user.name <span class="string">&quot;用户名&quot;</span></span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git config [--global] user.email <span class="string">&quot;邮箱地址&quot;</span></span></span><br></pre></td></tr></table></figure><h2 id="3-项目提交"><a href="#3-项目提交" class="headerlink" title="3.项目提交"></a>3.项目提交</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">将项目所有文件(.)添加至暂存区</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git add .</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">将项目指定文件或文件夹添加至暂存区</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git add fileName1 fileName2 ...</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">将暂存区文件添加至本地仓库</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git commit -m <span class="string">&quot;备注内容&quot;</span></span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">将文件推送至远程仓库，常用命令“git push origin main”</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git push 远程仓库名 分支名</span></span><br></pre></td></tr></table></figure><h1 id="三、其它常用命令"><a href="#三、其它常用命令" class="headerlink" title="三、其它常用命令"></a>三、其它常用命令</h1><h2 id="1-分支操作"><a href="#1-分支操作" class="headerlink" title="1.分支操作"></a>1.分支操作</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">列出所有分支</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git branch</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">列出所有远程分支</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git branch -r</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">新建一个分支但仍然要停留在当前分支</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git branch 分支名</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">新建一个分支并切换到新分支</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git checkout -b 分支名</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">合并指定分支到当前分支</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git merge 指定分支名</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除分支</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git branch -d 分支名</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除远程分支</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git push origin --delete 远程分支名 || git branch -dr 远程仓库/分支名</span></span><br></pre></td></tr></table></figure><h2 id="2-远程同步"><a href="#2-远程同步" class="headerlink" title="2.远程同步"></a>2.远程同步</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">显示当前origin所指向的远程仓库</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git remote -v</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">更改origin所指向的远程仓库</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git remote set-url origin 新远程仓库地址</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">显示某个远程仓库的信息</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git remote show 远程仓库</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">上传本地指定分支到远程仓库</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git push 远程仓库 本地分支名</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">强行推送当前分支到远程仓库</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git push 远程仓库 --force</span></span><br></pre></td></tr></table></figure><h2 id="3-其它"><a href="#3-其它" class="headerlink" title="3.其它"></a>3.其它</h2><p>详情请见网址<a href="https://gitee.com/all-about-git">Git 大全 - Gitee.com</a></p>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;最近又开始使用Git工具部署项目了，太久没用很多基本的语法都忘记了，再加上自己博客也好久没更新文章了。于是乎，接着这个机会就对Git知识进行一次总结吧。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="Git" scheme="https://www.hoi3vel.cn/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>CMake教程</title>
    <link href="https://www.hoi3vel.cn/2025/06/30/t-cmake/"/>
    <id>https://www.hoi3vel.cn/2025/06/30/t-cmake/</id>
    <published>2025-06-30T05:11:00.000Z</published>
    <updated>2025-11-21T13:28:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>自己在使用CLion集成环境编写C++程序时发现其自动生成了CMakeLists.txt文件，通过查阅相关资料发现其跟CMake有关，于是便写下这篇文章进行相关知识记录方便自己复习。</p></blockquote><span id="more"></span><h1 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h1><p><code>CMake</code>是一个支持跨平台的项目构建工具。另外还有一个更为人熟知的项目构建工具——<strong>Makefile</strong>(通过<code>make</code>命令进行项目的构建)，然而大多数<code>IDE</code>软件都集成了<code>Make</code>，比如：<code>Visual Code</code>的<code>nmake</code>、<code>Linux</code>下的<code>GNU make</code>以及<code>QT</code>的<code>QMake</code>等。<code>Makefile</code>通常依赖于当前的编译平台，有时程序员编写<code>Makefile</code>工作量会比较大且解决依赖关系时也容易出错。而<code>CMake</code>恰好能解决上述问题，其允许开发者指定整个工程的编译流程，再根据编译平台，自动生成本地化的<code>Makefile</code>和工程文件，最后用户只需要<code>make</code>编译即可。其编译流程如下图：<img src="https://img.hoi3vel.cn/tech/Cmake_01.webp" alt="Cmake执行流程图"></p><ul><li>蓝色虚线表示使用<code>Makefile</code>构建项目的过程</li><li>红色实现表示使用<code>CMake</code>构建项目的过程</li></ul><h1 id="二、CMake的使用"><a href="#二、CMake的使用" class="headerlink" title="二、CMake的使用"></a>二、CMake的使用</h1><p><code>CMake</code>支持大写、小写以及混合大小写的命令。如果在编写<code>CMakeLists.txt</code>文件时使用的工具有对应的命令提示，那么大小写随缘即可，无需太过在意。</p><h2 id="2-1-注释"><a href="#2-1-注释" class="headerlink" title="2.1 注释"></a>2.1 注释</h2><ul><li>注释行：<code>CMake</code>中使用<code>#</code>实现行注释，可以放在任何位置</li><li>注释块：<code>CMake</code>中使用<code>#[[]]</code>实现块注释</li></ul><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># 这是一个CMakeLists.txt文件</span><br><span class="line">cmake_minimum_required(VERSION 3.0.0)</span><br><span class="line"></span><br><span class="line">#[[这是一个CMakeLists.txt文件</span><br><span class="line">这是一个CMakeLists.txt文件</span><br><span class="line">这是一个CMakeLists.txt文件]]</span><br><span class="line">cmake_minimum_required(VERSION 3.0.0)</span><br></pre></td></tr></table></figure><h2 id="2-2-CMakeLists-txt文件编写"><a href="#2-2-CMakeLists-txt文件编写" class="headerlink" title="2.2 CMakeLists.txt文件编写"></a>2.2 CMakeLists.txt文件编写</h2><p>假设源文件夹中文件结构如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">tree</span></span><br><span class="line">.</span><br><span class="line">├── add.c</span><br><span class="line">├── div.c</span><br><span class="line">├── head.h</span><br><span class="line">├── main.c</span><br><span class="line">├── multi.c</span><br><span class="line">└── sub.c</span><br></pre></td></tr></table></figure><h3 id="2-2-1-生成的配置文件不单独放入源文件夹中的子文件中"><a href="#2-2-1-生成的配置文件不单独放入源文件夹中的子文件中" class="headerlink" title="2.2.1 生成的配置文件不单独放入源文件夹中的子文件中"></a>2.2.1 生成的配置文件不单独放入源文件夹中的子文件中</h3><p>在上述源文件夹中添加<code>CMakeLists.txt</code>。文件内容如下：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cmake_minimum_required(VERSION 3.0)</span><br><span class="line">project(CALC)</span><br><span class="line">add_executable(app add.c div.c main.c multi.c sub.c)</span><br></pre></td></tr></table></figure><p>上述文件中三个命令含义如下：</p><ul><li><code>cmake_minimum_required</code>: 指定使用的<code>CMake</code>最低版本。(改命令可选，若不加可能会有警告!)</li><li><code>project</code>: 定义工程的名称并可指定工程的版本、工程描述、web主页地址、支持的语言(默认支持所有的语言)。若不需要则可省略，只需指定出工程名即可。</li><li><code>add_executable</code>: 定义工程生成的可执行程序名字。(这里的可执行程序名字与项目名字无关)</li></ul><p>上述命令官方语法如下:</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"># PROJECT 指令的语法是：</span><br><span class="line">project(&lt;PROJECT-NAME&gt; [&lt;language-name&gt;...])</span><br><span class="line">project(&lt;PROJECT-NAME&gt;</span><br><span class="line">       [VERSION &lt;major&gt;[.&lt;minor&gt;[.&lt;patch&gt;[.&lt;tweak&gt;]]]]</span><br><span class="line">       [DESCRIPTION &lt;project-description-string&gt;]</span><br><span class="line">       [HOMEPAGE_URL &lt;url-string&gt;]</span><br><span class="line">       [LANGUAGES &lt;language-name&gt;...])</span><br><span class="line">add_executable(可执行程序名 源文件名称)</span><br><span class="line"># 样式1</span><br><span class="line">add_executable(app add.c div.c main.c multi.c sub.c)</span><br><span class="line"># 样式2</span><br><span class="line">add_executable(app add.c;div.c;main.c;multi.c;sub.c)</span><br></pre></td></tr></table></figure><p>Ps: 源文件名称可以有多个，若有多个可用<code>空格</code>或<code>;</code>隔开<br>在控制台执行<code>cmake CMakeLists.txt文件所在路径</code>后，<code>CMakeLists.txt</code>中的命令就会被执行。执行之后，源文件夹中的文件结构如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">tree -L 1</span></span><br><span class="line">.</span><br><span class="line">├── add.c</span><br><span class="line">├── CMakeCache.txt         # new add file</span><br><span class="line">├── CMakeFiles             # new add dir</span><br><span class="line">├── cmake_install.cmake    # new add file</span><br><span class="line">├── CMakeLists.txt</span><br><span class="line">├── div.c</span><br><span class="line">├── head.h</span><br><span class="line">├── main.c</span><br><span class="line">├── Makefile               # new add file</span><br><span class="line">├── multi.c</span><br><span class="line">└── sub.c</span><br></pre></td></tr></table></figure><p>可以看见在对应目录下生成了一个<code>Makefile</code>文件，这时再在控制台中执行<code>make</code>命令就可以对项目进行构建得到所需要的可执行程序了。结果如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">make</span></span><br><span class="line">Scanning dependencies of target app</span><br><span class="line">[ 16%] Building C object CMakeFiles/app.dir/add.c.o</span><br><span class="line">[ 33%] Building C object CMakeFiles/app.dir/div.c.o</span><br><span class="line">[ 50%] Building C object CMakeFiles/app.dir/main.c.o</span><br><span class="line">[ 66%] Building C object CMakeFiles/app.dir/multi.c.o</span><br><span class="line">[ 83%] Building C object CMakeFiles/app.dir/sub.c.o</span><br><span class="line"><span class="meta prompt_">[100%</span><span class="language-bash">] Linking C executable app</span></span><br><span class="line"><span class="meta prompt_">[100%</span><span class="language-bash">] Built target app</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看可执行程序是否已经生成</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">tree -L 1</span></span><br><span class="line">.</span><br><span class="line">├── add.c</span><br><span class="line">├── app# 生成的可执行程序</span><br><span class="line">├── CMakeCache.txt</span><br><span class="line">├── CMakeFiles</span><br><span class="line">├── cmake_install.cmake</span><br><span class="line">├── CMakeLists.txt</span><br><span class="line">├── div.c</span><br><span class="line">├── head.h</span><br><span class="line">├── main.c</span><br><span class="line">├── Makefile</span><br><span class="line">├── multi.c</span><br><span class="line">└── sub.c</span><br></pre></td></tr></table></figure><p>最终可执行程序<code>app</code>就被编译出来了，其名字由<code>CMakeLists.txt</code>中指定。</p><h3 id="2-2-2-生成的配置文件单独放入源文件夹中的子文件中"><a href="#2-2-2-生成的配置文件单独放入源文件夹中的子文件中" class="headerlink" title="2.2.2 生成的配置文件单独放入源文件夹中的子文件中"></a>2.2.2 生成的配置文件单独放入源文件夹中的子文件中</h3><p>在上面的例子中，若<code>CMakeLists.txt</code>文件所在目录执行了<code>cmake</code>命令之后就会生成一些目录和文件(包括<code>makefile</code>)文件。若再基于<code>makefile</code>文件执行<code>make</code>命令，程序在编译过程中还会产生一些中间文件和可执行文件，这样会导致整个项目目录看起来很混乱，不太容易管理和维护，此时我们就可以把生成的这些与项目源码无关的文件统一放到一个对应的目录里边，通常将整个目录命名为<code>build</code>。执行代码以及执行结果为：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">mkdir</span> build</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">cd</span> build</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">cmake ..</span></span><br><span class="line">-- The C compiler identification is GNU 5.4.0</span><br><span class="line">-- The CXX compiler identification is GNU 5.4.0</span><br><span class="line">-- Check for working C compiler: /usr/bin/cc</span><br><span class="line">-- Check for working C compiler: /usr/bin/cc -- works</span><br><span class="line">-- Detecting C compiler ABI info</span><br><span class="line">-- Detecting C compiler ABI info - done</span><br><span class="line">-- Detecting C compile features</span><br><span class="line">-- Detecting C compile features - done</span><br><span class="line">-- Check for working CXX compiler: /usr/bin/c++</span><br><span class="line">-- Check for working CXX compiler: /usr/bin/c++ -- works</span><br><span class="line">-- Detecting CXX compiler ABI info</span><br><span class="line">-- Detecting CXX compiler ABI info - done</span><br><span class="line">-- Detecting CXX compile features</span><br><span class="line">-- Detecting CXX compile features - done</span><br><span class="line">-- Configuring done</span><br><span class="line">-- Generating done</span><br><span class="line">-- Build files have been written to: /home/robin/Linux/build</span><br></pre></td></tr></table></figure><p>在上面的代码中，首先创建了一个目录<code>build</code>并进入至该目录中，然后再再<code>build</code>目录中执行<code>cmake</code>命令，由于<code>CMakeLists.txt</code>文件中<code>build</code>的上级目录中，因此执行<code>Shell</code>指令为<code>cmake ..</code>。(<code>..</code>代表当前目录的上级目录)<br>当上述命令执行完毕后，在<code>build</code>目录中会生成一个<code>makefile</code>文件。该目录下的文件结构如下:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">tree build -L 1</span></span><br><span class="line">build</span><br><span class="line">├── CMakeCache.txt</span><br><span class="line">├── CMakeFiles</span><br><span class="line">├── cmake_install.cmake</span><br><span class="line">└── Makefile</span><br><span class="line"></span><br><span class="line">1 directory, 3 files</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这样就可以在<code>build</code>目录中执行<code>make</code>命令编译项目，生成的相关文件自然也就被存储到<code>build</code>目录中了。</p><h2 id="2-3-进阶语法"><a href="#2-3-进阶语法" class="headerlink" title="2.3 进阶语法"></a>2.3 进阶语法</h2><p>在上面的例子中一共提供了5个源文件，假设这五个源文件需要被反复使用，每次都直接将它们的名字写出来比较麻烦，这时我们需要定义一个变量，该变量可将这些文件名存储起来，在<code>CMake</code>中定义变量需要使用关键字<code>set</code>。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># SET指令语法：</span><br><span class="line"># []中的参数为可选项</span><br><span class="line">SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])</span><br></pre></td></tr></table></figure><ul><li>VAR: 变量名</li><li>VALUE: 变量值</li></ul><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># 方式一: 各个源文件之间使用空格间隔</span><br><span class="line"># set(SRC_LIST add.c div.c main.c multi.c sub.c)</span><br><span class="line"></span><br><span class="line"># 方式二: 各个源文件之间使用分号;间隔</span><br><span class="line">set(SRC_LIST add.c;div.c;main.c;multi.c;sub.c)</span><br><span class="line">add_executable(app $&#123;SRC_LIST&#125;)</span><br></pre></td></tr></table></figure><h3 id="2-3-1-指定使用C-标准"><a href="#2-3-1-指定使用C-标准" class="headerlink" title="2.3.1 指定使用C++标准"></a>2.3.1 指定使用C++标准</h3><p>在编写C++程序时可能会用到C++11、C++14、C++17、C++20等新特性，那么就需要在编译的时候指定出要使用哪个标准：<code>g++ *.cpp -std=c++11 -o app</code>。此命令通过<code>-std=c++11</code>指定出要使用C++11编译程序，C++标准对应有一宏叫做<code>DCMAKE_CXX_STANDARD</code>。在<code>CMake</code>中想要指定C++有两种方式：</p><ul><li>方式一：在CMakeLists.txt中通过set命令指定<figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># 增加-std=c++11</span><br><span class="line">set(CMAKE_CXX_STANDARD 11)</span><br><span class="line"># 增加-std=c++14</span><br><span class="line">set(CMAKE_CXX_STANDARD 14)</span><br><span class="line"># 增加-std=c++17</span><br><span class="line">set(CMAKE_CXX_STANDARD 17)</span><br></pre></td></tr></table></figure></li><li>方式二：在执行<code>cmake</code>命令时指定出这个宏的值<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">增加-std=c++11</span></span><br><span class="line">cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=11</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">增加-std=c++14</span></span><br><span class="line">cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=14</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">增加-std=c++17</span></span><br><span class="line">cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=17</span><br></pre></td></tr></table></figure></li></ul><h3 id="2-3-2-指定输出的路径"><a href="#2-3-2-指定输出的路径" class="headerlink" title="2.3.2 指定输出的路径"></a>2.3.2 指定输出的路径</h3><p>在<code>CMake</code>中指定可执行程序输出的路径，也对应一个宏叫做<code>EXECUTABLE_OUTPUT_PATH</code>，它的值还是通过<code>set</code>命令进行设置。</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span>(HOME /home/robin/Linux/Sort)</span><br><span class="line"><span class="keyword">set</span>(EXECUTABLE_OUTPUT_PATH <span class="variable">$&#123;HOME&#125;</span>/bin)</span><br></pre></td></tr></table></figure><ul><li>第一行：定义一个变量用于存储一个绝对路径。</li><li>第二行：将拼接好的路径值设置给EXECUTABLE_OUTPUT_PATH&#96;宏。（若这个路径的子目录不存在则会自动生成）<br>由于可执行程序是基于 cmake 命令生成的 makefile 文件然后再执行 make 命令得到的，所以如果此处指定可执行程序生成路径的时候使用的是相对路径 .&#x2F;xxx&#x2F;xxx，那么这个路径中的 .&#x2F; 对应的就是 makefile 文件所在的那个目录。</li></ul><h3 id="2-3-3-搜索文件"><a href="#2-3-3-搜索文件" class="headerlink" title="2.3.3 搜索文件"></a>2.3.3 搜索文件</h3><p>若一个项目里边的源文件很多，在编写<code>CMakeLists.txt</code>文件的时候不可能将项目目录的各个文件一一罗列出来。在<code>CMake</code>中提供了文件搜索命令，可以使用<code>aux_source_directory</code>命令或者<code>file</code>命令。</p><ul><li>方式一：在<code>CMake</code>中使用<code>aux_source_directory</code>命令可以查找某个路径下的的所有源文件。<figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">aux_source_directory(&lt;dir&gt; &lt;variable&gt;)</span><br></pre></td></tr></table></figure></li><li>dir: 要搜索的目录</li><li>variable: 将从<code>dir</code>目录下搜索到的源文件列表存储到该变量中</li></ul><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">cmake_minimum_required</span>(VERSION <span class="number">3.0</span>)</span><br><span class="line"><span class="keyword">project</span>(CALC)</span><br><span class="line"><span class="keyword">include_directories</span>(<span class="variable">$&#123;PROJECT_SOURCE_DIR&#125;</span>/<span class="keyword">include</span>)</span><br><span class="line"><span class="comment"># 搜索 src 目录下的源文件</span></span><br><span class="line"><span class="keyword">aux_source_directory</span>(<span class="variable">$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;</span>/src SRC_LIST)</span><br><span class="line"><span class="keyword">add_executable</span>(app  <span class="variable">$&#123;SRC_LIST&#125;</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li>方式二：采用<code>file</code>命令<figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)</span><br></pre></td></tr></table></figure></li><li><code>GLOB</code>: 将指定目录下搜索到满足条件的所有文件名生成一个列表并将其存储到变量中。</li><li><code>GLOB_RECURSE</code>: 递归搜索指定目录，将搜索到的满足条件的文件名生成一个列表并将其存储到变量中。</li></ul><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">file(GLOB MAIN_SRC $&#123;CMAKE_CURRENT_SOURCE_DIR&#125;/src/*.cpp)</span><br><span class="line">file(GLOB MAIN_HEAD $&#123;CMAKE_CURRENT_SOURCE_DIR&#125;/include/*.h)</span><br><span class="line"># file(GLOB MAIN_HEAD &quot;$&#123;CMAKE_CURRENT_SOURCE_DIR&#125;/src/*.h&quot;)</span><br></pre></td></tr></table></figure><p>搜索当前目录的src目录下所有源文件并存储到变量中。</p><h1 id="三、最后的话"><a href="#三、最后的话" class="headerlink" title="三、最后的话"></a>三、最后的话</h1><p>以上是常用基础的<code>CMake</code>命令，还有很多需要完善的地方，后续会慢慢补充…</p>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;自己在使用CLion集成环境编写C++程序时发现其自动生成了CMakeLists.txt文件，通过查阅相关资料发现其跟CMake有关，于是便写下这篇文章进行相关知识记录方便自己复习。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="CMake" scheme="https://www.hoi3vel.cn/tags/CMake/"/>
    
    <category term="Guidance" scheme="https://www.hoi3vel.cn/tags/Guidance/"/>
    
  </entry>
  
  <entry>
    <title>淘宝电商平台商品信息爬取</title>
    <link href="https://www.hoi3vel.cn/2025/04/18/t-webScraping/"/>
    <id>https://www.hoi3vel.cn/2025/04/18/t-webScraping/</id>
    <published>2025-04-18T06:41:00.000Z</published>
    <updated>2025-11-21T13:33:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>为了之后的研究生生活，最近开始学习Python这门语言。刚刚过完了一遍Python基础语法，可是脑子还是感觉空空的。太难的项目暂时又写不出来，于是便尝试编写一个简单的爬虫程序，一是为了巩固Python基础，二是重新拾起爬虫的相关知识。本篇文章基于淘宝电商平台编写爬取电脑商品信息的爬虫程序。</p></blockquote><span id="more"></span><h2 id="一、写在前面"><a href="#一、写在前面" class="headerlink" title="一、写在前面"></a>一、写在前面</h2><p>爬虫(Web Crawler)是一种基于规则对网络中文本、图片等信息进行自动爬取的程序。其主要思想是通过模拟真实用户，向服务器发送请求进而持续对网页数据进行抓取，直到达到某一条件时停止。<br>本篇教程相关环境如下:</p><ul><li>Python 3.10.11</li><li>requets模块 2.31.0</li></ul><h2 id="二、关键步骤"><a href="#二、关键步骤" class="headerlink" title="二、关键步骤"></a>二、关键步骤</h2><p>爬虫基本工作流程主要有三个：①明确需求——要采集的网站地址以及目标数据内容；②抓包分析——打开浏览器自带的开发者工具获取包数据网址；③按指定需求对数据进行处理；</p><h3 id="1-导入相关模块"><a href="#1-导入相关模块" class="headerlink" title="1.导入相关模块"></a>1.导入相关模块</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"><span class="keyword">import</span> time</span><br></pre></td></tr></table></figure><ul><li>requests模块: 用于发送HTTP请求(get&#x2F;post)</li><li>json模块: 数据一般存储在JSON文件中，而该模块可实现将JSON数据转化为Python中的字典类型，方便数据处理</li><li>re模块: 该模块支持使用正则表达式对文本内容的提取，方便数据处理</li><li>hashlib模块: 存放MD5等数据加密函数，用于淘宝平台中<strong>Sign</strong>参数的加密逆向分析</li><li>csv模块: 类似Excel表格，用于数据保存</li><li>time模块: Python内置时间模块，用于获取时间戳以及模拟用户浏览操作</li></ul><h3 id="2-模拟浏览器"><a href="#2-模拟浏览器" class="headerlink" title="2.模拟浏览器"></a>2.模拟浏览器</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">headers = &#123;</span><br><span class="line">    <span class="string">&#x27;cookie&#x27;</span>: <span class="string">&#x27;cna=zyCGIBdAVWICAXjHIncvopiS; thw=cn; t=b218f49912339b0ceae556e0090e211b; xlly_s=1; _tb_token_=7a5b15963ff84; cookie2=16651aea2c313a912b01cb164e4e08a0; 3PcFlag=1744784663786; _hvn_lgc_=0; cookie3_bak=16651aea2c313a912b01cb164e4e08a0; unb=3971315337; lgc=%5Cu4E00%5Cu8F6E%5Cu5F2F%5Cu6708%5Cu4E00%5Cu7F15%5Cu76F8%5Cu601D; cancelledSubSites=empty; env_bak=FM%2Bgnk3pmwgJGNrZ6%2FSkIJoxCwTm6VI1qZ6TClrxgOc3; cookie17=UNkwcc4KQQFHcg%3D%3D; dnk=hoi3vel; tracknick=%5Cu4E00%5Cu8F6E%5Cu5F2F%5Cu6708%5Cu4E00%5Cu7F15%5Cu76F8%5Cu601D; _l_g_=Ug%3D%3D; sg=%E6%80%9D74; _nk_=%5Cu4E00%5Cu8F6E%5Cu5F2F%5Cu6708%5Cu4E00%5Cu7F15%5Cu76F8%5Cu601D; cookie1=U%2BIlWIeHLSADvBxDCm0kQKQsca6SNqfz%2FIpTHfq3ZQw%3D; sgcookie=E1003OSkTgC9VlYpf2%2FnITYkh7VA%2BCuHtM4rpO%2B0DYpiEHhSZi8%2B46SUAIXRls1C9TuK6mE77ohQhH3D7Vr1b4um%2BPEe%2BqJJGCi4WxE%2BQnQE0fQ%3D; havana_lgc2_0=eyJoaWQiOjM5NzEzMTUzMzcsInNnIjoiYWU2MzZmZDI5YjQ2MDNlNGUxMGU1YTkyYThhNjYyOTEiLCJzaXRlIjowLCJ0b2tlbiI6IjFjUWNFWDNhWm9oMl9BMHFTMzRtNUhnIn0; havana_lgc_exp=1775888706045; cookie3_bak_exp=1745043906045; wk_cookie2=1b3ee28d98c093084dd38ee1285362b2; wk_unb=UNkwcc4KQQFHcg%3D%3D; uc1=cookie16=U%2BGCWk%2F74Mx5tgzv3dWpnhjPaQ%3D%3D&amp;pas=0&amp;cookie14=UoYaj4%2Bx2t0h1w%3D%3D&amp;existShop=false&amp;cookie15=V32FPkk%2Fw0dUvg%3D%3D&amp;cookie21=URm48syIYB3rzvI4Dim4; sn=; uc3=id2=UNkwcc4KQQFHcg%3D%3D&amp;nk2=saDbd13P348Tq%2FXU0Jk%2FOg%3D%3D&amp;vt3=F8dD2EuMY7YkvXA3Qzs%3D&amp;lg2=VT5L2FSpMGV7TQ%3D%3D; csg=b9bc66e6; skt=ce2a91bede66482d; existShop=MTc0NDc4NDcwNg%3D%3D; uc4=nk4=0%40s8Wb7PJZDAD5%2BPfdt88U4O4L5YsttBggT1RI&amp;id4=0%40Ug46vpy6gA%2FUJ82j39S%2FC20NcRKR; _cc_=VFC%2FuZ9ajQ%3D%3D; _samesite_flag_=true; havana_sdkSilent=1744958760754; sdkSilent=1744970016688; mtop_partitioned_detect=1; _m_h5_tk=bd25aec960a5af5dae87a57bd23a76fe_1744909656357; _m_h5_tk_enc=ffb07dba49e6d2242032897636cd438c; tfstk=gGwrwi1R4TBPOMu-Z-Me_JQFb_H-Bv71UJgIxkqnV40oPkmnLr4C24ZhyyyEokF52X_JY3e47p95y_EHLvMh5N61CuESpvbsLYD804nLm2jjZUroe3Dh5N6XGncRPv4CcYla3Imto0mnqXjqnDmiKLcu-qcmjcToKy4nmjmIj30nZvcDiqnnK24nKikmkDMn-jUK-V9qbbjImYz_o14iaVqoudrY30Dewu02Kp24gbuMCqJHK-oucQngid5IS53S12zchLkUmDzq177wUyr3XWc4LUj7SrVU-mFA8BuatuNTtjSlEokrzjo7lhAqUW48LbFyvGEmErFtWbfAkmy7CXutgEbucouo_Wz5kp07_o4q1-TXCAVTuJliIg8JJm4GYJFy-BloDmu10ir891NT8FfC5BdKwSnq5gIJ9BhoDmu10iRp9bHx0VsR2; isg=BEJCPyO0nhwnFY2-jgYMqgg3k0ikE0YtYJe0OYxbdrVg3-JZdKCMPZIZj9ujiL7F&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;user-agent&#x27;</span>: <span class="string">&#x27;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li>cookie参数: 存放用户信息，用于检测是否有登录账号信息(登录与否都有相关信息)</li><li>user-agent参数: 用户代理，表示访问设备的基本信息</li></ul><p>两个参数获取途径如下：<br>①打开淘宝平台点击<a href="https://s.taobao.com/search?page=1&q=%E7%94%B5%E8%84%91&spm=a21bo.jianhua/a.201867-main.d2_first.5af92a89pa9CHZ&tab=all">电脑区</a>页面；</p><p>②<strong>F2</strong>或鼠标右键点击<strong>检查</strong>打开浏览器开发者工具。开发者工具界面如图：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1920/912;"><img class="lazy" src="https://img.hoi3vel.cn/tech/2025_04_18_02_01.webp" data-src="https://img.hoi3vel.cn/tech/2025_04_18_02_01.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div></p><p>③在开发者工具中选择 <strong>网络(network)</strong> 并刷新页面。操作如图：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1846/957;"><img class="lazy" src="https://img.hoi3vel.cn/tech/2025_04_18_02_02.webp" data-src="https://img.hoi3vel.cn/tech/2025_04_18_02_02.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div></p><p>④在开发者工具搜索栏中搜索任意商品名获取商品信息对应的JSON文件，点击搜索内容自动跳转至对应JSON文件，点击<strong>标头</strong>即可在此页面中依次找到cookie和user-agent参数。操作如图：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1838/904;"><img class="lazy" src="https://img.hoi3vel.cn/tech/2025_04_18_02_03.webp" data-src="https://img.hoi3vel.cn/tech/2025_04_18_02_03.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div></p><h3 id="3-获取数据包请求地址"><a href="#3-获取数据包请求地址" class="headerlink" title="3.获取数据包请求地址"></a>3.获取数据包请求地址</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">url = <span class="string">&#x27;https://h5api.m.taobao.com/h5/mtop.relationrecommend.wirelessrecommend.recommend/2.0/?&#x27;</span></span><br></pre></td></tr></table></figure><p><strong>Get</strong>请求的url地址参数直接在链接中(即?后部分)，通常有两种构造形式：①直接将请求地址存储在一个变量中；②额外构建查询参数请求(链接参数过长时使用)；</p><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1920/681;"><img class="lazy" src="https://img.hoi3vel.cn/tech/2025_04_18_03_01.webp" data-src="https://img.hoi3vel.cn/tech/2025_04_18_03_01.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div>在上图中，序号2地址即为完整数据包请求地址，序号1即为不带参数的数据包请求地址，此时需要额外构建查询请求参数。查询参数可在开发者工具中**载荷**页面查看，如图：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1920/678;"><img class="lazy" src="https://img.hoi3vel.cn/tech/2025_04_18_03_02.webp" data-src="https://img.hoi3vel.cn/tech/2025_04_18_03_02.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 构建查询参数</span></span><br><span class="line">url_params = &#123;</span><br><span class="line">    <span class="string">&#x27;jsv&#x27;</span>: <span class="string">&#x27;2.7.4&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;appKey&#x27;</span>: <span class="string">&#x27;12574478&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;t&#x27;</span>: eT,</span><br><span class="line">    <span class="string">&#x27;sign&#x27;</span>: sign,</span><br><span class="line">    <span class="string">&#x27;api&#x27;</span>: <span class="string">&#x27;mtop.relationrecommend.wirelessrecommend.recommend&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;v&#x27;</span>: <span class="string">&#x27;2.0&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;type&#x27;</span>: <span class="string">&#x27;jsonp&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;dataType&#x27;</span>: <span class="string">&#x27;jsonp&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;callback&#x27;</span>: <span class="string">&#x27;mtopjsonp5&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;data&#x27;</span>: ep_data,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在上述代码中，<code>t</code>、<code>sign</code>和<code>data</code>是实时参数，因此需要根据时间戳随时变化。</p><h3 id="4-查询参数Sign的逆向解密"><a href="#4-查询参数Sign的逆向解密" class="headerlink" title="4.查询参数Sign的逆向解密"></a>4.查询参数Sign的逆向解密</h3><p>淘宝平台的<code>sign</code>参数采用了MD5进行加密，此时我们需要对其解密。而解密的关键则是获取淘宝<code>sign</code>加密参数。获取思路则是通过在开发者工具搜索<code>sign:</code>找到相应的<code>main.js</code>文件并在<strong>“来源”</strong>面板中打开。如下图：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1920/673;"><img class="lazy" src="https://img.hoi3vel.cn/tech/2025_04_18_04_01.webp" data-src="https://img.hoi3vel.cn/tech/2025_04_18_04_01.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div></p><p>在来源面板中<code>Ctr+F</code>打开搜索按钮，输入<code>sign:</code>进行查询。如下图：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1920/679;"><img class="lazy" src="https://img.hoi3vel.cn/tech/2025_04_18_04_02.webp" data-src="https://img.hoi3vel.cn/tech/2025_04_18_04_02.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div>上图蓝色处即为断点也是淘宝<code>sign</code>加密参数即<code>eE(em.token + &quot;&amp;&quot; + eT + &quot;&amp;&quot; + eC + &quot;&amp;&quot; + ep.data)</code>。其中<code>eT</code>为时间戳，可用time模块进行模拟，而<code>em.token</code>、<code>eC</code>和<code>ep.data</code>参数可在控制台中输入相关参数进行获取对应的值。如下图: <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1920/685;"><img class="lazy" src="https://img.hoi3vel.cn/tech/2025_04_18_04_03.webp" data-src="https://img.hoi3vel.cn/tech/2025_04_18_04_03.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div><br>需要注意的<code>ep.data</code>参数中有些参数随着浏览页码的改变而改变，具体有<code>page</code>、<code>totalResults</code>、<code>sourceS</code>、<code>bcoffset</code>以及<code>ntoffset</code>参数，这些参数存放在上页JSON文件数据中。</p><h3 id="5-查找JSON文件中的目标数据位置"><a href="#5-查找JSON文件中的目标数据位置" class="headerlink" title="5.查找JSON文件中的目标数据位置"></a>5.查找JSON文件中的目标数据位置</h3><p>在开发者工具中点击<strong>网络(network)<strong>模块，并搜索任意商品数据定位至指定JSON数据文件，在</strong>预览</strong>块中可查看目标数据。如下图: <div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1920/682;"><img class="lazy" src="https://img.hoi3vel.cn/tech/2025_04_18_05_01.webp" data-src="https://img.hoi3vel.cn/tech/2025_04_18_05_01.webp" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div></div></p><h2 id="三、具体实现代码"><a href="#三、具体实现代码" class="headerlink" title="三、具体实现代码"></a>三、具体实现代码</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;Sign加密逆向分析</span></span><br><span class="line"><span class="string">    eE(em.token + &quot;&amp;&quot; + eT + &quot;&amp;&quot; + eC + &quot;&amp;&quot; + ep.data)</span></span><br><span class="line"><span class="string">    eT--时间戳, eC--appKey</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getSign</span>(<span class="params">eT, page, totalResults, sourceS, bc_offset, nt_offset</span>):</span><br><span class="line">    em_token = <span class="string">&#x27;bd25aec960a5af5dae87a57bd23a76fe&#x27;</span> <span class="comment"># 固定一个</span></span><br><span class="line">    eC = <span class="string">&#x27;12574478&#x27;</span> <span class="comment"># 固定</span></span><br><span class="line">    sign_params = &#123;</span><br><span class="line">        <span class="string">&quot;device&quot;</span>: <span class="string">&quot;HMA - AL00 &quot;</span>,</span><br><span class="line">        <span class="string">&quot;isBeta&quot;</span>: <span class="string">&quot;false&quot;</span>,</span><br><span class="line">        <span class="string">&quot;grayHair&quot;</span>: <span class="string">&quot;false&quot;</span>,</span><br><span class="line">        <span class="string">&quot;from&quot;</span>: <span class="string">&quot;nt_history&quot;</span>,</span><br><span class="line">        <span class="string">&quot;brand&quot;</span>: <span class="string">&quot;HUAWEI&quot;</span>,</span><br><span class="line">        <span class="string">&quot;info&quot;</span>: <span class="string">&quot;wifi&quot;</span>,</span><br><span class="line">        <span class="string">&quot;index&quot;</span>: <span class="string">&quot;4&quot;</span>,</span><br><span class="line">        <span class="string">&quot;rainbow&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">        <span class="string">&quot;schemaType&quot;</span>: <span class="string">&quot;auction&quot;</span>,</span><br><span class="line">        <span class="string">&quot;elderHome&quot;</span>: <span class="string">&quot;false&quot;</span>,</span><br><span class="line">        <span class="string">&quot;isEnterSrpSearch&quot;</span>: <span class="string">&quot;true&quot;</span>,</span><br><span class="line">        <span class="string">&quot;newSearch&quot;</span>: <span class="string">&quot;false&quot;</span>,</span><br><span class="line">        <span class="string">&quot;network&quot;</span>: <span class="string">&quot;wifi&quot;</span>,</span><br><span class="line">        <span class="string">&quot;subtype&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">        <span class="string">&quot;hasPreposeFilter&quot;</span>: <span class="string">&quot;false&quot;</span>,</span><br><span class="line">        <span class="string">&quot;prepositionVersion&quot;</span>: <span class="string">&quot;v2&quot;</span>,</span><br><span class="line">        <span class="string">&quot;client_os&quot;</span>: <span class="string">&quot;Android &quot;</span>,</span><br><span class="line">        <span class="string">&quot;gpsEnabled&quot;</span>:<span class="string">&quot;false&quot;</span>,</span><br><span class="line">        <span class="string">&quot;searchDoorFrom&quot;</span>: <span class="string">&quot;srp&quot;</span>,</span><br><span class="line">        <span class="string">&quot;debug_rerankNewOpenCard&quot;</span>: <span class="string">&quot;false&quot;</span>,</span><br><span class="line">        <span class="string">&quot;homePageVersion&quot;</span>: <span class="string">&quot;v7&quot;</span>,</span><br><span class="line">        <span class="string">&quot;searchElderHomeOpen&quot;</span>: <span class="string">&quot;false&quot;</span>,</span><br><span class="line">        <span class="string">&quot;search_action&quot;</span>: <span class="string">&quot;initiative&quot;</span>,</span><br><span class="line">        <span class="string">&quot;sugg&quot;</span>: <span class="string">&quot;_4_1&quot;</span>,</span><br><span class="line">        <span class="string">&quot;sversion&quot;</span>: <span class="string">&quot;13.6&quot;</span>,</span><br><span class="line">        <span class="string">&quot;style&quot;</span>: <span class="string">&quot;list&quot;</span>,</span><br><span class="line">        <span class="string">&quot;ttid&quot;</span>: <span class="string">&quot;600000@taobao_pc_10.7.0&quot;</span>,</span><br><span class="line">        <span class="string">&quot;needTabs&quot;</span>: <span class="string">&quot;true&quot;</span>,</span><br><span class="line">        <span class="string">&quot;areaCode&quot;</span>: <span class="string">&quot;CN&quot;</span>,</span><br><span class="line">        <span class="string">&quot;vm&quot;</span>: <span class="string">&quot;nw&quot;</span>,</span><br><span class="line">        <span class="string">&quot;countryNum&quot;</span>: <span class="string">&quot;156&quot;</span>,</span><br><span class="line">        <span class="string">&quot;m&quot;</span>: <span class="string">&quot;pc&quot;</span>,</span><br><span class="line">        <span class="string">&quot;page&quot;</span>: page, <span class="comment"># 翻页变参数</span></span><br><span class="line">        <span class="string">&quot;n&quot;</span>: <span class="number">48</span>,</span><br><span class="line">        <span class="string">&quot;q&quot;</span>: <span class="string">&quot;%E7%94%B5%E8%84%91&quot;</span>,</span><br><span class="line">        <span class="string">&quot;qSource&quot;</span>: <span class="string">&quot;url&quot;</span>,</span><br><span class="line">        <span class="string">&quot;pageSource&quot;</span>: <span class="string">&quot;a21bo.jianhua/a.201867-main.d2_first.5af92a89INtItN&quot;</span>,</span><br><span class="line">        <span class="string">&quot;tab&quot;</span>: <span class="string">&quot;all&quot;</span>,</span><br><span class="line">        <span class="string">&quot;pageSize&quot;</span>: <span class="string">&quot;48&quot;</span>,</span><br><span class="line">        <span class="string">&quot;totalPage&quot;</span>: <span class="string">&quot;100&quot;</span>,</span><br><span class="line">        <span class="string">&quot;totalResults&quot;</span>: totalResults, <span class="comment"># 翻页变参数</span></span><br><span class="line">        <span class="string">&quot;sourceS&quot;</span>: sourceS, <span class="comment"># 翻页变参数</span></span><br><span class="line">        <span class="string">&quot;sort&quot;</span>: <span class="string">&quot;_coefp&quot;</span>,</span><br><span class="line">        <span class="string">&quot;bcoffset&quot;</span>: bc_offset, <span class="comment"># 翻页变参数</span></span><br><span class="line">        <span class="string">&quot;ntoffset&quot;</span>: nt_offset, <span class="comment"># 翻页变参数</span></span><br><span class="line">        <span class="string">&quot;filterTag&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">        <span class="string">&quot;service&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">        <span class="string">&quot;prop&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">        <span class="string">&quot;loc&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">        <span class="string">&quot;start_price&quot;</span>: <span class="literal">None</span>,</span><br><span class="line">        <span class="string">&quot;end_price&quot;</span>: <span class="literal">None</span>,</span><br><span class="line">        <span class="string">&quot;startPrice&quot;</span>: <span class="literal">None</span>,</span><br><span class="line">        <span class="string">&quot;endPrice&quot;</span>: <span class="literal">None</span>,</span><br><span class="line">        <span class="string">&quot;categoryp&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">        <span class="string">&quot;ha3Kvpairs&quot;</span>: <span class="literal">None</span>,</span><br><span class="line">        <span class="string">&quot;myCNA&quot;</span>: <span class="string">&quot;zyCGIBdAVWICAXjHIncvopiS&quot;</span>,</span><br><span class="line">        <span class="string">&quot;screenResolution&quot;</span>: <span class="string">&quot;1536x864&quot;</span>,</span><br><span class="line">        <span class="string">&quot;userAgent&quot;</span>: <span class="string">&quot;Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36&quot;</span>,</span><br><span class="line">        <span class="string">&quot;couponUnikey&quot;</span>: <span class="string">&quot;&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">    info_data = &#123;</span><br><span class="line">        <span class="string">&#x27;appId&#x27;</span>: <span class="string">&#x27;34385&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;params&#x27;</span>: json.dumps(sign_params) <span class="comment"># 序列化处理</span></span><br><span class="line">    &#125;</span><br><span class="line">    ep_data = json.dumps(info_data).replace(<span class="string">&#x27; &#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">    aim_str = em_token + <span class="string">&#x27;&amp;&#x27;</span> + <span class="built_in">str</span>(eT) + <span class="string">&#x27;&amp;&#x27;</span> + eC + <span class="string">&#x27;&amp;&#x27;</span> + ep_data</span><br><span class="line">    md5 = hashlib.md5()</span><br><span class="line">    md5.update(aim_str.encode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    sign = md5.hexdigest()</span><br><span class="line">    <span class="keyword">return</span> sign, ep_data</span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;获取下一页的参数内容</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getNextParams</span>(<span class="params">json_data</span>):</span><br><span class="line">    totalResults = json_data[<span class="string">&#x27;data&#x27;</span>][<span class="string">&#x27;mainInfo&#x27;</span>][<span class="string">&#x27;totalResults&#x27;</span>]</span><br><span class="line">    sourceS = json_data[<span class="string">&#x27;data&#x27;</span>][<span class="string">&#x27;mainInfo&#x27;</span>][<span class="string">&#x27;sourceS&#x27;</span>]</span><br><span class="line">    bc_offset = json_data[<span class="string">&#x27;data&#x27;</span>][<span class="string">&#x27;mainInfo&#x27;</span>][<span class="string">&#x27;bcoffset&#x27;</span>]</span><br><span class="line">    nt_offset = json_data[<span class="string">&#x27;data&#x27;</span>][<span class="string">&#x27;mainInfo&#x27;</span>][<span class="string">&#x27;ntoffset&#x27;</span>]</span><br><span class="line">    <span class="keyword">return</span> totalResults, sourceS, bc_offset, nt_offset</span><br><span class="line"></span><br><span class="line"><span class="comment"># 1.模拟浏览器</span></span><br><span class="line">headers = &#123;</span><br><span class="line">    <span class="comment"># cookie-用户信息</span></span><br><span class="line">    <span class="string">&#x27;cookie&#x27;</span>: <span class="string">&#x27;cna=zyCGIBdAVWICAXjHIncvopiS; thw=cn; t=b218f49912339b0ceae556e0090e211b; xlly_s=1; _tb_token_=7a5b15963ff84; cookie2=16651aea2c313a912b01cb164e4e08a0; 3PcFlag=1744784663786; _hvn_lgc_=0; cookie3_bak=16651aea2c313a912b01cb164e4e08a0; unb=3971315337; lgc=%5Cu4E00%5Cu8F6E%5Cu5F2F%5Cu6708%5Cu4E00%5Cu7F15%5Cu76F8%5Cu601D; cancelledSubSites=empty; env_bak=FM%2Bgnk3pmwgJGNrZ6%2FSkIJoxCwTm6VI1qZ6TClrxgOc3; cookie17=UNkwcc4KQQFHcg%3D%3D; dnk=hoi3vel; tracknick=%5Cu4E00%5Cu8F6E%5Cu5F2F%5Cu6708%5Cu4E00%5Cu7F15%5Cu76F8%5Cu601D; _l_g_=Ug%3D%3D; sg=%E6%80%9D74; _nk_=%5Cu4E00%5Cu8F6E%5Cu5F2F%5Cu6708%5Cu4E00%5Cu7F15%5Cu76F8%5Cu601D; cookie1=U%2BIlWIeHLSADvBxDCm0kQKQsca6SNqfz%2FIpTHfq3ZQw%3D; sgcookie=E1003OSkTgC9VlYpf2%2FnITYkh7VA%2BCuHtM4rpO%2B0DYpiEHhSZi8%2B46SUAIXRls1C9TuK6mE77ohQhH3D7Vr1b4um%2BPEe%2BqJJGCi4WxE%2BQnQE0fQ%3D; havana_lgc2_0=eyJoaWQiOjM5NzEzMTUzMzcsInNnIjoiYWU2MzZmZDI5YjQ2MDNlNGUxMGU1YTkyYThhNjYyOTEiLCJzaXRlIjowLCJ0b2tlbiI6IjFjUWNFWDNhWm9oMl9BMHFTMzRtNUhnIn0; havana_lgc_exp=1775888706045; cookie3_bak_exp=1745043906045; wk_cookie2=1b3ee28d98c093084dd38ee1285362b2; wk_unb=UNkwcc4KQQFHcg%3D%3D; uc1=cookie16=U%2BGCWk%2F74Mx5tgzv3dWpnhjPaQ%3D%3D&amp;pas=0&amp;cookie14=UoYaj4%2Bx2t0h1w%3D%3D&amp;existShop=false&amp;cookie15=V32FPkk%2Fw0dUvg%3D%3D&amp;cookie21=URm48syIYB3rzvI4Dim4; sn=; uc3=id2=UNkwcc4KQQFHcg%3D%3D&amp;nk2=saDbd13P348Tq%2FXU0Jk%2FOg%3D%3D&amp;vt3=F8dD2EuMY7YkvXA3Qzs%3D&amp;lg2=VT5L2FSpMGV7TQ%3D%3D; csg=b9bc66e6; skt=ce2a91bede66482d; existShop=MTc0NDc4NDcwNg%3D%3D; uc4=nk4=0%40s8Wb7PJZDAD5%2BPfdt88U4O4L5YsttBggT1RI&amp;id4=0%40Ug46vpy6gA%2FUJ82j39S%2FC20NcRKR; _cc_=VFC%2FuZ9ajQ%3D%3D; _samesite_flag_=true; havana_sdkSilent=1744958760754; sdkSilent=1744970016688; mtop_partitioned_detect=1; _m_h5_tk=bd25aec960a5af5dae87a57bd23a76fe_1744909656357; _m_h5_tk_enc=ffb07dba49e6d2242032897636cd438c; tfstk=gGwrwi1R4TBPOMu-Z-Me_JQFb_H-Bv71UJgIxkqnV40oPkmnLr4C24ZhyyyEokF52X_JY3e47p95y_EHLvMh5N61CuESpvbsLYD804nLm2jjZUroe3Dh5N6XGncRPv4CcYla3Imto0mnqXjqnDmiKLcu-qcmjcToKy4nmjmIj30nZvcDiqnnK24nKikmkDMn-jUK-V9qbbjImYz_o14iaVqoudrY30Dewu02Kp24gbuMCqJHK-oucQngid5IS53S12zchLkUmDzq177wUyr3XWc4LUj7SrVU-mFA8BuatuNTtjSlEokrzjo7lhAqUW48LbFyvGEmErFtWbfAkmy7CXutgEbucouo_Wz5kp07_o4q1-TXCAVTuJliIg8JJm4GYJFy-BloDmu10ir891NT8FfC5BdKwSnq5gIJ9BhoDmu10iRp9bHx0VsR2; isg=BEJCPyO0nhwnFY2-jgYMqgg3k0ikE0YtYJe0OYxbdrVg3-JZdKCMPZIZj9ujiL7F&#x27;</span>,</span><br><span class="line">    <span class="comment"># user-agent-用户代理</span></span><br><span class="line">    <span class="string">&#x27;user-agent&#x27;</span>: <span class="string">&#x27;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2.数据包地址</span></span><br><span class="line">url = <span class="string">&#x27;https://h5api.m.taobao.com/h5/mtop.relationrecommend.wirelessrecommend.recommend/2.0/?&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3.获取时间戳</span></span><br><span class="line">eT = <span class="built_in">int</span>(time.time() * <span class="number">1000</span>) <span class="comment"># ms级</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4.获取加密参数</span></span><br><span class="line">page = <span class="number">1</span></span><br><span class="line">totalResults = <span class="number">4800</span></span><br><span class="line">sourceS = <span class="string">&#x27;0&#x27;</span></span><br><span class="line">bc_offset = <span class="string">&#x27;&quot;&quot;&#x27;</span></span><br><span class="line">nt_offset = <span class="string">&#x27;&quot;&quot;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 5.创建文件对象</span></span><br><span class="line">file = <span class="built_in">open</span>(<span class="string">&#x27;C:/Users/Yam/Desktop/Computer.csv&#x27;</span>, mode=<span class="string">&#x27;w&#x27;</span>, encoding=<span class="string">&#x27;UTF-8-SIG&#x27;</span>, newline=<span class="string">&#x27;&#x27;</span>)</span><br><span class="line"><span class="comment"># 字典写入方法</span></span><br><span class="line">csv_writer = csv.DictWriter(file, fieldnames=[<span class="string">&#x27;title&#x27;</span>, <span class="string">&#x27;price&#x27;</span>, <span class="string">&#x27;real_sale&#x27;</span>, <span class="string">&#x27;procity&#x27;</span>, <span class="string">&#x27;store&#x27;</span>, <span class="string">&#x27;detail&#x27;</span>])</span><br><span class="line"><span class="comment"># 写入表头</span></span><br><span class="line">csv_writer.writeheader()</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">25</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&#x27;第<span class="subst">&#123;i+<span class="number">1</span>&#125;</span>次爬取...&#x27;</span>)</span><br><span class="line">    page = i + <span class="number">1</span></span><br><span class="line">    sign, ep_data = getSign(eT, page, totalResults, sourceS, bc_offset, nt_offset)</span><br><span class="line">    url_params = &#123;</span><br><span class="line">        <span class="string">&#x27;jsv&#x27;</span>: <span class="string">&#x27;2.7.4&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;appKey&#x27;</span>: <span class="string">&#x27;12574478&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;t&#x27;</span>: <span class="built_in">str</span>(eT),</span><br><span class="line">        <span class="string">&#x27;sign&#x27;</span>: sign,</span><br><span class="line">        <span class="string">&#x27;api&#x27;</span>: <span class="string">&#x27;mtop.relationrecommend.wirelessrecommend.recommend&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;v&#x27;</span>: <span class="string">&#x27;2.0&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;type&#x27;</span>: <span class="string">&#x27;jsonp&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;dataType&#x27;</span>: <span class="string">&#x27;jsonp&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;callback&#x27;</span>: <span class="string">&#x27;mtopjsonp5&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;data&#x27;</span>: ep_data,</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment"># 发送请求</span></span><br><span class="line">    response = requests.get(url=url, params=url_params, headers=headers)</span><br><span class="line">    goods_info = response.text</span><br><span class="line">    <span class="comment"># print(goods_info)</span></span><br><span class="line">    json_str = re.findall(<span class="string">&#x27;mtopjsonp\d+\((.*)&#x27;</span>, goods_info)[<span class="number">0</span>][:-<span class="number">1</span>]</span><br><span class="line">    json_data = json.loads(json_str)</span><br><span class="line">    itemsArray = json_data[<span class="string">&#x27;data&#x27;</span>][<span class="string">&#x27;itemsArray&#x27;</span>]</span><br><span class="line">    <span class="keyword">for</span> item <span class="keyword">in</span> itemsArray:</span><br><span class="line">        <span class="keyword">if</span> <span class="string">&#x27;title&#x27;</span> <span class="keyword">not</span> <span class="keyword">in</span> item.keys():</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        dit = &#123;</span><br><span class="line">            <span class="string">&#x27;title&#x27;</span>: item[<span class="string">&#x27;title&#x27;</span>].replace(<span class="string">&#x27;&lt;span class=H&gt;&#x27;</span>, <span class="string">&#x27;&#x27;</span>).replace(<span class="string">&#x27;&lt;/span&gt;&#x27;</span>, <span class="string">&#x27;&#x27;</span>),</span><br><span class="line">            <span class="string">&#x27;price&#x27;</span>: item[<span class="string">&#x27;price&#x27;</span>],</span><br><span class="line">            <span class="string">&#x27;real_sale&#x27;</span>: item[<span class="string">&#x27;realSales&#x27;</span>],</span><br><span class="line">            <span class="string">&#x27;procity&#x27;</span>: item[<span class="string">&#x27;procity&#x27;</span>],</span><br><span class="line">            <span class="string">&#x27;store&#x27;</span>: item[<span class="string">&#x27;shopInfo&#x27;</span>][<span class="string">&#x27;title&#x27;</span>],</span><br><span class="line">            <span class="string">&#x27;detail&#x27;</span>: <span class="string">&#x27;https:&#x27;</span> + item[<span class="string">&#x27;auctionURL&#x27;</span>]</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment"># 写入数据</span></span><br><span class="line">        csv_writer.writerow(dit)</span><br><span class="line">    totalResults, sourceS, bc_offset, nt_offset = getNextParams(json_data)</span><br><span class="line">    time.sleep(<span class="number">30</span>) <span class="comment"># 睡眠30s</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&#x27;第<span class="subst">&#123;i+<span class="number">1</span>&#125;</span>次爬取成功!!!&#x27;</span>)</span><br><span class="line"></span><br><span class="line">file.close()</span><br></pre></td></tr></table></figure><p>上述代码中可变参数需要根据自身设备进行改变!!!</p>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;为了之后的研究生生活，最近开始学习Python这门语言。刚刚过完了一遍Python基础语法，可是脑子还是感觉空空的。太难的项目暂时又写不出来，于是便尝试编写一个简单的爬虫程序，一是为了巩固Python基础，二是重新拾起爬虫的相关知识。本篇文章基于淘宝电商平台编写爬取电脑商品信息的爬虫程序。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="Python" scheme="https://www.hoi3vel.cn/tags/Python/"/>
    
    <category term="Web Crawler" scheme="https://www.hoi3vel.cn/tags/Web-Crawler/"/>
    
  </entry>
  
  <entry>
    <title>Windows下安装Pytorch教程</title>
    <link href="https://www.hoi3vel.cn/2025/02/09/t-install-pytorch/"/>
    <id>https://www.hoi3vel.cn/2025/02/09/t-install-pytorch/</id>
    <published>2025-02-09T06:46:00.000Z</published>
    <updated>2025-11-21T13:32:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>最近在准备机器学习项目时，关于如何配置Pytorch运行环境问题忙活了好久，网上的教程也不尽人意，因此写下此篇文章方便以后自己回顾，也希望帮助跟我有同样困惑的小伙伴们。</p></blockquote><span id="more"></span><h2 id="一、写在前面"><a href="#一、写在前面" class="headerlink" title="一、写在前面"></a>一、写在前面</h2><p>本篇教程电脑配置如下：</p><ul><li>操作系统: Windows 11</li><li>显卡版本(独显): NVIDIA GeForce RTX 2060</li><li>Python: 3.9.12</li><li>Anaconda: Anaconda3 2022.05</li></ul><p>若电脑与上配置不同则在后续步骤中软件版本选择需要依据自身电脑做出改变。</p><h2 id="二、环境配置"><a href="#二、环境配置" class="headerlink" title="二、环境配置"></a>二、环境配置</h2><h3 id="2-1-安装Anaconda"><a href="#2-1-安装Anaconda" class="headerlink" title="2.1 安装Anaconda"></a>2.1 安装Anaconda</h3><p>Anaconda最大的优点是可以建立很多个独立Python版本的虚拟库环境，这极大方便了多个项目的环境切换。Anaconda版本选择无关紧要，它只决定默认环境(即<strong>base</strong>环境)的Python版本，而后续每个虚拟环境的Python版本可以由开发者自行决定。<a href="https://www.anaconda.com/">Anaconda官方下载链接</a></p><h3 id="2-2-安装CUDA-Toolkit"><a href="#2-2-安装CUDA-Toolkit" class="headerlink" title="2.2 安装CUDA Toolkit"></a>2.2 安装CUDA Toolkit</h3><blockquote><p>CUDA(Compute Unified Device Architecture): NAVIDIA开发用于NAVIDIA GPU并行计算的并行计算架构和编程模型。它是一种硬件架构，定义了GPU的并行计算单元、内存架构、线程模型等。<br>CUDA Toolkit: NAVIDIA提供用于GPU加速计算的开发工具包，用于支持CUDA架构的并行计算。它是一套软件工具，包含了一系列的工具、库和示例代码，用于开发、优化和部署基于CUDA的并行计算应用程序。</p></blockquote><ul><li>GPU需要与CUDA结合才能完成模型训练、Pytorch安装等任务</li><li>若没有CUDA, GPU在深度学习中就是个摆设</li><li>GPU(独显)推理速度是CPU(核显)的几十倍</li></ul><p>正确安装CUDA Toolkit需要三个条件：①确定显卡算力；②确定CUDA Runtime Version(即CUDA SDK)；③确定CUDA Driver Version；三者对应的关系应为：显卡算力&lt;&#x3D;CUDA Runtime Version&lt;&#x3D;CUDA Driver version</p><p>1.查看电脑显卡型号<br>打开任务管理器-&gt;点击<code>性能</code>-&gt;选择<code>GPU0</code>，在右上角即可显示当前电脑显卡型号。Ps: 若电脑配有显卡则有两个GPU，其中一个是核显即镶嵌在CPU中，厂家为CPU厂家，另一个则为独显，厂家为NVIDIA。具体视图如下：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1125/720;"><img class="lazy" src="https://img.hoi3vel.cn/tech/get_gpu.webp" data-src="https://img.hoi3vel.cn/tech/get_gpu.webp" alt="查看显卡型号" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div><div class="image-meta"><span class="image-caption center">查看显卡型号</span></div></div></p><p>2.确定显卡算力以及确定CUDA Runtime Version<br>知道显卡类型后，前往<a href="https://en.wikipedia.org/wiki/CUDA">网站</a>找到自己显卡版本所支持的算力。此电脑查询结果如下：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1098/752;"><img class="lazy" src="https://img.hoi3vel.cn/tech/get_gpu_compute.webp" data-src="https://img.hoi3vel.cn/tech/get_gpu_compute.webp" alt="获取显卡算力" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div><div class="image-meta"><span class="image-caption center">获取显卡算力</span></div></div><br>RTX 2060显卡算力为7.5，因此我们选择CUDA SDK支持算力区间包含7.5的版本即CUDA SDK&gt;&#x3D;11.0.</p><div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1568/604;"><img class="lazy" src="https://img.hoi3vel.cn/tech/get_gpu_sdk.webp" data-src="https://img.hoi3vel.cn/tech/get_gpu_sdk.webp" alt="获取CUDA SDK版本" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div><div class="image-meta"><span class="image-caption center">获取CUDA SDK版本</span></div></div><p>3.确定CUDA Driver Version<br>执行<code>win+r</code>输入<code>cmd</code>打开shell命令窗口，输入<code>nvidia-smi</code>命令，将会显示当前电脑所支持的CUDA版本。具体视图如下：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1074/649;"><img class="lazy" src="https://img.hoi3vel.cn/tech/get_cuda.webp" data-src="https://img.hoi3vel.cn/tech/get_cuda.webp" alt="查看CUDA型号" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div><div class="image-meta"><span class="image-caption center">查看CUDA型号</span></div></div></p><p>最终我们确定CUDA Toolkit的版本区间应该为11.0-12.1。前往<a href="https://developer.nvidia.com/cuda-toolkit-archive">CUDA官网</a>下载指定版本的CUDA Toolkit。安装完成后重启电脑，之后在shell窗口中输入指令<code>nvcc -V</code>检验是否安装成功。<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:767/265;"><img class="lazy" src="https://img.hoi3vel.cn/tech/cuda_install_finish.webp" data-src="https://img.hoi3vel.cn/tech/cuda_install_finish.webp" alt="CUDA安装成功截图" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div><div class="image-meta"><span class="image-caption center">CUDA安装成功截图</span></div></div><br>若出现如上结果则安装成功。</p><h3 id="2-3-安装Pytorch"><a href="#2-3-安装Pytorch" class="headerlink" title="2.3 安装Pytorch"></a>2.3 安装Pytorch</h3><p>前往<a href="https://pytorch.org/get-started/previous-versions/">Pytorch官网</a>下载与<strong>CUDA Toolkit版本</strong>相对应的Pytorch版本，可采用<code>conda指令</code>或者<code>pip指令</code>安装，推荐采用<code>conda指令</code>安装。注意：OSX栏里的指令安装核显(即CPU)版的Pytorch，而Linux and Windows栏里的指令安装独显(即GPU)版的Pytorch。<br>安装完成后，打开anaconda中对应虚拟环境的命令行输入指令<code>conda list</code>，若出现pytorch则说明安装成功。如图：<div class="tag-plugin image"><div class="image-bg" style="aspect-ratio:1476/752;"><img class="lazy" src="https://img.hoi3vel.cn/tech/get_pytorch.webp" data-src="https://img.hoi3vel.cn/tech/get_pytorch.webp" alt="Pytorch安装成功截图" data-fancybox="true"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div><div class="image-meta"><span class="image-caption center">Pytorch安装成功截图</span></div></div></p><h2 id="三、最后的话"><a href="#三、最后的话" class="headerlink" title="三、最后的话"></a>三、最后的话</h2><p>如果以上环境配置完成，编写如下<strong>python脚本</strong>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(torch.cuda.is_available())</span><br></pre></td></tr></table></figure><p>若运行结果为<strong>true</strong>，则说明Pytorch可正常使用主机显卡，否则需要检查CUDA Toolkit版本软件以及Pytorch软件版本重新进行适配安装。最后祝愿每一个科研人能够学有所成。</p>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;最近在准备机器学习项目时，关于如何配置Pytorch运行环境问题忙活了好久，网上的教程也不尽人意，因此写下此篇文章方便以后自己回顾，也希望帮助跟我有同样困惑的小伙伴们。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="Machine Learning" scheme="https://www.hoi3vel.cn/tags/Machine-Learning/"/>
    
    <category term="Pytorch" scheme="https://www.hoi3vel.cn/tags/Pytorch/"/>
    
    <category term="Windows" scheme="https://www.hoi3vel.cn/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>数组中的字符串匹配</title>
    <link href="https://www.hoi3vel.cn/2025/01/20/t-leetcode1408/"/>
    <id>https://www.hoi3vel.cn/2025/01/20/t-leetcode1408/</id>
    <published>2025-01-20T12:56:00.000Z</published>
    <updated>2025-11-21T13:33:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>今天刷Leetcode时，一道字符串匹配的题目卡了好久好久，后来才想到这是数据结构中串的朴素匹配算法的应用，因此写下此篇文章方便日后复习。</p></blockquote><span id="more"></span><h2 id="一、写在前面"><a href="#一、写在前面" class="headerlink" title="一、写在前面"></a>一、写在前面</h2><p>数据结构中，朴素匹配算法是一种简单而有效的字符串匹配方法，也被称为暴力搜索方法。它的基本思想是逐个比较主字符串和模式串的每个字符，以此确定是否存在匹配。</p><h2 id="二、具体题目"><a href="#二、具体题目" class="headerlink" title="二、具体题目"></a>二、具体题目</h2><p>有一个字符串数组<strong>words</strong>,数组中的每个字符串都可以看作是一个单词。请你按照<strong>任意</strong>顺序返回words中是其他单词的子字符串的所有单词。（Ps: 如果你可以删除<code>words[j]</code>最左侧或最右侧的若干字符得到<code>words[i]</code>，那么字符串<code>words[i]</code>就是<code>word[j]</code>的子字符串）</p><p>示例1:</p><ul><li>输入：words &#x3D; [“mass”,”as”,”hero”,”superhero”]</li><li>输出：[“as”,”hero”]</li><li>解释：”as” 是 “mass” 的子字符串，”hero” 是 “superhero” 的子字符串。因此[“hero”,”as”]是最终答案。</li></ul><p>示例2:</p><ul><li>输入：words &#x3D; [“leetcode”,”et”,”code”]</li><li>输出：[“et”,”code”]</li><li>解释：”et” 和 “code” 都是 “leetcode” 的子字符串。因此最终答案为[“et”, “code”]</li></ul><p>示例3:</p><ul><li>输入：words &#x3D; [“blue”,”green”,”bu”]</li><li>输出：[]</li></ul><p><a href="https://leetcode.cn/problems/string-matching-in-an-array/description/">Leetcode原题</a></p><h2 id="三、思路分析"><a href="#三、思路分析" class="headerlink" title="三、思路分析"></a>三、思路分析</h2><p>此题关键是编写判断一个字符串(str1)是否是另一个字符串(str2)的子字符串的函数，对此有两种算法可以解决：①简单朴素匹配算法；②KMP算法。由于KMP算法需要构建<strong>next</strong>数组较为复杂，因此解此题采用<strong>简单朴素匹配算法</strong>。将<strong>str1</strong>作为模式串，<strong>str2</strong>作为主字符串，通过遍历主字符串统计与模式串匹配个数，若匹配个数与模式串长度相等则匹配成功，否则匹配失败。（注：每一次单字符匹配失败都需置匹配统计量为0）<br>具体代码如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">isSub</span><span class="params">(<span class="type">char</span> *str1, <span class="type">char</span> *str2)</span> &#123; <span class="comment">//判断str1是否为str2的子字符串</span></span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">strlen</span>(str1) &gt; <span class="built_in">strlen</span>(str2)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line"><span class="comment">//int match = 0;</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="built_in">strlen</span>(str2); i++) &#123;</span><br><span class="line"><span class="type">int</span> match = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span>(str1[match] == str2[i]) &#123;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> j=i; j&lt;<span class="built_in">strlen</span>(str2); j++) &#123;</span><br><span class="line"><span class="keyword">if</span>(str1[match]==str2[j])&#123;</span><br><span class="line">match++;</span><br><span class="line"><span class="keyword">if</span>(match == <span class="built_in">strlen</span>(str1)) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;<span class="keyword">else</span> &#123;</span><br><span class="line">match = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span>** <span class="title function_">stringMatching</span><span class="params">(<span class="type">char</span> **words, <span class="type">int</span> wordsSize, <span class="type">int</span>* returnSize)</span> &#123;  <span class="comment">// 获取words数组中的子字符串</span></span><br><span class="line">    <span class="type">int</span> len = <span class="number">0</span>;</span><br><span class="line">    <span class="type">char</span> **ans = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;wordsSize; i++) &#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>; j&lt;wordsSize; j++) &#123;</span><br><span class="line">    <span class="keyword">if</span>(i==j) <span class="keyword">continue</span>;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(isSub(words[i], words[j])) &#123;</span><br><span class="line">    len++;</span><br><span class="line">    ans = (<span class="type">char</span>**)<span class="built_in">realloc</span>(ans, <span class="keyword">sizeof</span>(<span class="type">char</span>*)*len);</span><br><span class="line">    ans[len<span class="number">-1</span>] = (<span class="type">char</span>*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">char</span>)*(<span class="built_in">strlen</span>(words[i])+<span class="number">1</span>));</span><br><span class="line">    <span class="built_in">strcpy</span>(ans[len<span class="number">-1</span>], words[i]);</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">*returnSize = len;</span><br><span class="line"><span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="四、题外话"><a href="#四、题外话" class="headerlink" title="四、题外话"></a>四、题外话</h2><p>此题解法建立在以下条件下：</p><ul><li>1 &lt;&#x3D; words.length &lt;&#x3D; 100</li><li>1 &lt;&#x3D; words[i].length &lt;&#x3D; 30</li><li><code>words[i]</code>仅包含小写英文字母。</li><li>题目数据保证每个<code>words[i]</code>都是独一无二的。</li></ul><p>若无此限制，采用简单朴素匹配算法可能会超时。</p>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;今天刷Leetcode时，一道字符串匹配的题目卡了好久好久，后来才想到这是数据结构中串的朴素匹配算法的应用，因此写下此篇文章方便日后复习。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="C" scheme="https://www.hoi3vel.cn/tags/C/"/>
    
    <category term="Simple Matching" scheme="https://www.hoi3vel.cn/tags/Simple-Matching/"/>
    
  </entry>
  
  <entry>
    <title>C语言中函数间动态内存的传递</title>
    <link href="https://www.hoi3vel.cn/2024/09/08/t-dynamic-memory/"/>
    <id>https://www.hoi3vel.cn/2024/09/08/t-dynamic-memory/</id>
    <published>2024-09-08T02:23:00.000Z</published>
    <updated>2025-11-21T13:30:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>在编程实现从字符串提取连续的数字时，发现在函数内申请的动态内存无法传递到main函数中使用，基于此写下这篇文章，探究C语言中函数间动态内存的传递方式。</p></blockquote><span id="more"></span><h2 id="一、写在前面"><a href="#一、写在前面" class="headerlink" title="一、写在前面"></a>一、写在前面</h2><p>C语言中，动态申请的内存划分在堆中，其生存周期与程序生存周期一致，系统不会自动回收，需要程序员调用free()函数手动调用。既然如此，如果在函数中申请一个动态内存，那么如何在main函数中调用呢？</p><h2 id="二、具体实例"><a href="#二、具体实例" class="headerlink" title="二、具体实例"></a>二、具体实例</h2><h3 id="1-错误实例"><a href="#1-错误实例" class="headerlink" title="1.错误实例"></a>1.错误实例</h3><p>有如下代码：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">Func</span><span class="params">(<span class="type">int</span> a[], <span class="type">int</span> n)</span></span><br><span class="line">&#123;</span><br><span class="line">    a = (<span class="type">int</span>*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">int</span>)*n);</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;n; i++) a[i] = i;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> n = <span class="number">5</span>, *a = <span class="literal">NULL</span>;</span><br><span class="line">    Func(a, n);</span><br><span class="line">    <span class="keyword">if</span>(!a) <span class="built_in">printf</span>(<span class="string">&quot;a is null!\n&quot;</span>);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;n; i++) <span class="built_in">printf</span>(<span class="string">&quot;%d &quot;</span>, a[i]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上述代码中，在Func()函数中对a数组进行了动态内存申请，在main()函数中进行调用，程序运行结果为<code>a is null!</code>。造成这种结果是因为此时函数传递方式为值传递，这意味着将指针a传递给函数Func()时，实际上是传递了一个a的副本即当在Func()函数内为a分配内存时，实际上在修改a的副本而不是main()函数中的a。</p><h3 id="2-正确实例"><a href="#2-正确实例" class="headerlink" title="2.正确实例"></a>2.正确实例</h3><p>在C语言中，函数之间的动态内存传递方式主要有两种：①使用函数传参的方式。此时形参必须为<strong>双维指针</strong>，否则函数内分配的动态内存无法传入至主函数中。②采用函数返回值的形式，返回值为<strong>单维指针</strong>。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方式一：函数传参</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">Func</span><span class="params">(<span class="type">int</span> **a, <span class="type">int</span> n)</span></span><br><span class="line">&#123;</span><br><span class="line">    *a = (<span class="type">int</span>*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">int</span>)*n);</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;n; i++) (*a)[i] = i;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> n = <span class="number">5</span>, *a = <span class="literal">NULL</span>;</span><br><span class="line">    Func(&amp;a, n);</span><br><span class="line">    <span class="keyword">if</span>(!a) <span class="built_in">printf</span>(<span class="string">&quot;a is null!\n&quot;</span>);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;n; i++) <span class="built_in">printf</span>(<span class="string">&quot;%d &quot;</span>, a[i]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方式二: 函数返回值</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span>* <span class="title function_">Func</span><span class="params">(<span class="type">int</span> n)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> *temp = (<span class="type">int</span>*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">int</span>)*n);</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;n; i++) temp[i] = i;</span><br><span class="line">    <span class="keyword">return</span> temp;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> n = <span class="number">5</span>, *a = <span class="literal">NULL</span>;</span><br><span class="line">    a = Func(n);</span><br><span class="line">    <span class="keyword">if</span>(!a) <span class="built_in">printf</span>(<span class="string">&quot;a is null!\n&quot;</span>);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;n; i++) <span class="built_in">printf</span>(<span class="string">&quot;%d &quot;</span>, a[i]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="三、题外话"><a href="#三、题外话" class="headerlink" title="三、题外话"></a>三、题外话</h2><p>在C语言中动态内存的申请可采用malloc()和realloc()函数，前者仅需要传递一个函数参数即需要的内存大小，而后者则需要传递两个函数参数即realloc(ptr, size)，ptr为目的函数指针，成功时返回指向新内存块的指针且原内存将会释放；失败时返回NULL且原指针ptr指向的内存块仍然有效。若ptr为空指针，则该函数效果同malloc()函数。<br>实际上，realloc()函数用于对已分配内存的扩展，其扩展机制如下：</p><ul><li>若ptr所指的位置可以继续找到足够的连续内存空间，则该函数会直接在原有内存块的基础上扩展内存。此时返回的地址为ptr原地址；</li><li>若原位置不能扩展，则该函数会在堆中新寻找一个合适的位置并将原内存块的数据复制到新位置以及<strong>释放原有内存块</strong>。</li></ul>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;在编程实现从字符串提取连续的数字时，发现在函数内申请的动态内存无法传递到main函数中使用，基于此写下这篇文章，探究C语言中函数间动态内存的传递方式。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="C" scheme="https://www.hoi3vel.cn/tags/C/"/>
    
    <category term="Dynamic Memory" scheme="https://www.hoi3vel.cn/tags/Dynamic-Memory/"/>
    
  </entry>
  
  <entry>
    <title>最长公共子串和最长公共子序列</title>
    <link href="https://www.hoi3vel.cn/2024/07/06/t-dynamic-programming1/"/>
    <id>https://www.hoi3vel.cn/2024/07/06/t-dynamic-programming1/</id>
    <published>2024-07-06T05:00:00.000Z</published>
    <updated>2025-11-21T13:31:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>自己在刷LeetCode时遇见了最长公共子串这个题目，传统的编程思想使我对此题无从下手，后面查阅资料后了解到<strong>动态规划</strong>这一思想，因此便写下文章，记录如何使用<strong>动态规划</strong>解决最长公共子串和最长公共子序列问题。</p></blockquote><span id="more"></span><h2 id="一、写在前面"><a href="#一、写在前面" class="headerlink" title="一、写在前面"></a>一、写在前面</h2><p>当自己在网上搜什么是<strong>动态规划</strong>时，许许多多晦涩难懂的专业术语映入眼帘，令人望而生畏。然而动态规划遵循着一套固定的流程：递归的暴力解法-&gt;带备忘录的递归解法-&gt;非递归的动态规划解法。那么什么是动态规划呢？针对一个问题，我们把这个问题拆分成一个个子问题，然后将每个子问题的最优解记录下来(即”备忘录”)，相邻子问题之间层层递进，再根据子问题答案进行反推，最终得出原问题的最优解，这便是<strong>动态规划</strong>。简单一句话就是，<strong>拆分子问题，记住过往，减少重复计算</strong>。</p><h2 id="二、最长公共子串"><a href="#二、最长公共子串" class="headerlink" title="二、最长公共子串"></a>二、最长公共子串</h2><h3 id="1-题目描述"><a href="#1-题目描述" class="headerlink" title="1.题目描述"></a>1.题目描述</h3><p>给出两个字符串a和b的最长连续公共子串的长度。例如“abcbcde”和“bbcbce”的最长连续公共子串是“bcbc”，长度为4。</p><h3 id="2-思路解析"><a href="#2-思路解析" class="headerlink" title="2.思路解析"></a>2.思路解析</h3><p>假设字符串a和b的长度分别是m和n，绘制一张m×n的二维表格<strong>table[m][n]</strong>。取字符串a和b的字符前缀a’和b’，表格位置i，j上的数字含义是: 字符串a’和b’的且以它们尾巴字符结尾的最长公共子串的长度。<br><img src="https://img.hoi3vel.cn/tech/max_common_length_1.webp" alt="table表_1"><br>假设已经知道i-1和j-1处的数值，考虑i，j的数值：</p><ul><li>如果新的<strong>a[i]</strong>和<strong>b[j]</strong>相等，则最长公共子串得到扩展，因此数值+1。</li><li>否则以两个字符为尾巴的最长公共子串不复存在，因此数值<strong>清零</strong>。<br>下图中左右示例对应上述两种情况：<br><img src="https://img.hoi3vel.cn/tech/max_common_length_2.webp" alt="动态规划过程图"><br>于是得到填表规律：</li><li>如果当前格子对应的两个字符不同，则写0。</li><li>否则采用斜上方的格子的数值+1<br><img src="https://img.hoi3vel.cn/tech/max_common_length_3.webp" alt="填表规律图"><br>用代码描述这个填表规则：</li><li>若<strong>a[i]</strong>!&#x3D;<strong>b[j]</strong>，则填写<strong>table[i][j]</strong>&#x3D;0</li><li>否则，填写<strong>table[i][j]</strong>&#x3D;<strong>table[i-1][j-1]</strong>+1<br>表格中的最大值即为最长公共子串的长度，时间复杂度为O(m*n)。</li></ul><h3 id="3-代码实现"><a href="#3-代码实现" class="headerlink" title="3.代码实现"></a>3.代码实现</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 返回字符串 a 和 b 的最长公共子串的长度</span></span><br><span class="line"><span class="comment">// 例如 &quot;abcdbcdef&quot; 和 &quot;bbcbbcdee&quot; 的公共字符串是 &quot;bcde&quot; 长度是 4</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">LongestCommonSubstring</span><span class="params">(<span class="type">char</span> *a, <span class="type">int</span> a_length, <span class="type">char</span> *b, <span class="type">int</span> b_length)</span> &#123;</span><br><span class="line">    <span class="type">int</span> table[a_length][b_length];</span><br><span class="line">    <span class="type">int</span> max = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; a_length; i++) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; b_length; j++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (a[i] == b[j]) &#123;</span><br><span class="line">                <span class="keyword">if</span> (i &gt;= <span class="number">1</span> &amp;&amp; j &gt;= <span class="number">1</span>)</span><br><span class="line">                    table[i][j] = table[i - <span class="number">1</span>][j - <span class="number">1</span>] + <span class="number">1</span>;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    table[i][j] = <span class="number">1</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                table[i][j] = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (table[i][j] &gt; max) &#123;</span><br><span class="line">                max = table[i][j];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> max;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="三、最长公共子序列"><a href="#三、最长公共子序列" class="headerlink" title="三、最长公共子序列"></a>三、最长公共子序列</h2><h3 id="1-题目描述-1"><a href="#1-题目描述-1" class="headerlink" title="1.题目描述"></a>1.题目描述</h3><p>给出两个字符串a和b的最长公共子序列的长度。其和公共子串不同的是，公共子序列不要求连续。最长公共子序列是非常经典的动态规划问题，简称LCS问题。例如“abcbcde”和“acabdef”的最长公共子序列是“acbde”，长度为5。<a href="https://leetcode.cn/problems/longest-common-subsequence/description/">LeetCodet题目-最长公共子序列</a></p><h3 id="2-思路解析-1"><a href="#2-思路解析-1" class="headerlink" title="2.思路解析"></a>2.思路解析</h3><p>如果两个字符串的尾位置分别是i和j，假设它们的最长公共子序列的长度是函数<strong>f(i, j)</strong>:<img src="https://img.hoi3vel.cn/tech/max_common_list_1.webp" alt="最长公共子序列函数"><br>考虑函数的递推关系：</p><ul><li>如果两个字符串的尾字符相同，易知<strong>f(i, j)&#x3D;f(i-1, j-1)+1</strong>:<img src="https://img.hoi3vel.cn/tech/max_common_list_2.webp" alt="两字符串尾字符相同序列函数变化"></li><li>如果尾字符不同，上述递推关系则不成立:<img src="https://img.hoi3vel.cn/tech/max_common_list_3.webp" alt="两字符串尾字符不同序列函数变化"><br>原因在于，虽然尾字符不同，但一个字符串的尾字符可能和另一个字符串的前面的某个字符相同，导致最长公共子序列得到扩展。<img src="https://img.hoi3vel.cn/tech/max_common_list_4.webp" alt="尾字符与非尾字符对应"><br>注意，上图的关系可以进一步表达为：<img src="https://img.hoi3vel.cn/tech/max_common_list_5.webp" alt="尾字符与非尾字符对应进阶"><br>考虑其一般性，取<strong>f(i-1, j)和f(i, j-1)的最大值</strong>:<img src="https://img.hoi3vel.cn/tech/max_common_list_6.webp" alt="尾字符与非尾字符一般性"><br>假设字符串a和b的长度分别是m和n，绘制一张m×n的二维表格<strong>table[m][n]</strong>。取字符串a和b的前缀字符串“a’”和“b’”，表格位置i，j上的数字含义为f(i，j)即字符串a’和b’的最长公共子序列的长度。<img src="https://img.hoi3vel.cn/tech/max_common_list_7.webp" alt="table表_2"><br>利用已分析的递推关系，表格填写规则如下：</li><li>如果字符<strong>a[i]</strong>和<strong>b[j]</strong>相同，则最长公共子序列得到扩展，数值+1</li><li>否则，采用左边的和上面的方格中的数值的最大值<br><img src="https://img.hoi3vel.cn/tech/max_common_list_8.webp" alt="table表_2计算规则"><br>按照上述规律，字符串“abcbcde”和“acabdef”的填表过程如下：<img src="https://img.hoi3vel.cn/tech/max_common_list_9.webp" alt="table表_2填写过程"><br>上述规则映射至代码为：</li><li>若<strong>a[i]&#x3D;&#x3D;b[j]</strong>，则填写<strong>table[i][j]&#x3D;table[i-1][j-1]+1</strong></li><li>否则填写<strong>table[i][j]&#x3D;max(table[i-1][j], table[i][j-1])</strong><br>表格中最大值即为最长公共子序列的长度，时间复杂度为O(m*n)。</li></ul><h3 id="3-代码实现-1"><a href="#3-代码实现-1" class="headerlink" title="3.代码实现"></a>3.代码实现</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> MAX(a, b) (a) &gt; (b) ? (a) : (b)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 返回两个字符串 a 和 b 的最长公共子序列的长度</span></span><br><span class="line"><span class="comment">// 例如 &quot;abcbcde&quot; 和 &quot;acabdef&quot; 的最长公共子序列是 &quot;acbde&quot; ，长度为 5</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">LongestCommonSubsequence</span><span class="params">(<span class="type">char</span> *a, <span class="type">int</span> a_length, <span class="type">char</span> *b, <span class="type">int</span> b_length)</span> &#123;</span><br><span class="line">    <span class="type">int</span> table[a_length][b_length];</span><br><span class="line">    <span class="type">int</span> max = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; a_length; i++) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; b_length; j++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (a[i] == b[j]) &#123;</span><br><span class="line">                <span class="keyword">if</span> (i &gt;= <span class="number">1</span> &amp;&amp; j &gt;= <span class="number">1</span>)</span><br><span class="line">                    table[i][j] = table[i - <span class="number">1</span>][j - <span class="number">1</span>] + <span class="number">1</span>;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    table[i][j] = <span class="number">1</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (i &gt;= <span class="number">1</span> &amp;&amp; j &gt;= <span class="number">1</span>)</span><br><span class="line">                    table[i][j] = MAX(table[i - <span class="number">1</span>][j], table[i][j - <span class="number">1</span>]);</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (i &lt;= <span class="number">0</span> &amp;&amp; j &gt;= <span class="number">1</span>)</span><br><span class="line">                    table[i][j] = table[i][j - <span class="number">1</span>];</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (i &gt;= <span class="number">1</span> &amp;&amp; j &lt;= <span class="number">0</span>)</span><br><span class="line">                    table[i][j] = table[i - <span class="number">1</span>][j];</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    table[i][j] = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (max &lt; table[i][j]) max = table[i][j];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> max;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;自己在刷LeetCode时遇见了最长公共子串这个题目，传统的编程思想使我对此题无从下手，后面查阅资料后了解到&lt;strong&gt;动态规划&lt;/strong&gt;这一思想，因此便写下文章，记录如何使用&lt;strong&gt;动态规划&lt;/strong&gt;解决最长公共子串和最长公共子序列问题。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="Algorithm" scheme="https://www.hoi3vel.cn/tags/Algorithm/"/>
    
    <category term="Dynamic Programming" scheme="https://www.hoi3vel.cn/tags/Dynamic-Programming/"/>
    
  </entry>
  
  <entry>
    <title>Hexo博客引入Giscus评论系统</title>
    <link href="https://www.hoi3vel.cn/2024/05/31/t-comment-deploy/"/>
    <id>https://www.hoi3vel.cn/2024/05/31/t-comment-deploy/</id>
    <published>2024-05-31T14:13:00.000Z</published>
    <updated>2025-11-21T13:29:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>其实将Giscus评论系统引入Hexo博客中操作很简单，但是想到自己一开始看着<a href="https://hexo-theme-async.imalun.com/guide/config">Hexo-Theme-Async</a>文档所介绍的操作依然不知所措的场景，最后还是决定写篇文章讲讲具体的流程。</p></blockquote><span id="more"></span><h1 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h1><p>Giscus是由<a href="https://docs.github.com/en/discussions">Github Discussions</a>实现的评论系统，它主要有三个优点：①开源；②无跟踪、无广告且永久免费；③无需数据库，所有数据均存储在Github Discussions中。</p><h2 id="1-工作原理"><a href="#1-工作原理" class="headerlink" title="1.工作原理"></a>1.工作原理</h2><p>Gicsus使用Github Discussions作为数据库存储博客下面的评论。Giscus插件加载时会使用Github Discussions搜索API根据选定的映射方式(如URL、Pathname等)来查找与当前页面关联的discussion。若找不到匹配的discussion，Giscus bot就会在第一次有人留下评论或回应时自动创建一个discussion。访客通过登录Github账号在博客页面进行留言，而作者可以在Github对应仓库上管理评论。</p><h1 id="二、具体流程"><a href="#二、具体流程" class="headerlink" title="二、具体流程"></a>二、具体流程</h1><h2 id="1-引入Giscus"><a href="#1-引入Giscus" class="headerlink" title="1.引入Giscus"></a>1.引入Giscus</h2><p>Hexo主题<a href="https://hexo-theme-async.imalun.com/">Hexo-Theme-Async</a>支持Giscuss评论，因此引入步骤很简单，具体流程如下:</p><ul><li>在Github上选择一个仓库作为存储Discussions的仓库，一般选择博客本身所在的仓库(e.g.Github用户名&#x2F;Github用户名.github.io)，当然也可以新建一个仓库。</li><li>安装<a href="https://github.com/apps/giscus">Giscus</a></li><li>开启作为存储Discussions仓库的Discussions功能: 选择GIthub仓库-&gt;Settings-&gt;Features模块中勾选Discussions</li></ul><h2 id="2-配置Giscus"><a href="#2-配置Giscus" class="headerlink" title="2.配置Giscus"></a>2.配置Giscus</h2><p>上述操作完成后，进入<a href="https://giscus.app/zh-CN">Giscus官网</a>获取配置信息，填写完<strong>仓库</strong>和<strong>分类</strong>后就可在显示配置信息，生成的配置信息样例如下:<img src="https://img.hoi3vel.cn/tech/giscus_deploy.webp" alt="giscus配置信息样例"><br>分类可以选择<code>Announcements</code>、<code>Q&amp;A</code>等，不一定按照推荐选择分类。</p><p>在此配置信息中我们只需关注<strong>data-repo</strong>、<strong>data-repo-id</strong>、<strong>data-categories</strong>、<strong>data-category-id</strong>这些信息。</p><h2 id="3-引入至博客"><a href="#3-引入至博客" class="headerlink" title="3.引入至博客"></a>3.引入至博客</h2><p>在<code>_config.async.yml</code>中配置如下:<img src="https://img.hoi3vel.cn/tech/hexo_giscus.webp" alt="Hexo配置文件"><br>图中打码部分为专属配置信息，其中theme主题可自己更改，可供选择主题可在<a href="https://giscus.app/zh-CN">Giscus官网</a>查看。</p><h2 id="4-效果视图"><a href="#4-效果视图" class="headerlink" title="4.效果视图"></a>4.效果视图</h2><p>白天模式下:<img src="https://img.hoi3vel.cn/tech/giscus_example.webp" alt="giscus_exmaple"><br>黑夜模式下:<img src="https://img.hoi3vel.cn/tech/giscus_example_dark.webp" alt="giscus_example_dark"><br>评论的管理可在Github中对应仓库的<strong>Discussions</strong>中实现</p>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;其实将Giscus评论系统引入Hexo博客中操作很简单，但是想到自己一开始看着&lt;a href=&quot;https://hexo-theme-async.imalun.com/guide/config&quot;&gt;Hexo-Theme-Async&lt;/a&gt;文档所介绍的操作依然不知所措的场景，最后还是决定写篇文章讲讲具体的流程。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="Guidance" scheme="https://www.hoi3vel.cn/tags/Guidance/"/>
    
    <category term="Giscus" scheme="https://www.hoi3vel.cn/tags/Giscus/"/>
    
    <category term="Deployment" scheme="https://www.hoi3vel.cn/tags/Deployment/"/>
    
    <category term="Hexo" scheme="https://www.hoi3vel.cn/tags/Hexo/"/>
    
  </entry>
  
  <entry>
    <title>基于Hexo的静态博客部署至阿里云服务器</title>
    <link href="https://www.hoi3vel.cn/2024/05/29/t-hexo-deploy/"/>
    <id>https://www.hoi3vel.cn/2024/05/29/t-hexo-deploy/</id>
    <published>2024-05-29T09:00:00.000Z</published>
    <updated>2025-11-21T13:26:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>自己将本地博客部署到阿里云服务器过程中踩了许许多多的坑，网上的许多教程也不尽人意，因此写下这篇如何将本地博客部署至阿里云服务器的教程博客，希望对有此需求的人有所帮助。</p></blockquote><span id="more"></span><h2 id="一、写在前面"><a href="#一、写在前面" class="headerlink" title="一、写在前面"></a>一、写在前面</h2><h3 id="1-部署方式"><a href="#1-部署方式" class="headerlink" title="1.部署方式"></a>1.部署方式</h3><p>博客部署有两种选择：</p><ul><li>托管至GitHub或者Gitee上: 借用Github或Gitee的这一功能部署网站，操作简单且免费。国内访问速度方面Gitee&gt;Github。</li><li>部署至私有云服务器上：需要花钱购买服务器、域名，此方式自主性强且相应速度快。<br>在此文章中我将叙述如何将本地的Hexo静态博客部署至私有云服务器上。</li></ul><h3 id="2-相关环境"><a href="#2-相关环境" class="headerlink" title="2.相关环境"></a>2.相关环境</h3><ul><li>阿里云服务器: ①CPU&amp;内存-2核(vCPU) 2GiB; ②操作系统: Ubuntu 20.04 64位; ③公网带宽: 3Mbps</li><li>Nodejs版本(云服务器上):  v20.13.1</li><li>Git版本(云服务器上): v2.25.1</li><li>Nginx版本(云服务器上): v1.18.0(Ubuntu)</li></ul><h3 id="3-前置准备"><a href="#3-前置准备" class="headerlink" title="3.前置准备"></a>3.前置准备</h3><ul><li>本地成功搭建Hexo博客</li><li>本地成功安装了Git工具</li><li>域名与云服务器公有Ip实现映射绑定</li></ul><h2 id="二、工作原理"><a href="#二、工作原理" class="headerlink" title="二、工作原理"></a>二、工作原理</h2><p>本地撰写的博客会保存在source目录内，当我们输入<code>hexo generate</code>命令时，Hexo将会把我们写的Markdown文件渲染成静态文件(即网页文件)，输入<code>hexo deploy</code>时，Hexo会通过我们的配置好的免密登陆连接至服务器并将生成的静态文件推送至服务器的Git仓库内。当Git仓库内容发生变更时则会触发钩子文件进而将仓库内的内容部署至网站根目录下。<br>此外我们还需要配置好Nginx服务和域名解析，这样当用户访问我们博客的域名时，域名将会被重定向为云服务的公有Ip地址并通过Nginx服务访问网站根目录里的静态文件。相关流程图如下所示:<img src="https://img.hoi3vel.cn/tech/hexo_depoly.webp" alt="部署流程图"></p><h2 id="三、搭建步骤-针对云服务器"><a href="#三、搭建步骤-针对云服务器" class="headerlink" title="三、搭建步骤(针对云服务器)"></a>三、搭建步骤(针对云服务器)</h2><h3 id="1-服务端初始配置"><a href="#1-服务端初始配置" class="headerlink" title="1.服务端初始配置"></a>1.服务端初始配置</h3><ul><li>前往阿里云网站的控制台，记录云服务器的公网Ip: 控制台-&gt;云服务器ESC-&gt;公网Ip</li><li>设置阿里云服务器安全组：需要开放80端口(HTTP默认访问端口)、443端口(HTTPS默认访问端口)以及22端口。具体如下图:<img src="https://img.hoi3vel.cn/tech/security_group.webp" alt="安全组图"></li><li>重置服务器登陆密码，用于远程云登陆服务器：控制台-&gt;云服务ESC-&gt;服务器实例-&gt;全部操作(右上角)-&gt;重置实例密码</li></ul><h3 id="2-远程登陆服务器"><a href="#2-远程登陆服务器" class="headerlink" title="2.远程登陆服务器"></a>2.远程登陆服务器</h3><ul><li>打开Xshell并新建会话：①协议：SSH; ②主机: 云服务器公网Ip; ③端口号: 22</li><li>点击连接：①用户名: root; ②密码: 即上述重置服务器登陆密码时输入的密码</li><li>登陆成功后终端显示如下图:<img src="https://img.hoi3vel.cn/tech/Xshell_Connect.webp" alt="Xshell_Connect"></li></ul><h3 id="3-创建Hexo部署目录"><a href="#3-创建Hexo部署目录" class="headerlink" title="3.创建Hexo部署目录"></a>3.创建Hexo部署目录</h3><p>Hexo部署目录实质就是云服务器中存放博客的位置。使用Xshell成功连接远程服务器后输入如下命令:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p /data/blog # 注意这个路径可按照自定义来</span><br></pre></td></tr></table></figure><h3 id="4-安装Nodejs"><a href="#4-安装Nodejs" class="headerlink" title="4.安装Nodejs"></a>4.安装Nodejs</h3><ul><li><p>在Xshell终端输入如下命令:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install -y curl</span><br><span class="line">curl -fsSL https://deb.nodesource.com/setup_20.x -o nodesource_setup.sh</span><br><span class="line">sudo -E bash nodesource_setup.sh</span><br><span class="line">sudo apt-get install -y nodejs</span><br></pre></td></tr></table></figure></li><li><p>成功安装后依次输入命令: <code>node -v</code>和<code>npm -v</code>，若可显示相应版本号则说明安装成功<br>PS:上述命令安装的Nodejs版本为<strong>v20.13.1</strong>，如需要安装其他版本则可从<a href="https://github.com/nodesource/distributions">NodeSource</a>安装</p></li></ul><h3 id="5-安装Git"><a href="#5-安装Git" class="headerlink" title="5.安装Git"></a>5.安装Git</h3><ul><li><p>在Xshell终端输入命令: <code>sudo apt-get install git-core</code></p></li><li><p>成功安装后输入命令: <code>git --version</code>，若可显示其版本号则说明安装成功</p></li></ul><h3 id="6-安装Nginx"><a href="#6-安装Nginx" class="headerlink" title="6.安装Nginx"></a>6.安装Nginx</h3><ul><li><p>在Xshell终端输入命令: <code>sudo apt install nginx -y</code></p></li><li><p>成功安装后输入命令: <code>nginx -v</code>，若可显示版本号则说明安装成功</p></li><li><p>启动Nginx服务：<code>systemctl start nginx.service</code></p></li><li><p>查看Nginx服务运行状态: <code>systemctl status nginx</code>，若出现<font color='green'>active</font>字样则说明Nginx服务启动成功</p></li></ul><h3 id="7-配置Nginx"><a href="#7-配置Nginx" class="headerlink" title="7.配置Nginx"></a>7.配置Nginx</h3><ul><li><p>查看<strong>nginx.conf</strong>文件位置: <code>nginx -t</code>,一般为’&#x2F;etc&#x2F;nginx&#x2F;nginx.conf’</p></li><li><p>修改<strong>nginx.conf</strong>文件: <code>nano /etc/nginx/nginx.conf</code>，在<strong>http</strong>模块中添加如下代码：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">    listen 80;</span><br><span class="line">    server_name 域名;</span><br><span class="line">    return 301 https://$server_name$request_uri;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">server &#123;</span><br><span class="line">    listen 443 ssl;</span><br><span class="line">    server_name 域名;</span><br><span class="line">    # 证书文件名称pem文件</span><br><span class="line">    ssl_certificate /xxx/xxx/域名.pem;</span><br><span class="line">    # 证书私钥文件名称key文件</span><br><span class="line">    ssl_certificate_key /xxx/xxx/域名.key;</span><br><span class="line">    # ssl验证配置 缓存有效期</span><br><span class="line">    ssl_session_timeout 5m;</span><br><span class="line">    # 安全链接可选的加密协议</span><br><span class="line">    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;</span><br><span class="line">    # 配置加密套件和/加密算法</span><br><span class="line">    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;</span><br><span class="line">    # 使用服务器端的首选算法</span><br><span class="line">    ssl_prefer_server_ciphers on;</span><br><span class="line"></span><br><span class="line">    # 指定虚拟主机根目录</span><br><span class="line">    location / &#123;</span><br><span class="line">        root /data/blog; # root后路径为Hexo在云服务器的部署目录</span><br><span class="line">        index index.html index.htm;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Ps: ssl证书可从域名购买平台上申请，然后下载上传至云服务器自定义位置即可</p></li></ul><h3 id="8-添加git用户"><a href="#8-添加git用户" class="headerlink" title="8.添加git用户"></a>8.添加git用户</h3><ul><li><p><strong>root</strong>用户默认的权限过大，处于安全考虑为云服务器添加名为git的用户，用于将博客自动部署至云服务器上</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">useradd git # 添加名为git的用户</span><br><span class="line">passwd xxx # 修改git用户的密码</span><br></pre></td></tr></table></figure></li><li><p>为git用户授予sudo权限，执行<code>nano /etc/sudoers</code>，在<code>root ALL=(ALL:ALL) ALL</code>语句下添加<code>git ALL=(ALL:ALL) ALL</code></p></li></ul><h3 id="9-为git用户添加SSH密钥"><a href="#9-为git用户添加SSH密钥" class="headerlink" title="9.为git用户添加SSH密钥"></a>9.为git用户添加SSH密钥</h3><p>目前为止，对git仓库进行的所有操作都需要输入密码，我们可以通过为git用户添加SSH密钥的方式来实现免密登录。原理：在本地生成一对密钥文件（分别是公钥和私钥），将公钥文件上传到服务器上；服务器收到连接请求时，会将本地的公钥与服务器的公钥进行比对，用私钥解密服务器发来的一段信息并将其发回，验证通过后准许连接。</p><ul><li><p>在<strong>本地计算机</strong>上打开Git Bush执行命令: <code>ssh-keygen -t rsa</code>，生成密钥对。运行完后可在’C:\Users\用户名.ssh’文件下找到’id_rsa’和’id_rsa.pub’文件，前者为私钥文件，后者为公钥文件</p></li><li><p>在Xshell终端先执行<code>makdir -p /home/git/.ssh</code>，后执行<code>cd /home/git/.ssh</code>以及<code>nano authorized_keys</code>，输入内容为**’id_rsa.pub’**文件内容</p></li><li><p>在Xshell终端中执行如下代码：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">su root # 从git用户更换至root用户</span><br><span class="line">chmod 600 /home/git/.ssh/authorized_keys</span><br><span class="line">chmod 700 /home/git/.ssh</span><br></pre></td></tr></table></figure></li><li><p>将.ssh文件夹及其内的所有文件所有权转交给git用户: <code>chown -R git:git /home/git/.ssh</code></p></li><li><p>在<strong>本地计算机</strong>测试是否可以用ssh免密登录服务器，在Git Bash中执行<code>ssh -v git@xxx.xxx.xxx.xxx</code>(Ps:git@后填写服务器的公网Ip) ,若成功则会出现’Welcome to Alibaba Cloud Elastic Compute Service !’提示语。</p></li></ul><h3 id="10-创建Git仓库并配置自动部署"><a href="#10-创建Git仓库并配置自动部署" class="headerlink" title="10.创建Git仓库并配置自动部署"></a>10.创建Git仓库并配置自动部署</h3><p>这里的Git仓库（repository）可以理解为一个存放着不同版本代码的代码库，包含了项目所有的源文件和版本控制信息。但我们在这里需要用到的是裸库，即仅包含版本控制信息的仓库。后者不保存文件，常用作服务器仓库，实现数据共享和同步。自动部署的核心在于git-hooks（钩子），可以理解为一种脚本，在特定条件下触发时会调用钩子并执行钩子文件中的内容。</p><ul><li><p>Xshell切换到<strong>root</strong>用户，新建目录并在该目录下创建Git仓库</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /home/git</span><br><span class="line">git init --bare blog.git # 创建一个名为blog的仓库，--bare参数为创建裸库</span><br></pre></td></tr></table></figure></li><li><p>打开’&#x2F;home&#x2F;git&#x2F;blog.git&#x2F;hooks’目录下的<strong>post-receive</strong>钩子文件(若无则新建)，往其添加内容<code>git --work-tree=/data/blog --git-dir=/home/git/blog.git checkout -f</code>。(Ps:work-tree填写Hexo部署目录，git-dir填写Git仓库的目录)</p></li><li><p>为钩子文件及Git仓库目录配置权限。这里需要将我们创建的仓库及部署目录的所有权移交给git用户，因为Linux下创建文件时默认只有拥有者（即创建者）才具有读写权限，而Git进行push操作时登录的用户为git用户。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">chmod +x /home/git/blog.git/hooks/post-receive     #为钩子文件授予可执行权限（+x）</span><br><span class="line">chown -R git:git /home/git    #将仓库目录的所有权移交给git用户</span><br><span class="line">chown -R git:git /data/blog     #将Hexo部署目录的所有权移交给git用户</span><br></pre></td></tr></table></figure></li></ul><h3 id="11-在本地计算机中更改本地博客部署路径"><a href="#11-在本地计算机中更改本地博客部署路径" class="headerlink" title="11.在本地计算机中更改本地博客部署路径"></a>11.在本地计算机中更改本地博客部署路径</h3><ul><li><p>打开本地计算机的Hexo文件夹，在<strong>_config.yml</strong>文件中找到deploy语句并进行如下修改：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">deploy:</span><br><span class="line">    type: git</span><br><span class="line">    repository: git@xxx.xxx.xxx.xxx:/home/git/blog.git # git@云服务器公网Ip:云服务器Git仓库地址</span><br><span class="line">    branch: master</span><br></pre></td></tr></table></figure></li><li><p>依次执行命令<code>hexo clean</code>、<code>hexo generate</code>、<code>hexo deploy</code>并对云服务器’&#x2F;data&#x2F;blog’目录刷新，若有文件出现(该目录原为空目录)则说明博客已成功部署至云服务器上，至此可以在浏览器中输入域名来访问博客站点。(Ps：若域名未与公网Ip绑定&lt;即域名解析&gt;，则无法实现输入域名访问博客站点)</p></li></ul>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;自己将本地博客部署到阿里云服务器过程中踩了许许多多的坑，网上的许多教程也不尽人意，因此写下这篇如何将本地博客部署至阿里云服务器的教程博客，希望对有此需求的人有所帮助。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.hoi3vel.cn/categories/Tech/"/>
    
    
    <category term="Guidance" scheme="https://www.hoi3vel.cn/tags/Guidance/"/>
    
    <category term="Deployment" scheme="https://www.hoi3vel.cn/tags/Deployment/"/>
    
    <category term="Hexo" scheme="https://www.hoi3vel.cn/tags/Hexo/"/>
    
    <category term="Cloud Server" scheme="https://www.hoi3vel.cn/tags/Cloud-Server/"/>
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="https://www.hoi3vel.cn/2024/05/27/hello-world/"/>
    <id>https://www.hoi3vel.cn/2024/05/27/hello-world/</id>
    <published>2024-05-27T14:00:00.000Z</published>
    <updated>2025-11-21T13:10:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>蝉鸣是夏天的前言，也是我回忆的诗篇…</p></blockquote><span id="more"></span><div class="tag-plugin reel"><div class="content"><div class="title">定风波</div><div class="meta"><span>北宋·苏轼</span></div><div class="body"><div class="main"><p>三月七日，沙湖道中遇雨。雨具先去，同行皆狼狈，余独不觉。<br>已而遂晴，故作此词。<br>莫听穿林打叶声，何妨吟啸且徐行。<br>竹杖芒鞋轻胜马，谁怕？一蓑烟雨任平生。<br>料峭春风吹酒醒，微冷，山头斜照却相迎。<br>回首向来萧瑟处，归去，也无风雨也无晴。</p></div></div><div class="footer"></div></div></div>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;蝉鸣是夏天的前言，也是我回忆的诗篇…&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Life" scheme="https://www.hoi3vel.cn/categories/Life/"/>
    
    
    <category term="Blog" scheme="https://www.hoi3vel.cn/tags/Blog/"/>
    
  </entry>
  
</feed>
