<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ songyp0505 - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ songyp0505 - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 13 May 2026 08:54:06 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/author/songyp0505/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 如何写出整洁的代码——技巧与最佳实践 ]]>
                </title>
                <description>
                    <![CDATA[ 大家好！这篇文章讲的主要是如何写出“整洁”的代码。我刚开始成为一个程序员的时候，这个概念常常会有点模糊，后来我发现它有很多细微差别的地方和一些可能的解释。 所以在这篇文章中，我们将会讨论“整洁代码”指的是什么，它为什么这么重要，我们如何评估一个代码库整洁与否。同时你也会学习一些你能遵循的最佳实践和惯例，让你的代码更加整洁。 冲！ 目录  * 写出“整洁代码”意味着什么以及我为什么要注意它？  * 我如何评估一个代码库是否整洁？  * 让代码更整洁的技巧和惯例 * 有效、效率和简单      * 格式和语法     * 命名     * 简洁VS清晰     * 复用性     * 清晰的执行流程     ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-write-clean-code/</link>
                <guid isPermaLink="false">64775064932aa206e5722edd</guid>
                
                    <category>
                        <![CDATA[ 代码质量 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ songyp0505 ]]>
                </dc:creator>
                <pubDate>Wed, 31 May 2023 03:05:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/05/pexels-ken-tomita-389819.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-write-clean-code/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Write Clean Code – Tips and Best Practices (Full Handbook)</a>
      </p><!--kg-card-begin: markdown--><p>大家好！这篇文章讲的主要是如何写出“整洁”的代码。我刚开始成为一个程序员的时候，这个概念常常会有点模糊，后来我发现它有很多细微差别的地方和一些可能的解释。</p>
<p>所以在这篇文章中，我们将会讨论“整洁代码”指的是什么，它为什么这么重要，我们如何评估一个代码库整洁与否。同时你也会学习一些你能遵循的最佳实践和惯例，让你的代码更加整洁。</p>
<p>冲！</p>
<h2 id="">目录</h2>
<ul>
<li><a href="#what-does-it-mean-to-write-clean-code-and-why-should-i-care">写出“整洁代码”意味着什么以及我为什么要注意它？</a></li>
<li><a href="#how-can-i-assess-whether-a-codebase-is-clean-or-not">我如何评估一个代码库是否整洁？</a></li>
<li><a href="#tips-and-conventions-to-write-cleaner-code">让代码更整洁的技巧和惯例</a>
<ul>
<li><a href="#effectiveness-efficiency-and-simplicity">有效、效率和简单 </a></li>
<li><a href="#format-and-syntax">格式和语法</a></li>
<li><a href="#naming">命名</a></li>
<li><a href="#conciseness-versus-clarity">简洁VS清晰</a></li>
<li><a href="#re-usability">复用性</a></li>
<li><a href="#clear-flow-of-execution">清晰的执行流程</a></li>
<li><a href="#single-responsibility-principle">单一职责原则</a></li>
<li><a href="#having-a-single-source-of-truth">拥有“单一事实来源”</a></li>
<li><a href="#only-expose-and-consume-data-you-need">只暴露和使用你需要的数据</a></li>
<li><a href="#modularization">模块化</a></li>
<li><a href="#folder-structures">文件夹结构</a></li>
<li><a href="#documentation">文档化</a></li>
</ul>
</li>
<li><a href="#wrapping-up">总结</a></li>
</ul>
<h2 id="what-does-it-mean-to-write-clean-code-and-why-should-i-care">写出“整洁代码”意味着什么以及我为什么要注意它？</h2>
<p>整洁代码是一个术语，用来形容计算机代码容易阅读、理解和维护。整洁的代码需要用一种，能让代码简单、简洁且形象生动的方式进行书写。它遵循一套惯例、标准来使得代码容易阅读和理解。</p>
<p>整洁的代码脱离了复杂性、冗余以及一些“代码味”和反面模式，因为这些东西会让代码变得难以维护、调试以及修改。</p>
<p>我并不能夸大代码整洁的重要性。当代码很容易阅读和理解的时候，开发者们就能很容易地在代码库中工作。这就可以提高工作效率以及降低产生的错误。</p>
<p>并且，当代码是易于维护的，随着时间的推移，代码库也能不断提高和更新。这对于长期项目来说十分重要，尤其是代码在未来几年需要不断更新和维护的时候。</p>
<h2 id="how-can-i-assess-whether-a-codebase-is-clean-or-not">我如何评估一个代码库是否整洁？</h2>
<p>这里可以用很多方法。好的文档、前后一致的格式以及组织良好的代码库都是需要考虑的因素。</p>
<p>代码审查也可以帮助识别潜在的问题，确保代码遵循最佳实践和惯例。</p>
<p>测试也是一个很重要的方面，它可以帮助确保代码运行的情况跟预期的保持一致，而且能尽早地发现运行中的问题和错误。</p>
<p>你有很多工具、实践以及惯例可以直接引入，从而确保一个代码库是整洁的。</p>
<p>通过引入这些工具和实践，开发者们可以创建一个容易阅读、理解和维护的代码库。</p>
<p>还有一件事情也很重要，就是一定要记住在代码整洁这件事情上，不可避免地会粘连很多主观性，并且对于这件事情也有许许多多不同的观点和见解。一个代码库对一个人来说可能看起来很整洁、厉害，但是对另一个人或者另一个项目来说可能就不是这样了。</p>
<p>但是在这个问题上依然还是有很多惯例是我们可以遵守的，下面就说一下这些。</p>
<h2 id="tips-and-conventions-to-write-cleaner-code">让代码更整洁的技巧和惯例</h2>
<h3 id="effectiveness-efficiency-and-simplicity">有效、效率和简单</h3>
<p>当我需要思考如何向已有的代码库引入新的功能、或者如何找到某个特殊问题的答案的时候，我总会优先考虑这三个东西。</p>
<h4 id="">有效</h4>
<p>首先，我们的代码必须有效，这就意味着它必须能够解决它应该解决的问题。当然这是我们对我们自己的代码最基本的期望，但是如果我们实施的东西没有起到作用的话，此时考虑其他事情是毫无意义的。</p>
<h4 id="">效率</h4>
<p>第二，一旦我们的代码解决了问题，我们就应该检查它是否有效率。就时间和空间而言，程序运行时是否使用了合理的资源？它能不能运行得更快一些或者占用空间更少一些。</p>
<p>为了评估这一点，算法复杂度是一个你应该注意的事情。如果你对这个词不熟悉，你可以去我写的<a href="https://chinese.freecodecamp.org/news/introduction-to-algorithms-with-javascript-examples/#algorithmic-complexity">这一篇文章</a>看一下。</p>
<p>在效率这部分展开的话，下面有两个例子，都是计算一个数组中所有数字的总和。</p>
<pre><code class="language-javascript">// 不高效的示例
function sumArrayInefficient(array) {
  let sum = 0;
  for (let i = 0; i &lt; array.length; i++) {
    sum += array[i];
  }
  return sum;
}
</code></pre>
<p><code>sumArrayInefficient</code>函数使用<code>for</code>循环迭代数组，并将每个元素添加到<code>sum</code>变量中。虽然这是一个可行解，但是这样就没太有效率了，因为不管数组有多长，它都需要迭代整个数组。</p>
<pre><code class="language-javascript">// 高效的示例
function sumArrayEfficient(array) {
  return array.reduce((a, b) =&gt; a + b, 0);
}
</code></pre>
<p>在这里，<code>sumArrayEfficient</code> 函数使用<code>reduce</code> 方法去计算数组里的数字的总和。 <code>reduce</code> 方法将函数应用于数组的每个元素，并累加结果。在这种情况下，函数只需将每个元素添加到从0开始的累加器中。</p>
<p>这是一个更有效的解决方案，因为它只需要对数组进行一次迭代，并在进行时对每个元素执行求和操作。</p>
<h4 id="">简单</h4>
<p>最后是<strong>简单</strong>。这是最难评估的一个方面，因为它很主观，它取决于读代码的人。但是也有一些我们可以遵循的指南：</p>
<ol>
<li>你能简单地理解程序的每行代码在干什么吗？</li>
<li>函数和变量的定义是否都清晰地代表它们需要代表的内容？</li>
<li>在整个代码库中，代码的缩进和空格是否遵循正确的代码格式？</li>
<li>代码有可用的文档吗？评论是否用于解释程序的复杂部分？</li>
<li>你能以多快的速度识别出代码库的哪个部分是程序的某些功能？你能在不修改代码其他部分的前提下删除/增加新功能吗？</li>
<li>代码是否遵循模块化方法，在组件中分离不同的功能？</li>
<li>代码在可能的情况下会重复使用吗？</li>
<li>在整个代码库中，是否同样遵循相同的架构、设计和实现决策？</li>
</ol>
<p>通过遵循和优先考虑有效、效率和简单这三个概念，在考虑如何实施解决方案时，我们总是可以遵循一个指导方针。现在，让我们扩展一些可以帮助我们简化代码的指南。</p>
<h3 id="format-and-syntax">格式和语法</h3>
<p>在整个代码库中使用一致的格式和语法也是写出整洁代码的一个重要因素。这是因为一致的结构和语法能让代码可读性更强，并且能够更加容易理解。</p>
<p>当代码一致时，开发者们可以轻松地识别模式、理解代码的工作流程，这也就使得未来调试、维护和更新代码库变得更加容易。一致性也能帮助降低错误，因为它确保所有的开发者都遵循相同的标准和惯例。</p>
<p>我们应该考虑的一些格式和语法如下：</p>
<ul>
<li><strong>缩进和间距</strong></li>
</ul>
<pre><code class="language-javascript">// 缩进和间距不对
const myFunc=(number1,number2)=&gt;{
const result=number1+number2;
return result;
}

// 好的缩进和间距
const myFunc = (number1, number2) =&gt; {
    const result = number1 + number2
    return result
}
</code></pre>
<p>这里是同一个函数的例子，一个是没有缩进和空格，另一个是有合适的空格和缩进，我们可以发现第二个更容易阅读。</p>
<ul>
<li><strong>一致的语法</strong></li>
</ul>
<pre><code class="language-javascript">// 箭头函数，没有括号和返回
const multiplyByTwo = number =&gt; number * 2

// 函数，括号，返回
function multiplyByThree(number) {
    return number * 3;
}
</code></pre>
<p>同样，这里是用两种语法写出来的非常相似的函数。第一个用的是一个箭头定义的函数，没有括号和返回，而另一个是一个使用了括号和返回的相同函数。</p>
<p>两个都可以顺利的实现功能，但是我们应该力求在类似的操作中始终使用相同的语法，这样的话可以使得代码库更加可读，且代码量也更加均匀。</p>
<p>Linterns 和代码格式化程序是我们可以在项目中使用的很棒的工具，可以在我们的代码库中自动化语法和格式约定。如果你不熟悉这些工具，<a href="https://www.freecodecamp.org/news/using-prettier-and-jslint/">查看我的另一篇文章</a>。</p>
<ul>
<li><strong>一致的案例惯例</strong></li>
</ul>
<pre><code class="language-javascript">// camelCase
const myName = 'John'
// PascalCase
const MyName = 'John'
// snake_case
const my_name = 'John'
</code></pre>
<p>我们选择的案例遵循的惯例也是如此。上述三种情况都可以，但我们应该在整个项目中始终使用相同的用法。</p>
<h3 id="naming">命名</h3>
<p>清晰而有描述性地命名变量和函数，对于书写clean code也是十分钟重要的一个方面。它可以帮助提高代码的可读性和维护能力。当命名选择良好时，其他开发人员可以快速理解变量或函数在做什么，以及它与代码的其余部分有何关系。</p>
<p>下面是JavaScript中的两个例子，用来展示清晰且有描述性地命名的重要性：</p>
<pre><code class="language-javascript">// 示例 1：命名不规范
function ab(a, b) {
  let x = 10;
  let y = a + b + x;
  console.log(y);
}

ab(5, 3);
</code></pre>
<p>在本例中，我们有一个函数，它接受两个参数，将它们与常量值10相加，并将结果记录到控制台。函数名称和变量名选择不当，没有给出任何指示函数的作用或变量代表什么。</p>
<pre><code class="language-javascript">// 示例 1：命名规范
function calculateTotalWithTax(basePrice, taxRate) {
  const BASE_TAX = 10;
  const totalWithTax = basePrice + (basePrice * (taxRate / 100)) + BASE_TAX;
  console.log(totalWithTax);
}

calculateTotalWithTax(50, 20);
</code></pre>
<p>在这个例子中，我们有一个函数来计算产品的总价格，包括收取的税值。函数名称和变量名选择得很好，可以清楚地表明函数的作用以及变量代表什么。</p>
<p>这使得代码更容易阅读和理解，特别是对于未来可能使用代码库的其他开发人员来说。</p>
<h3 id="conciseness-versus-clarity">简洁 VS 清晰</h3>
<p>当涉及到编写整洁的代码时，在简洁和清晰之间取得平衡很重要。虽然保持代码简洁以提高其可读性和可维护性很重要，但同样重要的是确保代码清晰且易于理解。编写过于简洁的代码可能会导致混乱和错误，并可能使其他开发人员难以使用代码。</p>
<p>以下是两个例子，证明了简洁和清晰的重要性：</p>
<pre><code class="language-javascript">// 示例 1：清晰的函数
const countVowels = s =&gt; (s.match(/[aeiou]/gi) || []).length;
console.log(countVowels("hello world"));
</code></pre>
<p>本示例使用简洁的箭头函数和正则表达式来计算给定字符串中的元音数量。虽然代码非常简短且易于编写，但其他开发人员可能无法立即清楚正则表达式模式的工作原理，特别是如果他们不熟悉正则表达式语法。</p>
<pre><code class="language-javascript">// 例子 2：更省略、更清晰的函数
function countVowels(s) {
  const vowelRegex = /[aeiou]/gi;
  const matches = s.match(vowelRegex) || [];
  return matches.length;
}

console.log(countVowels("hello world"));
</code></pre>
<p>此示例使用传统函数和正则表达式来计算给定字符串中的元音数量，但以清晰易懂的方式进行计算。函数名称和变量名是描述性的，正则表达式模式存储在具有清晰名称的变量中。这使得很容易看到该功能在做什么以及它是如何工作的。</p>
<p>在编写代码时，在简洁和清晰之间取得平衡很重要。虽然简洁的代码可以提高可读性和可维护性，但对于将来可能使用代码库的其他开发人员来说，确保代码仍然清晰且易于理解很重要。</p>
<p>通过使用描述性函数和变量名，以及使用清晰可读的代码格式和注释，可以编写易于理解和处理的整洁的代码。</p>
<h3 id="re-usability">复用性</h3>
<p>代码可重用性是软件工程的一个基本概念，指的是代码无需修改即可多次使用的能力。</p>
<p>代码可重用性的重要性在于，它可以通过减少需要编写和测试的代码量来大大提高软件开发的效率和生产力。</p>
<p>通过重复使用现有代码，开发人员可以节省时间和精力，提高代码质量和一致性，并最大限度地降低引入错误和错误的风险。可重用代码还允许更多模块化和可扩展的软件架构，使其更容易随着时间的推移维护和更新代码库。</p>
<pre><code class="language-javascript">// 示例 1：没有复用
function calculateCircleArea(radius) {
  const PI = 3.14;
  return PI * radius * radius;
}

function calculateRectangleArea(length, width) {
  return length * width;
}

function calculateTriangleArea(base, height) {
  return (base * height) / 2;
}

const circleArea = calculateCircleArea(5);
const rectangleArea = calculateRectangleArea(4, 6);
const triangleArea = calculateTriangleArea(3, 7);

console.log(circleArea, rectangleArea, triangleArea);
</code></pre>
<p>这个例子定义了三个函数，分别计算圆、矩形和三角形的面积。每个函数执行一个特定的任务，但它们都不能重复用于其他类似任务。</p>
<p>此外，如果将来需要更改值，使用直接赋值的PI值可能会导致错误。该代码效率低下，因为它多次重复相同的逻辑。</p>
<pre><code class="language-javascript">// 示例 2：有复用
function calculateArea(shape, ...args) {
  if (shape === 'circle') {
    const [radius] = args;
    const PI = 3.14;
    return PI * radius * radius;
  } else if (shape === 'rectangle') {
    const [length, width] = args;
    return length * width;
  } else if (shape === 'triangle') {
    const [base, height] = args;
    return (base * height) / 2;
  } else {
    throw new Error(`Shape "${shape}" not supported.`);
  }
}

const circleArea = calculateArea('circle', 5);
const rectangleArea = calculateArea('rectangle', 4, 6);
const triangleArea = calculateArea('triangle', 3, 7);

console.log(circleArea, rectangleArea, triangleArea);
</code></pre>
<p>此示例定义了单个函数 calculateArea，该函数采用形状参数和可变数量的参数。基于形状参数，函数执行适当的计算并返回结果。</p>
<p>这种方法效率要高得多，因为它消除了为类似任务重复代码的需要。它也更加灵活和可扩展，因为将来可以很容易地添加额外的形状。</p>
<h3 id="clear-flow-of-execution">清晰的执行流程</h3>
<p>清晰的执行流程对于编写整洁的代码至关重要，因为它使代码更易于阅读、理解和维护。遵循清晰和逻辑结构的代码不容易出错，更容易修改和扩展，并且在时间和资源方面更有效率。</p>
<p>另一方面，意大利面条代码是一个术语，用于描述复杂且难以理解的代码，通常以长、混乱和无组织的代码块为特征。意大利面条代码可能是糟糕的设计决策、过度耦合或缺乏适当的文档和注释的结果。</p>
<p>下面是执行相同任务的两个 JavaScript 代码示例，一个执行流程清晰，另一个代码很杂乱：</p>
<pre><code class="language-javascript">// 示例 1：清晰的执行流程
function calculateDiscount(price, discountPercentage) {
  const discountAmount = price * (discountPercentage / 100);
  const discountedPrice = price - discountAmount;
  return discountedPrice;
}

const originalPrice = 100;
const discountPercentage = 20;
const finalPrice = calculateDiscount(originalPrice, discountPercentage);

console.log(finalPrice);

// 示例 2：意大利面条代码
const originalPrice = 100;
const discountPercentage = 20;

let discountedPrice;
let discountAmount;
if (originalPrice &amp;&amp; discountPercentage) {
  discountAmount = originalPrice * (discountPercentage / 100);
  discountedPrice = originalPrice - discountAmount;
}

if (discountedPrice) {
  console.log(discountedPrice);
}
</code></pre>
<p>正如我们所见，示例 1 遵循清晰且合乎逻辑的结构，其中包含一个函数，该函数接受必要的参数并返回计算结果。另一方面，示例 2 更加复杂，在任何函数之外声明了变量，并且使用多个 if 语句来检查代码块是否已成功执行。</p>
<h3 id="single-responsibility-principle">单一职责原则</h3>
<p>单一职责原则（SRP）是软件开发中的一项原则，它指出每个类或模块应该只有一个更改原因，或者换句话说，我们代码库中的每个实体都应该承担单一职责。</p>
<p>此原则有助于创建易于理解、维护和扩展的代码。</p>
<p>通过应用 SRP，我们可以创建更易于测试、重用和重构的代码，因为每个模块只处理单一职责。这使得它不太可能有副作用或依赖性，这些副作用或依赖性会使代码更难使用。</p>
<pre><code class="language-javascript">// 示例 1：没有 SRP
function processOrder(order) {
  // validate order
  if (order.items.length === 0) {
    console.log("Error: Order has no items");
    return;
  }
  
  // calculate total
  let total = 0;
  order.items.forEach(item =&gt; {
    total += item.price * item.quantity;
  });
  
  // apply discounts
  if (order.customer === "vip") {
    total *= 0.9;
  }
  
  // save order
  const db = new Database();
  db.connect();
  db.saveOrder(order, total);
}
</code></pre>
<p>在此示例中，<code>processOrder</code>函数处理多项职责：它验证订单、计算总额、应用折扣以及将订单保存到数据库中。这使得该功能冗长且难以理解，并且对一项职责的任何更改都可能影响其他职责，从而使其更难维护。</p>
<pre><code class="language-javascript">// 示例 2：有 SRP
class OrderProcessor {
  constructor(order) {
    this.order = order;
  }
  
  validate() {
    if (this.order.items.length === 0) {
      console.log("Error: Order has no items");
      return false;
    }
    return true;
  }
  
  calculateTotal() {
    let total = 0;
    this.order.items.forEach(item =&gt; {
      total += item.price * item.quantity;
    });
    return total;
  }
  
  applyDiscounts(total) {
    if (this.order.customer === "vip") {
      total *= 0.9;
    }
    return total;
  }
}

class OrderSaver {
  constructor(order, total) {
    this.order = order;
    this.total = total;
  }
  
  save() {
    const db = new Database();
    db.connect();
    db.saveOrder(this.order, this.total);
  }
}

const order = new Order();
const processor = new OrderProcessor(order);

if (processor.validate()) {
  const total = processor.calculateTotal();
  const totalWithDiscounts = processor.applyDiscounts(total);
  const saver = new OrderSaver(order, totalWithDiscounts);
  saver.save();
}
</code></pre>
<p>在此示例中，<code>processOrder</code> 函数已拆分为遵循 SRP 的两个类：<code>OrderProcessor</code>和<code>OrderSaver</code>。</p>
<p><code>OrderProcessor</code> 类负责验证订单、计算总额和应用折扣，而 <code>OrderSaver</code> 类负责将订单保存到数据库。</p>
<p>这使得代码更易于理解、测试和维护，因为每个类都有明确的职责，并且可以在不影响其他类的情况下进行修改或替换。</p>
<h3 id="having-a-single-source-of-truth">拥有“单一事实来源”</h3>
<p>拥有“单一事实来源”意味着代码库中只有一个地方存储特定的数据或配置，代码中对它的任何其他引用都指向那个来源。 这很重要，因为它确保数据一致并避免重复和不一致。</p>
<p>这里有一个例子来说明这个概念。 假设我们有一个应用程序需要显示一个城市的当前天气状况。我们可以通过两种不同的方式实现此功能：</p>
<pre><code class="language-javascript">// 操作 1：没有“单一事实来源”

// file 1: weatherAPI.js
const apiKey = '12345abcde';

function getCurrentWeather(city) {
  return fetch(`https://api.weather.com/conditions/v1/${city}?apiKey=${apiKey}`)
    .then(response =&gt; response.json());
}

// file 2: weatherComponent.js
const apiKey = '12345abcde';

function displayCurrentWeather(city) {
  getCurrentWeather(city)
    .then(weatherData =&gt; {
      // display weatherData on the UI
    });
}
</code></pre>
<p>在此选项中，API 密钥在两个不同的文件中重复，使其更难维护和更新。如果我们需要更改 API 密钥，我们必须记住在两个地方都更新它。</p>
<pre><code class="language-javascript">// 操作 2：有“单一事实来源”

// file 1: weatherAPI.js
const apiKey = '12345abcde';

function getCurrentWeather(city) {
  return fetch(`https://api.weather.com/conditions/v1/${city}?apiKey=${apiKey}`)
    .then(response =&gt; response.json());
}

export { getCurrentWeather, apiKey };


// file 2: weatherComponent.js
import { getCurrentWeather } from './weatherAPI';

function displayCurrentWeather(city) {
  getCurrentWeather(city)
    .then(weatherData =&gt; {
      // display weatherData on the UI
    });
}
</code></pre>
<p>在此选项中，API 密钥存储在一个位置（在 <code>weatherAPI.js</code> 文件中）并导出以供其他模块使用。这可确保 API 密钥只有一个真实来源，并避免重复和不一致。</p>
<p>如果我们需要更新 API 密钥，我们可以在一个地方进行，所有其他使用它的模块将自动获得更新后的值。</p>
<h3 id="only-expose-and-consume-data-you-need">只暴露和使用你需要的数据</h3>
<p>编写整洁代码的一个重要原则是只公开和使用特定任务所需的信息。这有助于降低复杂性、提高效率并避免因使用不必要的数据而导致的错误。</p>
<p>当不需要的数据被暴露或消耗时，可能会导致性能问题并使代码更难以理解和维护。</p>
<p>假设您有一个具有多个属性的对象，但您只需要使用其中的几个。一种方法是在每次需要时引用对象和特定属性。 但这可能会变得冗长且容易出错，尤其是当对象嵌套很深时。一种更整洁、更高效的解决方案是使用对象解构来仅公开和使用您需要的信息。</p>
<pre><code class="language-javascript">// 原始对象
const user = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  age: 25,
  address: {
    street: '123 Main St',
    city: 'Anytown',
    state: 'CA',
    zip: '12345'
  }
};

// 只暴露和使用 name 和 email 属性
const { name, email } = user;

console.log(name); // 'Alice'
console.log(email); // 'alice@example.com'
</code></pre>
<h3 id="modularization">模块化</h3>
<p>模块化是编写整洁代码的基本概念。它指的是将大型复杂代码分解为更小、更易于管理的模块或功能的做法。 这使代码更易于理解、测试和维护。</p>
<p>使用模块化提供了几个好处，例如：</p>
<ol>
<li>复用性：模块可以在应用程序的不同部分或其他应用程序中重复使用，节省开发时间和精力。</li>
<li>封装：模块允许您隐藏函数或对象的内部细节，只将基本接口暴露给外界。这有助于减少代码不同部分之间的耦合并提高整体代码质量。</li>
<li>可扩展性：通过将大代码分解成更小的模块化片段，您可以轻松添加或删除功能，而不会影响整个代码库。</li>
</ol>
<p>下面是一段 JavaScript 代码示例，它执行一个简单的任务，一个不使用模块化，另一个实现模块化。</p>
<pre><code class="language-javascript">// 没有模块化
function calculatePrice(quantity, price, tax) {
  let subtotal = quantity * price;
  let total = subtotal + (subtotal * tax);
  return total;
}

// 没有模块化
let quantity = parseInt(prompt("Enter quantity: "));
let price = parseFloat(prompt("Enter price: "));
let tax = parseFloat(prompt("Enter tax rate: "));

let total = calculatePrice(quantity, price, tax);
console.log("Total: $" + total.toFixed(2));
</code></pre>
<p>在上面的示例中，<code>calculatePrice</code> 函数用于根据商品的数量、价格和税率计算商品的总价。 但是，此功能并未模块化，并且与用户输入和输出逻辑紧密耦合。这会使测试和维护变得困难。</p>
<p>现在，让我们看一下使用模块化的相同代码的示例：</p>
<pre><code class="language-javascript">// 模块化
function calculateSubtotal(quantity, price) {
  return quantity * price;
}

function calculateTotal(subtotal, tax) {
  return subtotal + (subtotal * tax);
}

// 模块化
let quantity = parseInt(prompt("Enter quantity: "));
let price = parseFloat(prompt("Enter price: "));
let tax = parseFloat(prompt("Enter tax rate: "));

let subtotal = calculateSubtotal(quantity, price);
let total = calculateTotal(subtotal, tax);
console.log("Total: $" + total.toFixed(2));
</code></pre>
<p>在上面的示例中，<code>calculatePrice</code> 函数被分解为两个较小的函数：<code>calculateSubtotal</code> 和 <code>calculateTotal</code>。 这些函数现在已经模块化，分别负责计算小计和总计。 这使代码更易于理解、测试和维护，也使其在应用程序的其他部分更易于重用。</p>
<p>模块化也可以指将单个代码文件分成许多较小的文件，这些文件之后会编译回单个（或更少的文件）的做法。 这种做法与我们刚才谈到的好处相同。</p>
<p>如果您想知道如何使用模块在 JavaScript 中实现这一点，<a href="https://chinese.freecodecamp.org/news/modules-in-javascript/">查看我的另一篇文章</a>。</p>
<h3 id="folder-structures">文件夹结构</h3>
<p>选择一个好的文件夹结构是编写整洁代码的重要部分。组织良好的项目结构有助于开发人员轻松查找和修改代码，降低代码复杂度，并提高项目的可扩展性和可维护性。</p>
<p>另一方面，糟糕的文件夹结构会使理解项目架构、浏览代码库变得困难，并导致混乱和错误。</p>
<p>以下是使用 React 项目作为示例的好的和坏的文件夹结构示例：</p>
<pre><code class="language-javascript">// 文件夹结构不规范
my-app/
├── App.js
├── index.js
├── components/
│   ├── Button.js
│   ├── Card.js
│   └── Navbar.js
├── containers/
│   ├── Home.js
│   ├── Login.js
│   └── Profile.js
├── pages/
│   ├── Home.js
│   ├── Login.js
│   └── Profile.js
└── utilities/
    ├── api.js
    └── helpers.js
</code></pre>
<p>在此示例中，项目结构是围绕文件类型组织的，例如组件、容器和页面。</p>
<p>但是这种方法会导致混淆和重复，因为不清楚哪些文件属于哪里。例如，<code>Home</code>组件存在于<code>containers</code>和<code>pages</code>文件夹中。它还可能使查找和修改代码变得困难，因为开发人员可能需要导航多个文件夹才能找到他们需要的代码。</p>
<pre><code class="language-javascript">// 规范的文件夹结构
my-app/
├── src/
│   ├── components/
│   │   ├── Button/
│   │   │   ├── Button.js
│   │   │   ├── Button.module.css
│   │   │   └── index.js
│   │   ├── Card/
│   │   │   ├── Card.js
│   │   │   ├── Card.module.css
│   │   │   └── index.js
│   │   └── Navbar/
│   │       ├── Navbar.js
│   │       ├── Navbar.module.css
│   │       └── index.js
│   ├── pages/
│   │   ├── Home/
│   │   │   ├── Home.js
│   │   │   ├── Home.module.css
│   │   │   └── index.js
│   │   ├── Login/
│   │   │   ├── Login.js
│   │   │   ├── Login.module.css
│   │   │   └── index.js
│   │   └── Profile/
│   │       ├── Profile.js
│   │       ├── Profile.module.css
│   │       └── index.js
│   ├── utils/
│   │   ├── api.js
│   │   └── helpers.js
│   ├── App.js
│   └── index.js
└── public/
    ├── index.html
    └── favicon.ico
</code></pre>
<p>在此示例中，项目结构是围绕组件、页面和实用程序等功能进行组织的。每个功能都有自己的文件夹，其中包含与该功能相关的所有文件。</p>
<p>这种方法使得查找和修改代码变得容易，因为与功能相关的所有文件都位于同一个文件夹中。它还减少了代码重复和复杂性，因为功能是分开的，并且它们的相关文件被组织在一起。</p>
<p>总的来说，一个好的文件夹结构应该围绕功能而不是文件类型来组织，并且应该便于查找和修改代码。 清晰且合乎逻辑的结构可以使项目更易于维护、理解和扩展，而混乱和不一致的结构会导致错误和混乱。</p>
<p>如果您有兴趣了解更多相关信息，[在本文中我写了关于软件架构的文章](/news/an-introduction-to-software-architecture-patterns/#different-folder -structures-to-know)我扩展了文件夹结构和您可以遵循的众所周知的模式的主题。</p>
<h3 id="documentation">文档化</h3>
<p>文档是编写整洁代码的重要组成部分。适当的文档不仅可以帮助编写代码的开发人员将来更好地理解代码，还可以让其他开发人员更容易阅读和理解代码库。当代码有良好的文档记录时，可以节省调试和维护代码的时间和精力。</p>
<p>在无法实施简单易懂的解决方案、业务逻辑相当复杂的情况以及必须与不熟悉代码库的人交互的情况下，文档化尤为重要。</p>
<p>记录代码的一种方法是使用注释。 注释可以提供上下文并解释代码的作用。 但重要的是要明智地使用注释，只在必要时评论并避免多余或不必要的注释。</p>
<p>另一种记录代码的方法是使用内联文档。 内联文档嵌入在代码本身中，可用于解释特定功能或代码片段的作用。内联文档通常与 <a href="https://jsdoc.app/">JSDoc</a> 等工具结合使用，它提供了在 JavaScript 中记录代码的标准。</p>
<p>像 Typescript 这样的工具还可以为我们的代码库提供自动文档，这非常有帮助。</p>
<p>如果您想了解更多关于 Typescript 的信息，我之前写了一篇<a href="https://www.freecodecamp.org/news/an-introduction-to-typescript/#otherfunctionalitiesoftypescript">初学者友好指南</a>。</p>
<p>最后，Swagger 和 Postman 等工具可用于记录 API，提供一种轻松理解如何与它们交互的方法</p>
<p>如果您有兴趣了解如何完全实施、测试、使用和记录 API，可以查看我最近为 <a href="https://www.freecodecamp.org/news/build-consume-and-document-a-rest-api/">REST API</a> 和 <a href="https://www.freecodecamp.org/news/building-consuming-and-documenting-a-graphql-api/">GraphQL API</a> 写的两份指南。</p>
<h2 id="wrapping-up">总结</h2>
<p>最后，还是像之前那样，我希望你喜欢这篇文章并学到新东西。</p>
<p>如果需要，你也可以在 <a href="https://www.linkedin.com/in/germancocca/">LinkedIn</a> 或 <a href="https://twitter.com/CoccaGerman">Twitter</a> 上关注我。下篇文章见！</p>
<figure class="kg-card kg0card0image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/05/giphy.gif " class="kg-image" width="600" height="400" alt="giphy" loading="lazy"></figure><!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 正则表达式元字符 \d 代表什么意思 ]]>
                </title>
                <description>
                    <![CDATA[ 正则表达式，也称为 RegEx 或 RegExp，是用于匹配整个字符串或字符串特定部分的定义模式。该字符串包括所有字符，无论是字母、符号还是数字都行。 在这篇文章中，我们将会讲解正则符号\d，这个符号可以匹配数字。 我们将讨论什么内容  * 正则表达式中的 \d 是什么  * 如何使用 \d 元字符  * 数字字符集 [0-9] 和元字符 \d 之间的区别是什么  * 总结 正则表达式中的 `\d` 是什么 \d  在正则表达式中不只是一个“字符”，它是用于匹配字符串的“元字符”之一。 按照定义，元字符是在定义匹配字符串模式时具有特殊含义的字符。 所以，\d  是一个可以匹配 0 到 9 中的任何数字的元字符。你可以使用它来匹配数字或数字集，例如电话号码、编号等。 除了\d，正则表达式中还有很多元字符，比如下面的：  * \w  匹配所有单词字符（小写字母a到z、大写字母A到Z、数字0到9和下划线_）  * \D  ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/what-does-d-mean-in-regex/</link>
                <guid isPermaLink="false">640aa03ff2e3690630c997be</guid>
                
                    <category>
                        <![CDATA[ 正则表达式 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ songyp0505 ]]>
                </dc:creator>
                <pubDate>Fri, 10 Mar 2023 03:16:20 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/03/start-graph--2-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/what-does-d-mean-in-regex/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Regular Expression Metacharacters - What Does \d Mean in RegEx?</a>
      </p><!--kg-card-begin: markdown--><p>正则表达式，也称为 RegEx 或 RegExp，是用于匹配整个字符串或字符串特定部分的定义模式。该字符串包括所有字符，无论是字母、符号还是数字都行。</p>
<p>在这篇文章中，我们将会讲解正则符号<code>\d</code>，这个符号可以匹配数字。</p>
<h2 id="">我们将讨论什么内容</h2>
<ul>
<li><a href="#whatisdinregex">正则表达式中的 <code>\d</code> 是什么</a></li>
<li><a href="#howtousethedmetacharacter">如何使用 <code>\d</code> 元字符</a></li>
<li><a href="#whatisthedifferencebetweenthedigitcharacterset09anddmetacharacter">数字字符集 <code>[0-9]</code> 和元字符 <code>\d</code> 之间的区别是什么</a></li>
<li><a href="#conclusion">总结</a></li>
</ul>
<h2 id="whatisdinregex">正则表达式中的 `\d` 是什么</h2>
<p><code>\d</code> 在正则表达式中不只是一个“字符”，它是用于匹配字符串的“元字符”之一。</p>
<p>按照定义，元字符是在定义匹配字符串模式时具有特殊含义的字符。</p>
<p>所以，<strong><code>\d</code> 是一个可以匹配 0 到 9 中的任何数字的元字符</strong>。你可以使用它来匹配数字或数字集，例如电话号码、编号等。</p>
<p>除了<code>\d</code>，正则表达式中还有很多元字符，比如下面的：</p>
<ul>
<li><code>\w</code> 匹配所有单词字符（小写字母a到z、大写字母A到Z、数字0到9和下划线_）</li>
<li><code>\D</code> 匹配所有非数字字符。它是 <code>\d</code> 的补集</li>
<li><code>\W</code> 匹配所有非单词字符</li>
<li><code>\s</code> 匹配所有空格字符，包括空格、制表符和回车符等。</li>
</ul>
<h2 id="howtousethedmetacharacter">如何使用 `\d` 元字符</h2>
<p>下面我们一起看看怎样用<code>\d</code>元字符匹配数字。</p>
<p>第一个例子是匹配<code>7253289593</code>这个数字ID。</p>
<p>这是一个十位数，要想匹配它，你可以重复写十次<code>\d</code>元字符：</p>
<figure class="kg-card kg-card-image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/03/Screenshot-2023-03-02-at-12.10.33.png" alt="Screenshot-2023-03-02-at-12.10.33" class="kg-image" width="600" height="400" loading="lazy"><figcaption></figcaption></figure>
<p>或者也可以写一次<code>\d</code>，然后让它自己重复十次：</p>
<figure class="kg-card kg-card-image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/03/Screenshot-2023-03-02-at-12.11.39.png" alt="Screenshot-2023-03-02-at-12.11.39" class="kg-image" width="600" height="400" loading="lazy"><figcaption></figcaption></figure>
<p>你也可以匹配手机号，以美国手机号为例：</p>
<figure class="kg-card kg-card-image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/03/Screenshot-2023-03-02-at-12.16.25.png" alt="Screenshot-2023-03-02-at-12.16.25" class="kg-image" width="600" height="400" loading="lazy"><figcaption></figcaption></figure>
<p>或者这样匹配尼日利亚手机号码：</p>
<figure class="kg-card kg-card-image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/03/Screenshot-2023-03-02-at-12.17.33.png" alt="Screenshot-2023-03-02-at-12.17.33" class="kg-image" width="600" height="400" loading="lazy"><figcaption></figcaption></figure>
<p><code>\d</code>元字符同样可以在 JavaScript 中使用：</p>
<pre><code class="language-js">// 测试一个数字ID
let id = '7253289593';
let regex1 = /\d{10}/g;

console.log(regex1.test(id)); // true

// 测试美国手机号码
let UsPhoneNum = '(123) 456-7890';
let regex2 = /\(\d{3}\)\s\d{3}-\d{4}/g;

console.log(regex2.test(UsPhoneNum)); // true

// 测试尼日利亚手机号码
let naijaPhoneNum = '08133333333';
let regex3 = /\d{11}/g;

console.log(regex3.test(naijaPhoneNum)); // true
</code></pre>
<h2 id="whatisthedifferencebetweenthedigitcharacterset09anddmetacharacter">数字字符集 `[0-9]` 和元字符 `\d` 之间的区别是什么</h2>
<p><code>[0-9]</code> 字符集和 <code>\d</code> 元字符之间没有什么很大的区别</p>
<p>一个可能存在的区别，就是某些正则表达式的语法不支持 <code>\d</code> 元字符，但支持 <code>[0-9]</code> 字符集。例如，grep 的正则表达式不支持 <code>\d</code> 元字符。</p>
<p>所以如果你在正则表达式中使用 <code>[0-9]</code> 代替 <code>\d</code>，你仍然能够进行字符的匹配。</p>
<p>例如，我们可以使用 <code>[0-9]</code> 而不是 <code>\d</code> 来匹配前面例子中的数字ID：</p>
<figure class="kg-card kg-card-image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/03/Screenshot-2023-03-02-at-12.28.17.png" alt="Screenshot-2023-03-02-at-12.28.17" class="kg-image" width="600" height="400" loading="lazy"><figcaption></figcaption></figure>
<p>可以看到这样依然可以匹配成功，因此，<code>[0-9]</code> 字符集和 <code>\d</code> 元字符之间没有任何差别。</p>
<h2 id="conclusion">总结</h2>
<p>这篇文章介绍了 <code>\d</code> 正则表达式元字符是什么，以及如何使用它。我们还看了一些例子，并将<code>\d</code> 与数字字符集 <code>[0-9]</code> 进行了比较，以便了解它们的工作原理和区别。</p>
<p>感谢阅读。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 为什么 Python 适合初学者？如何开始学习 Python？ ]]>
                </title>
                <description>
                    <![CDATA[ 与其他编程语言相比较，Python有着更为简单的语法，所以学习Python对于很多刚进入编程领域的初学者来说是一个很好的选择。 Python还是一门应用领域很广的编程语言，这也就意味着你可以在各种各样的工作和领域中使用它。 跟很多刚进入技术领域的新手一样，我在寻找如何正确学习编程的道路上也遇到了很多困难。我只能来来回地调整学习方式，以找到一种正确且让自己感到舒服的学习方法。 刚开始进入技术领域时，我学习的是一些Web开发的基础知识，比如HTML、CSS和一点Javascript的知识。后开我开始试着学习不同的编程语言，以期找到最适合我的编程语言。 我学过一点C语言和Java，甚至还学过Pascal（主要是学校在大一的时候开设了相关的课程）。 这些都发生在我学习Python之前，但是尝试了Python并且学习了一部分内容以后，我决定将它作为我的主力编程语言。 接着读下去，你就可以更好地理解为什么Python是初学者最容易学习的编程语言，并且还有它的好处，以及帮助你入门的资源和技巧。 为什么学习Python 不论你是一个初学者还是已经学过几门编程语言，我们都不得不承认每新学一 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/why-learn-python-and-how-to-get-started/</link>
                <guid isPermaLink="false">6405c369f2e3690630c9950e</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ songyp0505 ]]>
                </dc:creator>
                <pubDate>Thu, 02 Mar 2023 10:00:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/03/OOP--2-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/why-learn-python-and-how-to-get-started/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Why Python is Good for Beginners – and How to Start Learning It</a>
      </p><!--kg-card-begin: markdown--><p>与其他编程语言相比较，Python有着更为简单的语法，所以学习Python对于很多刚进入编程领域的初学者来说是一个很好的选择。</p>
<p>Python还是一门应用领域很广的编程语言，这也就意味着你可以在各种各样的工作和领域中使用它。</p>
<p>跟很多刚进入技术领域的新手一样，我在寻找如何正确学习编程的道路上也遇到了很多困难。我只能来来回地调整学习方式，以找到一种正确且让自己感到舒服的学习方法。</p>
<p>刚开始进入技术领域时，我学习的是一些Web开发的基础知识，比如HTML、CSS和一点Javascript的知识。后开我开始试着学习不同的编程语言，以期找到最适合我的编程语言。</p>
<p>我学过一点C语言和Java，甚至还学过Pascal（主要是学校在大一的时候开设了相关的课程）。</p>
<p>这些都发生在我学习Python之前，但是尝试了Python并且学习了一部分内容以后，我决定将它作为我的主力编程语言。</p>
<p>接着读下去，你就可以更好地理解为什么Python是初学者最容易学习的编程语言，并且还有它的好处，以及帮助你入门的资源和技巧。</p>
<h2 id="python">为什么学习Python</h2>
<p>不论你是一个初学者还是已经学过几门编程语言，我们都不得不承认每新学一门编程语言都是一项新的挑战。当你没有正确的资料和指导时更是如此。但是如果有了正确的帮助，你可以把这个挑战变成一个值得享受的经历。</p>
<p>Guido van Rossum在1989年12月3日正式创造出了Python，他有着这样一句设计理念：<strong>用一种方法，最好是只有一种方法来做一件事</strong>。</p>
<p>Python是一种解释型、高级、通用编程语言。作为一种编程语言，Python使用大部分英文关键字，因此与其他语言相比，它具有更少的例外和特殊情况。</p>
<p>现在我们来一起看看你可能想要学习Python的一些原因。</p>
<h3 id="python">Python简单的语法</h3>
<p>Python是一门语法简单的编程语言，这也是为什么它被很多刚进入技术领域的初学者作为第一语言。我理解仅凭这一点可能还不够激励人，但是相信我——经过透彻的讲解，你也会被说服的。</p>
<p>跟其他语言相比较，Python可以用极其简单的语法结构，所以它可以使用较少的代码来完成一些特定的工作。这就可以使得初学者在短时间内学习代码基础和理解编程语言。</p>
<p>除此以外，Python还是一门解释型的编程语言，这就意味着你不需要使用编译器去写代码和运行代码</p>
<p>咱们来举一个例子，如何使用Python、Java、C++来分别输出“Hello World”语句：</p>
<p>这里是Python输出“Hello World”的方式</p>
<pre><code class="language-python">print("Hello World)
</code></pre>
<p>这是Java：</p>
<pre><code class="language-java">public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, world!");
  }
}
</code></pre>
<p>然后这是C++：</p>
<pre><code class="language-C++">#include &lt;iostream&gt;
using namespace std;

int main() {
  cout &lt;&lt; "Hello, world!" &lt;&lt; endl;
  return 0;
}
</code></pre>
<p>看完上面的代码片段就可以发现，相比于其他语言，Python有着最简要和最简单的语法结构。</p>
<h3 id="python">Python的多功能性</h3>
<p>这一条也是Python能成为一个非常流行的编程语言的原因。不论你处在什么领域，什么工作，你都可能在个人项目或工作中使用Python。</p>
<p>例如：</p>
<ul>
<li><strong>工业应用</strong> – Python被广泛用于各种领域，包括网络开发、数据科学、机器学习、人工智能、科学计算等等。最近，它的影响在人工智能和ML领域得到了真正的体现，使其成为一种高需求的技能。</li>
<li><strong>框架和代码库</strong> – Python有着大量的框架和代码库，所有这些都使它在创建应用程序时更容易使用，并能帮助你在更短的时间内开发项目。</li>
<li><strong>兼容性</strong> – Python是一种跨平台语言，这意味着在一个操作系统中编写的代码可以在另一个操作系统中执行而没有任何问题。这对于从事网络项目的开发人员来说非常有用，他们希望在多种设备上测试项目的功能。</li>
<li><strong>开源</strong> – 我喜欢Python的其中一个原因是它有一个非常活跃的开源社区，这让那些想要学习和为语言做出贡献的人有很多资源可以使用，而且这也让Python成为了一个不断进化、越来越好的语言。</li>
</ul>
<h2 id="python">作为一个新的开发者，学习Python的好处</h2>
<p>作为一个进入技术领域的初学者，学习Python会有很多好处。除了我们刚才讨论的内容外，这里还有一些：</p>
<h3 id="">在各行业的受欢迎程度</h3>
<p>Python被用于许多行业，如网络开发、数据科学、人工智能、金融、教育、研究、安全等等。</p>
<p>拥有Python知识和技能可以为各种职业机会打开大门，如软件工程师、DevOps工程师、数据科学家、研究分析师等，使你在就业市场上获得优势。</p>
<h3 id="">实用性</h3>
<p>如果你对Python得心应手，你将能够很好地利用这些技能，解决现实世界的问题。</p>
<p>凭借其广泛的库和框架，你将能够开发出能够帮助预测结果、可视化数据和理解不同趋势的应用程序和系统，Python在帮助你实现任务自动化方面也非常出色。</p>
<h3 id="">社区和支持</h3>
<p>Python社区提供了许多资源来帮助第一次学习的人。这些资源包括<a href="https://www.freecodecamp.org/news/the-python-handbook/">在线教程</a>、论坛、编码挑战、<a href="https://www.freecodecamp.org/news/learn-python-free-python-courses-for-beginners/">课程</a>等等。</p>
<p>freeCodeCamp也提供了一个Python课程，<a href="https://www.freecodecamp.org/learn/scientific-computing-with-python/">你可以在这里查看</a>。</p>
<p>这类支持可以帮助新人在尝试建立自己的职业生涯时获得动力和参与感。</p>
<h2 id="python">学习Python的小贴士</h2>
<p>尽管Python是一个很好的初学者编程语言，但这并不意味着它容易上手。你需要采取必要的措施并找到正确的工具来帮助你在学习道路上前行。</p>
<p>首先，一个正确的资源是很好的开始，一个路线图也会很有帮助。</p>
<p>这一点不太需要担心，<a href="https://roadmap.sh/python">roadmap.sh</a>是一个开源项目，它有一个专门为不同职业道路制作的路线图和指导，其中就包括Python。</p>
<p>当我们了解了如何学习以后，我们现在就可以着手于帮助你实现学习目标的资源了。</p>
<h3 id="python">学习Python的资源</h3>
<p>Python的学习资源有好多好多，其中有免费的也有付费的。</p>
<ul>
<li>循序渐进的课程平台有：Codecademy、W3Schools、Pythontutorial.net、Python.org、谷歌的Python Class，以及 Educative。</li>
<li>全程指导课程平台有：freeCodeCamp、Udemy、Coursera、edX、Programiz，微软的Introduction to Python Course。</li>
</ul>
<p>还有一件需要注意的事情：写代码对于完全掌握Python是不可或缺的一步，熟能生巧。</p>
<p>构建项目是一个提升理解代码能力的好方法，先从一些小项目开始，循序渐进，逐渐增加到更复杂的项目。</p>
<p>为了帮助你开始，这里有一个清单你可以看看：<a href="https://www.freecodecamp.org/news/python-projects-for-beginners/">Python项目清单</a></p>
<p>也可以尝试在公开环境中学习，比如参加在线社区，或者在社交媒体上分享你知道的东西以及回答问题。例如，你可以在推特上参加#100DaysofCode活动，或参加其他挑战，同时你也会得到你提出的问题的答案。</p>
<p>记住，在学习编程时，先理解基本概念，例如数据类型、函数和控制结构，然后再深入学习高级主题，这非常重要。</p>
<p>作为一名有志成为Python开发者的学习者，学会如何调试你的代码是非常关键的，这对你成功成为开发者非常重要。所以，确保你在早期学习并掌握这些技巧。</p>
<h3 id="">如何保持积极的态度</h3>
<p>老话说得好：</p>
<blockquote>
<p>活到老学到老。</p>
</blockquote>
<p>每天都会有新东西值得你去学习，保持积极的态度可以帮助你走得更远，这里是一些让你保持积极的一些建议。</p>
<ul>
<li>制定可以实现的目标 – 设定实现目标非常重要，因为它可以帮助你保持动力。例如，你可以制定每天编写一个简单的程序来实现某些任务的目标。</li>
<li>在实现过程中庆祝小胜利 – 这会让你保持鼓励和渴望获得更多的胜利。</li>
<li>寻找导师、学习伙伴或加入学习小组 – 这将帮助你向他人学习，并且记得在必要时休息一下。</li>
<li>最后，记住学习需要时间和耐心。如果你不理解某个概念，不要灰心丧气，继续练习，你一定会成功的。</li>
</ul>
<h2 id="">总结</h2>
<p>在这个指南中，我重点强调了一些原因，说明为什么你应该考虑把Python作为你的第一门编程语言。你也了解了Python的不同应用以及你可以用它做什么。</p>
<p>为了帮助你开始学习，我强烈建议你充分利用所有可用资源，包括这里列出的资源以及其他在线资源（例如书籍），以更好地理解这门语言。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何爬取百度资讯页并制作一张词云图作为论文插图 ]]>
                </title>
                <description>
                    <![CDATA[ 论文中的插图在一定程度上影响着评审人对于论文质量的评价，并且人文社科类论文通常需要进行大量文字分析，词云图可以通过高亮或放大词频数较高的关键词，来突出显示所选文字材料中的重点。本文以指定搜索词的百度结果页为例，制作一张词云图。 1.导入库 import requests,lxml from bs4 import BeautifulSoup import csv,jieba,wordcloud,paddle from wordcloud import WordCloud, ImageColorGenerator import numpy as np import matplotlib.pyplot as plt from PIL import Image 这里用到的库主要分为三个部分： 1. 对百度结果进行爬虫操作，主要用到 requests、lxml、BeautifulSoup 2.对爬虫结果进行分词，主要用到 csv、jieba、paddle 3.对分词结果进行词云绘制，主要用到 wordcloud、numpy、matplotlib、PIL 如果不确定自己设备里是否有上述的库文 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/wordcloud/</link>
                <guid isPermaLink="false">63bd55b681727e0763146207</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ songyp0505 ]]>
                </dc:creator>
                <pubDate>Thu, 02 Feb 2023 03:32:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/02/test-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>论文中的插图在一定程度上影响着评审人对于论文质量的评价，并且人文社科类论文通常需要进行大量文字分析，词云图可以通过高亮或放大词频数较高的关键词，来突出显示所选文字材料中的重点。本文以指定搜索词的百度结果页为例，制作一张词云图。</p><h2 id="1-">1.导入库</h2><pre><code class="language-Python">import requests,lxml
from bs4 import BeautifulSoup
import csv,jieba,wordcloud,paddle
from wordcloud import WordCloud, ImageColorGenerator
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image</code></pre><p>这里用到的库主要分为三个部分：<br>1. 对百度结果进行爬虫操作，主要用到 requests、lxml、BeautifulSoup<br>2.对爬虫结果进行分词，主要用到 csv、jieba、paddle<br>3.对分词结果进行词云绘制，主要用到 wordcloud、numpy、matplotlib、PIL</p><p>如果不确定自己设备里是否有上述的库文件，可以在命令行中执行安装操作，有的话会提示已安装，没有的话会自动安装：</p><pre><code class="language-python">pip install 目标库</code></pre><h2 id="2-">2.网址处理</h2><p>本文用到的搜索信息是“基层治理”和“两邻理论”，在网址处理环节，需要先构建出每一页的链接：</p><pre><code class="language-python">base_url = " https://www.baidu.com/s?tn=news&amp;ie=utf-8&amp;word=两邻+\"基层\"&amp;pn="
urls = [base_url+str(i) for i in range(0,291,10)]
print(urls)</code></pre><p>out:</p><pre><code class="language-bash">[' https://www.baidu.com/s?tn=news&amp;ie=utf-8&amp;word=%E4%B8%A4%E9%82%BB+%22%E5%9F%BA%E5%B1%82%22&amp;pn=0'
...
' https://www.baidu.com/s?tn=news&amp;ie=utf-8&amp;word=%E4%B8%A4%E9%82%BB+%22%E5%9F%BA%E5%B1%82%22&amp;pn=290']</code></pre><p>首先是构建一个 <code>base_url</code>，<code>tn</code> 参数设置为 <code>news</code> 代表“资讯”栏目，<code>word</code> 代表搜索词，<code>pn</code> 参数代表页数，但是并不能直接序列设置。通过网址观察可以发现，第一页的 <code>pn</code> 为 0，第二页的 <code>pn</code> 为 10，所以通过range函数构造一个序列，从 0 开始，步长为 10，总共爬取 30 页。</p><h2 id="3-">3.文章网址汇总</h2><p>因为网址数量并不大，所以直接以字典加列表的方式存储：</p><pre><code class="language-python">results =[]
for url in urls:
    res = requests.get(url,headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'})
    soup = BeautifulSoup(res.text,'lxml')
    for i in soup.find_all("h3"):
        try:
            result = {
                "title":i.a["aria-label"][3:],
                "url":i.a["href"]
            }
            results.append(result)
            print(result)
        except:
            print(url)</code></pre><p>out:</p><pre><code class="language-bash">{'title': '沈阳市和平区:以“百千万”党员志愿服务践行“两邻”理念_综合...', 'url': 'https://lszhdj.lndj.gov.cn/portaluploads/html/site-5/info/42600.html'}
{'title': '浑南区五三街道召开“两邻学堂”启动仪式 暨“奋斗者..._手机网易网', 'url': 'https://3g.163.com/local/article/HDAIO22104229BRM.html'}
....
{'title': '【两邻生活】党建引领 锚定难点 沈北新区散体楼合围治理破“四难...', 'url': 'http://society.sohu.com/a/547395443_121123868'}
{'title': '“两邻”理念推动基层治理 铁西区物业管理实现新突破(一):党建...', 'url': 'http://liaomedia.net/index.php?m=Index&amp;c=Content&amp;a=index&amp;cid=51&amp;aid=2785'}</code></pre><p>第一步主要使用 requests 库的 <code>get</code> 方法，获取网页内容，然后通过 BeautifulSoup 的网页解析功能，将网页以 lxml 形式格式化，以方便网页子节点的获取。</p><p>第二步主要是在网页中获取文章网址，通过网页源代码信息可以发现，文章链接在 h3 标签下，所以可以通过 <code>find_all</code> 方法获取所有 <code>h3</code> 节点，然后通过 <code>for</code> 循环，读取每个 <code>h3</code> 标签中的信息并处理。</p><pre><code class="language-html">&lt;h3 class="news-title_1YtI1 "&gt;
    &lt;a href="https://www.sohu.com/a/551186753_121123868" 
       aria-label="标题：【两邻生活】道义街道:“暖心三部曲”奏响“为民服务主旋律..."&gt;
        道义街道:“暖心三部曲”奏响“为民服务主旋律...
    &lt;/a&gt;
&lt;/h3&gt;</code></pre><p>观察 <code>h3</code> 标签下的 <code>a</code> 标签，新闻标题存在于 <code>aria-label</code> 标签下，新闻链接存在于 <code>href</code> 标签下，所以在 <code>result</code> 字典里，直接读取 <code>a</code> 标签下的上述两个属性即可。</p><p>第三步把存有每条新闻的 <code>result</code> 字典放进 <code>results</code> 列表中即可。</p><h2 id="4-"><strong>4.确定每个网站的新闻数量</strong></h2><p>首先需要确定链接中的网站：</p><pre><code class="language-python">websites=[]
for i in results:
    websites.append(i["url"].split('/')[2])</code></pre><p>对链接进行分词，网址结构为 <strong>https://www.x.x/x </strong>，以 “/” 为分词，第三个元素为主网址，python 列表从 0 开始，所以第三个元素的序列为 2。将每篇文章的主网址放进 <code>websites</code> 列表中。</p><pre><code class="language-python">from collections import Counter
counter = Counter(websites)
print(counter)</code></pre><p>out:</p><pre><code class="language-python">Counter({'baijiahao.baidu.com': 40, 'mp.weixin.qq.com': 40, 'www.sohu.com': 22, 
'weibo.com': 15, 'view.inews.qq.com': 15, 'news.syd.com.cn': 14, 'www.lndj.gov.cn': 10, '3g.163.com': 10, 'new.qq.com': 8, 'lszhdj.lndj.gov.cn': 8, 'xw.qq.com': 7, 'finance.sina.com.cn': 5, 'www.fx361.com': 4, 'www.meipian.cn': 4, 'liaoning.news.163.com': 4, 'www.shenyang.gov.cn': 4, 'www.163.com': 4, 'k.sina.com.cn': 3, 'society.sohu.com': 3, 'www.ln.chinanews.com.cn': 3, 'liaoning.nen.com.cn': 3, 'liaomedia.net': 2, 'finance.sina.cn': 2, 'neunews.neu.edu.cn': 2, 'mzj.shenyang.gov.cn': 2, 'ln.people.com.cn': 2, 'www.doc88.com': 2, 'cpu.baidu.com': 2, 'www.mca.gov.cn': 1, 'djyj.12371.cn': 1, 'rmh.pdnews.cn': 1, 'www.rmlt.com.cn': 1, 'heping.nen.com.cn': 1, 'www.syyh.gov.cn': 1, 'www.qlwb.com.cn': 1, 'card.weibo.com': 1, 'www.renrendoc.com': 1, 'wap.eastmoney.com': 1, 'gov.sohu.com': 1, 'www.xhzaixian.cn': 1, 'app.myzaker.com': 1, 'www.sinatec.ln.cn': 1, 'www.hunnan.gov.cn': 1, 'www.12371.cn': 1, 'ex.chinadaily.com.cn': 1, 'news.lnd.com.cn': 1, 'www.xyshjj.cn': 1, 'mini.eastday.com': 1, 'k.sina.cn': 1, 'www.360doc.com': 1, 'www.lnminjin.gov.cn': 1, 'news.sohu.com': 1, 'www.zhangqiaokeyan.com': 1, 'www.spp.gov.cn': 1, 'www.chinanews.com': 1, 'cj.sina.cn': 1, 'm.chinanews.com': 1, 'news.cnhubei.com': 1, 'shenyang.creb.com.cn': 1, 'zhuanlan.zhihu.com': 1, 'xysy.shenyang.gov.cn': 1, 'finance.nen.com.cn': 1, 'dy.163.com': 1, 'ln.cri.cn': 1, 'www.chinanews.com.cn': 1})</code></pre><p>然后对网站进行计数，这里用到的是 <code>Counter</code>，可以直接将列表中的元素分组计数。可以发现新闻数量最多的网站分别是“百度百家号”、“微信公众号”、“搜狐新闻”、“微博”，这里选用前三个网站，具体实践过程可以根据不同网站的新闻数量占比酌情选择。</p><h2 id="5-"><strong>5.为每个网站编写规则</strong></h2><pre><code class="language-python">def get_website_info(url):
    result = []
    p =[]
    Aresult = []
    website=url.split('/')[2]
    target_web = ['baijiahao.baidu.com','new.qq.com','mp.weixin.qq.com','news.sohu.com','www.sohu.com']
    try:
        if website in target_web:
            res = requests.get(url,headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70'})
            soup = BeautifulSoup(res.text,'lxml')
            if website == "baijiahao.baidu.com": 
                p = soup.find_all("p")
            elif website == "new.qq.com":
                p = soup.find_all(class_="one-p")
            elif website == "mp.weixin.qq.com":
                p = soup.find("div", {"id": "js_content"})
            elif website == "news.sohu.com" or "www.sohu.com":
                p = soup.find_all("article",class_="article")
            for i in p:
                    if i.text != "":
                        result.append(i.text)
            Aresult = "".join(result)
    except:
        return result
    return Aresult</code></pre><p>由于抓取的内容是文字信息，且所在的网站无明显反爬策略，所以只需要找出文章所在的位置即可，方法跟第三小节中的一样，只需要找出相关的 html 标签即可。</p><p>百家号的文章存在于p标签内；腾讯新闻的文章存在于 class 为 <code>one-p</code> 的 <code>p</code> 标签内；微信公众号的文章存在于 <code>id</code> 为 <code>js_content</code> 的 <code>div</code> 标签内；搜狐有两个相关的网站，但是网页结构都是一样的，文章存在于 class 为 <code>article</code> 的 <code>p</code> 标签内。</p><p>此处只指定了数量排名靠前的网站的抓取策略，其余的网站会自动过滤并返回空信息。</p><h2 id="6-csv-">6.将所有文章写入 csv 文件中</h2><pre><code class="language-python">countt = 0
with open("webinfo.csv","w",newline="",encoding="utf-8-sig") as f:
    writer = csv.writer(f)
    writer.writerow(["title","url","content"])
    for i in results:
        c = get_website_info(i["url"])
        time.sleep(2)
        if c != "":
            content = c
            w = {
                "title":i["title"],
                "url":i["url"],
                "content":c
            }
            writer.writerow([w["title"],w["url"],w["content"]])
            countt += 1
        if countt % 10 == 0:
            print("已完成{}条".format(countt))
f.close()</code></pre><p>out:</p><pre><code class="language-bash">'已完成10条'
'已完成20条'
'已完成30条'
....
'已完成270条'</code></pre><p>这一步是为了把文章内容保存在文件中，避免下一步绘制词云时操作不当丢失变量信息。</p><p><code>countt</code> 变量是用来计算已经写入了多少条文章，每写入一条就 +1。写入 csv 文件时，用的 <code>csv.writer</code>，以及 <code>writer.writerow</code>，前者是用来生成一个可写入对象，后者可以将数据按行写入文件。</p><p>在写入数据时，先把标题、链接和新闻内容合并成字典，然后以字典的形式按行写入，这样就能把整体的新闻数据以符合数据表的形式存储，如果后续对此数据表有可视化操作或者其他自定义操作的话，会更加的方便。</p><p>当然也可以直接把所有新闻按顺序写入，不按照这种结构化的数据处理，即：</p><pre><code class="language-python">countt = 0
with open("webinfo.csv","w",newline="",encoding="utf-8-sig") as f:
    writer = csv.writer(f)
#   writer.writerow(["title","url","content"])
    for i in results:
        c = get_website_info(i["url"])
        time.sleep(2)
        if c != "":
#           content = c
#           w = {
#               "title":i["title"],
#               "url":i["url"],
#               "content":c
#           }
#           writer.writerow([w["title"],w["url"],w["content"]])
			writer.writerow(c)
            countt += 1
        if countt % 10 == 0:
            print("已完成{}条".format(countt))
f.close()</code></pre><p>其中 <code>#</code> 代表注释，即运行时不执行此行代码。</p><p>写入数据时，<code>open</code> 方法里的 <code>w</code> 参数指的是写入，纯写入，即打开文件后，不管文件里之前有没有数据，都会从 0 开始写入数据。这里因为只写入一次，所以用的 <code>w</code> 模式，如果需要多次写入，可以用 <code>a</code>，即“追加写入”模式。</p><p>中间的 <code>time.sleep(2)</code> 指的是在此处暂停 2 秒，因为程序运行速度很快，<code>get_website_info</code> 方法的用途是爬取网页内容，如果中间不进行停止的话很容易会被网站判定为爬虫程序，进而阻止数据获取，所以需要在每爬完一个网站后暂停 2 秒，这个时间和位置可以根据实际情况自己调整。</p><h2 id="7-">7.分词处理</h2><pre><code class="language-python">#读取 webinfo.csv
p_list = []
with open("webinfo.csv","r",encoding="utf-8-sig") as f:
    reader = csv.reader(f)
    for i in reader:
        if i[0] == "title":
            continue
        else:
            p_list.append(i[2])
f.close()

#将文章分词
p_list_2 = []
paddle.enable_static()
jieba.enable_paddle()
for i in p_list:
    seg_list = jieba.cut(i, cut_all=False)#精确模式，即尽可能保留名词
    p_list_2.append(" ".join(seg_list))

#读取停用词并删除
with open("baidu_stopwords.txt","r",encoding="utf-8-sig") as stopwords:
    stop_words = [i.strip() for i in stopwords.readlines()]
    data_1 = ''
    for i in p_list_2:
        for j in i:
            if j not in stop_words:
                data_1 += j</code></pre><p>首先需要读取上一小节的 csv 文件，在 <code>open</code> 方法中，之前写入时用的是 <code>w</code>，这里需要用到 <code>r</code>，即 read。由于上一节在写入文件时候，是结构化写入，所以在读取的时候，也需要层层读取。第一步用 <code>csv.reader</code> 生成一个可读取的对象，第二步开始读取上述 csv 文件，先省略第一行，从第二行开始读取，因为每一行的结构都是标题、网址、文章内容，所以读的时候只需要读每一行的第 3 个元素就行了。然后把所有文章添加到一个列表中。</p><p>然后需要对所有文章进行分词，用到的是 jieba 库，在用 jieba 库之前，需要开启 paddle，即 <code>paddle.enable_static()</code>，然后在 jieba 中启用 paddle。分词时的主要操作，就是把一段文本分割成单个的词汇，即 <code>jieba.cut</code>，然后把分词后的内容汇总在一起，这里都汇总在了 <code>p_list_2</code> 里，查看一下 <code>p_list_2</code> 的内容就能发现分词结果了。</p><pre><code class="language-python">[in ] print(p_list_2)
[out] 
['社会 治理 是 在 执政党 领导 下 由 政府 组织 主导 、 吸纳 社会 组织 等 多方面 治理 主体 参与 、 对 社会 公共事务 进行 的 治理 活动 ， 是 党 在 治国 理政 理念 升华 后 对 社会 建设 提出 的 基本 要求 。 '
....
'强调 社会 治理 参与 主体 、 方法 、 路径 等 在 智能化 技术 支撑 下 的 实现 效果 ,  “ 两邻 ” 理念 下  ， 从 智能化 基层 社会 治理 建设 的 基本 内容 要求 入手 ， 在 规划 建设 、 制度 建设 、 资源 建设 、 开放 应用 、 平台 建设 、 人才 建设']</code></pre><p>最后一步需要进行停用词的删除，从上述的分解结果可以发现有很多无意义的虚词，如“是”、“由”、“上”、“下”、“的”、“在”等等，所以需要进行停用词删除，<a href="https://github.com/goto456/stopwords">常用的停用词库</a>有百度、四川大学、哈工大等，这里使用的是<a href="https://github.com/goto456/stopwords/blob/master/baidu_stopwords.txt">百度停用词表</a>。去除停用词的主要逻辑就是从已分词的列表中剔除掉停用词表中的词。剔除后我们再输出一下 <code>data_1</code> 就可以发现不同之处了：</p><pre><code class="language-bash">[out]
['社会 治理   执政党 领导 下  政府 组织 主导 、 吸纳 社会 组织  方面 治理 主体 参 、  社会 公共事务 进行  治理 活动 ，  党  治国 理政 理念 升华 后  社会 建设 提出  基 求 。 
....
强调 社会 治理 参 主体 、 方法 、 路径   智化 技术 支撑 下  实现 效果 , 两邻  理念 下，  智化 基层 社会 治理 建设  基 内容 求 入手 ，  规划 建设 、 制度 建设 、 资源 建设 、 开放 应 、 平台 建设 、 人才 建设']</code></pre><h2 id="8-">8.词云绘制</h2><pre><code class="language-python">from wordcloud import WordCloud, ImageColorGenerator
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

font = r'C:\Windows\Fonts\SIMLI.ttf';     # 自定义字体

py_mask = np.array(Image.open('LN.png'))  # 词云基准图
img_colors = ImageColorGenerator(py_mask) # 读取颜色

# 输入wordcloud
wc1 = WordCloud(
	mask = py_mask, 
    font_path=font, 
	background_color="white").generate(data_1)
wc1.recolor(color_func=img_colors)        # 上色
plt.imshow(wc1, interpolation='bilinear')
plt.axis('off')                           # 关闭坐标轴
plt.show()                                # 输出图片
wc1.to_file('wordcloud.png')                   # 生成文件</code></pre><p>绘制词云的时候需要先导入上方的几个库，具体可以看第一节的介绍内容</p><p>在主体部分，第一步是导入自己要使用的字体，Windows 系统的字体一般存放在上述路径中，也可以自己从字体网站下载，然后运行代码的时候只需要把该字体的路径填好就可以了。</p><p>第二步是对词云基准图进行解析，这里选用的是辽宁省的地图，图片如下方所示。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2023/02/2.png" class="kg-image" alt="2" width="1418" height="906" loading="lazy"><figcaption>辽宁省地图</figcaption></figure><p>在选取基准图的时候要注意，图片上白色区域是不会被读取的，也就是说词云只会显示在白色区域以外，所以如果有形状要求的话尽量选择白底色的图片。首先使用 Image 打开图片，然后将图片转为 numpy 数据类型，此时的 numpy 数据为三维数据，第一、第二维数据为像素位置，第三维数据为颜色。</p><pre><code class="language-python">[in ] print(Image.open('LN.png'))
[out] '&lt;PIL.PngImagePlugin.PngImageFile image mode=RGBA size=1418x906 at 0x21DB3157730&gt;'

[in ] print(py_mask.shape)
[out] '(906, 1418, 4)'</code></pre><p>读取完基准图后通过 ImageColorGenerator 读取图片的颜色信息，以用于最后词云图上色。</p><p>第三步则是词云的绘制，使用的是 WordCloud，其中需要设置的是参数有：</p><ul><li><code>mask</code>：词云形状，即之前读取的词云基准图；</li><li><code>font_path</code>：字体文件路径，即一开始设置的 font 变量；</li><li><code>background_color</code>：背景颜色，通常设置为白色，可自己调节。</li></ul><p>绘制完词云需要进行上色，通过`WordCloud.recolor(color_func=img_colors)` 上色，<code>img_colors</code> 就是之前从图片中读取的颜色信息。</p><p>下一步需要将词云信息转换为图片，使用的是 <code>matplotlib.pyplot.plt.imshow</code>，具体步骤为 <code>plt.imshow(wc1, interpolation='bilinear')</code>，第一个参数为词云信息，第二个参数是插值算法，这里的 <code>bilinear</code> 指的是双线性插值，目的是让词云内的文字图像更加平滑，也可以使用其他算法，详细内容可以参见官方文档：<a href="https://matplotlib.org/3.1.1/gallery/images_contours_and_fields/interpolation_methods.html">Interpolations for imshow/matshow</a>。</p><p>最后就是对 <code>plt()</code> 的细化，包括隐藏坐标轴、在信息输出栏中输出图片和保存图片。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2023/02/test-2.png" class="kg-image" alt="test-2" width="1418" height="906" loading="lazy"><figcaption>词云</figcaption></figure><p>提示：在词云绘制完成后，如果发现仍有一些多余的词，可以重新将这些词添加进停用词表中重新生成新词云。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2023/02/test-3.png" class="kg-image" alt="test-3" width="1418" height="906" loading="lazy"><figcaption>调整停用词后</figcaption></figure><p>以上，仅作为学习分享，欢迎进行交流探讨，如果文中有错误也欢迎联系我修正，十分感谢，联系方式已经贴在下面的 Blog 里了。</p><p>这是我的 Blog：<a href="https://blog.176free.top/">songyp0505</a> ，欢迎交流，及本文原文页：<a href="https://blog.176free.top/2022/04/25/crawlerAndwordcloud/">基层治理爬虫及词云</a>。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何通过 Python 创建一个 Telegram 机器人 ]]>
                </title>
                <description>
                    <![CDATA[ 自动化的聊天机器人可以有效地促进互动，Slack、Discord和其他平台都支持创建机器人。 在这篇文章里，笔者会教你做一个能告诉你星座运势的Telegram聊天机器人，话不多说，马上开始！ 如何获取你的机器人Token值 在Telegram上创建一个机器人之前，你需要先跟 BotFather说一声，顾名思义，BotFather就是所有Bot的Father，但它并不是一个真实的人，也是一个机器人。 1. 在Telegram上搜索 @botfather。 BotFather机器人2. 点击Start按钮与BotFather聊天。 点击Start按钮3. 输入 /newbot ，然后输入你的机器人名字，再输入你的机器人ID，接着BotFather就会给你一个token值，后面我们会用这个token值来访问Telegram的API。 获取访问token注意： 一定要安全的存放上述申请到的token，因为这个token可以直接操控的你机器人。 如何初始化你的代码环境 现在需要准备一下本地的代码环境。在Python中，很多库可以创建Telegram机器人，这里我们用的是pyTele ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-create-a-telegram-bot-using-python/</link>
                <guid isPermaLink="false">63a12730a7bffa07c74419ad</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Telegram ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ songyp0505 ]]>
                </dc:creator>
                <pubDate>Tue, 20 Dec 2022 03:28:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/12/Telegram-Bot.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-create-a-telegram-bot-using-python/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Create a Telegram Bot using Python</a>
      </p><!--kg-card-begin: markdown--><p>自动化的聊天机器人可以有效地促进互动，Slack、Discord和其他平台都支持创建机器人。</p>
<p>在这篇文章里，笔者会教你做一个能告诉你星座运势的Telegram聊天机器人，话不多说，马上开始！</p>
<h2 id="token">如何获取你的机器人Token值</h2>
<p>在Telegram上创建一个机器人之前，你需要先跟 BotFather说一声，顾名思义，BotFather就是所有Bot的Father，但它并不是一个真实的人，也是一个机器人。</p>
<p>1.  在Telegram上搜索 @botfather。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-092357.png" alt="BotFather机器人" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>BotFather机器人</figcaption>
</figure>
<p>2. 点击Start按钮与BotFather聊天。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-092531.png" alt="点击Start按钮" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>点击Start按钮</figcaption>
</figure>
<p>3. 输入 <code>/newbot</code>，然后输入你的机器人名字，再输入你的机器人ID，接着BotFather就会给你一个token值，后面我们会用这个token值来访问Telegram的API。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-093337.png" alt="获取访问token" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>获取访问token</figcaption>
</figure>
<p><strong>注意：</strong> 一定要安全的存放上述申请到的token，因为这个token可以直接操控的你机器人。</p>
<h2 id="">如何初始化你的代码环境</h2>
<p>现在需要准备一下本地的代码环境。在Python中，很多库可以创建Telegram机器人，这里我们用的是<a href="https://pypi.org/project/pyTelegramBotAPI/">pyTelegramBotAPI</a>。这是一个简单但是扩展性很强的库，并且有着很好的同步和异步处理能力。</p>
<p>用pip命令安装pyTelegramBotAPI：</p>
<pre><code class="language-bash">pip install pyTelegramBotAPI
</code></pre>
<p>然后用你常用的代码编辑器创建一个 <code>.env</code> 文件，并在其中写下这一行来存储你的token：</p>
<pre><code class="language-bash">export BOT_TOKEN=your-bot-token-here
</code></pre>
<p>（译者注）Linux/Mac系统下可以直接用终端在当前文件夹内使用 <code>source .env</code>命令，让BOT_TOKEN的值生效，而在Windows系统中，需要在终端中输入以下命令来设置环境变量：</p>
<pre><code class="language-bash">set BOT_TOKEN=your-bot-token-here
</code></pre>
<h2 id="">如何创建第一个机器人</h2>
<p>TeleBot类里面存储了所有的API接口，其中有很多方法可以用来监听收到的消息，同时也有像<code>send_message()</code>, <code>send_document()</code>这类方法来发送消息。</p>
<p>新建一个 <code>bot.py</code> 文件，然后把下面的代码粘贴到文件中：</p>
<pre><code class="language-python">import os

import telebot

BOT_TOKEN = os.environ.get('BOT_TOKEN')

bot = telebot.TeleBot(BOT_TOKEN)
</code></pre>
<p>在上面的代码中，我们使用 os 库的目的是读取我们系统中的环境变量。</p>
<p>如果还记得的话，之前我们设置了一个 <code>BOT_TOKEN</code> 的环境变量，这里我们把它读取出来并将其值赋给变量<code>BOT_TOKEN</code>。然后，我们使用 <code>TeleBot</code>这个类，来创建一个机器人实例，并把<code>BOT_TOKEN</code>的值赋给它。</p>
<p>然后我们需要注册消息处理器，消息处理器包括消息必须通过的过滤器。在消息通过过滤器的时候，装饰器修改过的函数就会被调用，同时，此条消息会作为一个参数传入函数中。</p>
<p>让我们定义一个能处理 <code>/start</code> 和 <code>/hello</code> 命令的消息处理器。</p>
<pre><code class="language-python">@bot.message_handler(commands=['start', 'hello'])
def send_welcome(message):
    bot.reply_to(message, "Howdy, how are you doing?")
</code></pre>
<p>对于装饰器修改过的消息处理函数，任何名称都行，但是只能接受一个参数，也就是消息。</p>
<p>下面实现另一个消息处理器，能返回任意一条收到的消息。</p>
<pre><code class="language-python">@bot.message_handler(func=lambda msg: True)
def echo_all(message):
    bot.reply_to(message, message.text)
</code></pre>
<p>上面用到了一个<code> lambda</code> 函数，如果我们需要回应所有的消息，那我们就需要从<code>lambda</code>函数中返回一个 <code>True</code>。</p>
<p>你现在有了一个简单的机器人，它可以对<code>/start</code> 和 <code>/hello </code>命令做出回应，并且可以返回其他发给它的消息。把下面这条代码放在<code>bot.py</code>的末尾，然后就可以启动机器人了。</p>
<pre><code class="language-python">bot.infinity_polling()
</code></pre>
<p>好啦！这样我们就完成了一个Telegram机器人，运行这个Python文件，然后就可以通过Telegram与机器人进行交互了。</p>
<p>如果你没保存机器人的联系方式，你可以通过之前设置的用户名来搜索。你可以尝试发送<code>/hello</code> 和 <code>/start</code> 以及其他消息感受一下。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-101334.png" alt="测试机器人" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>测试机器人</figcaption>
</figure>
<p>注意：所有的消息处理器都按照它们在源文件中声明的顺序进行测试。</p>
<p>获取更多如何使用pyTelegramBotAPI库的学习资料，你可以去他们的<a href="https://github.com/eternnoir/pyTelegramBotAPI">官方文档</a>了解。</p>
<h2 id="">如何写一个星座运势机器人</h2>
<p>现在让我们把注意力放在星座运势机器人上。我们会在机器人中使用消息链。机器人会先问你是什么星座，然后会问你需要哪天的运势，接着就会返回那一天对应的星座运势。</p>
<p>往程序内部分析，这个机器人主要是通过API来获取星座运势数据的。</p>
<p>我们将会使用一个笔者在另一个教程中写的<a href="https://horoscope-app-api.vercel.app/">星座运势API</a>来进行后续的工作。如果你想学一下如何做一个相同的API，你可以看一下这份<a href="https://ashutoshkrris.hashnode.dev/how-to-create-a-horoscope-api-with-beautiful-soup-and-flask">教程</a>。请确保在开始之前你已经在<a href="https://horoscope-app-api.vercel.app/">网站</a>上探索了API的用法。</p>
<h2 id="">如何抓取运势数据</h2>
<p>我们来创建一个实用的函数以抓取到某个特定日期的星座运势。</p>
<pre><code class="language-python">import requests

def get_daily_horoscope(sign: str, day: str) -&gt; dict:
    """通过特定的星座获取运势。

    关键字解释:
    sign:str - 星座
    day:str - 格式化的日期 (YYYY-MM-DD) 或 TODAY 或 TOMORROW 或 YESTERDAY
    Return:dict - JSON data
    """
    url = "https://horoscope-app-api.vercel.app/api/v1/get-horoscope/daily"
    params = {"sign": sign, "day": day}
    response = requests.get(url, params)

    return response.json()
</code></pre>
<p>在上面的Python代码中，我们创建了一个函数，它能接收<code>sign</code> 和<code>day</code>两个字符串参数，然后返回 JSON数据。我们需要向API链接发送一个GET请求，并且把<code>sign</code> 和<code>day</code> 作为参数加在请求里。</p>
<p>如果你测试一下上面的函数，你会得到一个跟下面类似的返回值：</p>
<pre><code class="language-json">{
   "data":{
      "date": "Dec 15, 2022",
      "horoscope_data": "Lie low during the day and try not to get caught up in the frivolous verbiage that dominates the waking hours. After sundown, feel free to speak your mind. You may notice that there is a sober tone and restrictive sensation today that leaves you feeling like you will never be able to break free from your current situation. Don't get caught in this negative mindset."
   },
   "status": 200,
   "success": true
}
</code></pre>
<p>注意：你可以通过这个<a href="https://ashutoshkrris.hashnode.dev/how-to-interact-with-web-services-using-python">教程</a>来探索更多关于 <code>requests </code>库的内容。</p>
<h2 id="">如何添加一个消息处理器</h2>
<p>现在我们有了一个能返回运势数据的函数，下一步让我们创建一个消息处理器，以让我们的机器人能向用户询问他们的星座。</p>
<pre><code class="language-python">@bot.message_handler(commands=['horoscope'])
def sign_handler(message):
    text = "你的星座是什么?\\n选一个: *Aries*, *Taurus*, *Gemini*, *Cancer,* *Leo*, *Virgo*, *Libra*, *Scorpio*, *Sagittarius*, *Capricorn*, *Aquarius*, and *Pisces*."
    sent_msg = bot.send_message(message.chat.id, text, parse_mode="Markdown")
    bot.register_next_step_handler(sent_msg, day_handler)
</code></pre>
<p>上面的函数跟我们之前定义的有一些不同，机器人的运势查询功能需要被 <code>/horoscope</code> 命令调用。这个函数在我们给用户发消息时执行，你会发现有个参数<code>parse_mode</code>，我们把它的值设置为了<strong>Markdown</strong>，这是为了消息向用户发送时更规范。</p>
<p>因为我们要使用消息链，所以我们使用了 <code>register_next_step_handler()</code> 方法。这个方法接收两个参数：<strong>sent_msg</strong> 和 <strong>day_handler</strong>，所以我们把 <code>sent_msg</code> 变量和一个新的 <code>day_handler</code> 函数作为参数传递给它。</p>
<p>Let’s define the <code>day_handler()</code> function that accepts the message.<br>
让我们定义一个能接收消息的<code>day_handler()</code>函数。</p>
<pre><code class="language-python">def day_handler(message):
    sign = message.text
    text = "你想知道哪天的呀？\\n选一个吧: *TODAY*, *TOMORROW*, *YESTERDAY*, 或其他 YYYY-MM-DD 格式的日期。"
    sent_msg = bot.send_message(message.chat.id, text, parse_mode="Markdown")
    bot.register_next_step_handler(sent_msg, fetch_horoscope, sign.capitalize())
</code></pre>
<p>我们从<code>message.text</code>中获取到星座信息，跟之前的函数类似，机器人会问你想知道哪天的运势。</p>
<p>最后，我们同样再把<code>sent_msg</code>和 <code>fetch_horoscope</code> 输入到 <code>register_next_step_handler()</code> 方法，不过需要给 <code>fetch_horoscope</code> 再加一个<code>sign</code>参数。</p>
<p>让我们创建<code>fetch_horoscope()</code>函数来接收消息和星座。</p>
<pre><code class="language-python">def fetch_horoscope(message, sign):
    day = message.text
    horoscope = get_daily_horoscope(sign, day)
    data = horoscope["data"]
    horoscope_message = f'*运势:* {data["horoscope_data"]}\\n*星座:* {sign}\\n*日期:* {data["date"]}'
    bot.send_message(message.chat.id, "你的运势来啦!")
    bot.send_message(message.chat.id, horoscope_message, parse_mode="Markdown")
</code></pre>
<p>这是我们写的最后一个函数，在这里，我们从传入函数的<code>sign</code>参数获取星座名称、从<code>message.text</code>获取日期。</p>
<p>然后，我们用<code>get_daily_horoscope()</code> 函数抓取运势信息并构建我们的消息。最后，我们把含有星座运势的数据以对话消息的形式发送给用户。</p>
<h2 id="">机器人示例</h2>
<p>如果你运行Python文件的话，你可以测试这些功能，这里是一个<a href="https://www.youtube.com/watch?v=nTHI2rPV_RE">YouTube视频示例</a>。</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/nTHI2rPV_RE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" title="Horoscope Bot using Python" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><!--kg-card-begin: markdown--><h2 id="">推荐下一步</h2>
<p>到目前为止，一旦我们停止Python应用程序，机器人就会停止工作。为了让它一直运行，你可以把机器人部署在Heroku、Render等平台上。</p>
<p>这是本文代码的<a href="https://github.com/ashutoshkrris/Telegram-Horoscope-Bot">GitHub仓库链接</a> - 欢迎随时查看。</p>
<p>在探索<a href="https://core.telegram.org/">Telegram APIs</a>后，你可以向这个机器人添加更多的功能。</p>
<p>感谢阅读！你可以在<a href="https://twitter.com/ashutoshkrris">Twitter</a>或<a href="https://ireadblog.com/">博客</a>关注我。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
