参加中国pycon2017

今天一天都在浦软大厦听pycon2017的演讲。一天听下来,觉得还是有收获的,演讲质量好坏不一,挑几个我觉得不错的分享一下。

质量比较好的两个长演讲,侯凯老师,分享了爱因互动的搭建对话机器人,介绍了他们公司的架构,问答机器人的基本功能、难点等。包括后端的架构,消息如何路由,如何统一不同平台消息的格式。消息服务主要有消息通道(缩短消息的传输耗时,使用Amazon SQS等)和消息路由(基于消息内容的路由器)。对于IO密集型的任务使用异步Asyncio。但是需要显式的协程切换,对代码的入侵性大(这个之前也有人吐槽,asyncio很有传染性,一不留神整个项目都是异步代码了。另一种方案是Gevents,对代码的侵入性小,可以屏蔽细节,但是缺点是对代码细节的掌控就没有了。

其余的演讲内容是灵活性以及部署上线。这部分我笔记不详细,就不说了。

最后侯凯老师分享了向Python3的迁移的一些经验,鼓励使用Python3.吐槽一下,这竟然是一整天唯一一次听到python3的地方……

达达的廖瑞奇,介绍了达达平台的搭建。达达去年的演讲质量也很高。内容也挺有意思的,主要分了三个部分,包括动态定价,时间预估,路径规划。

动态定价主要是因为供需不平衡,目前美团、滴滴、饿了么等众包平台都有动态定价,目的是为了(优化目标)提高接单率,缩短接单时间。影响一个订单的因素有空间(空间热力图)、时间、天气等,另外订单的因素比如距离、重量、楼层等也会影响供需。

路径规划这个很有意思,是一个NP-hard问题。规划一个路径需要满足1.必须先取件再送件;2.在满足1的情况下总路径越短越好。主要用了遗传算法(启发式算法,比较复杂,解决方案没有听懂……)。

订单指派提到了一个技术,基于地理位置的hash:GEO-hash。

其他的王剑锋老师讲了GOODERP开发历程,这是一个开源的ERP。感觉此人有很深的执念啊,多次被别人劝退(不要开源了)还是走到了现在,佩服佩服。

Ethereum on Python演讲感觉比较乱,有时将区块链原理有时将他们自己的技术,没怎么听明白。感觉如果不明白区块链听了这个演讲也是白听,明白了更是白听。

华为有一个快速演讲,分享了一套能分析Python性能的工具,不过还没开源,听说明年春天能开源。现场还来了CPython唯一的中国core开发者,可惜没有演讲。

其他没啥好说的了。都是一些tensorflow的我也听不懂。pycon被人吐槽了不是一次两次了,主题跟python都没什么关系,哎。

另外本次我唯一想吐槽的就是七牛的姚唐仁,主题是《富媒体机器学习平台搭建》。演讲混乱,语无伦次。一直在重复为什么要机器学习,为什么要用多媒体(富媒体忽悠人的吧),人工审核不过来啊,他能展开说成:如果人工审核,需要多少多少时间,我们有多少数据,用机器学习怎么样,加上用人工…… 巴拉巴拉……。完全没干货!根本没用心准备,就是靠着七牛大厂来吹牛来了。

贴几张活动图片,主办素质还是可以的,真心希望国内的python发展好,那些目的不纯(不是本着分享技术)的演讲还是别来了。

华为大牛 定制CPython解释器

抽奖ps4

茶歇蛋糕

志愿者合影

全体参会人员和嘉宾合影

 

爬虫常用的工具

前面写过一篇《如何成为一名爬虫工程师》,这篇博文介绍一些写爬虫的神器。比如查看网络请求,模拟网络请求等。本文不会涉及到编程工具例如vim,tmux等(虽然这些工具我也极力推荐,应该人手一份)。

curl

cURL

curl是在命令行用url语法传输文本的工具。用curl可以模拟任何从浏览器发出的请求,并且是以纯文本的形式,所以任何header、cookies都一目了然。有时候你碰到“为什么在浏览器请求能成功,在代码中就不行”的问题时,用curl验证一下是最靠谱的办法。

curl的用法非常简单,curl + "url"就可以向一个url发送请求,显示服务器响应。-H "header"可以给请求加上headers。-i参数可以打印出响应头,大写的-I只打印响应头。-L参数开启301,302跳转等。学习会这些基本就可以上手了,希望详细的了解curl可以阅读《everything curl》这本书(免费书籍:PDF在线阅读

一个请求一般带有很多headers,一个一个输入太费劲了,型号,我们可以使用chrome的copy as curl功能。

此命令会将一个网络请求转换成curl放到剪切板,直接在命令行粘贴就可以了。

对了,写爬虫chrome是最大的神器,推荐阅读酷壳的《CHROME开发者工具的小技巧》

但是如此长的一个命令在命令行上修改起来特别麻烦(要让光标跳来跳去的),所以再推荐一个GUI的模拟发送请求的工具postman

postman

postman可以让你像在chrome中修改css那样,修改请求的header,cookies等任何东西。除了全平台的app之外,还有chrome app可以用。可以将请求保存下来,下次继续工作的时候可以使用。当然postman也支持从curl粘贴导入,这样我们可以把任意chrome的请求导入到postman。不过地方比较坑,我第一次花了很长时间才找到,在 import > Paste Raw Text (竟然不是Paste cURL)。这里提醒一下,我有一次用postman的请求怎么也不对,但是用curl直接在终端就对了,这说明postman有一些bug,不能完全模拟请求。同事推荐了paw,还没用过,下次用过再谈(好像是mac专用的)。

curl to requests

这是一个在线的工具,可以将curl命令转换成python的requests库代码写成的命令(还支持node和PHP)。这样就省去了很多手工排格式的麻烦操作(其实可以做成vim插件的讲)。

python -m “json.tool”

这是python自带的json库,不用任何安装,直接用echo str | python -m 'json.tool'就可以在终端格式化json(之前我一直傻傻的粘贴到vscode里面……)

可以配合curl使用,直接让返回的json可读。

如果你使用Vim,用Vim打开一个file.json文件,可以直接在buffer中输入:%!python -m 'json.too'来将当前buffer的json内容格式化。非常有用。

EditThisCookie

顾名思义,这是用来修改cookies的。如果你要模拟登陆,可以尝试一个一个删掉cookie看哪一个是起作用的。

Proxifier

写爬虫被封ip是很正常的事情。被封了就得挂代理去访问目标网站。浏览器可以用switch Omega,终端可以用proxychains。但还是有些不方便。p4又有一些bug,并不是所有终端程序都可以走代理的。

这个时候就可以使用proxifier自定义流量规则,甚至可以全局转发到代理,一个都跑不了,非常稳!

暂时就想起来这个多,以后有别的还会在这篇博客更新。

 

介绍Python2和Python3的兼容库six

Python2和Python3有很大的不同,six这个库为此提供了一个兼容的方案。使用six写的代码可以不用修改就运行在Python2或Python3上。注意这个six并不是让Python2写的代码兼容Python3,如果你需要迁移工具,那么你要找的是2to3。如果你的代码需要同时需要运行在Python2和Python3上,那么你就需要six!

原理其实很简单,six对Python2和Python3的名字等做了统一,比如Python2的字符串叫str,Python3叫unicode,那么就可以使用six.text_type。如果运行在Python2上,six.text_type就是unicode,如果运行在Python3上,six.text_type就是str

six库只有一个文件:six.py。这样做可以方便地拷贝到你的项目中。也可以使用pip安装。

(为什么叫six? 因为2 * 3 = 6。为什么不是+,因为*更牛逼)

下面讲一下six有哪些功能。

统一了Python2和Python3的类型

如上文提到的,使用six中的类型,能同时在Python2和Python3中正确运行,就不用自己在写代码的时候自己判断到底是Python2的unicode还是Python3的str了!

Python2:

Python3

(由于iPython不再支持Python2,所以博主用的REPL for python2是默认的,但是在这里不影响结果)

内部对象属性的重命名

Python3修改了一些解释器内部属性的名字,例如Python2的dictionary.iterlists()在Python3中变成了dictionary.lists(),(讲真我觉得Python3的名字改得好!)。

使用six可以同时兼容这两种名字,但是需要将调用的格式改为 six.iterlists(dictionary, **kwargs)

全部的名字兼容可以参考文档。(话说我不太明白为什么保留的是Python2的名字,不用Python3的)

移动模块的位置

Python3重新组织了很多模块的位置,例如Python2的HTMLParser,在Python3中是html.parser。

我们可以使用six导入:

其他

其他的内容可以在官方的文档找到,基本上就是通过six来调用,而不是自己对Python判断。包括:

  • 提供了二进制和文本数据的兼容
  • uniittest assert的兼容
  • urllib库改动的兼容
  • 高级的自定义move

参考

https://pythonhosted.org/six

 

我的WordPress全站HTTPS啦

终于,本站的强制HTTPS已经开启了,所有的HTTP流量都会被强制转向HTTPS。

虽在早在去年我就申请了HTTPS证书,也更换了独立ip支持了HTTPS,但是并没有强制开启HTTPS,因为我发现根据网上的教程设置.htasccess会失败。后来发先用了really simple SSL插件可以做到http://kawabangga.com跳转到HTTPS,还能帮你将站内绝对路径的链接自动换成HTTPS,看起来不错,就没再管。但是一直有一个遗留问题就是,http://www.kawabangga.com是跳不过去的。

今天小红老师帮我调了一下,最后发现了问题所在。不得不说,姜还是老的辣,要是我自己调我觉得永远搞不定。

问题描述:本在在西部数码的虚拟空间上,有独立ip,支持HTTPS访问,但是HTTP访问www不会跳转到HTTPS。

首先,我们按照网上普遍的教程,在.htaccess(这是apache的配置文件)写:

会发生无限重定向,浏览器无法打开网页。所以看起来是80端口判断永远为真。然后尝试了各种别的判断条件,比如:

也还是不行。

这时候,小红老师说,不是条件判断不正确,而是到你机器上的流量永远是HTTP流量。虽然使用了HTTPS协议,但是空间提供商在内网没有必要使用HTTPS(HTTPS有加密过程更加消耗资源),所以内部是使用HTTP传输数据的。HTTPS包到达空间商的时候,空间提供商使用HTTP协议将此包转发到你的虚拟空间上(就是HTTPS外面套了一层HTTP),所以到达apache的流量永远都是HTTP流量,就发生了无限重定向。

这时候我才发现,原来西部数码自己提供了一个设置方法(放在一个不知名的小页面上,坑),如下:

果然,可以发现这里使用的参数是HTTP:From-Https,是自定义的一个参数!

另外还有一个小插曲,多次修改.htaccess的时候,小红老师看见我把所有的参数都删掉还震惊了一下,我自信的说这些我都不知道是什么,没用。于是部署好HTTPS的跳转之后,除了首页所有的页面都404了……

原来我设置了伪静态,.htaccess里面需要做相应的设置的…… 最后的正确配置如下:

终于一切都正常了。这是我一年来的一块心病啊,终于解决了。

博客四年了,停靠在西部数码也有三年了,同事们都对虚拟空间,wordpress这种东西嗤之以鼻,不过我觉得挺好的。这个博客我上来除了写东西之外几乎没有花很多精力维护,有什么问题客服解决的也很及时。虽然有很多坑,但是我觉得VPS的坑更多。况且四年前我的技术维护vps肯定有很多安全问题。这几年来wordpress的写作体验一直在提升,我很满意。对博客的SEO,amp等新技术也支持的比较好。国外的vps速度慢,国内的需要备案,对我来说选择就很有限了。所以综上看来,这几乎是我最好的选择。还是多总结,能给自己带来收获,一年多一百少一百的倒无所谓。

 

理解Python的import

Python的import是一个使用非常频繁的操作,这是在一个模块中使用另一个模块的代码的操作(几乎所有的语言都有类似的语句)。import语句是最常用的方法,但不是唯一的方法,还有importlib.import_module() 和 __import__() 等。本文解释import语句。

import做了两件事情:

  1. 搜索该名字的module(其实使用的还是内置的__import__()方法)然后初始化一个对象
  2. 将结果与本地的一个变量名绑定(默认就是这个module的名字)。

意味着,这两句是等价的:import package as pk 和 pg = __import__('package')

值得注意的是,只调用__import__()只会执行搜索动作,初始化module对象,最后丢掉该对象(因为没有赋值操作)。这点很容易将内置的__imoprt__()方法和import语句搞混。

一、Packages

Python中只有一种module类型(type),所有的module,无论是C语言实现的,还是Python实现的,还是别的,都是这种了类型。为了使名字空间有等级,Python又有packages的概念。注意这里只是一个“概念”而并不是类型,packages只是一种特殊的“module”。官方文档中将packages比作文件系统的文件夹,module比作文件(但又不完全是)。所有的package都是module,而并不是所有的module都是package。

Python的package又有两种:

Regular package

Regular package就是一个包含 __init__.py文件的文件夹。当此包导入的时候,会执行这个__init__.py文件,然后就可以包名访问包内的名字了。例如下面的文件结构:

当导入parent.one时会执行parent/__init__.pyparent/one/__init__.py

Namespace package

这是PEP 420提出的。

Namespace package由各种不同的部分(portions)组成。portion可以是zip文件,可以使网络上的资源,可以是文件系统中某个位置的资源。Namespace package在文件系统中可能没有实际的文件来表示,可能是虚拟的module。

# TODO read 420 理解namespace

下面来讲import的搜索顺序。

二、搜索

开始搜索之前,需要一个“合适的名字”(来自import语法或者__import__()函数等),即一个通过点分隔的路径名。比如foo.bar.baz,Python会先导入foo,然后导入foo.bar,然后导入foo.bar.baz,如果中间有任何失败,就会抛出ModuleNotFoundError

缓存(sys.modules)

import首先搜索的地方是sys.modules,此为一个键值对的mapping,作为一个缓存,保存了中间形式的路径。所以如果之前import了 foo.bar.baz,那么sys.modules中就会存在foo, foo.bar, foo.bar.baz。每一个名字都对应一个module对象。

import的时候,如果在sys.modules中找到就会返回这个module对象,import过程结束。如果根据key找到了,但是value是None,就会抛出ModuleNotFoundError,如果key不存在,就继续搜索过程。

sys.modules是可写的,删除任何key并不会销毁对象(因为可能别的模块引用了这个模块),但是你可以将value设置为None强制抛出ModuleNotFoundError。下面的代码展示了这个操作(注意使用ipython结果不同,ipython在启动的时候import 了re库)。

要注意的是,如果你有module对象的引用(在sys.module的缓存),但是重新import了module对象,这两个module不会一致。也就是说你再引用一遍,代码中的该module还是从sys.module中的,所以你拿不到新的module。但是使用importlib.reload() 就可以将改module对象重新初始化一遍。这在REPL中测试代码的时候比较实用。

Finder和Loader

如果在缓存(sys.module)中没有找到的话,python就开始import机制来导入这个包。Python的import协议包括两部分:Finder和Loader。只要实现了这两部分,就算是一个importer,importer如果发现自己可以导入目标包的话,就会返回他们自己。顾名思义,Finder的任务是定位包,并不做载入的部分,Loader做真正载入包的工作。Python有三个默认的importer:一个可以导入build-in module,第二个可以导入frozen module, 第三个可以搜索import path。

三、加载

加载过程的伪代码如下:

注意一下细节:

  • sys.module已经存在目标模块的名字,import直接返回此模块
  • 在Loader执行模块代码之前,先将其放入到sys.module。这一点非常重要,可以防止循环导入(模块可能在代码中导入该模块自己)。
  • 如果loading失败,失败的模块(只有失败的模块)会从sys.module删除
  • 在module创建但是执行代码之前,import会设置 import-related 模块属性,作为后续的总结
  • 模块执行的部分才是模块的名字空间真正暴露的时候,这部分完全由loader处理。
  • loading的时候创建的模块、传入exec_module()的模块可能并不是最后返回的模块

Loader

Loader提供了loading最重要的部分:模块的执行。import机制会调用importlib.abc.Loader.exec_module(),参数只有一个,要执行的module对象。其返回值被丢弃。Loader必须满足两个要求:

  • module必须是一个Python的module对象,Loader必须在module的全局名字空间下(module.__dict__)执行module的代码
  • 如果不能执行代码,必须抛出ImportError,其余所有异常都会被忽略

子模块

当子模块的loading执行的时候,将和父模块的名字空间绑定。参考下例:

导入foo将使spamspam.foo属性:

就先写到这里,其实还有一个比较重要的话题就是模块的搜索顺序,以及子模块之间如何组织。新手尝尝遇到在子模块中遇到不能import其他模块的问题,以后再写吧。

彩蛋

import this可以看到Zen of Python

import antigravity可以看到一幅漫画

参考和了解更多

  1. python文档:https://docs.python.org/3/reference/import.html
  2. import hook和meta path