<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Chen Xi</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://dajinzhu.cn/</id>
  <link href="https://dajinzhu.cn/" rel="alternate"/>
  <link href="https://dajinzhu.cn/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Chen Xi</rights>
  <subtitle>Chen Xi's Blog</subtitle>
  <title>金猪博客</title>
  <updated>2024-07-31T03:43:41.000Z</updated>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="操作系统" scheme="https://dajinzhu.cn/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    <category term="Docker" scheme="https://dajinzhu.cn/tags/Docker/"/>
    <category term="Linux" scheme="https://dajinzhu.cn/tags/Linux/"/>
    <category term="Windows" scheme="https://dajinzhu.cn/tags/Windows/"/>
    <content>
      <![CDATA[<p>Docker 是一种轻量级的虚拟化技术，允许开发者在容器中创建、部署和运行应用程序。本文将详细介绍 Docker 的安装、基本使用、常用命令，并演示如何在 Docker 容器中安装 Windows 11。</p><hr /><h2 id="1-安装-docker"><a class="markdownIt-Anchor" href="#1-安装-docker"></a> 1. 安装 Docker</h2><h3 id="11-在-ubuntu-上安装-docker"><a class="markdownIt-Anchor" href="#11-在-ubuntu-上安装-docker"></a> 1.1 在 Ubuntu 上安装 Docker</h3><h4 id="步骤-1更新包管理器"><a class="markdownIt-Anchor" href="#步骤-1更新包管理器"></a> 步骤 1：更新包管理器</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br></pre></td></tr></table></figure><h4 id="步骤-2安装必要的依赖"><a class="markdownIt-Anchor" href="#步骤-2安装必要的依赖"></a> 步骤 2：安装必要的依赖</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install apt-transport-https ca-certificates curl software-properties-common</span><br></pre></td></tr></table></figure><h4 id="步骤-3添加-docker-的官方-gpg-密钥"><a class="markdownIt-Anchor" href="#步骤-3添加-docker-的官方-gpg-密钥"></a> 步骤 3：添加 Docker 的官方 GPG 密钥</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -fsSL https://download.docker.com/linux/ubuntu/gpg | <span class="built_in">sudo</span> gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg</span><br></pre></td></tr></table></figure><h4 id="步骤-4添加-docker-的-apt-存储库"><a class="markdownIt-Anchor" href="#步骤-4添加-docker-的-apt-存储库"></a> 步骤 4：添加 Docker 的 APT 存储库</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">&quot;deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu <span class="subst">$(lsb_release -cs)</span> stable&quot;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/apt/sources.list.d/docker.list &gt; /dev/null</span><br></pre></td></tr></table></figure><h4 id="步骤-5安装-docker"><a class="markdownIt-Anchor" href="#步骤-5安装-docker"></a> 步骤 5：安装 Docker</h4><figure class="highlight bash"><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="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install docker-ce</span><br></pre></td></tr></table></figure><h4 id="步骤-6启动并启用-docker"><a class="markdownIt-Anchor" href="#步骤-6启动并启用-docker"></a> 步骤 6：启动并启用 Docker</h4><figure class="highlight bash"><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="built_in">sudo</span> systemctl start docker</span><br><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> docker</span><br></pre></td></tr></table></figure><h3 id="12-在-windows-上安装-docker"><a class="markdownIt-Anchor" href="#12-在-windows-上安装-docker"></a> 1.2 在 Windows 上安装 Docker</h3><ol><li>访问 <a class="link"   href="https://www.docker.com/products/docker-desktop" >Docker Desktop for Windows<i class="fas fa-external-link-alt"></i></a> 官网，下载 Docker Desktop 安装程序。</li><li>运行安装程序，按照提示完成安装。</li><li>安装完成后，启动 Docker Desktop。</li></ol><h3 id="13-在-macos-上安装-docker"><a class="markdownIt-Anchor" href="#13-在-macos-上安装-docker"></a> 1.3 在 macOS 上安装 Docker</h3><ol><li>访问 <a class="link"   href="https://www.docker.com/products/docker-desktop" >Docker Desktop for Mac<i class="fas fa-external-link-alt"></i></a> 官网，下载 Docker Desktop 安装程序。</li><li>运行安装程序，按照提示完成安装。</li><li>安装完成后，启动 Docker Desktop。</li></ol><h2 id="2-docker-的基本使用"><a class="markdownIt-Anchor" href="#2-docker-的基本使用"></a> 2. Docker 的基本使用</h2><h3 id="21-验证安装"><a class="markdownIt-Anchor" href="#21-验证安装"></a> 2.1 验证安装</h3><p>安装完成后，可以使用以下命令验证 Docker 是否正确安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker --version</span><br></pre></td></tr></table></figure><p>输出类似于：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Docker version 20.10.8, build 3967b7d</span><br></pre></td></tr></table></figure><h3 id="22-运行第一个容器"><a class="markdownIt-Anchor" href="#22-运行第一个容器"></a> 2.2 运行第一个容器</h3><p>使用以下命令运行一个简单的 Hello World 容器：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run hello-world</span><br></pre></td></tr></table></figure><p>如果成功运行，会看到类似以下输出：</p><figure class="highlight plaintext"><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">Hello from Docker!</span><br><span class="line">This message shows that your installation appears to be working correctly.</span><br></pre></td></tr></table></figure><h3 id="23-常用-docker-命令"><a class="markdownIt-Anchor" href="#23-常用-docker-命令"></a> 2.3 常用 Docker 命令</h3><ul><li><strong>docker run</strong>：运行一个新的容器。</li><li><strong>docker ps</strong>：列出当前运行的容器。</li><li><strong>docker ps -a</strong>：列出所有容器（包括停止的）。</li><li><strong>docker stop &lt;container_id&gt;</strong>：停止一个运行中的容器。</li><li><strong>docker start &lt;container_id&gt;</strong>：启动一个停止的容器。</li><li><strong>docker rm &lt;container_id&gt;</strong>：删除一个容器。</li><li><strong>docker images</strong>：列出本地的镜像。</li><li><strong>docker rmi &lt;image_id&gt;</strong>：删除一个镜像。</li><li><strong>docker logs &lt;container_id&gt;</strong>：查看容器的日志。</li></ul><h2 id="3-在-docker-容器中安装-windows-11"><a class="markdownIt-Anchor" href="#3-在-docker-容器中安装-windows-11"></a> 3. 在 Docker 容器中安装 Windows 11</h2><p>请注意，Docker 容器主要用于运行 Linux 容器，而 Windows 容器只能在 Windows 主机上运行。运行 Windows 11 需要虚拟化软件（如 Hyper-V 或 VirtualBox），因此 Docker 不适用于安装和运行完整的 Windows 11 操作系统。下面我们将演示如何在 Docker 中运行一个 Windows 11 的应用程序镜像，而非整个操作系统。</p><h3 id="31-安装-windows-容器支持仅-windows-主机"><a class="markdownIt-Anchor" href="#31-安装-windows-容器支持仅-windows-主机"></a> 3.1 安装 Windows 容器支持（仅 Windows 主机）</h3><p>如果你在 Windows 主机上使用 Docker Desktop，需要启用 Windows 容器支持：</p><ol><li>启动 Docker Desktop。</li><li>右键单击 Docker 图标，选择 “Switch to Windows containers”。</li></ol><h3 id="32-拉取-windows-11-应用程序镜像"><a class="markdownIt-Anchor" href="#32-拉取-windows-11-应用程序镜像"></a> 3.2 拉取 Windows 11 应用程序镜像</h3><p>由于无法直接运行完整的 Windows 11 操作系统，我们可以运行一个 Windows Server 核心镜像来模拟 Windows 环境：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker pull mcr.microsoft.com/windows/servercore:ltsc2022</span><br></pre></td></tr></table></figure><h3 id="33-运行-windows-server-核心容器"><a class="markdownIt-Anchor" href="#33-运行-windows-server-核心容器"></a> 3.3 运行 Windows Server 核心容器</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -it mcr.microsoft.com/windows/servercore:ltsc2022 cmd</span><br></pre></td></tr></table></figure><p>这将启动一个基于 Windows Server 核心的容器，并打开一个命令行提示符。你可以在此基础上安装和运行 Windows 应用程序。</p><h3 id="34-安装-windows-应用程序"><a class="markdownIt-Anchor" href="#34-安装-windows-应用程序"></a> 3.4 安装 Windows 应用程序</h3><p>在容器内，你可以通过 <code>choco</code>（Chocolatey）等包管理工具安装 Windows 应用程序。首先，安装 Chocolatey：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Set-ExecutionPolicy</span> Bypass <span class="literal">-Scope</span> <span class="keyword">Process</span> <span class="literal">-Force</span>; [<span class="type">System.Net.ServicePointManager</span>]::SecurityProtocol = [<span class="type">System.Net.ServicePointManager</span>]::SecurityProtocol <span class="operator">-bor</span> <span class="number">3072</span>; <span class="built_in">iex</span> ((<span class="built_in">New-Object</span> System.Net.WebClient).DownloadString(<span class="string">&#x27;https://community.chocolatey.org/install.ps1&#x27;</span>))</span><br></pre></td></tr></table></figure><p>安装完成后，可以使用 <code>choco</code> 安装应用程序，例如：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">choco install notepadplusplus <span class="literal">-y</span></span><br></pre></td></tr></table></figure><p>安装完成后，你可以运行安装的应用程序：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\Program Files\Notepad++\notepad++.exe</span><br></pre></td></tr></table></figure><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h2><p>金猪言：本文详细介绍了如何在 Ubuntu、Windows 和 macOS 上安装 Docker，以及如何使用 Docker 的基本命令。虽然 Docker 主要用于运行 Linux 容器，但在 Windows 主机上也可以运行 Windows 容器。</p><p>Docker开源，免费，Docker 的核心组件，例如 Docker Engine，是在 Apache 2.0 许可证下发布的，这是一个开源许可证。你可以自由地使用、修改和分发 Docker 的源代码。所以使用成本几乎为0，建议大规模使用。</p>]]>
    </content>
    <id>https://dajinzhu.cn/2024/07/31/docker-start-win11/</id>
    <link href="https://dajinzhu.cn/2024/07/31/docker-start-win11/"/>
    <published>2024-07-31T03:43:41.000Z</published>
    <summary>介绍 Docker 的安装、镜像与容器管理命令，并说明在 Windows 11 环境中的使用与配置方法。</summary>
    <title>Docker 容器安装与使用教程：安装 Windows 11 示例</title>
    <updated>2024-07-31T03:43:41.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="操作系统" scheme="https://dajinzhu.cn/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    <category term="Linux" scheme="https://dajinzhu.cn/tags/Linux/"/>
    <category term="Ubuntu" scheme="https://dajinzhu.cn/tags/Ubuntu/"/>
    <category term="Kernel" scheme="https://dajinzhu.cn/tags/Kernel/"/>
    <content>
      <![CDATA[<p>Ubuntu 是一个流行的 Linux 发行版，广泛应用于服务器和桌面环境。内核（Kernel）是操作系统的核心部分，它管理系统资源和硬件交互。为了获得最新的功能、性能提升和安全补丁，有时需要更新到最新版本的内核。本教程将详细介绍如何在 Ubuntu 系统中更新内核。</p><hr /><h2 id="1-准备工作"><a class="markdownIt-Anchor" href="#1-准备工作"></a> 1. 准备工作</h2><h3 id="11-备份数据"><a class="markdownIt-Anchor" href="#11-备份数据"></a> 1.1 备份数据</h3><p>在更新内核之前，备份重要数据是非常重要的。内核更新可能会导致系统不稳定或不兼容的问题，因此备份可以确保数据的安全。</p><h3 id="12-确认系统版本"><a class="markdownIt-Anchor" href="#12-确认系统版本"></a> 1.2 确认系统版本</h3><p>首先，确认你的 Ubuntu 版本。可以使用以下命令查看：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lsb_release -a</span><br></pre></td></tr></table></figure><p>输出类似于：</p><figure class="highlight plaintext"><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">Distributor ID: Ubuntu</span><br><span class="line">Description:    Ubuntu 20.04.2 LTS</span><br><span class="line">Release:        20.04</span><br><span class="line">Codename:       focal</span><br></pre></td></tr></table></figure><h3 id="13-检查当前内核版本"><a class="markdownIt-Anchor" href="#13-检查当前内核版本"></a> 1.3 检查当前内核版本</h3><p>使用以下命令检查当前的内核版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">uname</span> -r</span><br></pre></td></tr></table></figure><p>输出类似于：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">5.4.0-74-generic</span><br></pre></td></tr></table></figure><h2 id="2-更新内核的方法"><a class="markdownIt-Anchor" href="#2-更新内核的方法"></a> 2. 更新内核的方法</h2><p>更新 Ubuntu 内核有多种方法，包括使用 Ubuntu 提供的官方更新、第三方工具（如 <code>Ubuntu Kernel Update Utility</code>，UKUU），或手动安装。在这里，我们将介绍两种常见的方法：通过官方存储库更新和手动安装最新的主线内核。</p><h3 id="21-通过官方存储库更新"><a class="markdownIt-Anchor" href="#21-通过官方存储库更新"></a> 2.1 通过官方存储库更新</h3><p>这是最安全和推荐的方法，适合大多数用户。Ubuntu 定期更新其存储库中的内核版本，用户可以通过系统更新获得这些更新。</p><h4 id="步骤-1更新包管理器"><a class="markdownIt-Anchor" href="#步骤-1更新包管理器"></a> 步骤 1：更新包管理器</h4><p>首先，更新包管理器缓存：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br></pre></td></tr></table></figure><h4 id="步骤-2升级系统"><a class="markdownIt-Anchor" href="#步骤-2升级系统"></a> 步骤 2：升级系统</h4><p>升级系统软件包，包括内核：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt upgrade</span><br></pre></td></tr></table></figure><p>如果有新的内核版本可用，它将被下载并安装。系统可能会提示重启，以应用新内核。</p><h4 id="步骤-3重启系统"><a class="markdownIt-Anchor" href="#步骤-3重启系统"></a> 步骤 3：重启系统</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> reboot</span><br></pre></td></tr></table></figure><p>重启后，使用 <code>uname -r</code> 再次检查当前的内核版本，以确认更新是否成功。</p><h3 id="22-手动安装最新的主线内核"><a class="markdownIt-Anchor" href="#22-手动安装最新的主线内核"></a> 2.2 手动安装最新的主线内核</h3><p>对于需要最新功能或特定内核版本的用户，可以手动安装主线内核。主线内核是由 Linux 内核社区发布的标准版本，不属于特定的 Linux 发行版。Ubuntu 内核团队也提供这些内核的编译版本。</p><h4 id="步骤-1下载最新内核"><a class="markdownIt-Anchor" href="#步骤-1下载最新内核"></a> 步骤 1：下载最新内核</h4><p>访问 <a class="link"   href="https://kernel.ubuntu.com/~kernel-ppa/mainline/" >Ubuntu 主线内核 PPA<i class="fas fa-external-link-alt"></i></a> 网站，找到最新的内核版本目录。下载适合你系统的内核包（通常包括 <code>headers</code> 和 <code>image</code> 两个部分）。</p><p>例如，如果你需要安装 <code>5.13.0</code> 内核，可以下载以下文件（假设系统是 64 位）：</p><ul><li><code>linux-headers-5.13.0-051300_5.13.0-051300.202107041934_all.deb</code></li><li><code>linux-headers-5.13.0-051300-generic_5.13.0-051300.202107041934_amd64.deb</code></li><li><code>linux-image-unsigned-5.13.0-051300-generic_5.13.0-051300.202107041934_amd64.deb</code></li><li><code>linux-modules-5.13.0-051300-generic_5.13.0-051300.202107041934_amd64.deb</code></li></ul><h4 id="步骤-2安装下载的内核包"><a class="markdownIt-Anchor" href="#步骤-2安装下载的内核包"></a> 步骤 2：安装下载的内核包</h4><p>切换到下载目录，然后使用 <code>dpkg</code> 命令安装内核包：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dpkg -i *.deb</span><br></pre></td></tr></table></figure><p>该命令会安装所有以 <code>.deb</code> 结尾的文件。如果遇到依赖问题，可以使用以下命令安装缺失的依赖项：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt --fix-broken install</span><br></pre></td></tr></table></figure><h4 id="步骤-3更新-grub-引导器"><a class="markdownIt-Anchor" href="#步骤-3更新-grub-引导器"></a> 步骤 3：更新 GRUB 引导器</h4><p>内核安装后，需要更新 GRUB 引导器，以便系统可以选择新内核启动：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> update-grub</span><br></pre></td></tr></table></figure><h4 id="步骤-4重启系统"><a class="markdownIt-Anchor" href="#步骤-4重启系统"></a> 步骤 4：重启系统</h4><p>安装完成后，重启系统：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> reboot</span><br></pre></td></tr></table></figure><p>重启后，使用 <code>uname -r</code> 检查是否成功切换到新内核。</p><h2 id="3-回退内核版本"><a class="markdownIt-Anchor" href="#3-回退内核版本"></a> 3. 回退内核版本</h2><p>在更新内核后，如果遇到问题，可以通过 GRUB 引导菜单选择旧的内核启动系统。进入 GRUB 菜单的方法是：</p><ol><li>开机时按住 <code>Shift</code> 键（或 <code>Esc</code> 键）。</li><li>在 GRUB 菜单中选择 <code>Advanced options for Ubuntu</code>。</li><li>选择旧版本的内核启动。</li></ol><h2 id="4-注意事项"><a class="markdownIt-Anchor" href="#4-注意事项"></a> 4. 注意事项</h2><ul><li><strong>测试环境先行</strong>：在生产环境中更新内核之前，最好先在测试环境中进行测试。</li><li><strong>稳定性优先</strong>：最新的主线内核可能不如 Ubuntu 官方内核稳定，建议仅在需要最新特性的情况下使用。</li><li><strong>备份数据</strong>：确保重要数据已经备份，以防意外情况。</li></ul><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h2><p>更新 Ubuntu 的内核可以带来更好的性能、安全性和新功能。本教程介绍了通过官方存储库更新内核和手动安装主线内核的两种方法。无论选择哪种方法，都要注意备份数据和测试新内核的稳定性。<br />金猪言：更新内核可以让你的操作系统碰见更大的惊喜</p>]]>
    </content>
    <id>https://dajinzhu.cn/2024/07/31/ubuntu-replace-kernel/</id>
    <link href="https://dajinzhu.cn/2024/07/31/ubuntu-replace-kernel/"/>
    <published>2024-07-31T03:30:00.000Z</published>
    <summary>介绍通过 Ubuntu 官方仓库和主线内核包更新 Linux 内核，以及验证、回退和风险控制方法。</summary>
    <title>如何在 Ubuntu 系统中更新到最新版内核</title>
    <updated>2024-07-31T03:30:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="人工智能" scheme="https://dajinzhu.cn/categories/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/"/>
    <category term="python" scheme="https://dajinzhu.cn/tags/python/"/>
    <category term="深度学习" scheme="https://dajinzhu.cn/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="AI" scheme="https://dajinzhu.cn/tags/AI/"/>
    <content>
      <![CDATA[<p>卷积神经网络（Convolutional Neural Network, CNN）是一种深度学习模型，特别擅长处理图像数据。它通过使用卷积运算来提取特征，并通过池化层和全连接层来分类或回归。这篇教程将详细介绍 CNN 的基本原理和结构，并提供一个使用 Python 实现的示例。</p><hr><h2 id="1-cnn-的基本概念"><a class="markdownIt-Anchor" href="#1-cnn-的基本概念"></a> 1. CNN 的基本概念</h2><h3 id="11-卷积层convolutional-layer"><a class="markdownIt-Anchor" href="#11-卷积层convolutional-layer"></a> 1.1 卷积层（Convolutional Layer）</h3><p>卷积层是 CNN 的核心组件，用于提取输入数据的特征。卷积层通过卷积核（或滤波器）在输入数据上滑动，并计算点积，从而生成特征图（feature map）。</p><ul><li><strong>卷积核（Filter）</strong>：通常是一个小矩阵，用于检测特定的特征，例如边缘、纹理等。</li><li><strong>步幅（Stride）</strong>：卷积核滑动的步伐。步幅越大，特征图越小。</li><li><strong>填充（Padding）</strong>：在输入数据的边界添加零值，以控制特征图的大小。</li></ul><p><strong>公式</strong>：<br>特征图的大小计算公式为：</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>Output size</mtext><mo>=</mo><mfrac><mrow><mo stretchy="false">(</mo><mtext>Input size</mtext><mo>−</mo><mtext>Filter size</mtext><mo>+</mo><mn>2</mn><mo>×</mo><mtext>Padding</mtext><mo stretchy="false">)</mo></mrow><mtext>Stride</mtext></mfrac><mo>+</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\text{Output size} = \frac{(\text{Input size} - \text{Filter size} + 2 \times \text{Padding})}{\text{Stride}} + 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord text"><span class="mord">Output size</span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:2.113em;vertical-align:-0.686em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">Stride</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mopen">(</span><span class="mord text"><span class="mord">Input size</span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord text"><span class="mord">Filter size</span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord text"><span class="mord">Padding</span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span></span></span></span></p><p>（金猪言：公式部分可稍微理解，我也不是很懂，了解原理即可）</p><h3 id="12-激活函数activation-function"><a class="markdownIt-Anchor" href="#12-激活函数activation-function"></a> 1.2 激活函数（Activation Function）</h3><p>激活函数引入非线性，使得模型可以表示更复杂的函数。常用的激活函数有 ReLU（Rectified Linear Unit）、Sigmoid 和 Tanh。</p><ul><li><strong>ReLU</strong>：( f(x) = \max(0, x) )</li><li><strong>Sigmoid</strong>：( f(x) = \frac{1}{1 + e^{-x}} )</li><li><strong>Tanh</strong>：( f(x) = \tanh(x) )</li></ul><h3 id="13-池化层pooling-layer"><a class="markdownIt-Anchor" href="#13-池化层pooling-layer"></a> 1.3 池化层（Pooling Layer）</h3><p>池化层用于减少特征图的大小，从而减少计算量和防止过拟合。常见的池化操作有最大池化（Max Pooling）和平均池化（Average Pooling）。</p><ul><li><strong>最大池化</strong>：取池化窗口中的最大值。</li><li><strong>平均池化</strong>：取池化窗口中的平均值。</li></ul><h3 id="14-全连接层fully-connected-layer"><a class="markdownIt-Anchor" href="#14-全连接层fully-connected-layer"></a> 1.4 全连接层（Fully Connected Layer）</h3><p>全连接层连接卷积层输出的所有特征，用于综合特征信息进行分类或回归。全连接层通常在 CNN 的最后一层。</p><h2 id="2-cnn-的结构"><a class="markdownIt-Anchor" href="#2-cnn-的结构"></a> 2. CNN 的结构</h2><p>典型的 CNN 结构由以下几部分组成：</p><ol><li><strong>输入层</strong>：接受输入数据（如图像）。</li><li><strong>多个卷积层和池化层</strong>：提取特征。</li><li><strong>全连接层</strong>：分类或回归。</li><li><strong>输出层</strong>：产生最终结果，如类别标签。</li></ol><h2 id="3-cnn-的实现"><a class="markdownIt-Anchor" href="#3-cnn-的实现"></a> 3. CNN 的实现</h2><p>下面是一个使用 Python 和 TensorFlow/Keras 库实现的 CNN 示例。该模型用于对手写数字进行分类（MNIST 数据集）。</p><h3 id="31-环境设置"><a class="markdownIt-Anchor" href="#31-环境设置"></a> 3.1 环境设置</h3><p>首先，确保安装了必要的库：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install tensorflow</span><br></pre></td></tr></table></figure><h3 id="32-导入库和数据集"><a class="markdownIt-Anchor" href="#32-导入库和数据集"></a> 3.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><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="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras <span class="keyword">import</span> layers, models</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras.datasets <span class="keyword">import</span> mnist</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras.utils <span class="keyword">import</span> to_categorical</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载 MNIST 数据集</span></span><br><span class="line">(train_images, train_labels), (test_images, test_labels) = mnist.load_data()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 数据预处理</span></span><br><span class="line">train_images = train_images.reshape((<span class="number">60000</span>, <span class="number">28</span>, <span class="number">28</span>, <span class="number">1</span>)).astype(<span class="string">'float32'</span>) / <span class="number">255</span></span><br><span class="line">test_images = test_images.reshape((<span class="number">10000</span>, <span class="number">28</span>, <span class="number">28</span>, <span class="number">1</span>)).astype(<span class="string">'float32'</span>) / <span class="number">255</span></span><br><span class="line"></span><br><span class="line">train_labels = to_categorical(train_labels)</span><br><span class="line">test_labels = to_categorical(test_labels)</span><br></pre></td></tr></table></figure><h3 id="33-构建模型"><a class="markdownIt-Anchor" href="#33-构建模型"></a> 3.3 构建模型</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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">model = models.Sequential()</span><br><span class="line">model.add(layers.Conv2D(<span class="number">32</span>, (<span class="number">3</span>, <span class="number">3</span>), activation=<span class="string">'relu'</span>, input_shape=(<span class="number">28</span>, <span class="number">28</span>, <span class="number">1</span>)))</span><br><span class="line">model.add(layers.MaxPooling2D((<span class="number">2</span>, <span class="number">2</span>)))</span><br><span class="line">model.add(layers.Conv2D(<span class="number">64</span>, (<span class="number">3</span>, <span class="number">3</span>), activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(layers.MaxPooling2D((<span class="number">2</span>, <span class="number">2</span>)))</span><br><span class="line">model.add(layers.Conv2D(<span class="number">64</span>, (<span class="number">3</span>, <span class="number">3</span>), activation=<span class="string">'relu'</span>))</span><br><span class="line"></span><br><span class="line">model.add(layers.Flatten())</span><br><span class="line">model.add(layers.Dense(<span class="number">64</span>, activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(layers.Dense(<span class="number">10</span>, activation=<span class="string">'softmax'</span>))</span><br></pre></td></tr></table></figure><h3 id="34-编译和训练模型"><a class="markdownIt-Anchor" href="#34-编译和训练模型"></a> 3.4 编译和训练模型</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">model.<span class="built_in">compile</span>(optimizer=<span class="string">'adam'</span>,</span><br><span class="line">              loss=<span class="string">'categorical_crossentropy'</span>,</span><br><span class="line">              metrics=[<span class="string">'accuracy'</span>])</span><br><span class="line"></span><br><span class="line">model.fit(train_images, train_labels, epochs=<span class="number">5</span>, batch_size=<span class="number">64</span>, validation_data=(test_images, test_labels))</span><br></pre></td></tr></table></figure><h3 id="35-模型评估"><a class="markdownIt-Anchor" href="#35-模型评估"></a> 3.5 模型评估</h3><figure class="highlight python"><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">test_loss, test_acc = model.evaluate(test_images, test_labels)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f"Test accuracy: <span class="subst">{test_acc}</span>"</span>)</span><br></pre></td></tr></table></figure><h3 id="36-保存和加载模型"><a class="markdownIt-Anchor" href="#36-保存和加载模型"></a> 3.6 保存和加载模型</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"><span class="comment"># 保存模型</span></span><br><span class="line">model.save(<span class="string">'mnist_cnn.h5'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载模型</span></span><br><span class="line">new_model = models.load_model(<span class="string">'mnist_cnn.h5'</span>)</span><br></pre></td></tr></table></figure><h2 id="4-进阶内容"><a class="markdownIt-Anchor" href="#4-进阶内容"></a> 4. 进阶内容</h2><h3 id="41-数据增强"><a class="markdownIt-Anchor" href="#41-数据增强"></a> 4.1 数据增强</h3><p>数据增强是提高模型泛化能力的技术之一。它通过随机变换训练数据（如旋转、缩放、翻转等），生成更多样的数据样本，防止过拟合。</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><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="keyword">from</span> tensorflow.keras.preprocessing.image <span class="keyword">import</span> ImageDataGenerator</span><br><span class="line"></span><br><span class="line">datagen = ImageDataGenerator(</span><br><span class="line">    rotation_range=<span class="number">10</span>,</span><br><span class="line">    width_shift_range=<span class="number">0.1</span>,</span><br><span class="line">    height_shift_range=<span class="number">0.1</span>,</span><br><span class="line">    zoom_range=<span class="number">0.1</span>,</span><br><span class="line">    horizontal_flip=<span class="literal">False</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用增强后的数据进行训练</span></span><br><span class="line">datagen.fit(train_images)</span><br><span class="line">model.fit(datagen.flow(train_images, train_labels, batch_size=<span class="number">64</span>),</span><br><span class="line">          epochs=<span class="number">5</span>, validation_data=(test_images, test_labels))</span><br></pre></td></tr></table></figure><h3 id="42-正则化"><a class="markdownIt-Anchor" href="#42-正则化"></a> 4.2 正则化</h3><p>正则化技术，如 L2 正则化和 Dropout，可以防止模型过拟合。</p><ul><li><strong>L2 正则化</strong>：在损失函数中加入权重的 L2 范数，以限制权重大小。</li><li><strong>Dropout</strong>：在训练期间随机丢弃一定比例的神经元，减少模型对特定路径的依赖。</li></ul><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="comment"># 使用 L2 正则化和 Dropout</span></span><br><span class="line">model.add(layers.Dense(<span class="number">64</span>, kernel_regularizer=tf.keras.regularizers.l2(<span class="number">0.001</span>), activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(layers.Dropout(<span class="number">0.5</span>))</span><br></pre></td></tr></table></figure><h3 id="43-迁移学习"><a class="markdownIt-Anchor" href="#43-迁移学习"></a> 4.3 迁移学习</h3><p>迁移学习利用预训练的模型（如 VGG、ResNet 等）来提取特征，然后在新的数据集上进行微调。这种方法在数据有限的情况下非常有效。</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> tensorflow.keras.applications <span class="keyword">import</span> VGG16</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载预训练模型，不包含顶层的全连接层</span></span><br><span class="line">conv_base = VGG16(weights=<span class="string">'imagenet'</span>, include_top=<span class="literal">False</span>, input_shape=(<span class="number">150</span>, <span class="number">150</span>, <span class="number">3</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 冻结卷积基</span></span><br><span class="line">conv_base.trainable = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 构建新的模型</span></span><br><span class="line">model = models.Sequential([</span><br><span class="line">    conv_base,</span><br><span class="line">    layers.Flatten(),</span><br><span class="line">    layers.Dense(<span class="number">256</span>, activation=<span class="string">'relu'</span>),</span><br><span class="line">    layers.Dropout(<span class="number">0.5</span>),</span><br><span class="line">    layers.Dense(<span class="number">1</span>, activation=<span class="string">'sigmoid'</span>)</span><br><span class="line">])</span><br></pre></td></tr></table></figure><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h2><p>卷积神经网络是深度学习中处理图像数据的核心工具。它通过卷积层提取特征，通过池化层减少数据维度和计算量，通过全连接层进行分类或回归。本文介绍了 CNN 的基本概念和结构，并通过 TensorFlow/Keras 提供了一个具体的实现示例。随着技术的进步，CNN 在计算机视觉等领域的应用越来越广泛。人脸识别离不开它，所以有必要对其进行了解。</p>]]>
    </content>
    <id>https://dajinzhu.cn/2024/07/29/cnn-1/</id>
    <link href="https://dajinzhu.cn/2024/07/29/cnn-1/"/>
    <published>2024-07-29T15:24:46.000Z</published>
    <summary>系统讲解 CNN 的卷积层、池化层和全连接层，并使用 TensorFlow 与 MNIST 展示完整入门示例。</summary>
    <title>卷积神经网络（CNN）入门教程</title>
    <updated>2024-07-29T15:24:46.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="人工智能" scheme="https://dajinzhu.cn/categories/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/"/>
    <category term="机器学习" scheme="https://dajinzhu.cn/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <category term="python" scheme="https://dajinzhu.cn/tags/python/"/>
    <content>
      <![CDATA[<p>无监督学习（Unsupervised Learning）是一种机器学习方法，用于从未标注的数据中发现模式、特征和结构。在无监督学习中，算法并没有预先提供的数据标签或结果，而是通过分析数据的结构和关系，从数据中自动提取信息和洞察。无监督学习的目标通常包括数据聚类、降维和密度估计等任务。本文理论与实践难度偏大，至于编程应用，本文只是采用简略说明。</p><h3 id="无监督学习的主要类型"><a class="markdownIt-Anchor" href="#无监督学习的主要类型"></a> 无监督学习的主要类型</h3><ol><li><strong>聚类（Clustering）</strong></li><li><strong>降维（Dimensionality Reduction）</strong></li><li><strong>密度估计（Density Estimation）</strong></li><li><strong>异常检测（Anomaly Detection）</strong></li></ol><h3 id="1-聚类clustering"><a class="markdownIt-Anchor" href="#1-聚类clustering"></a> 1. 聚类（Clustering）</h3><p><strong>聚类</strong>是无监督学习中最常见的任务之一，旨在将数据集分成若干组或簇（Clusters），使得同一簇内的数据点在某种意义上更加相似，而不同簇之间的差异则较大。聚类算法没有先验的标签来告诉哪些样本属于哪个簇，而是通过分析数据点之间的相似性来自动分类。</p><h4 id="11-k-means-聚类"><a class="markdownIt-Anchor" href="#11-k-means-聚类"></a> 1.1. K-Means 聚类</h4><p>K-Means 是一种流行的聚类算法，其基本思想是将数据分为 (K) 个簇。具体步骤如下：</p><ol><li>随机选择 (K) 个初始的簇中心（质心）。</li><li>对于每个数据点，计算其到各簇中心的距离，并分配到最近的簇。</li><li>更新每个簇的中心为该簇所有数据点的均值。</li><li>重复步骤2和3，直到簇中心不再变化或达到最大迭代次数。</li></ol><p><strong>K-Means算法示例</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.cluster <span class="keyword">import</span> KMeans</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成样本数据</span></span><br><span class="line">X = np.array([[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">1</span>, <span class="number">4</span>], [<span class="number">1</span>, <span class="number">0</span>], [<span class="number">10</span>, <span class="number">2</span>], [<span class="number">10</span>, <span class="number">4</span>], [<span class="number">10</span>, <span class="number">0</span>]])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化KMeans</span></span><br><span class="line">kmeans = KMeans(n_clusters=<span class="number">2</span>, random_state=<span class="number">0</span>).fit(X)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取簇标签</span></span><br><span class="line">labels = kmeans.labels_</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"Cluster labels:"</span>, labels)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取簇中心</span></span><br><span class="line">centers = kmeans.cluster_centers_</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"Cluster centers:"</span>, centers)</span><br></pre></td></tr></table></figure><h4 id="12-层次聚类"><a class="markdownIt-Anchor" href="#12-层次聚类"></a> 1.2. 层次聚类</h4><p>层次聚类是一种建立层次树形结构的聚类方法。主要有两种方法：</p><ul><li><strong>自底向上（凝聚式）</strong>：从每个数据点开始，将最近的两个簇合并，直到达到预定的簇数或距离阈值。</li><li><strong>自顶向下（分裂式）</strong>：从所有数据点作为一个簇开始，不断分裂，直到达到预定的簇数或其他停止条件。</li></ul><h3 id="2-降维dimensionality-reduction"><a class="markdownIt-Anchor" href="#2-降维dimensionality-reduction"></a> 2. 降维（Dimensionality Reduction）</h3><p>降维是无监督学习中的另一个重要任务，主要用于减少数据的维度，同时尽可能保留原始数据的重要信息。这对于可视化、压缩和噪声去除等问题非常有用。</p><h4 id="21-主成分分析pca"><a class="markdownIt-Anchor" href="#21-主成分分析pca"></a> 2.1. 主成分分析（PCA）</h4><p>PCA 是一种广泛使用的降维技术，它通过线性变换将数据投影到一个新的坐标系中，使得数据的最大方差方向（主成分）对齐新的坐标轴。PCA 的目标是找到最小化信息损失的子空间表示。</p><p><strong>PCA算法示例</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.decomposition <span class="keyword">import</span> PCA</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成样本数据</span></span><br><span class="line">X = np.array([[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">1</span>, <span class="number">4</span>], [<span class="number">1</span>, <span class="number">0</span>], [<span class="number">10</span>, <span class="number">2</span>], [<span class="number">10</span>, <span class="number">4</span>], [<span class="number">10</span>, <span class="number">0</span>]])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化PCA</span></span><br><span class="line">pca = PCA(n_components=<span class="number">1</span>)</span><br><span class="line">X_reduced = pca.fit_transform(X)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"Reduced data:"</span>, X_reduced)</span><br></pre></td></tr></table></figure><h4 id="22-t-分布随机邻居嵌入t-sne"><a class="markdownIt-Anchor" href="#22-t-分布随机邻居嵌入t-sne"></a> 2.2. t-分布随机邻居嵌入（t-SNE）</h4><p>t-SNE 是一种非线性降维方法，常用于高维数据的可视化。它通过将高维数据映射到低维空间，同时尽量保持原数据点之间的局部距离关系。</p><h3 id="3-密度估计density-estimation"><a class="markdownIt-Anchor" href="#3-密度估计density-estimation"></a> 3. 密度估计（Density Estimation）</h3><p>密度估计是指估计数据分布的概率密度函数。在无监督学习中，密度估计用于理解数据的分布特性，例如检测数据中的异常值（离群点）。</p><h4 id="31-核密度估计kernel-density-estimation-kde"><a class="markdownIt-Anchor" href="#31-核密度估计kernel-density-estimation-kde"></a> 3.1. 核密度估计（Kernel Density Estimation, KDE）</h4><p>KDE 是一种非参数密度估计方法，使用核函数平滑数据点，估计出数据的概率密度函数。KDE 能够提供对数据分布的直观理解，并用于异常检测。</p><p><strong>KDE算法示例</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><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="keyword">from</span> sklearn.neighbors <span class="keyword">import</span> KernelDensity</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成样本数据</span></span><br><span class="line">X = np.array([[<span class="number">1</span>], [<span class="number">2</span>], [<span class="number">3</span>], [<span class="number">4</span>], [<span class="number">5</span>], [<span class="number">6</span>]])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化KDE</span></span><br><span class="line">kde = KernelDensity(kernel=<span class="string">'gaussian'</span>, bandwidth=<span class="number">0.5</span>).fit(X)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 评估密度</span></span><br><span class="line">scores = kde.score_samples(X)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"Density scores:"</span>, scores)</span><br></pre></td></tr></table></figure><h3 id="4-异常检测anomaly-detection"><a class="markdownIt-Anchor" href="#4-异常检测anomaly-detection"></a> 4. 异常检测（Anomaly Detection）</h3><p>异常检测用于识别数据中不寻常或离群的样本，这些样本可能代表异常事件或错误。无监督学习中的异常检测不依赖于已标注的异常样本，而是根据数据的分布特性自动识别。</p><h4 id="41-高斯混合模型gaussian-mixture-model-gmm"><a class="markdownIt-Anchor" href="#41-高斯混合模型gaussian-mixture-model-gmm"></a> 4.1. 高斯混合模型（Gaussian Mixture Model, GMM）</h4><p>GMM 是一种概率模型，可以看作是多个高斯分布的加权和。它用于建模数据分布，并检测那些在该分布下概率较低的样本，即可能的异常值。</p><h3 id="无监督学习的优势和挑战"><a class="markdownIt-Anchor" href="#无监督学习的优势和挑战"></a> 无监督学习的优势和挑战</h3><h4 id="优势"><a class="markdownIt-Anchor" href="#优势"></a> 优势</h4><ol><li><strong>数据需求少</strong>：无监督学习不需要标注数据，适合于数据标注成本高的领域。</li><li><strong>自动发现结构</strong>：能够自动发现数据的隐藏结构和模式，如聚类和降维。</li></ol><h4 id="挑战"><a class="markdownIt-Anchor" href="#挑战"></a> 挑战</h4><ol><li><strong>结果解释</strong>：无监督学习结果的解释通常比有监督学习更具挑战，因为没有标签可参考。</li><li><strong>模型评价</strong>：由于缺乏标签，评估模型的效果不如有监督学习直观，通常需要使用间接方法。</li></ol><h3 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h3><p>金猪言：无监督学习是一种强大的工具，能够在缺乏标签的情况下，从数据中发现有用的信息。它在数据探索、预处理、模式发现等方面有着广泛的应用。理解和掌握无监督学习的原理和方法，对于数据科学和机器学习的深入研究和实际应用都有着重要意义。</p>]]>
    </content>
    <id>https://dajinzhu.cn/2024/07/29/ai-unsupervised-learning/</id>
    <link href="https://dajinzhu.cn/2024/07/29/ai-unsupervised-learning/"/>
    <published>2024-07-28T16:00:00.000Z</published>
    <summary>从聚类、降维和密度估计出发介绍无监督学习的基本原理、常见算法与 Python 实践思路。</summary>
    <title>浅谈无监督学习：原理与方法详解（Python版）</title>
    <updated>2024-07-28T16:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="区块链" scheme="https://dajinzhu.cn/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    <category term="btc" scheme="https://dajinzhu.cn/tags/btc/"/>
    <category term="eth" scheme="https://dajinzhu.cn/tags/eth/"/>
    <category term="sol" scheme="https://dajinzhu.cn/tags/sol/"/>
    <content>
      <![CDATA[<p>Solana、以太坊（Ethereum）和比特币（Bitcoin）是三种不同的区块链网络和加密货币，它们在设计目标、技术实现和应用场景上有显著的区别。以下是对这三者的详细比较（这三个区块链系统或网络是目前上市面上比较具有影响力的）：</p><h3 id="1-基本概述"><a class="markdownIt-Anchor" href="#1-基本概述"></a> 1. <strong>基本概述</strong></h3><ul><li><strong>比特币（Bitcoin）</strong><ul><li><strong>创建时间</strong>: 2009年，由中本聪（Satoshi Nakamoto）创建。</li><li><strong>主要功能</strong>: 去中心化的数字货币，用于点对点的电子现金系统。</li><li><strong>共识机制</strong>: 工作量证明（Proof of Work, PoW）。</li><li><strong>主要特征</strong>: 安全性高、分散化程度高、可作为价值存储和支付手段。</li></ul></li><li><strong>以太坊（Ethereum）</strong><ul><li><strong>创建时间</strong>: 2015年，由Vitalik Buterin等人创建。</li><li><strong>主要功能</strong>: 智能合约和去中心化应用（dApps）平台。</li><li><strong>共识机制</strong>: 最初使用PoW，计划转向权益证明（Proof of Stake, PoS）通过以太坊2.0升级。</li><li><strong>主要特征</strong>: 可编程性强，支持智能合约和dApps，活跃的开发者社区和生态系统。</li></ul></li><li><strong>Solana</strong><ul><li><strong>创建时间</strong>: 2020年，由Anatoly Yakovenko等人创建。</li><li><strong>主要功能</strong>: 高性能去中心化应用和加密货币交易平台。</li><li><strong>共识机制</strong>: 历史证明（Proof of History, PoH）与权益证明（Proof of Stake, PoS）结合。</li><li><strong>主要特征</strong>: 高吞吐量、低交易成本、快速确认时间，适合高频交易和复杂应用。</li></ul></li></ul><h3 id="2-技术架构和共识机制"><a class="markdownIt-Anchor" href="#2-技术架构和共识机制"></a> 2. <strong>技术架构和共识机制</strong></h3><ul><li><strong>比特币</strong><ul><li>使用PoW机制，通过矿工解决复杂的数学问题来验证交易和生成新的区块。</li><li>区块生成时间约为10分钟。</li><li>由于其设计，比特币网络的吞吐量较低（大约每秒7笔交易）。</li></ul></li><li><strong>以太坊</strong><ul><li>目前使用PoW，但通过以太坊2.0计划过渡到PoS，以提高网络效率和可扩展性。</li><li>支持图灵完备的智能合约，允许开发者在区块链上编写和部署复杂的dApps。</li><li>区块生成时间约为15秒，吞吐量比比特币高。</li></ul></li><li><strong>Solana</strong><ul><li>创新地使用PoH机制，这是一种为验证交易时间顺序和提高效率而设计的机制。</li><li>结合PoS来提高网络的安全性和去中心化程度。</li><li>Solana的架构设计允许非常高的交易吞吐量（每秒数万笔交易）和极低的确认时间（大约400毫秒）。</li></ul></li></ul><h3 id="3-扩展性和性能"><a class="markdownIt-Anchor" href="#3-扩展性和性能"></a> 3. <strong>扩展性和性能</strong></h3><ul><li><strong>比特币</strong><ul><li>扩展性有限，每秒只能处理少量交易，且交易费用可能较高。</li><li>通过闪电网络等二层解决方案尝试提高交易速度和降低费用。</li></ul></li><li><strong>以太坊</strong><ul><li>面临扩展性挑战，尤其在网络高负荷时，交易费用可能很高。</li><li>以太坊2.0引入分片（sharding）和PoS来改善可扩展性和效率。</li></ul></li><li><strong>Solana</strong><ul><li>高度可扩展，能够处理大规模交易量而不会显著增加成本。</li><li>适合高频交易和实时应用，如去中心化金融（DeFi）和NFT市场。</li></ul></li></ul><h3 id="4-应用场景和生态系统"><a class="markdownIt-Anchor" href="#4-应用场景和生态系统"></a> 4. <strong>应用场景和生态系统</strong></h3><ul><li><strong>比特币</strong><ul><li>主要用作价值存储（数字黄金）和支付手段。</li><li>生态系统以钱包、交易所和支付服务为主。</li></ul></li><li><strong>以太坊</strong><ul><li>丰富的dApp生态系统，包括DeFi、NFT、去中心化自治组织（DAO）等。</li><li>支持复杂的智能合约和去中心化应用。</li></ul></li><li><strong>Solana</strong><ul><li>也支持dApps和智能合约，但以其高性能和低成本吸引了许多高频交易和DeFi项目。</li><li>快速增长的生态系统，涵盖游戏、NFT、支付等领域。</li></ul></li></ul><h3 id="5-社区和开发者支持"><a class="markdownIt-Anchor" href="#5-社区和开发者支持"></a> 5. <strong>社区和开发者支持</strong></h3><ul><li><strong>比特币</strong><ul><li>拥有最广泛的用户基础和全球社区。</li><li>开发者生态主要集中在核心协议的维护和改进上。</li></ul></li><li><strong>以太坊</strong><ul><li>活跃的开发者社区，持续进行协议改进和dApp开发。</li><li>以太坊基金会和企业联盟支持其发展。</li></ul></li><li><strong>Solana</strong><ul><li>年轻但快速增长的社区和开发者生态。</li><li>有多个孵化器和加速器支持新项目的开发。</li></ul></li></ul><h3 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h3><ul><li><strong>比特币</strong>：注重安全性和去中心化，主要用作价值存储和支付手段，吞吐量较低，适合长期价值存储。</li><li><strong>以太坊</strong>：灵活的智能合约平台，支持广泛的去中心化应用，正在通过以太坊2.0改进扩展性。</li><li><strong>Solana</strong>：专注于高性能和低成本，适合高频交易和复杂应用场景，是一个新的、快速增长的智能合约平台。</li></ul><p>个人看法：以太坊我认为是发展的最具有体系，最完备的，是区块链系统的最早突破，比特币作为最早的加密货币只有简单的交易功能，但在区块链系统中占有重要地位，且作为重要的金融资产，Solana的低成本则使代币，合约等走进千家万户，甚至产生meme代币板块，成为十分热门焦点(炒土狗)。</p>]]>
    </content>
    <id>https://dajinzhu.cn/2024/07/27/btc-eth-sol/</id>
    <link href="https://dajinzhu.cn/2024/07/27/btc-eth-sol/"/>
    <published>2024-07-27T15:59:36.000Z</published>
    <summary>对比比特币、以太坊与 Solana 的设计目标、共识机制、性能特点、生态系统和主要应用场景。</summary>
    <title>详谈比特币、以太坊、和Solana</title>
    <updated>2024-07-27T15:59:36.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="网站公告" scheme="https://dajinzhu.cn/categories/%E7%BD%91%E7%AB%99%E5%85%AC%E5%91%8A/"/>
    <category term="个人随想" scheme="https://dajinzhu.cn/tags/%E4%B8%AA%E4%BA%BA%E9%9A%8F%E6%83%B3/"/>
    <category term="公告" scheme="https://dajinzhu.cn/tags/%E5%85%AC%E5%91%8A/"/>
    <content>
      <![CDATA[<h3 id="网站历史"><a class="markdownIt-Anchor" href="#网站历史"></a> 网站历史</h3><p><em>烟愁雨啸奈华生，宫阙簪椐旧帝城。</em></p><p><em>若问古今兴废事，请君只看洛阳城。</em></p><p>我第一次建立这个<strong>网站</strong>的时候大约2018年时间，原先的<strong>jzbk.xyz</strong>的网址如今已经不复存在了，当时是使用<strong>WordPress</strong>的动态<strong>PHP</strong>博客的方式建站，后来经过几次变迁，选择使用静态的<strong>Hexo</strong>搭建。同时高额的服务器成本也省下了，历次使用<strong>Vercel</strong>、<strong>Cloudflare</strong>进行静态托管。但是都还是<strong>响应式</strong>的布局，性能似乎没有下降，更加轻便，简单。</p><p>我曾经建立过<strong>金猪论坛</strong>，但由于没有<strong>备案</strong>，且当时用的是<strong>dicuss</strong>，只能在阿里云中使用学生优惠(服务器在境内)，多种原因导致我停止建设。且国外的<strong>vultr，搬瓦工</strong>等VPS云服务器的托管商并不是百度等国内搜索引擎<strong>SEO</strong>有好的，网站建设被迫失败。</p><p>如今，我的博客在<strong>Cloudflare</strong>使用<strong>Works and Pages</strong>搭建，每日配额请求<strong>十万</strong>，是十分核算的，续费了这个域名，如今高考将近，无法维护服务器，而选择托管的方案，是个似乎明智的选择。</p><h3 id="网站初衷"><a class="markdownIt-Anchor" href="#网站初衷"></a> 网站初衷</h3><p>维基百科定义：<strong>博客</strong>（英语：Blog）是一种在线日记型式的<strong>个人网站</strong>，借由张帖子章、图片或影片来记录生活、抒发情感或分享信息。</p><p>首先是<strong>个人日记</strong>，如今这个时代，<strong>朋友圈</strong>、<strong>QQ说说</strong>等盛行，<strong>微博</strong>等早已取代博客，但是从定义中可以发现，博客与以上不同的是，他是一种<strong>个人网站</strong>，以上的平台均在一定的<strong>社交媒体</strong>当中，虽说有方便等优势，但<strong>个人网站</strong>无疑是展现自我的最佳方式，不受<strong>腾讯</strong>等平台节制，更享有言论自由，应是真正属于自我的空间。(你可以自主的决定是否开启评论功能)</p><p>本网站仅仅记录自我的<strong>个人随想、技术分享</strong>，别无其他，大多为本人个人纪念。</p><h3 id="网站理想"><a class="markdownIt-Anchor" href="#网站理想"></a> 网站理想</h3><p>随着科技进步，博客的技术不断进步，<strong>Hexo</strong>我认为是最好的博客系统，开源，高度模块化。如今后有闲暇时间，将投入<strong>Hexo主题</strong>制作，而本站也应成为Hexo主题的改革先行区。不断完善博客功能，不断适配不同的载体，从<strong>css、js</strong>中追求更快地响应速度。随着年月的累积，本站文章也将不断丰富。</p><h3 id="欢迎"><a class="markdownIt-Anchor" href="#欢迎"></a> 欢迎</h3><p>最后欢迎大家的到访！</p><p><em><strong>大金猪</strong></em></p><p><em><strong>2024年7月</strong></em></p>]]>
    </content>
    <id>https://dajinzhu.cn/2024/07/23/new-website/</id>
    <link href="https://dajinzhu.cn/2024/07/23/new-website/"/>
    <published>2024-07-23T15:00:00.000Z</published>
    <summary>回顾金猪博客从 WordPress 到 Hexo 的建站历程，并说明博客复建后的内容方向与维护计划。</summary>
    <title>金猪博客复建公告</title>
    <updated>2024-07-23T15:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="网络技术" scheme="https://dajinzhu.cn/categories/%E7%BD%91%E7%BB%9C%E6%8A%80%E6%9C%AF/"/>
    <category term="IPv6" scheme="https://dajinzhu.cn/tags/IPv6/"/>
    <category term="Python" scheme="https://dajinzhu.cn/tags/Python/"/>
    <category term="IP" scheme="https://dajinzhu.cn/tags/IP/"/>
    <content>
      <![CDATA[<p>随着互联网的迅猛发展，全球 IP 地址的需求量不断增加。然而，传统的 IPv4 协议由于地址空间有限，逐渐无法满足需求。为了解决这一问题，IPv6（Internet Protocol version 6）应运而生。本文将详细介绍 IPv6 的基础概念、特性、优势以及其在编程中的应用。</p><hr /><h2 id="1-什么是-ipv6"><a class="markdownIt-Anchor" href="#1-什么是-ipv6"></a> 1. 什么是 IPv6？</h2><p>IPv6 是一种新的互联网协议，旨在替代现行的 IPv4。IPv6 使用 128 位地址空间，相较于 IPv4 的 32 位地址空间，能够提供更多的 IP 地址。IPv6 的地址格式如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2001:0db8:85a3:0000:0000:8a2e:0370:7334</span><br></pre></td></tr></table></figure><p>这种 128 位地址格式能够支持约 3.4 × 10^38 个唯一地址，彻底解决了 IPv4 地址耗尽的问题。</p><h2 id="2-ipv6-的优势"><a class="markdownIt-Anchor" href="#2-ipv6-的优势"></a> 2. IPv6 的优势</h2><h3 id="21-更大的地址空间"><a class="markdownIt-Anchor" href="#21-更大的地址空间"></a> 2.1 更大的地址空间</h3><p>IPv6 的 128 位地址提供了一个几乎无限的地址空间，这使得每个设备都可以拥有一个全球唯一的 IP 地址。这对未来互联网设备的增长至关重要。</p><h3 id="22-内置的安全性"><a class="markdownIt-Anchor" href="#22-内置的安全性"></a> 2.2 内置的安全性</h3><p>IPv6 标准内置了 IPsec（IP 安全协议），提供了数据包加密和身份验证的功能。这增强了数据传输的安全性。</p><h3 id="23-无需网络地址转换nat"><a class="markdownIt-Anchor" href="#23-无需网络地址转换nat"></a> 2.3 无需网络地址转换（NAT）</h3><p>由于 IPv6 提供了足够的地址空间，每个设备可以拥有唯一的公共 IP 地址，消除了对 NAT 的需求。NAT 在 IPv4 中用于解决地址不足的问题，但也带来了一些网络复杂性和性能问题。</p><h3 id="24-自动配置"><a class="markdownIt-Anchor" href="#24-自动配置"></a> 2.4 自动配置</h3><p>IPv6 支持无状态自动配置（Stateless Address Autoconfiguration, SLAAC），设备可以自动生成自己的 IP 地址，而无需手动配置或依赖 DHCP 服务器。</p><h2 id="3-ipv6-地址类型"><a class="markdownIt-Anchor" href="#3-ipv6-地址类型"></a> 3. IPv6 地址类型</h2><h3 id="31-单播地址unicast"><a class="markdownIt-Anchor" href="#31-单播地址unicast"></a> 3.1 单播地址（Unicast）</h3><p>单播地址用于唯一标识网络中的单个接口。IPv6 中的单播地址包括以下几种类型：</p><ul><li><strong>全局单播地址（Global Unicast Address）</strong>：类似于 IPv4 的公共地址，可在全球范围内路由。</li><li><strong>链路本地地址（Link-Local Address）</strong>：仅在本地链路（同一子网）内有效，常用于邻居发现和网络地址配置。</li></ul><h3 id="32-多播地址multicast"><a class="markdownIt-Anchor" href="#32-多播地址multicast"></a> 3.2 多播地址（Multicast）</h3><p>多播地址用于标识一组接口，数据包发送到多播地址时，会传送给属于该地址的所有接口。IPv6 不再使用广播地址，取而代之的是多播地址。</p><h3 id="33-任播地址anycast"><a class="markdownIt-Anchor" href="#33-任播地址anycast"></a> 3.3 任播地址（Anycast）</h3><p>任播地址也标识一组接口，但数据包只发送到离源地址最近的接口。任播地址主要用于负载均衡和服务的高可用性。</p><h2 id="4-ipv6-的地址表示法"><a class="markdownIt-Anchor" href="#4-ipv6-的地址表示法"></a> 4. IPv6 的地址表示法</h2><p>IPv6 地址使用 8 组 16 进制数表示，每组 4 个字符，中间用冒号分隔。例如：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2001:0db8:85a3:0000:0000:8a2e:0370:7334</span><br></pre></td></tr></table></figure><p>可以使用简写规则简化表示：</p><ul><li><strong>去掉前导零</strong>：将每组 16 进制数的前导零去掉。例如 <code>0034</code> 可以简写为 <code>34</code>。</li><li><strong>双冒号（::）简写</strong>：一组连续的零可以用 <code>::</code> 代替，但 <code>::</code> 只能出现一次。例如 <code>2001:0db8:0000:0000:0000:0000:0000:0001</code> 可以简写为 <code>2001:0db8::1</code>。</li></ul><h2 id="5-在编程中的应用"><a class="markdownIt-Anchor" href="#5-在编程中的应用"></a> 5. 在编程中的应用</h2><h3 id="51-ipv6-地址的生成"><a class="markdownIt-Anchor" href="#51-ipv6-地址的生成"></a> 5.1 IPv6 地址的生成</h3><p>在编程中，生成 IPv6 地址常用于测试和开发环境。以下是一个使用 Python 生成随机 IPv6 地址的示例：</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><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> random</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">random_ipv6</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;:&quot;</span>.join(<span class="string">f&quot;<span class="subst">&#123;random.randint(<span class="number">0</span>, <span class="number">0xffff</span>):x&#125;</span>&quot;</span> <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">8</span>))</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(random_ipv6())</span><br></pre></td></tr></table></figure><p>这个函数生成的 IPv6 地址可能是无效的，但对于测试目的来说已经足够。</p><h3 id="52-使用-python-进行-ipv6-网络编程"><a class="markdownIt-Anchor" href="#52-使用-python-进行-ipv6-网络编程"></a> 5.2 使用 Python 进行 IPv6 网络编程</h3><p>在 Python 中，<code>socket</code> 库支持 IPv6 网络编程。以下是一个简单的示例，展示如何使用 IPv6 创建一个服务器和客户端：</p><h4 id="服务器端"><a class="markdownIt-Anchor" href="#服务器端"></a> 服务器端：</h4><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 IPv6 TCP 套接字</span></span><br><span class="line">server_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 绑定到 IPv6 地址和端口</span></span><br><span class="line">server_socket.bind((<span class="string">&#x27;::&#x27;</span>, <span class="number">12345</span>))</span><br><span class="line">server_socket.listen(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Server is listening on port 12345...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    client_socket, addr = server_socket.accept()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Connection from <span class="subst">&#123;addr&#125;</span>&quot;</span>)</span><br><span class="line">    client_socket.sendall(<span class="string">b&quot;Hello, IPv6 world!&quot;</span>)</span><br><span class="line">    client_socket.close()</span><br></pre></td></tr></table></figure><h4 id="客户端"><a class="markdownIt-Anchor" href="#客户端"></a> 客户端：</h4><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 IPv6 TCP 套接字</span></span><br><span class="line">client_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 连接到服务器</span></span><br><span class="line">client_socket.connect((<span class="string">&#x27;::1&#x27;</span>, <span class="number">12345</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 接收数据</span></span><br><span class="line">data = client_socket.recv(<span class="number">1024</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;Received: <span class="subst">&#123;data.decode()&#125;</span>&quot;</span>)</span><br><span class="line">client_socket.close()</span><br></pre></td></tr></table></figure><p>在这个示例中，服务器监听 IPv6 地址 <code>::</code>（即所有 IPv6 地址）上的 12345 端口，而客户端连接到本地回环地址 <code>::1</code> 上的同一端口。</p><h2 id="6-部署-ipv6-的注意事项"><a class="markdownIt-Anchor" href="#6-部署-ipv6-的注意事项"></a> 6. 部署 IPv6 的注意事项</h2><h3 id="61-ipv6-地址规划"><a class="markdownIt-Anchor" href="#61-ipv6-地址规划"></a> 6.1 IPv6 地址规划</h3><p>IPv6 地址空间非常大，但仍需合理规划和分配。通常，应根据网络的层次结构和拓扑进行分配，以便管理和路由。</p><h3 id="62-ipv6-安全性"><a class="markdownIt-Anchor" href="#62-ipv6-安全性"></a> 6.2 IPv6 安全性</h3><p>尽管 IPv6 内置了 IPsec，但其安全性依然依赖于正确的配置和管理。网络管理员应确保防火墙和安全策略适应 IPv6 的新特性和要求。</p><h3 id="63-兼容性问题"><a class="markdownIt-Anchor" href="#63-兼容性问题"></a> 6.3 兼容性问题</h3><p>尽管 IPv6 正在逐步推广，但一些老旧系统和设备可能不完全支持 IPv6。确保网络中的设备和软件能够兼容 IPv6 是成功迁移的关键。</p><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h2><p>金猪言：IPv6 是互联网的未来，其广阔的地址空间和现代化特性为网络发展提供了坚实的基础。理解和掌握 IPv6 的基础知识以及在编程中的应用，对现代网络技术的学习和应用至关重要。IPv6的地址数目据说比地球的沙子还多，IPv4却只有40多亿，但是尽管提出了多年，仍然依旧替代不了IPv4，这值得深思。</p>]]>
    </content>
    <id>https://dajinzhu.cn/2020/07/12/ipv6/</id>
    <link href="https://dajinzhu.cn/2020/07/12/ipv6/"/>
    <published>2020-07-12T15:00:00.000Z</published>
    <summary>介绍 IPv6 地址结构、协议特性、相对 IPv4 的优势，以及网络配置和编程中的常见应用。</summary>
    <title>IPv6 详解：下一代互联网协议的基础与应用</title>
    <updated>2020-07-12T15:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="编程" scheme="https://dajinzhu.cn/categories/%E7%BC%96%E7%A8%8B/"/>
    <category term="Git" scheme="https://dajinzhu.cn/tags/Git/"/>
    <content>
      <![CDATA[<p>Git 是一种分布式版本控制系统，由 Linus Torvalds 开发。它主要用于跟踪文件的更改，尤其是软件开发项目中的源代码。Git 允许多个开发者协作，同时保持项目的历史记录。本教程将详细介绍 Git 的基础知识和常用命令，帮助你高效地使用 Git 进行版本控制。</p><hr /><h2 id="1-git-的基本概念"><a class="markdownIt-Anchor" href="#1-git-的基本概念"></a> 1. Git 的基本概念</h2><h3 id="11-版本控制"><a class="markdownIt-Anchor" href="#11-版本控制"></a> 1.1 版本控制</h3><p>版本控制是一种管理文件变化的系统，它允许用户回溯到以前的版本。版本控制系统（VCS）分为集中式和分布式两种。Git 属于分布式 VCS，每个开发者的工作目录都是一个完整的代码仓库。</p><h3 id="12-仓库repository"><a class="markdownIt-Anchor" href="#12-仓库repository"></a> 1.2 仓库（Repository）</h3><p>Git 仓库是一个包含项目所有文件和版本历史的目录。仓库可以分为本地仓库和远程仓库。本地仓库是开发者电脑上的版本库，而远程仓库通常托管在服务器上（如 GitHub）。</p><h3 id="13-快照snapshot"><a class="markdownIt-Anchor" href="#13-快照snapshot"></a> 1.3 快照（Snapshot）</h3><p>Git 以快照的形式存储项目的每个版本。每个快照记录了文件在某一时刻的状态，并将其与前一个快照进行关联。与传统的增量存储不同，Git 的快照机制高效且安全。</p><h3 id="14-分支branch"><a class="markdownIt-Anchor" href="#14-分支branch"></a> 1.4 分支（Branch）</h3><p>分支是 Git 中的一个重要概念，它允许用户在同一项目中进行并行开发。主分支（master 或 main）通常用于存储稳定的代码，而其他分支用于开发新功能或修复 Bug。</p><h2 id="2-git-的安装与配置"><a class="markdownIt-Anchor" href="#2-git-的安装与配置"></a> 2. Git 的安装与配置</h2><h3 id="21-安装-git"><a class="markdownIt-Anchor" href="#21-安装-git"></a> 2.1 安装 Git</h3><h4 id="在-windows-上安装"><a class="markdownIt-Anchor" href="#在-windows-上安装"></a> 在 Windows 上安装</h4><ol><li>访问 <a class="link"   href="https://git-scm.com/" >Git 官网<i class="fas fa-external-link-alt"></i></a> 下载适合 Windows 的安装包。</li><li>运行安装程序，并按照提示完成安装。</li></ol><h4 id="在-macos-上安装"><a class="markdownIt-Anchor" href="#在-macos-上安装"></a> 在 macOS 上安装</h4><p>使用 Homebrew 安装 Git：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install git</span><br></pre></td></tr></table></figure><p>或者访问 <a class="link"   href="https://git-scm.com/" >Git 官网<i class="fas fa-external-link-alt"></i></a> 下载并安装。</p><h4 id="在-linux-上安装"><a class="markdownIt-Anchor" href="#在-linux-上安装"></a> 在 Linux 上安装</h4><p>使用包管理器安装 Git（以 Ubuntu 为例）：</p><figure class="highlight bash"><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="built_in">sudo</span> apt-get update</span><br><span class="line"><span class="built_in">sudo</span> apt-get install git</span><br></pre></td></tr></table></figure><h3 id="22-配置-git"><a class="markdownIt-Anchor" href="#22-配置-git"></a> 2.2 配置 Git</h3><p>安装完成后，需要配置 Git 的用户名和电子邮件地址。这些信息会与每次提交关联。</p><figure class="highlight bash"><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">git config --global user.name <span class="string">&quot;Your Name&quot;</span></span><br><span class="line">git config --global user.email <span class="string">&quot;your.email@example.com&quot;</span></span><br></pre></td></tr></table></figure><p>检查配置是否正确：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --list</span><br></pre></td></tr></table></figure><h2 id="3-git-的基本操作"><a class="markdownIt-Anchor" href="#3-git-的基本操作"></a> 3. Git 的基本操作</h2><h3 id="31-初始化仓库"><a class="markdownIt-Anchor" href="#31-初始化仓库"></a> 3.1 初始化仓库</h3><p>在本地创建一个新的 Git 仓库：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git init</span><br></pre></td></tr></table></figure><h3 id="32-克隆远程仓库"><a class="markdownIt-Anchor" href="#32-克隆远程仓库"></a> 3.2 克隆远程仓库</h3><p>从远程仓库克隆项目到本地：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> &lt;repository-url&gt;</span><br></pre></td></tr></table></figure><h3 id="33-查看仓库状态"><a class="markdownIt-Anchor" href="#33-查看仓库状态"></a> 3.3 查看仓库状态</h3><p>查看工作目录的当前状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git status</span><br></pre></td></tr></table></figure><h3 id="34-添加文件到暂存区"><a class="markdownIt-Anchor" href="#34-添加文件到暂存区"></a> 3.4 添加文件到暂存区</h3><p>将文件添加到暂存区：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add &lt;file&gt;</span><br></pre></td></tr></table></figure><p>添加所有文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add .</span><br></pre></td></tr></table></figure><h3 id="35-提交更改"><a class="markdownIt-Anchor" href="#35-提交更改"></a> 3.5 提交更改</h3><p>将暂存区的更改提交到本地仓库：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git commit -m <span class="string">&quot;Commit message&quot;</span></span><br></pre></td></tr></table></figure><h3 id="36-查看提交历史"><a class="markdownIt-Anchor" href="#36-查看提交历史"></a> 3.6 查看提交历史</h3><p>查看项目的提交历史：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">log</span></span><br></pre></td></tr></table></figure><h3 id="37-分支操作"><a class="markdownIt-Anchor" href="#37-分支操作"></a> 3.7 分支操作</h3><h4 id="创建分支"><a class="markdownIt-Anchor" href="#创建分支"></a> 创建分支</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch &lt;branch-name&gt;</span><br></pre></td></tr></table></figure><h4 id="切换分支"><a class="markdownIt-Anchor" href="#切换分支"></a> 切换分支</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout &lt;branch-name&gt;</span><br></pre></td></tr></table></figure><h4 id="合并分支"><a class="markdownIt-Anchor" href="#合并分支"></a> 合并分支</h4><p>将分支合并到当前分支：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git merge &lt;branch-name&gt;</span><br></pre></td></tr></table></figure><h3 id="38-推送到远程仓库"><a class="markdownIt-Anchor" href="#38-推送到远程仓库"></a> 3.8 推送到远程仓库</h3><p>将本地提交推送到远程仓库：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin &lt;branch-name&gt;</span><br></pre></td></tr></table></figure><h3 id="39-拉取远程更新"><a class="markdownIt-Anchor" href="#39-拉取远程更新"></a> 3.9 拉取远程更新</h3><p>从远程仓库拉取并合并更新：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git pull</span><br></pre></td></tr></table></figure><h2 id="4-高级操作"><a class="markdownIt-Anchor" href="#4-高级操作"></a> 4. 高级操作</h2><h3 id="41-变基rebase"><a class="markdownIt-Anchor" href="#41-变基rebase"></a> 4.1 变基（Rebase）</h3><p>变基可以将一个分支上的修改移动到另一个分支之上，从而保持提交历史的线性。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git rebase &lt;base-branch&gt;</span><br></pre></td></tr></table></figure><h3 id="42-cherry-pick"><a class="markdownIt-Anchor" href="#42-cherry-pick"></a> 4.2 Cherry-pick</h3><p>将特定的提交应用到当前分支：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git cherry-pick &lt;commit-hash&gt;</span><br></pre></td></tr></table></figure><h3 id="43-取消更改"><a class="markdownIt-Anchor" href="#43-取消更改"></a> 4.3 取消更改</h3><h4 id="取消暂存的文件"><a class="markdownIt-Anchor" href="#取消暂存的文件"></a> 取消暂存的文件</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset &lt;file&gt;</span><br></pre></td></tr></table></figure><h4 id="取消提交"><a class="markdownIt-Anchor" href="#取消提交"></a> 取消提交</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset --soft HEAD~1</span><br></pre></td></tr></table></figure><h4 id="丢弃工作目录的更改"><a class="markdownIt-Anchor" href="#丢弃工作目录的更改"></a> 丢弃工作目录的更改</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -- &lt;file&gt;</span><br></pre></td></tr></table></figure><h3 id="44-标签tag"><a class="markdownIt-Anchor" href="#44-标签tag"></a> 4.4 标签（Tag）</h3><p>创建标签用于标记特定的提交（如发布版本）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag -a v1.0 -m <span class="string">&quot;Version 1.0&quot;</span></span><br></pre></td></tr></table></figure><p>推送标签到远程：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin v1.0</span><br></pre></td></tr></table></figure><h2 id="5-git-的最佳实践"><a class="markdownIt-Anchor" href="#5-git-的最佳实践"></a> 5. Git 的最佳实践</h2><ol><li><strong>频繁提交</strong>：小而频繁的提交可以帮助更好地跟踪更改，并在出现问题时更容易回滚。</li><li><strong>使用有意义的提交消息</strong>：清晰描述每次提交的目的和内容。</li><li><strong>定期推送和拉取</strong>：保持本地和远程仓库的同步，避免冲突。</li><li><strong>使用分支</strong>：将新功能和修复与主分支隔离，确保主分支的稳定性。</li><li><strong>代码评审</strong>：在合并到主分支之前，通过代码评审来保证代码质量。</li></ol><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h2><p>金猪言：Git 是一个强大且灵活的版本控制系统，广泛应用于软件开发中。通过本教程，你可以了解 Git 的基本概念、常用命令和一些高级操作。掌握这些技能将帮助你更好地管理项目，并与团队协作。记住，实践是掌握 Git 的最好方式，所以多尝试、多使用。</p>]]>
    </content>
    <id>https://dajinzhu.cn/2020/05/17/git-1/</id>
    <link href="https://dajinzhu.cn/2020/05/17/git-1/"/>
    <published>2020-05-17T15:00:00.000Z</published>
    <summary>面向初学者介绍 Git 的核心概念、仓库操作、分支管理、远程协作与常用命令。</summary>
    <title>Git 使用教程与常用命令详解</title>
    <updated>2020-05-17T15:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="编程" scheme="https://dajinzhu.cn/categories/%E7%BC%96%E7%A8%8B/"/>
    <category term="python" scheme="https://dajinzhu.cn/tags/python/"/>
    <content>
      <![CDATA[<p>在基础的爬虫开发基础上，进阶爬虫涉及更复杂的技术和策略，例如处理动态网页内容、反爬虫机制、数据存储优化、并发爬取和使用代理等。本教程将介绍这些高级主题，帮助你开发更强大和灵活的爬虫。（本文难度偏大，请酌情考虑学习）注意：文章是在初级教程的基础上进行的改进，采取更先进的爬虫技术，注意文章发布时可能与读者阅读时的环境大有不同。</p><h3 id="1-处理动态内容"><a class="markdownIt-Anchor" href="#1-处理动态内容"></a> 1. 处理动态内容</h3><p>现代网站通常使用 JavaScript 动态加载内容，简单的 HTTP 请求可能无法获取完整的网页数据。为了解决这个问题，我们可以使用以下几种方法：</p><h4 id="11-使用-selenium-模拟浏览器"><a class="markdownIt-Anchor" href="#11-使用-selenium-模拟浏览器"></a> 1.1. 使用 Selenium 模拟浏览器</h4><p>Selenium 是一个强大的工具，可以自动化网页浏览器操作，包括处理动态内容。它支持多种浏览器，如 Chrome 和 Firefox。</p><p><strong>示例：使用 Selenium 抓取动态内容</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><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="keyword">from</span> selenium <span class="keyword">import</span> webdriver</span><br><span class="line"><span class="keyword">from</span> selenium.webdriver.common.by <span class="keyword">import</span> By</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置Chrome浏览器</span></span><br><span class="line">driver = webdriver.Chrome()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打开目标网址</span></span><br><span class="line">driver.get(<span class="string">&#x27;https://example-dynamic-site.com&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等待JavaScript加载</span></span><br><span class="line">time.sleep(<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找所需元素</span></span><br><span class="line">elements = driver.find_elements(By.TAG_NAME, <span class="string">&#x27;h2&#x27;</span>)</span><br><span class="line"><span class="keyword">for</span> element <span class="keyword">in</span> elements:</span><br><span class="line">    <span class="built_in">print</span>(element.text)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭浏览器</span></span><br><span class="line">driver.quit()</span><br></pre></td></tr></table></figure><h4 id="12-分析网络请求"><a class="markdownIt-Anchor" href="#12-分析网络请求"></a> 1.2. 分析网络请求</h4><p>有时，动态内容通过网络请求加载。在浏览器的开发者工具中，可以监视网络请求，找到加载数据的API端点，并直接请求该数据。</p><p><strong>示例：抓取API数据</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><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="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">api_url = <span class="string">&#x27;https://example-api.com/data&#x27;</span></span><br><span class="line">response = requests.get(api_url)</span><br><span class="line">data = response.json()</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> item <span class="keyword">in</span> data[<span class="string">&#x27;items&#x27;</span>]:</span><br><span class="line">    <span class="built_in">print</span>(item[<span class="string">&#x27;title&#x27;</span>])</span><br></pre></td></tr></table></figure><h3 id="2-反爬虫机制和对策"><a class="markdownIt-Anchor" href="#2-反爬虫机制和对策"></a> 2. 反爬虫机制和对策</h3><p>许多网站有反爬虫措施，如IP封禁、验证码、动态内容加载等。以下是应对这些挑战的一些策略：</p><h4 id="21-使用代理池"><a class="markdownIt-Anchor" href="#21-使用代理池"></a> 2.1. 使用代理池</h4><p>使用代理池可以伪装爬虫的IP地址，减少被封禁的风险。</p><p><strong>示例：使用代理</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><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">proxies = &#123;</span><br><span class="line">    <span class="string">&#x27;http&#x27;</span>: <span class="string">&#x27;http://your.proxy.com:1234&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;https&#x27;</span>: <span class="string">&#x27;https://your.proxy.com:1234&#x27;</span>,</span><br><span class="line">&#125;</span><br><span class="line">response = requests.get(url, proxies=proxies)</span><br></pre></td></tr></table></figure><h4 id="22-请求头伪装"><a class="markdownIt-Anchor" href="#22-请求头伪装"></a> 2.2. 请求头伪装</h4><p>通过修改请求头，模仿真实用户的请求，可以避免一些基本的反爬虫检测。</p><p><strong>示例：自定义请求头</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><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">headers = &#123;</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/58.0.3029.110 Safari/537.3&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;Referer&#x27;</span>: <span class="string">&#x27;https://example.com&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;Accept-Language&#x27;</span>: <span class="string">&#x27;en-US,en;q=0.9&#x27;</span>,</span><br><span class="line">&#125;</span><br><span class="line">response = requests.get(url, headers=headers)</span><br></pre></td></tr></table></figure><h4 id="23-避免重复请求"><a class="markdownIt-Anchor" href="#23-避免重复请求"></a> 2.3. 避免重复请求</h4><p>使用 <code>robots.txt</code> 文件和网站地图可以指导爬虫遵循网站的爬取规则，避免不必要的重复请求。</p><h3 id="3-数据存储优化"><a class="markdownIt-Anchor" href="#3-数据存储优化"></a> 3. 数据存储优化</h3><p>对于大量数据的存储和处理，简单的文件系统可能不足以应对。这时，数据库系统可以提供更好的性能和组织方式。</p><h4 id="31-使用关系型数据库"><a class="markdownIt-Anchor" href="#31-使用关系型数据库"></a> 3.1. 使用关系型数据库</h4><p>将数据存储在关系型数据库（如 MySQL、PostgreSQL）中，便于进行复杂查询和数据管理。</p><p><strong>示例：使用 SQLite 存储数据</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><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="keyword">import</span> sqlite3</span><br><span class="line"></span><br><span class="line">conn = sqlite3.connect(<span class="string">&#x27;data.db&#x27;</span>)</span><br><span class="line">c = conn.cursor()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建表</span></span><br><span class="line">c.execute(<span class="string">&#x27;&#x27;&#x27;CREATE TABLE articles (title TEXT, link TEXT)&#x27;&#x27;&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 插入数据</span></span><br><span class="line">c.execute(<span class="string">&quot;INSERT INTO articles (title, link) VALUES (?, ?)&quot;</span>, (title, link))</span><br><span class="line"></span><br><span class="line">conn.commit()</span><br><span class="line">conn.close()</span><br></pre></td></tr></table></figure><h4 id="32-使用nosql数据库"><a class="markdownIt-Anchor" href="#32-使用nosql数据库"></a> 3.2. 使用NoSQL数据库</h4><p>对于更灵活的数据结构，可以使用 NoSQL 数据库，如 MongoDB。</p><p><strong>示例：使用 MongoDB 存储数据</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><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="keyword">from</span> pymongo <span class="keyword">import</span> MongoClient</span><br><span class="line"></span><br><span class="line">client = MongoClient(<span class="string">&#x27;localhost&#x27;</span>, <span class="number">27017</span>)</span><br><span class="line">db = client[<span class="string">&#x27;mydatabase&#x27;</span>]</span><br><span class="line">collection = db[<span class="string">&#x27;articles&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 插入数据</span></span><br><span class="line">article = &#123;<span class="string">&quot;title&quot;</span>: title, <span class="string">&quot;link&quot;</span>: link&#125;</span><br><span class="line">collection.insert_one(article)</span><br></pre></td></tr></table></figure><h3 id="4-并发爬取"><a class="markdownIt-Anchor" href="#4-并发爬取"></a> 4. 并发爬取</h3><p>并发爬取可以显著提高爬虫的效率。我们可以使用多线程或多进程来同时爬取多个网页。</p><h4 id="41-使用多线程"><a class="markdownIt-Anchor" href="#41-使用多线程"></a> 4.1. 使用多线程</h4><p><code>threading</code> 库可以用于实现简单的多线程爬虫。</p><p><strong>示例：多线程爬虫</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">urls = [<span class="string">&#x27;https://example.com/page1&#x27;</span>, <span class="string">&#x27;https://example.com/page2&#x27;</span>, ...]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fetch</span>(<span class="params">url</span>):</span><br><span class="line">    response = requests.get(url)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&#x27;<span class="subst">&#123;url&#125;</span>: <span class="subst">&#123;response.status_code&#125;</span>&#x27;</span>)</span><br><span class="line"></span><br><span class="line">threads = []</span><br><span class="line"><span class="keyword">for</span> url <span class="keyword">in</span> urls:</span><br><span class="line">    t = threading.Thread(target=fetch, args=(url,))</span><br><span class="line">    threads.append(t)</span><br><span class="line">    t.start()</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> t <span class="keyword">in</span> threads:</span><br><span class="line">    t.join()</span><br></pre></td></tr></table></figure><h4 id="42-使用-asyncio-和-aiohttp"><a class="markdownIt-Anchor" href="#42-使用-asyncio-和-aiohttp"></a> 4.2. 使用 <code>asyncio</code> 和 <code>aiohttp</code></h4><p>对于I/O密集型的任务（如网络请求），<code>asyncio</code> 和 <code>aiohttp</code> 提供了异步编程的支持，可以更高效地处理并发请求。</p><p><strong>示例：异步爬虫</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> aiohttp</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch</span>(<span class="params">session, url</span>):</span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">with</span> session.get(url) <span class="keyword">as</span> response:</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">await</span> response.text()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>(<span class="params">urls</span>):</span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">with</span> aiohttp.ClientSession() <span class="keyword">as</span> session:</span><br><span class="line">        tasks = [fetch(session, url) <span class="keyword">for</span> url <span class="keyword">in</span> urls]</span><br><span class="line">        htmls = <span class="keyword">await</span> asyncio.gather(*tasks)</span><br><span class="line">        <span class="keyword">for</span> html <span class="keyword">in</span> htmls:</span><br><span class="line">            <span class="built_in">print</span>(html)</span><br><span class="line"></span><br><span class="line">urls = [<span class="string">&#x27;https://example.com/page1&#x27;</span>, <span class="string">&#x27;https://example.com/page2&#x27;</span>, ...]</span><br><span class="line">asyncio.run(main(urls))</span><br></pre></td></tr></table></figure><h3 id="5-数据清洗和分析"><a class="markdownIt-Anchor" href="#5-数据清洗和分析"></a> 5. 数据清洗和分析</h3><p>爬取的数据通常需要进行清洗和整理。可以使用 Pandas 等数据分析库进行数据处理。</p><p><strong>示例：使用 Pandas 清洗数据</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 假设我们有一组抓取的数据</span></span><br><span class="line">data = [&#123;<span class="string">&#x27;title&#x27;</span>: <span class="string">&#x27;Title 1&#x27;</span>, <span class="string">&#x27;link&#x27;</span>: <span class="string">&#x27;http://example.com/1&#x27;</span>&#125;,</span><br><span class="line">        &#123;<span class="string">&#x27;title&#x27;</span>: <span class="string">&#x27;Title 2&#x27;</span>, <span class="string">&#x27;link&#x27;</span>: <span class="string">&#x27;http://example.com/2&#x27;</span>&#125;]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 转换为 DataFrame</span></span><br><span class="line">df = pd.DataFrame(data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 数据清洗，如删除重复项</span></span><br><span class="line">df = df.drop_duplicates()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 分析数据，如统计各标题长度</span></span><br><span class="line">df[<span class="string">&#x27;title_length&#x27;</span>] = df[<span class="string">&#x27;title&#x27;</span>].apply(<span class="built_in">len</span>)</span><br><span class="line"><span class="built_in">print</span>(df)</span><br></pre></td></tr></table></figure><h3 id="6-监控和维护"><a class="markdownIt-Anchor" href="#6-监控和维护"></a> 6. 监控和维护</h3><p>爬虫在运行过程中，可能会遇到网站结构变化、反爬虫策略更新等问题。因此，监控和维护是长期使用爬虫的关键。可以设置日志、错误处理和报警系统来确保爬虫的稳定运行。</p><p><strong>示例：设置日志</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><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="keyword">import</span> logging</span><br><span class="line"></span><br><span class="line">logging.basicConfig(filename=<span class="string">&#x27;crawler.log&#x27;</span>, level=logging.INFO)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fetch</span>(<span class="params">url</span>):</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        response = requests.get(url)</span><br><span class="line">        response.raise_for_status()</span><br><span class="line">        logging.info(<span class="string">f&#x27;Success: <span class="subst">&#123;url&#125;</span>&#x27;</span>)</span><br><span class="line">    <span class="keyword">except</span> requests.RequestException <span class="keyword">as</span> e:</span><br><span class="line">        logging.error(<span class="string">f&#x27;Error fetching <span class="subst">&#123;url&#125;</span>: <span class="subst">&#123;e&#125;</span>&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例使用</span></span><br><span class="line">fetch(<span class="string">&#x27;https://example.com&#x27;</span>)</span><br></pre></td></tr></table></figure><h3 id="7-法律和道德考量"><a class="markdownIt-Anchor" href="#7-法律和道德考量"></a> 7. 法律和道德考量</h3><p>在编写和运行爬虫时，必须遵守法律法规和道德规范。特别要注意以下几点：</p><ul><li>遵循网站的 <code>robots.txt</code> 指令。</li><li>不要过度爬取，避免给目标服务器带来过重负担。</li><li>不要抓取私人或敏感信息。</li><li>遵守版权和数据保护法律。</li></ul><h3 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h3><p>金猪言：进阶爬虫开发涉及处理更复杂的网页结构、应对反爬虫机制、提升爬取效率和优化数据存储等方面。通过学习和掌握这些技术和策略，你可以构建更高效和健壮的爬虫系统。记住，爬虫开发不仅仅是技术问题，还涉及到法律和道德的考量。在实践中，应始终遵循合法和道德的操作规范。</p>]]>
    </content>
    <id>https://dajinzhu.cn/2020/04/25/python-spider-2/</id>
    <link href="https://dajinzhu.cn/2020/04/25/python-spider-2/"/>
    <published>2020-04-25T15:10:00.000Z</published>
    <summary>讲解动态页面、反爬策略、代理、并发抓取和数据存储等 Python 爬虫进阶主题。</summary>
    <title>Python 进阶爬虫教程</title>
    <updated>2020-04-25T15:10:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="编程" scheme="https://dajinzhu.cn/categories/%E7%BC%96%E7%A8%8B/"/>
    <category term="python" scheme="https://dajinzhu.cn/tags/python/"/>
    <category term="爬虫" scheme="https://dajinzhu.cn/tags/%E7%88%AC%E8%99%AB/"/>
    <content>
      <![CDATA[<p>Python 是一种非常适合开发网络爬虫的编程语言，得益于其简单易学的语法和强大的库支持。笔者高中时期就已经学校普及了Python语言（当然是入门）。本文将为你介绍如何使用 Python 编写一个简单的爬虫（这就是python的强悍之处），逐步讲解基本概念和操作。我们将使用 <code>requests</code> 库进行网页请求，使用 <code>BeautifulSoup</code> 进行 HTML 解析。</p><h4 id="前置准备"><a class="markdownIt-Anchor" href="#前置准备"></a> 前置准备</h4><p>在开始之前，你需要确保 Python 已经安装在你的系统上，并且安装了 <code>requests</code> 和 <code>beautifulsoup4</code> 库。如果还没有安装，可以通过以下命令进行安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install requests beautifulsoup4</span><br></pre></td></tr></table></figure><h3 id="1-爬虫的基本概念"><a class="markdownIt-Anchor" href="#1-爬虫的基本概念"></a> 1. 爬虫的基本概念</h3><ul><li><strong>网页抓取</strong>：爬虫的核心是从网页中抓取数据。这涉及到向目标网页发送请求，接收响应数据并提取其中的有用信息。</li><li><strong>HTML 解析</strong>：抓取到的网页通常是 HTML 格式的文本。我们需要解析 HTML，以提取特定的数据，如文本、图片、链接等。</li><li><strong>数据存储</strong>：抓取的数据通常需要存储到文件或数据库中，以便后续处理。</li></ul><h3 id="2-爬虫的基本流程"><a class="markdownIt-Anchor" href="#2-爬虫的基本流程"></a> 2. 爬虫的基本流程</h3><ol><li><strong>发送请求</strong>：使用 <code>requests</code> 库向目标 URL 发送 HTTP 请求。</li><li><strong>获取响应</strong>：接收目标服务器返回的响应，通常包括 HTML 内容。</li><li><strong>解析内容</strong>：使用 <code>BeautifulSoup</code> 解析 HTML 内容，提取所需数据。</li><li><strong>处理和存储数据</strong>：对提取的数据进行处理，并将其保存到指定位置。</li></ol><h3 id="3-代码示例"><a class="markdownIt-Anchor" href="#3-代码示例"></a> 3. 代码示例</h3><p>我们以抓取一个简单的博客页面的标题和链接为例，讲解上述流程。</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><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="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> bs4 <span class="keyword">import</span> BeautifulSoup</span><br><span class="line"></span><br><span class="line"><span class="comment"># Step 1: 发送请求</span></span><br><span class="line">url = <span class="string">&#x27;https://example-blog.com&#x27;</span></span><br><span class="line">response = requests.get(url)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Step 2: 检查请求是否成功</span></span><br><span class="line"><span class="keyword">if</span> response.status_code == <span class="number">200</span>:</span><br><span class="line">    <span class="comment"># Step 3: 解析内容</span></span><br><span class="line">    soup = BeautifulSoup(response.text, <span class="string">&#x27;html.parser&#x27;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 假设博客文章标题在 &lt;h2&gt; 标签内，链接在 &lt;a&gt; 标签的 href 属性中</span></span><br><span class="line">    articles = soup.find_all(<span class="string">&#x27;h2&#x27;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> article <span class="keyword">in</span> articles:</span><br><span class="line">        title = article.text</span><br><span class="line">        link = article.find(<span class="string">&#x27;a&#x27;</span>)[<span class="string">&#x27;href&#x27;</span>]</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&#x27;Title: <span class="subst">&#123;title&#125;</span>, Link: <span class="subst">&#123;link&#125;</span>&#x27;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&#x27;Failed to retrieve content: <span class="subst">&#123;response.status_code&#125;</span>&#x27;</span>)</span><br></pre></td></tr></table></figure><h3 id="4-详细解释"><a class="markdownIt-Anchor" href="#4-详细解释"></a> 4. 详细解释</h3><h4 id="41-发送请求"><a class="markdownIt-Anchor" href="#41-发送请求"></a> 4.1. 发送请求</h4><p><code>requests.get(url)</code> 用于发送 HTTP GET 请求，获取指定 URL 的内容。<code>response</code> 对象包含了服务器返回的所有信息，包括状态码、响应头、响应体等。</p><h4 id="42-检查请求状态"><a class="markdownIt-Anchor" href="#42-检查请求状态"></a> 4.2. 检查请求状态</h4><p>通过检查 <code>response.status_code</code> 可以确定请求是否成功。通常，200 代表成功，其他状态码如 404（找不到页面）、500（服务器错误）等表示请求失败。</p><h4 id="43-解析-html-内容"><a class="markdownIt-Anchor" href="#43-解析-html-内容"></a> 4.3. 解析 HTML 内容</h4><p><code>BeautifulSoup</code> 是一个用于解析 HTML 和 XML 的库。我们将 <code>response.text</code> 传递给 <code>BeautifulSoup</code>，并指定解析器（如 <code>html.parser</code>）。然后，我们可以使用 <code>find_all</code> 方法查找所有指定标签（如 <code>h2</code>）的内容。</p><h4 id="44-提取和处理数据"><a class="markdownIt-Anchor" href="#44-提取和处理数据"></a> 4.4. 提取和处理数据</h4><p>在这个示例中，我们假设每篇文章的标题在 <code>h2</code> 标签内，链接在 <code>a</code> 标签的 <code>href</code> 属性中。我们通过遍历 <code>articles</code> 列表，提取每篇文章的标题和链接。</p><h3 id="5-进阶技巧"><a class="markdownIt-Anchor" href="#5-进阶技巧"></a> 5. 进阶技巧</h3><h4 id="51-模拟浏览器行为"><a class="markdownIt-Anchor" href="#51-模拟浏览器行为"></a> 5.1. 模拟浏览器行为</h4><p>有些网站可能会检测爬虫并阻止其访问。在这种情况下，可以通过设置请求头，模拟正常用户的浏览器行为。例如：</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><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">headers = &#123;</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/58.0.3029.110 Safari/537.3&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line">response = requests.get(url, headers=headers)</span><br></pre></td></tr></table></figure><h4 id="52-处理动态内容"><a class="markdownIt-Anchor" href="#52-处理动态内容"></a> 5.2. 处理动态内容</h4><p>有些网站使用 JavaScript 动态加载内容。对于这种情况，可以使用 Selenium 等工具模拟浏览器操作，或者分析网络请求，直接抓取所需数据。</p><h4 id="53-数据存储"><a class="markdownIt-Anchor" href="#53-数据存储"></a> 5.3. 数据存储</h4><p>可以将抓取的数据保存到文件、数据库，或者通过 API 接口发送到其他系统。常见的存储格式包括 CSV、JSON、SQL 等。</p><h4 id="54-避免爬虫陷阱"><a class="markdownIt-Anchor" href="#54-避免爬虫陷阱"></a> 5.4. 避免爬虫陷阱</h4><p>一些网站可能会通过隐藏链接或脚本来检测和阻止爬虫。要避免进入死循环或被屏蔽，可以设置合理的请求间隔、尊重网站的 <code>robots.txt</code> 规则，并使用 IP 代理来分散请求。</p><h3 id="6-道德和法律考虑"><a class="markdownIt-Anchor" href="#6-道德和法律考虑"></a> 6. 道德和法律考虑</h3><p>在编写和运行网络爬虫时，必须遵守相关法律法规和网站的使用政策。例如，尊重网站的 <code>robots.txt</code> 文件，不抓取敏感或私人数据，不进行过度抓取导致网站服务器负担过重等。</p><h3 id="结论"><a class="markdownIt-Anchor" href="#结论"></a> 结论</h3><p>以上是一个简单的 Python 爬虫教程，涵盖了基本的爬虫流程和技巧。在实际应用中，根据不同的目标网站和数据需求，可能需要更复杂的技术和策略。大金猪希望这个教程能帮助你入门网络爬虫的开发。</p>]]>
    </content>
    <id>https://dajinzhu.cn/2020/04/25/python-spider-1/</id>
    <link href="https://dajinzhu.cn/2020/04/25/python-spider-1/"/>
    <published>2020-04-25T15:00:00.000Z</published>
    <summary>使用 requests 与 BeautifulSoup 讲解网页请求、HTML 解析和数据提取，完成 Python 爬虫入门。</summary>
    <title>Python 初级爬虫教程</title>
    <updated>2020-04-25T15:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Chen Xi</name>
    </author>
    <category term="编程" scheme="https://dajinzhu.cn/categories/%E7%BC%96%E7%A8%8B/"/>
    <category term="Go" scheme="https://dajinzhu.cn/tags/Go/"/>
    <category term="安装教程" scheme="https://dajinzhu.cn/tags/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/"/>
    <content>
      <![CDATA[<p>Go语言是一种由Google开发的开源编程语言，以其简单、高效和并发编程的强大能力而著称。我是特别喜欢这种语言，本教程将指导你如何在Windows系统上安装Go语言环境，并编写一个简单的Hello World程序，本章节由作者亲自测试。</p><h2 id="1-下载并安装go语言"><a class="markdownIt-Anchor" href="#1-下载并安装go语言"></a> 1. 下载并安装Go语言</h2><h3 id="11-前往go的官方网站"><a class="markdownIt-Anchor" href="#11-前往go的官方网站"></a> 1.1 前往Go的官方网站</h3><p>首先，访问Go语言的官方网站 <a class="link"   href="https://golang.org/dl/" >https://golang.org/dl/<i class="fas fa-external-link-alt"></i></a> 以下载最新版本的Go语言安装包。</p><h3 id="12-下载适用于windows的安装包"><a class="markdownIt-Anchor" href="#12-下载适用于windows的安装包"></a> 1.2 下载适用于Windows的安装包</h3><p>在下载页面中，你会看到多个版本的Go语言安装包。选择适用于Windows系统的版本（通常是一个以 <code>.msi</code> 结尾的文件），点击下载。</p><h3 id="13-安装go语言"><a class="markdownIt-Anchor" href="#13-安装go语言"></a> 1.3 安装Go语言</h3><p>下载完成后，双击 <code>.msi</code> 文件开始安装。按照安装向导的指示完成安装。默认情况下，Go语言将被安装到 <code>C:\Go</code> 目录下。</p><h2 id="2-配置环境变量"><a class="markdownIt-Anchor" href="#2-配置环境变量"></a> 2. 配置环境变量</h2><h3 id="21-设置goroot"><a class="markdownIt-Anchor" href="#21-设置goroot"></a> 2.1 设置GOROOT</h3><p><code>GOROOT</code> 是Go的安装目录。默认情况下，这个路径是 <code>C:\Go</code>。通常不需要手动设置<code>GOROOT</code>，因为安装程序会自动配置。</p><h3 id="22-设置gopath"><a class="markdownIt-Anchor" href="#22-设置gopath"></a> 2.2 设置GOPATH</h3><p><code>GOPATH</code> 是你自己的Go工作区的路径，用于存储你的Go项目和依赖包。你可以将它设置为任意路径，例如 <code>C:\Users\&lt;YourUserName&gt;\go</code>。</p><h4 id="设置环境变量步骤"><a class="markdownIt-Anchor" href="#设置环境变量步骤"></a> 设置环境变量步骤：</h4><ol><li>打开控制面板，进入“系统和安全” -&gt; “系统”，然后点击“高级系统设置”。</li><li>在“系统属性”窗口中，点击“环境变量”。</li><li>在“系统变量”区域，点击“新建”按钮，添加以下两个变量：<ul><li><code>GOPATH</code>：设置为你的Go工作区路径（例如 <code>C:\Users\&lt;YourUserName&gt;\go</code>）。</li><li><code>PATH</code>：在已有的PATH变量中，添加Go的安装目录和<code>GOPATH\bin</code>，例如 <code>C:\Go\bin;C:\Users\&lt;YourUserName&gt;\go\bin</code>。</li></ul></li></ol><h2 id="3-验证安装"><a class="markdownIt-Anchor" href="#3-验证安装"></a> 3. 验证安装</h2><h3 id="31-检查go版本"><a class="markdownIt-Anchor" href="#31-检查go版本"></a> 3.1 检查Go版本</h3><p>打开命令提示符（CMD），输入以下命令检查Go版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">go version</span><br></pre></td></tr></table></figure><p>你应该会看到类似 <code>go version go1.x.x windows/amd64</code> 的输出，表示Go已正确安装。</p><h3 id="32-创建hello-world程序"><a class="markdownIt-Anchor" href="#32-创建hello-world程序"></a> 3.2 创建Hello World程序</h3><ol><li><p>在命令提示符中，导航到你的Go工作区目录，创建一个新目录用于存放你的项目，例如 <code>hello</code>：</p><figure class="highlight bash"><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="built_in">mkdir</span> %GOPATH%\src\hello</span><br><span class="line"><span class="built_in">cd</span> %GOPATH%\src\hello</span><br></pre></td></tr></table></figure></li><li><p>创建一个名为 <code>main.go</code> 的文件，并使用文本编辑器（例如Notepad）打开它，输入以下代码：</p><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    fmt.Println(<span class="string">&quot;Hello, World!&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>保存并关闭文件。</p></li></ol><h3 id="33-编译并运行程序"><a class="markdownIt-Anchor" href="#33-编译并运行程序"></a> 3.3 编译并运行程序</h3><p>在命令提示符中，确保你在 <code>hello</code> 目录下，然后输入以下命令编译并运行程序：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">go run main.go</span><br></pre></td></tr></table></figure><p>你应该会看到输出：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Hello, World!</span><br></pre></td></tr></table></figure><p>这表示你的Go开发环境已成功配置，并且你已编写并运行了第一个Go程序！</p><h2 id="4-总结"><a class="markdownIt-Anchor" href="#4-总结"></a> 4. 总结</h2><p>通过上述步骤，你已经在Windows系统上成功安装了Go语言环境，并编写并运行了一个简单的Hello World程序。现在，你可以开始探索Go语言的更多功能和特性，继续开发你的Go项目。</p>]]>
    </content>
    <id>https://dajinzhu.cn/2018/07/25/golang-start/</id>
    <link href="https://dajinzhu.cn/2018/07/25/golang-start/"/>
    <published>2018-07-24T16:00:00.000Z</published>
    <summary>逐步介绍在 Windows 中安装 Go、配置环境变量并运行第一个 Hello World 程序。</summary>
    <title>如何在Windows系统上安装Go语言环境并编写Hello World程序</title>
    <updated>2018-07-24T16:00:00.000Z</updated>
  </entry>
</feed>
