<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Ruby China 社区 Python 节点</title>
    <link>https://ruby-china.org/</link>
    <description>Ruby China 社区 Python 节点最新发帖。</description>
    <item>
      <title>分享我写的小脚本：借助 GPT-4 来批量翻译 markdown 文件</title>
      <description>&lt;h2 id="项目地址"&gt;项目地址&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/daqing/gpt-translator" rel="nofollow" target="_blank"&gt;https://github.com/daqing/gpt-translator&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="简介"&gt;简介&lt;/h2&gt;
&lt;p&gt;最近工作中，需要把 vuepress 搭建的英文网站，批量翻译成其他几种语言（简体中文，繁体中文，德语，日语等等）。&lt;/p&gt;

&lt;p&gt;由于文件很多，手动去一个个翻译，时间成本非常高，于是，研究了一下，写了这个批量翻译的脚本。&lt;/p&gt;
&lt;h2 id="用法"&gt;用法&lt;/h2&gt;
&lt;p&gt;1) 首先，clone 项目代码到本地&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone git@github.com:daqing/gpt-translator
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2) 设置环境变量&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export OPENAI_API_KEY='sk-zPByWXXXXXXXXXXXXXXXXXXXXXXX'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3) 执行 &lt;code&gt;main.py&lt;/code&gt; 脚本&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python main.py --base-lang en --target-lang zh-CN --input ./demo/example.en.md --output ./example.zh-CN.md
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>daqing</author>
      <pubDate>Sat, 27 Apr 2024 20:59:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/43668</link>
      <guid>https://ruby-china.org/topics/43668</guid>
    </item>
    <item>
      <title>Django 5.0 release 发布了</title>
      <description>&lt;p&gt;&lt;a href="https://docs.djangoproject.com/en/dev/releases/5.0/" rel="nofollow" target="_blank"&gt;https://docs.djangoproject.com/en/dev/releases/5.0/&lt;/a&gt;&lt;/p&gt;</description>
      <author>lidashuang</author>
      <pubDate>Mon, 04 Dec 2023 23:50:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/43498</link>
      <guid>https://ruby-china.org/topics/43498</guid>
    </item>
    <item>
      <title>请教 Python 的 coroutine 的 task 的同步语义</title>
      <description>&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;started at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%X&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;finished at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%X&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;output:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;started at 17:13:52
hello
world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;finished at 17:13:55&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;task1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;task2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;started at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%X&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Wait until both tasks are completed (should take
&lt;/span&gt;    &lt;span class="c1"&gt;# around 2 seconds.)
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;task1&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;finished at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%X&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that expected output now shows that the snippet runs 1 second faster than before:
output:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;started at 17:14:32
hello
world
finished at 17:14:34
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看出 task 比 coroutine 快了一秒，也就是 python 是同步执行多个 task 的，
我知道 coroutine 只有一个操作系统线程，yield 来 yield 去，那么 asyncio 是怎么实现 task 的，
有同步问题么？（即一个 task 访问修改一个数据结构，另一个 task 同时访问修改该数据结构，
会 corrupt 该数据结构么？)&lt;/p&gt;</description>
      <author>femto</author>
      <pubDate>Sat, 05 Aug 2023 17:05:35 +0800</pubDate>
      <link>https://ruby-china.org/topics/43257</link>
      <guid>https://ruby-china.org/topics/43257</guid>
    </item>
    <item>
      <title>请教有什么好的 Python 中文论坛？</title>
      <description>&lt;p&gt;请教有什么好的 Python 中文论坛？
就像 ruby-china 之于 ruby 一样，
Thanks.&lt;/p&gt;</description>
      <author>femto</author>
      <pubDate>Sat, 05 Aug 2023 15:07:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/43254</link>
      <guid>https://ruby-china.org/topics/43254</guid>
    </item>
    <item>
      <title>vscode 的 Jupyter notebook 的 PDB 功能问题</title>
      <description>&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaojiba/8ffba203-7b01-4e58-a2de-40777babff47.png!large" title="" alt=""&gt;
用 import pdb; pdb.set_trace() 就可以调试&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/xiaojiba/462c990f-f56d-47ec-b0b2-6385a8adc7b1.png!large" title="" alt=""&gt;
用 breakpint() 本来应该也会有同样的效果，但是点击 run 之后发现什么也没有&lt;/p&gt;

&lt;p&gt;大家遇到这样问题没有，除了 vscode 的 line by line 的调试，我不喜欢用 vscode 自带的打断点方法&lt;/p&gt;</description>
      <author>xiaojiba</author>
      <pubDate>Mon, 27 Mar 2023 17:08:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/42967</link>
      <guid>https://ruby-china.org/topics/42967</guid>
    </item>
    <item>
      <title>高中生开发的粤语编程语言求 star</title>
      <description>&lt;p&gt;&lt;a href="https://github.com/StepfenShawn/Cantonese" rel="nofollow" target="_blank"&gt;https://github.com/StepfenShawn/Cantonese&lt;/a&gt;&lt;/p&gt;</description>
      <author>StepfenShawn</author>
      <pubDate>Tue, 03 May 2022 23:10:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/42366</link>
      <guid>https://ruby-china.org/topics/42366</guid>
    </item>
    <item>
      <title>Golang 常见设计模式之装饰模式</title>
      <description>&lt;p&gt;想必只要是熟悉 Python 的同学对装饰模式一定不会陌生，这类 Python 从语法上原生支持的装饰器，大大提高了装饰模式在 Python 中的应用。尽管 Go 语言中装饰模式没有 Python 中应用的那么广泛，但是它也有其独到的地方。接下来就一起看下装饰模式在 Go 语言中的应用。&lt;/p&gt;

&lt;p&gt;## 简单装饰器&lt;/p&gt;

&lt;p&gt;我们通过一个简单的例子来看一下装饰器的简单应用，首先编写一个 hello 函数：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
package main

import "fmt"

func hello() {
    fmt.Println("Hello World!")
}

func main() {
    hello()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完成上面代码后，执行会输出“Hello World!”。接下来通过以下方式，在打印“Hello World!”前后各加一行日志：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func hello() {
    fmt.Println("before")
    fmt.Println("Hello World!")
    fmt.Println("after")
}

func main() {
    hello()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码执行后输出：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;before
Hello World!
after
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然我们可以选择一个更好的实现方式，即单独编写一个专门用来打印日志的 logger 函数，示例如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

func logger(f func()) func() {
    return func() {
        fmt.Println("before")
        f()
        fmt.Println("after")
    }
}

func hello() {
    fmt.Println("Hello World!")
}

func main() {
    hello := logger(hello)
    hello()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到 logger 函数接收并返回了一个函数，且参数和返回值的函数签名同 hello 一样。然后我们在原来调用 hello() 的位置进行如下修改：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hello := logger(hello)
hello()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样我们通过 logger 函数对 hello 函数的包装，更加优雅的实现了给 hello 函数增加日志的功能。执行后的打印结果仍为：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;before
Hello World!
after
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其实 logger 函数也就是我们在 Python 中经常使用的装饰器，因为 logger 函数不仅可以用于 hello，还可以用于其他任何与 hello 函数有着同样签名的函数。&lt;/p&gt;

&lt;p&gt;当然如果想使用 Python 中装饰器的写法，我们可以这样做：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
package main

import "fmt"

func logger(f func()) func() {
    return func() {
        fmt.Println("before")
        f()
        fmt.Println("after")
    }
}

// 给 hello 函数打上 logger 装饰器
@logger
func hello() {
    fmt.Println("Hello World!")
}

func main() {
    // hello 函数调用方式不变
    hello()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但很遗憾，上面的程序无法通过编译。因为 Go 语言目前还没有像 Python 语言一样从语法层面提供对装饰器语法糖的支持。&lt;/p&gt;
&lt;h2 id="装饰器实现中间件"&gt;装饰器实现中间件&lt;/h2&gt;
&lt;p&gt;尽管 Go 语言中装饰器的写法不如 Python 语言精简，但它被广泛运用于 Web 开发场景的中间件组件中。比如 Gin Web 框架的如下代码，只要使用过就肯定会觉得熟悉：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.New()

    // 使用中间件
    r.Use(gin.Logger(), gin.Recovery())

    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    _ = r.Run(":8888")
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如示例中使用 gin.Logger() 增加日志，使用 gin.Recovery() 来处理 panic 异常一样，在 Gin 框架中可以通过 r.Use(middlewares...) 的方式给路由增加非常多的中间件，来方便我们拦截路由处理函数，并在其前后分别做一些处理逻辑。&lt;/p&gt;

&lt;p&gt;而 Gin 框架的中间件正是使用装饰模式来实现的。下面我们借用 Go 语言自带的 http 库进行一个简单模拟。这是一个简单的 Web Server 程序，其监听 8888 端口，当访问 /hello 路由时会进入 handleHello 函数逻辑：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "fmt"
    "net/http"
)

func loggerMiddleware(f http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("before")
        f(w, r)
        fmt.Println("after")
    }
}

func authMiddleware(f http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if token := r.Header.Get("token"); token != "fake_token" {
            _, _ = w.Write([]byte("unauthorized\n"))
            return
        }
        f(w, r)
    }
}

func handleHello(w http.ResponseWriter, r *http.Request) {
    fmt.Println("handle hello")
    _, _ = w.Write([]byte("Hello World!\n"))
}

func main() {
    http.HandleFunc("/hello", authMiddleware(loggerMiddleware(handleHello)))
    fmt.Println(http.ListenAndServe(":8888", nil))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们分别使用 loggerMiddleware、authMiddleware 函数对 handleHello 进行了包装，使其支持打印访问日志和认证校验功能。如果我们还需要加入其他中间件拦截功能，可以通过这种方式进行无限包装。&lt;/p&gt;

&lt;p&gt;启动这个 Server 来验证下装饰器：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ec5ba136453647a1940ba7ecb404ea56~tplv-k3u1fbpfcp-watermark.image?" title="" alt="1.png"&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a1168d1e56284dfe89164c190fde82cb~tplv-k3u1fbpfcp-watermark.image?" title="" alt="2.png"&gt;&lt;/p&gt;

&lt;p&gt;对结果进行简单分析可以看到，第一次请求 /hello 接口时，由于没有携带认证 token，收到了 unauthorized 响应。第二次请求时携带了 token，则得到响应“Hello World!”，并且后台程序打印如下日志：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;before
handle hello
after
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这说明中间件执行顺序是先由外向内进入，再由内向外返回。而这种一层一层包装处理逻辑的模型有一个非常形象且贴切的名字，洋葱模型。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dccdcb1bf5724cb8a090ce2bcd78f8ac~tplv-k3u1fbpfcp-watermark.image?" title="" alt="3.png"&gt;&lt;/p&gt;

&lt;p&gt;但用洋葱模型实现的中间件有一个直观的问题。相比于 Gin 框架的中间件写法，这种一层层包裹函数的写法不如 Gin 框架提供的 r.Use(middlewares...) 写法直观。&lt;/p&gt;

&lt;p&gt;Gin 框架源码的中间件和 handler 处理函数实际上被一起聚合到了路由节点的 handlers 属性中。其中 handlers 属性是 HandlerFunc 类型切片。对应到用 http 标准库实现的 Web Server 中，就是满足 func(ResponseWriter, *Request) 类型的 handler 切片。&lt;/p&gt;

&lt;p&gt;当路由接口被调用时，Gin 框架就会像流水线一样依次调用执行 handlers 切片中的所有函数，再依次返回。这种思想也有一个形象的名字，就叫作流水线（Pipeline）。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f76a39f35f784f68bb691a12fa61142b~tplv-k3u1fbpfcp-watermark.image?" title="" alt="4.png"&gt;&lt;/p&gt;

&lt;p&gt;接下来我们要做的就是将 handleHello 和两个中间件 loggerMiddleware、authMiddleware 聚合到一起，同样形成一个 Pipeline。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "fmt"
    "net/http"
)

func authMiddleware(f http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if token := r.Header.Get("token"); token != "fake_token" {
            _, _ = w.Write([]byte("unauthorized\n"))
            return
        }
        f(w, r)
    }
}

func loggerMiddleware(f http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("before")
        f(w, r)
        fmt.Println("after")
    }
}

type handler func(http.HandlerFunc) http.HandlerFunc

// 聚合 handler 和 middleware
func pipelineHandlers(h http.HandlerFunc, hs ...handler) http.HandlerFunc {
    for i := range hs {
        h = hs[i](h)
    }
    return h
}

func handleHello(w http.ResponseWriter, r *http.Request) {
    fmt.Println("handle hello")
    _, _ = w.Write([]byte("Hello World!\n"))
}

func main() {
    http.HandleFunc("/hello", pipelineHandlers(handleHello, loggerMiddleware, authMiddleware))
    fmt.Println(http.ListenAndServe(":8888", nil))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们借用 pipelineHandlers 函数将 handler 和 middleware 聚合到一起，实现了让这个简单的 Web Server 中间件用法跟 Gin 框架用法相似的效果。&lt;/p&gt;

&lt;p&gt;再次启动 Server 进行验证：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9cf7e7cff65d42198b5d2ccdeef41f40~tplv-k3u1fbpfcp-watermark.image?" title="" alt="5.png"&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2d7864850e8a4be6a81fa4c35461dfd3~tplv-k3u1fbpfcp-watermark.image?" title="" alt="6.png"&gt;&lt;/p&gt;

&lt;p&gt;改造成功，跟之前使用洋葱模型写法的结果如出一辙。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;简单了解了 Go 语言中如何实现装饰模式后，我们通过一个 Web Server 程序中间件，学习了装饰模式在 Go 语言中的应用。&lt;/p&gt;

&lt;p&gt;需要注意的是，尽管 Go 语言实现的装饰器有类型上的限制，不如 Python 装饰器那般通用。就像我们最终实现的 pipelineHandlers 不如 Gin 框架中间件强大，比如不能延迟调用，通过 c.Next() 控制中间件调用流等。但不能因为这样就放弃，因为 GO 语言装饰器依然有它的用武之地。&lt;/p&gt;

&lt;p&gt;Go 语言是静态类型语言不像 Python 那般灵活，所以在实现上要多费一点力气。希望通过这个简单的示例，相信对大家深入学习 Gin 框架有所帮助。&lt;/p&gt;
&lt;h4 id="推荐阅读"&gt;推荐阅读&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://www.upyun.com/tech/article/696/%E4%B8%A4%E6%8B%9B%E6%8F%90%E5%8D%87%E7%A1%AC%E7%9B%98%E5%AD%98%E5%82%A8%E6%95%B0%E6%8D%AE%E7%9A%84%E5%86%99%E5%85%A5%E6%95%88%E7%8E%87.html" rel="nofollow" target="_blank" title=""&gt;两招提升硬盘存储数据的写入效率&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.upyun.com/tech/article/698/%E3%80%90%E7%A8%8B%E5%BA%8F%E5%91%98%E7%9A%84%E5%AE%9E%E7%94%A8%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90%E3%80%91%20Mac%20%E6%95%88%E7%8E%87%E7%A5%9E%E5%99%A8%20Alfred.html" rel="nofollow" target="_blank" title=""&gt;【程序员的实用工具推荐】Mac 效率神器 Alfred&lt;/a&gt;&lt;/p&gt;</description>
      <author>upyun</author>
      <pubDate>Wed, 23 Mar 2022 11:13:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/42249</link>
      <guid>https://ruby-china.org/topics/42249</guid>
    </item>
    <item>
      <title>Python 开发工程师  深圳！！区块链行业  Python 和 GO 语言均可</title>
      <description>&lt;p&gt;Base 深圳南山&lt;/p&gt;

&lt;p&gt;岗位要求：
1、精通 Python 语言，熟悉 Linux 环境下的开发，具有 2 年以上后端开发经验，有区块链开发经验优先。
2、熟悉 MVC 架构，熟练掌握 Django，DRF 或 FastApi 开发框架，熟悉 Celery。
3、掌握基本的数据库设计能力，具备良好的编码习惯及开发文档书写习惯，能够进行需求分析并进行对应的功能设计。
4、熟练掌握 MySQL 及至少一种非关系型数据库，如 redis、mongodb，理解其使用场景及限制。&lt;/p&gt;

&lt;p&gt;开发语言：
    - Python/Flask,Django，Golang/Gin,Beego,GoFrame
    - Vue, React, ElementUI, 
    - MySQL, Redis&lt;/p&gt;

&lt;p&gt;行业：区块链   支付系统开发&lt;/p&gt;

&lt;p&gt;薪酬范围：15K-20K&lt;/p&gt;</description>
      <author>aurtech</author>
      <pubDate>Fri, 11 Mar 2022 15:25:29 +0800</pubDate>
      <link>https://ruby-china.org/topics/42203</link>
      <guid>https://ruby-china.org/topics/42203</guid>
    </item>
    <item>
      <title>Golang 常见设计模式之选项模式</title>
      <description>&lt;p&gt;熟悉 Python 开发的同学都知道，Python 有默认参数的存在，使得我们在实例化一个对象的时候，可以根据需要来选择性的覆盖某些默认参数，以此来决定如何实例化对象。当一个对象有多个默认参数时，这个特性非常好用，能够优雅地简化代码。&lt;/p&gt;

&lt;p&gt;而 Go 语言从语法上是不支持默认参数的，所以为了实现既能通过默认参数创建对象，又能通过传递自定义参数创建对象，我们就需要通过一些编程技巧来实现。对于这些程序开发中的常见问题，软件行业的先行者们总结了许多解决常见场景编码问题的最佳实践，这些最佳实践后来成为了我们所说的设计模式。其中选项模式在 Go 语言开发中会经常用到。&lt;/p&gt;

&lt;p&gt;通常我们有以下三种方法来实现通过默认参数创建对象，以及通过传递自定义参数创建对象：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;使用多个构造函数&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认参数选项&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;选项模式&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="通过多构造函数实现"&gt;通过多构造函数实现&lt;/h2&gt;
&lt;p&gt;第一种方式是通过多构造函数实现，下面是一个简单例子：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

const (
    defaultAddr = "127.0.0.1"
    defaultPort = 8000
)

type Server struct {
    Addr string
    Port int
}

func NewServer() *Server {
    return &amp;amp;Server{
        Addr: defaultAddr,
        Port: defaultPort,
    }
}

func NewServerWithOptions(addr string, port int) *Server {
    return &amp;amp;Server{
        Addr: addr,
        Port: port,
    }
}

func main() {
    s1 := NewServer()
    s2 := NewServerWithOptions("localhost", 8001)
    fmt.Println(s1)  // &amp;amp;{127.0.0.1 8000}
    fmt.Println(s2)  // &amp;amp;{localhost 8001}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里我们为 Server 结构体实现了两个构造函数：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;NewServer：无需传递参数即可直接返回 Server 对象&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NewServerWithOptions：需要传递 addr 和 port 两个参数来构造 Server 对象&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果通过默认参数创建的对象即可满足需求，不需要对 Server 进行定制时，我们可以使用 NewServer 来生成对象（s1）。而如果需要对 Server 进行定制时，我们则可以使用 NewServerWithOptions 来生成对象（s2）。&lt;/p&gt;
&lt;h2 id="通过默认参数选项实现"&gt;通过默认参数选项实现&lt;/h2&gt;
&lt;p&gt;另外一种实现默认参数的方案，是为要生成的结构体对象定义一个选项结构体，用来生成要创建对象的默认参数，代码实现如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

const (
    defaultAddr = "127.0.0.1"
    defaultPort = 8000
)

type Server struct {
    Addr string
    Port int
}

type ServerOptions struct {
    Addr string
    Port int
}

func NewServerOptions() *ServerOptions {
    return &amp;amp;ServerOptions{
        Addr: defaultAddr,
        Port: defaultPort,
    }
}

func NewServerWithOptions(opts *ServerOptions) *Server {
    return &amp;amp;Server{
        Addr: opts.Addr,
        Port: opts.Port,
    }
}

func main() {
    s1 := NewServerWithOptions(NewServerOptions())
    s2 := NewServerWithOptions(&amp;amp;ServerOptions{
        Addr: "localhost",
        Port: 8001,
    })
    fmt.Println(s1)  // &amp;amp;{127.0.0.1 8000}
    fmt.Println(s2)  // &amp;amp;{localhost 8001}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们为 Server 结构体专门实现了一个 ServerOptions 用来生成默认参数，调用 NewServerOptions 函数即可获得默认参数配置，构造函数 NewServerWithOptions 接收一个 *ServerOptions 类型作为参数。所以我们可以通过以下两种方式来完成功能：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;直接将调用 NewServerOptions 函数的返回值传递给 NewServerWithOptions 来实现通过默认参数生成对象（s1）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;通过手动构造 ServerOptions 配置来生成定制对象（s2）&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="通过选项模式实现"&gt;通过选项模式实现&lt;/h2&gt;
&lt;p&gt;以上两种方式虽然都能够完成功能，但却有以下缺点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;通过多构造函数实现的方案需要我们在实例化对象时分别调用不同的构造函数，代码封装性不强，会给调用者增加使用负担。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;通过默认参数选项实现的方案需要我们预先构造一个选项结构，当使用默认参数生成对象时代码看起来比较冗余。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而选项模式可以让我们更为优雅地解决这个问题。代码实现如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import "fmt"

const (
    defaultAddr = "127.0.0.1"
    defaultPort = 8000
)

type Server struct {
    Addr string
    Port int
}

type ServerOptions struct {
    Addr string
    Port int
}

type ServerOption interface {
    apply(*ServerOptions)
}

type FuncServerOption struct {
    f func(*ServerOptions)
}

func (fo FuncServerOption) apply(option *ServerOptions) {
    fo.f(option)
}

func WithAddr(addr string) ServerOption {
    return FuncServerOption{
        f: func(options *ServerOptions) {
            options.Addr = addr
        },
    }
}

func WithPort(port int) ServerOption {
    return FuncServerOption{
        f: func(options *ServerOptions) {
            options.Port = port
        },
    }
}

func NewServer(opts ...ServerOption) *Server {
    options := ServerOptions{
        Addr: defaultAddr,
        Port: defaultPort,
    }

    for _, opt := range opts {
        opt.apply(&amp;amp;options)
    }

    return &amp;amp;Server{
        Addr: options.Addr,
        Port: options.Port,
    }
}

func main() {
    s1 := NewServer()
    s2 := NewServer(WithAddr("localhost"), WithPort(8001))
    s3 := NewServer(WithPort(8001))
    fmt.Println(s1)  // &amp;amp;{127.0.0.1 8000}
    fmt.Println(s2)  // &amp;amp;{localhost 8001}
    fmt.Println(s3)  // &amp;amp;{127.0.0.1 8001}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;乍一看我们的代码复杂了很多，但其实调用构造函数生成对象的代码复杂度是没有改变的，只是定义上的复杂。&lt;/p&gt;

&lt;p&gt;我们定义了 ServerOptions 结构体用来配置默认参数。因为 Addr 和 Port 都有默认参数，所以 ServerOptions 的定义和 Server 定义是一样的。但有一定复杂性的结构体中可能会有些参数没有默认参数，必须让用户来配置，这时 ServerOptions 的字段就会少一些，大家可以按需定义。&lt;/p&gt;

&lt;p&gt;同时，我们还定义了一个 ServerOption 接口和实现了此接口的 FuncServerOption 结构体，它们的作用是让我们能够通过 apply 方法为 ServerOptions 结构体单独配置某项参数。&lt;/p&gt;

&lt;p&gt;我们可以分别为每个默认参数都定义一个 WithXXX 函数用来配置参数，如这里定义的 WithAddr 和 WithPort，这样用户就可以通过调用 WithXXX 函数来定制需要覆盖的默认参数。&lt;/p&gt;

&lt;p&gt;此时默认参数定义在构造函数 NewServer 中，构造函数的接收一个不定长参数，类型为 ServerOption，在构造函数内部通过一个 for 循环调用每个传进来的 ServerOption 对象的 apply 方法，将用户配置的参数依次赋值给构造函数内部的默认参数对象 options 中，以此来替换默认参数，for 循环执行完成后，得到的 options 对象将是最终配置，将其属性依次赋值给 Server 即可生成新的对象。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;通过 s2 和 s3 的打印结果可以发现，使用选项模式实现的构造函数更加灵活，相较于前两种实现，选项模式中我们可以自由的更改其中任意一项或多项默认配置。&lt;/p&gt;

&lt;p&gt;虽然选项模式确实会多写一些代码，但多数情况下这都是值得的。比如 Google 的 gRPC 框架 Go 语言实现中创建 gRPC server 的构造函数 NewServer 就使用了选项模式，感兴趣的同学可以看下其源码的实现思想其实和这里的示例程序如出一辙。&lt;/p&gt;

&lt;p&gt;以上就是我关于 Golang 选项模式的一点经验，希望今天的分享能够给你带来一些帮助。&lt;/p&gt;
&lt;h3 id="推荐阅读"&gt;推荐阅读&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.upyun.com/tech/article/682/%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%B8%B2%E6%9F%93%E5%9F%BA%E7%A1%80.html" rel="nofollow" target="_blank" title=""&gt;服务端渲染基础&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.upyun.com/tech/article/683/%E4%BA%91%E5%8E%9F%E7%94%9F%E7%81%B0%E5%BA%A6%E6%9B%B4%E6%96%B0%E5%AE%9E%E8%B7%B5.html" rel="nofollow" target="_blank" title=""&gt;云原生灰度更新实践&lt;/a&gt;&lt;/p&gt;</description>
      <author>upyun</author>
      <pubDate>Tue, 11 Jan 2022 11:16:37 +0800</pubDate>
      <link>https://ruby-china.org/topics/42067</link>
      <guid>https://ruby-china.org/topics/42067</guid>
    </item>
    <item>
      <title>请求帮助：想要读取中文 txt 文件，之前已按可以读取英文或数字 txt 文件最简单的方式进行后报错，现在换了一种新方式如图，却输出一堆解码。我的最终目的是输出中文，而不是解码。</title>
      <description>&lt;h2 id="测试读取txt文件（英文）："&gt;测试读取 txt 文件（英文）：&lt;/h2&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;C:\Users\MacBooK\Desktop\222.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;file_object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;connect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;系统报告：可正常读取&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="测试读取txt文件（中文）："&gt;测试读取 txt 文件（中文）：&lt;/h2&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;C:\Users\MacBooK\Desktop\111.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;file_object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;connects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;connects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{}-{} time&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;系统报告：可正常读取&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 警告：读取为非原文
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <author>F735_T1971_YYH</author>
      <pubDate>Wed, 29 Dec 2021 22:59:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/42040</link>
      <guid>https://ruby-china.org/topics/42040</guid>
    </item>
    <item>
      <title>实战经验分享：使用 PyO3 来构建你的 Python 模块</title>
      <description>&lt;p&gt;PyO3 主要用于创建原生 Python 的扩展模块。PyO3 还支持从 Rust 二进制文件运行 Python 代码并与之交互，可以实现 rust 与 Python 代码共存。在一些对性能要求较高的模块上，可以考虑使用 PyO3 构建对应的功能模块。PyO3 的功能分离，不用过多担心模块之间的耦合性，并且在速度上能有一定的提升。&lt;/p&gt;

&lt;p&gt;github 地址：&lt;a href="https://github.com/PyO3/pyo3" rel="nofollow" target="_blank"&gt;https://github.com/PyO3/pyo3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;版本规定如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Python 3.6+&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rust 1.41+&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;接下来我们通过一个小的 demo 了解一下从 PyO3 编译模块到 Python 中正常使用的整个流程。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo new --lib string-sum
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="创建项目"&gt;创建项目&lt;/h2&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# lib.rs
[package]
name = "string-sum"
version = "0.1.0"
edition = "2018"

[lib]
name = "string_sum"
# "cdylib" is necessary to produce a shared library for Python to import from.
#
# Downstream Rust code (including code in `bin/`, `examples/`, and `tests/`) will not be able
# to `use string_sum;` unless the "rlib" or "lib" crate type is also included, e.g.:
# crate-type = ["cdylib", "rlib"]
crate-type = ["cdylib"]

[dependencies.pyo3]
version = "0.14.1"
features = ["extension-module"] // 扩展模块，像其他的还有auto-initialize
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/lib.rs
use std::usize;

use  pyo3::prelude::*;

// like this
// def sum_as_string(a:str, b:str) -&amp;gt; str:
//      return a+b
#[pyfunction]
fn sum_as_string(a: usize, b: usize) -&amp;gt; PyResult&amp;lt;String&amp;gt;{
    Ok((a+b).to_string())
}

// Mount method to module 
#[pymodule]
fn string_sum(py: Python, m: &amp;amp;PyModule) -&amp;gt; PyResult&amp;lt;()&amp;gt;{
    m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
    Ok(())
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="编译与使用"&gt;编译与使用&lt;/h2&gt;
&lt;p&gt;编译完成之后，我们会在 target 文件夹下面发现一个 wheel 文件。文件名组合为“模块名 + 当前 Python 版本 + 当前系统型号”，比如：string_sum-0.1.0-cp39-cp39-macosx_10_7_x86_64.whl&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
pip3 install ./target/wheel/string_sum-0.1.0-cp39-cp39-macosx_10_7_x86_64.whl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建 python 文件：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# example.py
from string_sum import sum_as_string
print(sum_as_string(1,2))
# echo 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;编译工具的选择和使用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;官方提供了两种编译工具的选择：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;rust 写的 maturin&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;传统的 setup.py 的方式&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;使用 maturin 编译&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 安装 
pip3 install maturin
# 编译
maturin build
# maturin publish 发布
# 虚拟环境中使用 会自动去寻找/target/wheel/ 下的 *.wheel文件然后安装
virtualenv venv
source ./venv/bin/activate
maturin develop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;使用 setup.py 编译&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 安装
pip3 install setuptools-rust
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编写 setup.py 文件：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# setup.py


from setuptools import setup
from setuptools_rust import Binding, RustExtension

setup(
    # 包名称
    name="string_sum", 
    # 包版本 
    version="0.1",
    # rust扩展 其中"string_sum.string_sum"中
    # 第一个string_sum 指的是当前的包
    # 第二个指的是
    # #[pymodule]
    # fn string_sum(py: Python, m: &amp;amp;PyModule) -&amp;gt; PyResult&amp;lt;()&amp;gt;{
    #     m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
    #     Ok(())
    # }
    # 中的string_sum
    rust_extensions=[
        RustExtension(
            "string_sum.string_sum", 
            binding=Binding.PyO3,
            debug=False
            )
    ],
    # 需要创建一个文件夹 string_sum
    packages=["string_sum"],
    # rust extensions are not zip safe, just like C-extensions.
    zip_safe=False,
    # 标注
    classifiers=[
        "License :: OSI Approved :: MIT License",
        "Development Status :: 3 - Alpha",
        "Intended Audience :: Developers",
        "Programming Language :: Python",
        "Programming Language :: Rust",
        "Operating System :: POSIX",
        "Operating System :: MacOS :: MacOS X",
    ],
    include_package_data=True
)
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 打包
mkdir string_sum
touch string_sum/__init__.py
virtualenv venv &amp;amp;&amp;amp; source venv/bin/activate
pip setup.py build &amp;amp;&amp;amp; pip setup.py install &amp;amp;&amp;amp; pip setup.py develop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/upyun/dc16b72f-35b2-45ab-8507-a39f77b0a248.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;会引用本地的文件：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/upyun/5bb18c8f-4294-4c94-9f22-33e99ca964d2.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;docker 中的应用&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;同样的，如果创建的 App 本身是在 docker 内部运行的。那么第一步我们需要安装 rust 的环境 dockerfile。具体如下：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
curl https://sh.rustup.rs &lt;span class="nt"&gt;-sSf&lt;/span&gt; | bash &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.cargo/env
rustc &lt;span class="nt"&gt;--version&lt;/span&gt;
python setup.py &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ddockerfile 
FROM python:3.7
WORKDIR /app
ADD . /app
RUN pip install --upgrade pip \
    &amp;amp;&amp;amp; pip install -r requirements.txt
RUN ./init.sh
CMD [python, xx.py]
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# requirements.txt
semantic-version==2.8.5
setuptools-rust==0.12.1
toml==0.10.2
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# rust国内镜像源 config
# /root/.cargo/config
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
[term]
verbose = true
color = 'auto'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;具体目录如下：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-rw-r--r-- Cargo.lock
-rw-r--r-- Cargo.toml
-rw-r--r-- config           # 配置文件
-rw-r--r-- Dockerfile
-rwxrwxrwx init.sh          # 初始化rust环境脚本
-rw-r--r-- requirements.txt
-rw-r--r-- setup.py         # 打包脚本
drwxr-xr-x src              # rust项目
drwxr-xr-x string_sum 
-rw-r--r-- xx.py            # 可行性测试文件
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="用 PyO3 写一个 Python 的rsa加解密包"&gt;用 PyO3 写一个 Python 的 rsa 加解密包&lt;/h2&gt;
&lt;p&gt;看过之前的文章的小伙伴&lt;a href="http://mp.weixin.qq.com/s?__biz=MjM5ODc5ODgyMw==&amp;amp;mid=2653586291&amp;amp;idx=1&amp;amp;sn=8b1541477de9d08bf6a34564928f20ab&amp;amp;chksm=bd1b17fb8a6c9eed4716acd4da23e7e2f8ddce7c9988a60a39937ebdd5835ff4726e102ab4ea&amp;amp;scene=21#wechat_redirect" rel="nofollow" target="_blank" title=""&gt;《灵魂画手：漫画图解 SSH》&lt;/a&gt; ，应该对 rsa 的整个加解密流程有所了解啦。那我们不妨用 PyO3 来构建一个 Python 的 rsa 加解密包。使用场景如下：&lt;/p&gt;

&lt;p&gt;客户端本地生成公私钥，通过前期认证过程，将公钥发送给服务端保存，后期通信过程中，客户端主动发送消息给服务端，客户端通过私钥对信息加密，服务端通过对应的公钥进行解密。&lt;/p&gt;

&lt;p&gt;github 地址：&lt;a href="https://github.com/hzjsea/pyo3-crypto" rel="nofollow" target="_blank"&gt;https://github.com/hzjsea/pyo3-crypto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;后续又扩展了一些内容，比如 MD5 加密，签名等等。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 自动化脚本
#!/bin/bash

echo "init......"

# set python version 
# INSTALL_PYTHON_VERSION=python3.6

find_python() {
        set +e
        unset BEST_VERSION
        for V in 37 3.7 38 3.8 39 3.9 3; do
                if which python$V &amp;gt;/dev/null; then
                        if [ "$BEST_VERSION" = "" ]; then
                                BEST_VERSION=$V
                        fi
                fi
        done
        echo $BEST_VERSION
        set -e
}

if [ "$INSTALL_PYTHON_VERSION" = "" ]; then
        INSTALL_PYTHON_VERSION=$(find_python)
fi

# This fancy syntax sets INSTALL_PYTHON_PATH to "python3.7", unless
# INSTALL_PYTHON_VERSION is defined.
# If INSTALL_PYTHON_VERSION equals 3.8, then INSTALL_PYTHON_PATH becomes python3.8
# 找不到就python3.7
INSTALL_PYTHON_PATH=python${INSTALL_PYTHON_VERSION:-3.7}
echo $INSTALL_PYTHON_PATH

echo "Python version is $INSTALL_PYTHON_VERSION"
$INSTALL_PYTHON_PATH -m venv venv
if [ ! -f "activate" ]; then
        ln -s venv/bin/activate .
fi

. ./activate

python -m pip install --upgrade pip
python -m pip install wheel
python -m pip install -r ./requirements.txt
maturin build
maturin develop

current_shell=$(echo $SHELL)
if current_shell=/bin/bash; then
    echo  "PASS: source /venv/bin/activate &amp;gt;&amp;gt; ~/.bashrc"
elif current_shell=/bin/zsh;then
    echo "PASS: source /venv/bin/activate &amp;gt;&amp;gt; ~/.zshrc"
fi
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//  src/lib.rs 文件
use std::u32;
use pyo3::prelude::*;
use openssl::rsa::{Padding,Rsa};

const SECRET: &amp;amp;'static str = "CHFfxQA3tqEZgKusgwZjmI5lFsoZxXGXnQLA97oYga2M33sLwREZyy1mWCM8GIIA";

mod crypto_utils {
    use hmac::{Hmac, Mac, NewMac};
    use sha2::Sha256;
    use std::fmt::Write;

    type Hmacsha256 = Hmac&amp;lt;Sha256&amp;gt;;
    fn encode_hex(bytes: &amp;amp;[u8]) -&amp;gt; String {
        let mut s = String::with_capacity(bytes.len() * 2);
        for &amp;amp;b in bytes {
            match write!(&amp;amp;mut s, "{:02x}", b) {
                Ok(_) =&amp;gt; {},
                Err(_) =&amp;gt; {}
            };
        }
        s
    }

    pub fn hash_hmac(secret: &amp;amp;str, msg: &amp;amp;str) -&amp;gt; String {
        let mut mac = Hmacsha256::new_from_slice(secret.as_bytes()).expect("HMAC can take key of any size");
        mac.update(msg.as_bytes());
        let result = mac.finalize();
        let code_bytes = result.into_bytes();
        encode_hex(&amp;amp;code_bytes)
    }

}

// create public/private key  create_key(1024)
fn create_key(len:u32) -&amp;gt; (String,String){
    let rsa = openssl::rsa::Rsa::generate(len).unwrap();
    let pubkey = String::from_utf8(rsa.public_key_to_pem().unwrap()).unwrap();
    let prikey  = String::from_utf8(rsa.private_key_to_pem().unwrap()).unwrap();

    (pubkey, prikey)
}



#[pyclass]
struct Crypto {
    // #[pyo3(get, set)]
    // pubkey: String,
    // #[pyo3(get,set)]
    // prikey: String,
    pub_key: Rsa&amp;lt;openssl::pkey::Public&amp;gt;,
    pri_key: Rsa&amp;lt;openssl::pkey::Private&amp;gt;
}

#[pyfunction]
fn generate_key(len:u32) -&amp;gt; (String, String){
    create_key(len)
}

#[pymethods]
impl Crypto {
    #[new]
    pub fn __new__(pubkey: &amp;amp;str,prikey: &amp;amp;str) -&amp;gt; Self {
        Crypto {
            // pubkey: pubkey.to_owned(),
            // prikey: prikey.to_owned(),
            pub_key: Rsa::public_key_from_pem(pubkey.as_bytes()).unwrap(),
            pri_key: Rsa::private_key_from_pem(prikey.as_bytes()).unwrap(),
        }
    }

    // public decrypt 
    pub fn public_decrypt(&amp;amp;self, msg:&amp;amp;str) -&amp;gt; String {
        let mut out: [u8; 4096] = [0;4096];
        let decoded = openssl::base64::decode_block(msg).unwrap();
        if let Ok(size) = self.pub_key.public_decrypt(&amp;amp;decoded, &amp;amp;mut out, Padding::PKCS1) {
            let real_size = if size &amp;gt; 4096 {4096} else {size};
            // openssl::base64::encode_block(&amp;amp;out[..real_size])
            String::from_utf8(out[..real_size].to_vec()).unwrap()
        } else {
            String::default()
        }
    }

    // public encrypt 
    pub fn public_encrypt(&amp;amp;self, msg:&amp;amp;str) -&amp;gt; String {
        let mut out: [u8; 4096] = [0;4096];
        if let Ok(size) = self.pub_key.public_encrypt(msg.as_bytes(), &amp;amp;mut out, Padding::PKCS1) {
            let real_size = if size &amp;gt; 4096 {4096}else{size};
            openssl::base64::encode_block(&amp;amp;out[..real_size])
        } else {
            String::default()
        }
    }

    // private encrypt
    pub fn private_encrypt(&amp;amp;self, msg:&amp;amp;str) -&amp;gt; String{
        let mut out: [u8; 4096] = [0;4096];
        if let Ok(size) = self.pri_key.private_encrypt(msg.as_bytes(), &amp;amp;mut out, Padding::PKCS1) {
            let real_size = if size &amp;gt; 4096 {4096}else{size};
            openssl::base64::encode_block(&amp;amp;out[..real_size])
        } else {
            String::default()
        }
    }

    // private decrypt
    pub fn private_decrypt(&amp;amp;self, msg: &amp;amp;str) -&amp;gt; String{
        let mut out: [u8; 4096] = [0;4096];
        let decoded = openssl::base64::decode_block(msg).unwrap();
        if let Ok(size) = self.pri_key.private_decrypt(&amp;amp;decoded, &amp;amp;mut out, Padding::PKCS1) {
            let real_size = if size &amp;gt; 4096 {4096} else {size};
            // openssl::base64::encode_block(&amp;amp;out[..real_size])
            String::from_utf8(out[..real_size].to_vec()).unwrap()
        } else {
            String::default()
        } 
    }

    // sign
    pub fn sign(&amp;amp;self, msg: &amp;amp;str) -&amp;gt;String {
        crypto_utils::hash_hmac(SECRET, msg)
    }
}

#[pymodule]
fn yacrypto(_py: Python, m: &amp;amp;PyModule) -&amp;gt; PyResult&amp;lt;()&amp;gt; {
    m.add_class::&amp;lt;Crypto&amp;gt;()?;
    m.add_function(wrap_pyfunction!(generate_key, m)?).unwrap();
    Ok(())
}


#[cfg(test)]
mod tests {
    use base64;
    #[test]
    fn works(){

        // create rsa
        let rsa = openssl::rsa::Rsa::generate(1024).unwrap();
        // create public key 
        let public_key = rsa.public_key_to_pem().unwrap();
        println!("{:?}", String::from_utf8(public_key.clone()));
        let private_key = rsa.private_key_to_pem().unwrap();


        let data = "hellowo\n\t\rrld";
        // public encrypt 
        let mut buf:Vec&amp;lt;u8&amp;gt; = vec![0;rsa.size() as usize];
        let rsa_pub = openssl::rsa::Rsa::public_key_from_pem(&amp;amp;public_key).unwrap();
        let _ = rsa_pub.public_encrypt(data.as_bytes(), &amp;amp;mut buf , openssl::rsa::Padding::PKCS1);

        // private decrypt =&amp;gt; 
        let data = buf;
        let mut buf:Vec&amp;lt;u8&amp;gt; = vec![0;rsa.size() as usize];
        let rsa_pri  = openssl::rsa::Rsa::private_key_from_pem(&amp;amp;private_key).unwrap();
        if let Ok(size) = rsa_pri.private_decrypt(&amp;amp;data, &amp;amp;mut buf, openssl::rsa::Padding::PKCS1){
            let real_size = if size &amp;gt; 1024 {1024} else {size};
            let buf = &amp;amp;buf[..real_size];
            let base64_string = openssl::base64::encode_block(&amp;amp;buf);
            let result = base64::decode(base64_string);
            println!("{:?}",result);
            // println!("buf =&amp;gt; {:?}",openssl::base64::encode_block(&amp;amp;buf))

            let echo_str = String::from_utf8((&amp;amp;buf).to_vec()).unwrap();
            println!("{:?}",echo_str);

        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="推荐阅读"&gt;推荐阅读&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://www.upyun.com/tech/article/654/webpack%20%E4%BB%8E%200%20%E5%88%B0%201%20%E6%9E%84%E5%BB%BA%20vue.html" rel="nofollow" target="_blank" title=""&gt;webpack 从 0 到 1 构建 vue&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.upyun.com/tech/article/653/Ansible%20%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8.html" rel="nofollow" target="_blank" title=""&gt;Ansible 快速入门&lt;/a&gt;&lt;/p&gt;</description>
      <author>upyun</author>
      <pubDate>Thu, 14 Oct 2021 15:13:02 +0800</pubDate>
      <link>https://ruby-china.org/topics/41771</link>
      <guid>https://ruby-china.org/topics/41771</guid>
    </item>
    <item>
      <title>请求帮助 Ruby [hash] 与 Python [dict]</title>
      <description>&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;007&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;a: &lt;/span&gt;&lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;b: &lt;/span&gt;&lt;span class="s1"&gt;'2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;c: &lt;/span&gt;&lt;span class="s1"&gt;'3'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:a&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:b&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:c&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

&lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;010&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;011&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;c&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="mi"&gt;30&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;怎样用 ruby 得到和 python 同样得结果呢？&lt;/p&gt;

&lt;p&gt;已经尝试过：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;015&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;space: &lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;016&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;space: &lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;
&lt;span class="c1"&gt;# space: a string that is put after, a : or , delimiter (default: '')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ruby json string &lt;code&gt;:&lt;/code&gt; 或 &lt;code&gt;,&lt;/code&gt; 后无空格&lt;/p&gt;

&lt;p&gt;py   json string &lt;code&gt;:&lt;/code&gt; 或 &lt;code&gt;,&lt;/code&gt; 后存在空格 &lt;/p&gt;</description>
      <author>dawei</author>
      <pubDate>Mon, 15 Jun 2020 13:21:06 +0800</pubDate>
      <link>https://ruby-china.org/topics/39971</link>
      <guid>https://ruby-china.org/topics/39971</guid>
    </item>
    <item>
      <title>5 分钟入门 IPython 和 Jupyter Notebook</title>
      <description>&lt;p&gt;这两个都是关于交互式计算的环境。
IPython 是一个优化的交互环境，没有太多可说的。&lt;/p&gt;

&lt;p&gt;Jupyter 是一个跨语言的交互计算环境，Notebook 是一个 web 应用，可以把交互过程记录和保存起来。&lt;/p&gt;

&lt;p&gt;1 安装 anoconda，比较容易。&lt;/p&gt;

&lt;p&gt;2 终端里运行 jupyter notebook，顺利的话浏览器里会看到一个页面。&lt;/p&gt;

&lt;p&gt;3 点右上角的新建，就打开一个新的 notebook。&lt;/p&gt;

&lt;p&gt;4 我演示一个画图的小例子。&lt;/p&gt;

&lt;p&gt;首先执行这个，注意前面的%，&lt;/p&gt;

&lt;p&gt;&lt;code&gt;%matplotlib inline&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;接下来就可以画图了。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2020/8c64ae1b-ea78-4dcd-aa30-cff6973158a4.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="结束"&gt;结束&lt;/h2&gt;
&lt;p&gt;感觉还是很简单和方便的，也挺有趣吧。&lt;/p&gt;

&lt;p&gt;顺便再说一个类似 github 的网站 gitee，优点是速度快，而且提供了轻 pr 功能，参与开源不需要 clone。&lt;/p&gt;</description>
      <author>chenge</author>
      <pubDate>Sat, 09 May 2020 16:37:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/39850</link>
      <guid>https://ruby-china.org/topics/39850</guid>
    </item>
    <item>
      <title>爬虫入门基础-HTTP 协议过程</title>
      <description>&lt;p&gt;当我们在浏览器中输入 URL 后，会发生什么？比如&lt;a href="https://www.baidu.com" rel="nofollow" target="_blank"&gt;https://www.baidu.com&lt;/a&gt;
我们可以用 curl -v &lt;a href="https://www.baidu.com" rel="nofollow" target="_blank"&gt;https://www.baidu.com&lt;/a&gt; 来研究这个过程，会发现总共有 4 个子过程。
1、查找域名对应的 IP 地址&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rebuilt URL to: &lt;a href="https://www.baidu.com/" rel="nofollow" target="_blank"&gt;https://www.baidu.com/&lt;/a&gt;*   Trying 14.215.177.38...
我们可以通过 nslookup &lt;a href="http://www.baidu.com%E6%A8%A1%E6%8B%9F%E8%BF%99%E4%B8%AA%E8%BF%87%E7%A8%8B%EF%BC%8Cdns%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%94%E5%9B%9E%E4%B8%A4%E4%B8%AAIP%E5%9C%B0%E5%9D%80%EF%BC%8Ccurl%E4%BC%9A%E9%9A%8F%E6%9C%BA%E9%80%89%E7%94%A8%E5%85%B6%E4%B8%AD%E4%B8%80%E4%B8%AAIP%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%9B%E8%A1%8C%E8%AE%BF%E9%97%AE%E3%80%82" title=""&gt;www.baidu.com 模拟这个过程，dns 服务器返回两个 IP 地址，curl 会随机选用其中一个 IP 服务器进行访问。&lt;/a&gt;
&lt;img src="https://l.ruby-china.com/photo/2020/cf1c0ad9-3f75-4ef2-85d3-4ccf6acc6d8a.png!large" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2020/77191dc0-9ea8-44d7-803c-0ac41bd022a4.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2、向 IP 对应的服务器发送 SSL 协商请求，进行 SSL 协商&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connected to &lt;a href="http://www.baidu.com" rel="nofollow" target="_blank" title=""&gt;www.baidu.com&lt;/a&gt; (14.215.177.38) port 443 (#0)
下图是通过 Wireshark 抓包获取的协商过程。
&lt;img src="https://l.ruby-china.com/photo/2020/e05ce92a-ca03-45ad-823b-e769407bef70.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3、SSL 协商完成后，向 IP 对应服务器发起 GET 请求&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GET / HTTP/1.1
Host: &lt;a href="http://www.baidu.com" rel="nofollow" target="_blank" title=""&gt;www.baidu.com&lt;/a&gt;
User-Agent: curl/7.54.0
网站会检查是不是真的浏览器访问。需加上 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.1276.73 Safari/537.36，表明你是浏览器访问即可。有时还会检查是否带Referer信息还会检查你的Referer是否合法。
&lt;img src="https://l.ruby-china.com/photo/2020/4e33d46c-4a6b-48eb-bc37-e9a0185793cd.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;4、服务器响应请求，发回网页内容
&amp;lt; HTTP/1.1 200 OK
&amp;lt; Connection: keep-alive
&amp;lt; Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
&amp;lt;
&amp;lt;!DOCTYPE html&amp;gt;...&lt;/p&gt;</description>
      <author>suger77</author>
      <pubDate>Wed, 06 May 2020 17:32:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/39835</link>
      <guid>https://ruby-china.org/topics/39835</guid>
    </item>
    <item>
      <title>告别 Ruby，转向 Python</title>
      <description>&lt;p&gt;很久没来了，我打算转向数据分析、数据挖掘、机器学习方向，所以只好选择 python 了。我看好的 elixir 似乎也没什么起色。&lt;/p&gt;

&lt;p&gt;分享免费公开课：&lt;/p&gt;

&lt;p&gt;Python 数据分析与展示 (北京理工大学 )
课程介绍了 numpy 等三个基础包，以及 anoconda IDE。我也刚开始学习，感觉老师讲解很清晰。&lt;/p&gt;

&lt;p&gt;&lt;span class="embed-responsive embed-responsive-16by9"&gt;&lt;iframe class="embed-responsive-item" src="//player.bilibili.com/player.html?aid=10101509" allowfullscreen=""&gt;&lt;/iframe&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <author>chenge</author>
      <pubDate>Wed, 15 Apr 2020 09:25:58 +0800</pubDate>
      <link>https://ruby-china.org/topics/39745</link>
      <guid>https://ruby-china.org/topics/39745</guid>
    </item>
    <item>
      <title>Python 新手] 爬虫练习：爬取起点中文网的小说排行并存入 excel 表格中</title>
      <description>&lt;p&gt;使用的 python 库
1.request 库，用于向服务器发起请求信息。
2.lxml 库，用于解析服务器返回的 HTML 文件。
3.time 库，设置爬取时间差，防止短时间内多次页面请求而被限制访问。
4.xwlt 库，用于将数据存入 excel 表格之中。&lt;/p&gt;

&lt;p&gt;爬取思路
1.爬取页面的网址为&lt;a href="https://www.qidian.com/all?page=1page%E7%9A%84%E5%80%BC%E4%B8%8D%E4%B8%80%E6%A0%B7%EF%BC%8C%E7%94%B1%E6%AD%A4%E5%8F%AF%E4%BB%A5%E5%BE%97%E5%88%B0%E6%89%80%E6%9C%89%E9%A1%B5%E9%9D%A2%E7%9A%84%E7%BD%91%E5%9D%80%E3%80%82" rel="nofollow" target="_blank"&gt;https://www.qidian.com/all?page=1page的值不一样，由此可以得到所有页面的网址。&lt;/a&gt;，经过手动浏览可以发现页面之间
2.需要爬取的信息如下图所示：
在这里插入图片描述
3.在信息提取完成之后使用 xlwt 库将它们存入 excel 表格中。&lt;/p&gt;

&lt;p&gt;爬虫代码
import xlwt
from lxml import etree
import request
import time&lt;/p&gt;

&lt;p&gt;#伪装请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36'
                  ' (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36'
    }&lt;/p&gt;

&lt;p&gt;all_info_list = [] #存储每部小说的各种信息列表&lt;/p&gt;

&lt;p&gt;#定义获取爬虫信息的函数
def get_info(url):
    res = requests.get(url, headers=headers)
    selector = etree.HTML(res.text)
    #采用 xpath 方法对网页信息进行搜索
    infos = selector.xpath('//ul[&lt;a href="/class" class="user-mention" title="@class"&gt;&lt;i&gt;@&lt;/i&gt;class&lt;/a&gt;="all-img-list cf"]/li') #找到信息的循环点
    for info in infos:
        title = info.xpath('div[2]/h4/a/text()')[0]
        author = info.xpath('div[2]/p[&lt;a href="/class" class="user-mention" title="@class"&gt;&lt;i&gt;@&lt;/i&gt;class&lt;/a&gt;="author"]/a[&lt;a href="/class" class="user-mention" title="@class"&gt;&lt;i&gt;@&lt;/i&gt;class&lt;/a&gt;="name"]/text()')[0]
        style1 = info.xpath('div[2]/p[&lt;a href="/class" class="user-mention" title="@class"&gt;&lt;i&gt;@&lt;/i&gt;class&lt;/a&gt;="author"]/a[2]/text()')[0]
        style2 = info.xpath('div[2]/p[&lt;a href="/class" class="user-mention" title="@class"&gt;&lt;i&gt;@&lt;/i&gt;class&lt;/a&gt;="author"]/a[3]/text()')[0]
        style = style1+'-'+style2
        complete = info.xpath('div[2]/p[&lt;a href="/class" class="user-mention" title="@class"&gt;&lt;i&gt;@&lt;/i&gt;class&lt;/a&gt;="author"]/span/text()')[0]
        introduce = info.xpath('div[2]/p[&lt;a href="/class" class="user-mention" title="@class"&gt;&lt;i&gt;@&lt;/i&gt;class&lt;/a&gt;="intro"]/text()')[0].strip()
        info_list = [title, author, style, complete, introduce]
        all_info_list.append(info_list)
    #爬取成功后等待两秒
    time.sleep(2)&lt;/p&gt;

&lt;p&gt;#主程序入口
if &lt;strong&gt;name&lt;/strong&gt; == "&lt;strong&gt;main&lt;/strong&gt;":
    urls = ['&lt;a href="https://www.qidian.com/all?page=.format(str(i))" rel="nofollow" target="_blank"&gt;https://www.qidian.com/all?page=.format(str(i))&lt;/a&gt;{}' for i in range(1, 101)]
    for url in urls:
        get_info(url)
    #写好 excel 表格中的各个属性名
    header = ['书名', '作者', '类型', '状态', '简介']
    book = xlwt.Workbook(encoding='utf-8')
    sheet = book.add_sheet('sheet1')
    for h in range(len(header)):
        sheet.write(0, h, header[h])
    i = 1
   #将提取到的信息写入 excel 表格中
    for list in all_info_list:
        j = 0
        for data in list:
            sheet.write(i, j, data) 
            j += 1
        i += 1
    #将信息保存在 D:/reptile 目录下的 xiaoshuo.xls 文件中
    book.save('D:/reptile/xiaoshuo.xls')&lt;/p&gt;

&lt;p&gt;爬取结果
在这里插入图片描述
&lt;img src="https://l.ruby-china.com/photo/2020/c5637c73-f192-4039-bcde-847011e5b945.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;总结
虽然能成功的爬取小说的大部分信息，但是由于小说的字数信息在网页上并不是不变的，使用没能爬取到小说的字数信息，本次爬虫练习基本上完成了爬虫的功能，但在复杂页面的爬取上面还得继续学习。&lt;/p&gt;</description>
      <author>suger</author>
      <pubDate>Wed, 08 Apr 2020 17:34:02 +0800</pubDate>
      <link>https://ruby-china.org/topics/39722</link>
      <guid>https://ruby-china.org/topics/39722</guid>
    </item>
    <item>
      <title>Python 基本算法：快速排序</title>
      <description>&lt;p&gt;快速排序是一种有趣的算法，也是软件工程师的最爱，它具有一些独特的优势和怪癖值得研究。快速排序可以非常高效，通常优于合并排序，尽管在某些情况下可以使其像气泡排序一样缓慢。与往常一样，我们将首先深入探讨此特定算法的工作原理，然后再探讨其行为方式的更细微之处。&lt;/p&gt;
&lt;h2 id="快速排序：概述"&gt;快速排序：概述&lt;/h2&gt;
&lt;p&gt;我们将从一个未排序的数组开始：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arr = [9,7,4,2,3,6,8,5,1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;快速排序的工作原理是从数组内部的某个位置选择一个项目，然后将所有项目与该项目进行比较。我们将此项目称为&lt;em&gt;枢轴&lt;/em&gt;。对数组进行排序后，枢轴左侧的所有内容都将小于枢轴，右侧的所有内容都将更大。快速排序使它从未排序数组的末端到中间。当它在左侧找到应该在右侧的项目，然后又在右侧找到了应该在左侧的项目时，它将交换这两个项目。&lt;/p&gt;

&lt;p&gt;您可以将数据透视表左侧的数组部分和数据透视表右侧的数组部分视为自己的子数组。现在，我们将它们视为自己的独特子数组，然后将算法递归应用于每个子数组。此递归除法和比较方案与合并排序采用相同的除法方法，因此，此处的并行操作可以轻松了解为什么平均需要&lt;em&gt;O（n * log（n））&lt;/em&gt;时间。&lt;/p&gt;

&lt;p&gt;为了说明这一点并分析分治法的实现方式，我们将选择元素尽可能靠近数组的中间位置。在算法的第一次迭代中，我们将选择数字&lt;code&gt;3&lt;/code&gt;，以中间为中心。选择我们的支点之后，这就是开始之前我们的子数组的样子：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://hackernoon.com/photos/LVKPdFUlNZdz67kwlPMB8hqj1BC3-eh333z24" title="" alt="img"&gt;&lt;/p&gt;

&lt;p&gt;那么，如何围绕枢轴有效地对这两个子数组进行排序？我们可以简单地遍历数组以查看右侧是否小于枢轴，然后将它们移至左侧，反之亦然。如果我们在左侧和右侧进行迭代，移动适当的项目，最终将得到一个位于轴心一侧的数组，以及一个包含其他未排序元素的数组。我们将需要遍历未排序数组的其余部分，并将属于另一个数组的项目推到另一个数组上。&lt;/p&gt;

&lt;p&gt;最终得到一对看起来像这样的数组：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://hackernoon.com/photos/LVKPdFUlNZdz67kwlPMB8hqj1BC3-t223zi9" title="" alt="img"&gt;&lt;/p&gt;

&lt;p&gt;现在，我们知道左侧数组中的所有内容都属于枢轴的左侧，而右侧数组中的所有内容都属于枢轴的右侧。现在，我们可以递归地将此逻辑应用于所有这些子数组，直到对每个项目进行排序为止。至少，这就是分而治之的方法。&lt;/p&gt;

&lt;p&gt;实际的快速排序算法不会将任何内容分解为较小的子数组。在这里，在执行比较之前将数组递归地分成几对的行为仅用于直观地说明&lt;em&gt;为什么&lt;/em&gt;其平均复杂度是&lt;em&gt;O（n * log（n））&lt;/em&gt;，我们将在后面进行探讨。&lt;/p&gt;
&lt;h2 id="时间和空间"&gt;时间和空间&lt;/h2&gt;
&lt;p&gt;虽然我们在先前的安装中已经讨论了很多时间复杂性，但是我们还需要讨论的一件事是&lt;em&gt;空间复杂性&lt;/em&gt;。如果执行得当，快速排序算法实际上不会递归地划分馈入自身的子数组。在介绍它做什么之前，让我们看一下为什么它&lt;em&gt;不这样&lt;/em&gt;做。我们可以参考本系列前面部分之一的“合并排序”的 Python 代码：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def merge_sort(unsorted):
    if len(unsorted) &amp;gt; 1:
        mid = len(unsorted)//2
        left = merge_sort(unsorted[:mid])
        right = merge_sort(unsorted[mid:])  
        result = merge(left, right)
        return result
    else:
        return unsorted
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这里，我们可以开始分析它如何利用空间。它需要一个未排序的数组，并分配&lt;em&gt;另外两个&lt;/em&gt;数组，每个数组的大小是传递的数组大小的一半。然后，将这两个数组都馈送到同一函数中，该函数再次递归地为另外 2 个数组分配空间。因此，例如，让我们看一个包含 8 个元素的数组。在第一次迭代中，我们总是为新数组分配&lt;em&gt;n / 2 个&lt;/em&gt;空间，从整个左侧向下移动，然后递归地向上移动并移至右侧。这里确切的空间复杂度并不重要，要理解的重要一点是它需要额外的空间，并且为这些操作分配和释放内存会影响性能。&lt;/p&gt;

&lt;p&gt;除了分配额外的空间来保存正在处理的子数组外，还可以仅传递概述&lt;em&gt;原始数组上&lt;/em&gt;正在处理的子数组的索引的函数。这允许通过直接在实际数组上执行操作来对数组进行排序，这称为&lt;em&gt;就地排序&lt;/em&gt;。&lt;/p&gt;
&lt;h2 id="到位排序"&gt;到位排序&lt;/h2&gt;
&lt;p&gt;就地排序具有仅占用&lt;em&gt;O（1）&lt;/em&gt;额外空间的优点。假设您的“快速排序”功能只有 3 个变量：枢轴，左侧和右侧边界。如果使用 C 语言编写，则意味着每个函数调用仅需为 3 个变量分配空间，这可能只是 4 个字节的无符号整数或总共 12 个字节。传递给它的数组是否为 40,000,000（40,000,000）无关紧要，它在调用时仍只需要分配 12 个字节。这就是为什么在进行原位排序时认为它具有&lt;em&gt;O（1）&lt;/em&gt;空间复杂性，所需空间量恒定且不会增长的原因。&lt;/p&gt;

&lt;p&gt;在较早的概述中，该算法被解释为在子数组上进行手动迭代，并且仅在将项目与数据点进行比较之后才移动项目。就地执行此操作需要稍有不同的方法来执行相同的操作。考虑我们原始的未排序数组&lt;code&gt;arr&lt;/code&gt;， &lt;code&gt;[9,7,4,2,3,6,8,5,1]&lt;/code&gt;。有 9 个项目，如果我们选择了中间项目， &lt;code&gt;arr[4]&lt;/code&gt;，我们的重点是 &lt;code&gt;3&lt;/code&gt;。与其创建单独的左右数组，不如通过设置一个&lt;code&gt;left index&lt;/code&gt;还有一个 
[&lt;a href="https://www.objectx.cn/forum.php?mod=forumdisplay&amp;amp;fid=45&amp;amp;filter=typeid&amp;amp;typeid=13:" rel="nofollow" target="_blank"&gt;https://www.objectx.cn/forum.php?mod=forumdisplay&amp;amp;fid=45&amp;amp;filter=typeid&amp;amp;typeid=13:&lt;/a&gt;] &lt;a href="https://www.objectx.cn/forum.php?mod=forumdisplay&amp;amp;fid=45&amp;amp;filter=typeid&amp;amp;typeid=13" rel="nofollow" target="_blank"&gt;https://www.objectx.cn/forum.php?mod=forumdisplay&amp;amp;fid=45&amp;amp;filter=typeid&amp;amp;typeid=13&lt;/a&gt; "left index"&lt;/p&gt;

&lt;p&gt;，它将从数组的左右边界开始。
我们从左边的项目开始，然后将其与我们的数据中心进行比较。如果左侧的项目小于枢轴，也就是说，左侧枢轴指向的项目&lt;em&gt;属于&lt;/em&gt;枢轴的左侧，则我们将&lt;code&gt;left index&lt;/code&gt;
向前转发并比较该数字。我们继续前进&lt;code&gt;[left index][https://www.objectx.cn/forum.php?mod=forumdisplay&amp;amp;fid=45&amp;amp;filter=typeid&amp;amp;typeid=13]&lt;/code&gt;向前，直到找到不属于枢轴左侧的项目。找到此类物品后，我们将停止&lt;code&gt;left index&lt;/code&gt;
然后开始比较 &lt;code&gt;right index&lt;/code&gt;到枢纽。当左侧的项目属于右侧，而右侧的项目属于左侧时，将交换这两个项目。自第一项以来&lt;code&gt;left index&lt;/code&gt; 看着， &lt;code&gt;arr[0]&lt;/code&gt;，是 &lt;code&gt;9&lt;/code&gt;
，它属于枢轴的右侧，我们从 &lt;code&gt;arr[0]&lt;/code&gt;。自第一项以来&lt;code&gt;right index&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;右侧枢轴选择的平均时间比中位数 3 的&lt;em&gt;时间长 30 倍&lt;/em&gt;！右侧枢轴选择比其他方法快得多，但&lt;em&gt;只有&lt;/em&gt;在传递了真正随机的，未排序的数组&lt;em&gt;时才&lt;/em&gt;起作用。如果该优点通过了大部分或完全已经排序的数组，则该优点会很快消失并严重损害性能。&lt;/p&gt;

&lt;p&gt;虽然在完全随机和完全排序的列表中，随机选择的枢轴的行为几乎完全相同，但是在处理可以进行某种排序的数组时，中位数 3 枢轴选择显然是赢家。实际上，在所有类别中，“中位数 3”选择方案花费了大约 2/3 的时间作为随机选择方案。这是因为在这种情况下，中位数 3 &lt;em&gt;始终&lt;/em&gt;会创建&lt;em&gt;完美&lt;/em&gt;平衡的数组，而随机选择的 3 会生成合理平衡但仍不平衡的数组。&lt;/p&gt;</description>
      <author>lml</author>
      <pubDate>Thu, 02 Apr 2020 23:26:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/39695</link>
      <guid>https://ruby-china.org/topics/39695</guid>
    </item>
    <item>
      <title>开发利器，预设变量到 Python 控制台</title>
      <description>&lt;p&gt;原文地址：&lt;a href="https://www.jianshu.com/p/884e0b2f5a45" rel="nofollow" target="_blank"&gt;https://www.jianshu.com/p/884e0b2f5a45&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;在用 Rails 开发 Web 应用时，经常需要调试某些类的 API，看一下输出是什么。
比如执行 view helper API：&lt;a href="https://ruby-china.org/topics/3506" title=""&gt;https://ruby-china.org/topics/3506&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;最近用 mongo 存爬取的数据，但是对 mongo 的 CRUD 操作不熟悉，经常需要进入 python 控制台调试 API。&lt;/p&gt;

&lt;p&gt;每次都要敲入 &lt;code&gt;$ python&lt;/code&gt;，然后实例化 mongo 对象，期间还要加载 &lt;code&gt;.env&lt;/code&gt; 文件里的环境变量，过程实在麻烦。所以想像 Rails 一样，搭建一个预设变量的控制台，提前创建出 mongo 对象，方便自己调试。&lt;/p&gt;

&lt;p&gt;首先新建一个文件 &lt;code&gt;console.py&lt;/code&gt;，把下面代码拷贝进去&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pymongo&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt;

&lt;span class="n"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# 读取环境变量
&lt;/span&gt;
&lt;span class="c1"&gt;# 设置常用的变量
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pymongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MONGO_URI&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MONGO_DATABASE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="n"&gt;question_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;question_stats&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;answers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;answers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;interact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;# 进入python控制台，并且传入本地变量
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在控制台执行，就能访问提前设置好的变量啦&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python console.py            
Python 3.7.4 (default, Aug 13 2019, 15:17:50) 
[Clang 4.0.1 (tags/RELEASE_401/final)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
&amp;gt;&amp;gt;&amp;gt; db
Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'zhihu_spider')
&amp;gt;&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;想起以前开发 Android App 时，也没有控制台。调试一个涉及到页面的几个 API，还得打包，安装 App 到手机上（现在打包速度提升不少了），点一下按钮或者进入主页面来触发代码，这个开发效率，一言难尽，想哭，哈哈&lt;/p&gt;</description>
      <author>hjiangwen</author>
      <pubDate>Sun, 29 Mar 2020 12:52:31 +0800</pubDate>
      <link>https://ruby-china.org/topics/39680</link>
      <guid>https://ruby-china.org/topics/39680</guid>
    </item>
    <item>
      <title>新手使用 Python 采集数据的尴尬事之代理 IP</title>
      <description>&lt;p&gt;背景，原本不是爬虫的，因公司业务需求需要一些前程无忧的数据，被迫上岗，简单的学了些 python。因为网站的特殊性，访问次数多了就要封 ip，意味着还要找 ip。所以在网上随便找了家代理叫亿牛云，然后跟客服沟通了下我这个小白的需求，客服跟我推介了他们家的爬虫动态转发代理，说适合我这样的小白使用，使用方式比较简单，我就抱着试试的想法接受了。客服给我发了一段关于 python 怎么使用他们家代理的代码示例，&lt;a href="https://www.16yun.cn/help/ss_demo/#1python" rel="nofollow" target="_blank"&gt;https://www.16yun.cn/help/ss_demo/#1python&lt;/a&gt;。打开看了之后我以为示例就是可以直接使用的，而且客服也说过代码示例可以直接复制使用，我就直接复制然后把目标网站改成了我自己的，代码如下&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#! -*- encoding:utf-8 -*-
&lt;/span&gt;
        &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
        &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

        &lt;span class="c1"&gt;# 要访问的目标页面
&lt;/span&gt;        &lt;span class="n"&gt;targetUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;www.mkt.51job.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="c1"&gt;# 要访问的目标HTTPS页面
&lt;/span&gt;        &lt;span class="c1"&gt;# targetUrl = "https://www.mkt.51job.com"
&lt;/span&gt;
        &lt;span class="c1"&gt;# 代理服务器(产品官网 www.16yun.cn)
&lt;/span&gt;        &lt;span class="n"&gt;proxyHost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;t.16yun.cn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;proxyPort&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;31111&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="c1"&gt;# 代理隧道验证信息
&lt;/span&gt;        &lt;span class="n"&gt;proxyUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;proxyPass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="n"&gt;proxyMeta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://%(user)s:%(pass)s@%(host)s:%(port)s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxyHost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxyPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxyUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxyPass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;# 设置 http和https访问都是用HTTP代理
&lt;/span&gt;        &lt;span class="n"&gt;proxies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxyMeta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxyMeta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


        &lt;span class="c1"&gt;#  设置IP切换头
&lt;/span&gt;        &lt;span class="n"&gt;tunnel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Proxy-Tunnel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tunnel&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;



        &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就去运行，结果是这样&lt;/p&gt;

&lt;p&gt;然后就去找客服，结果人家说那只是代码示例，里面的代理参数是需要他们提供重新配置才可以使用，顿时就觉得自己真的很小白，然后请客服开通了代理测试了下，结果运行起了，对我这样的小白来还好使用的方式简单，不然又要各种查资料了。我想很多人都是这样吧，刚开始的时候各种小白问题都有可能存在，看来不管是哪个领域，还是要深入的学习才好！&lt;/p&gt;</description>
      <author>suger</author>
      <pubDate>Wed, 25 Mar 2020 17:32:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/39665</link>
      <guid>https://ruby-china.org/topics/39665</guid>
    </item>
    <item>
      <title>Python 学习初级编程实例（一）</title>
      <description>&lt;p&gt;题目：有 1、2、3、4 四个数字，能组成多少个互不相同且无重复数字的三位数？都是多少？&lt;/p&gt;

&lt;p&gt;程序分析：可填在百位、十位、个位的数字都是 1、2、3、4。组成所有的排列后再去 掉不满足条件的排列。&lt;/p&gt;

&lt;p&gt;程序源代码：&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;img src="https://l.ruby-china.com/photo/2020/32ad4ada-465e-4d22-883a-641e411eb44e.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;题目：一个整数，它加上 100 和加上 268 后都是一个完全平方数，请问该数是多少？&lt;/p&gt;

&lt;p&gt;程序分析：在 10000 以内判断，将该数加上 100 后再开方，加上 268 后再开方，如果开方后的结果满足如下条件，即是结果。请看具体分析：&lt;/p&gt;

&lt;p&gt;程序源代码：&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;268&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;268&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class="nf"&gt;print &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2020/509d6d17-652c-40dd-bf7b-9002f285be3e.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;题目：输入三个整数 x,y,z，请把这三个数由小到大输出。&lt;/p&gt;

&lt;p&gt;程序分析：我们想办法把最小的数放到 x 上，先将 x 与 y 进行比较，如果 x&amp;gt;y 则将 x 与 y 的值进行交换，然后再用 x 与 z 进行比较，如果 x&amp;gt;z 则将 x 与 z 的值进行交换，这样能使 x 最小。&lt;/p&gt;

&lt;p&gt;程序源代码：&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;raw_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#append() 方法向列表的尾部添加一个新的元素
&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;#sort() 函数用于对原列表进行排序，如果指定参数，则使用比较函数指定的比较函数
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>suger</author>
      <pubDate>Tue, 17 Mar 2020 17:10:54 +0800</pubDate>
      <link>https://ruby-china.org/topics/39623</link>
      <guid>https://ruby-china.org/topics/39623</guid>
    </item>
  </channel>
</rss>
