首页 开发编程 正文

php怎么记住单词 如何快速精通python

python的入门其实只需要掌握几个关键点就行,一、了解环境二、了解数据结构三、了解基本函数四、了解Nuypm计算包——————正文警告:它会自动创建一个本地环境localhost。(我就不用helloworld)灰色框是输入程序的地方,shift+回车执行灰色区域的代码,将Python脚本分成片段式运行。和excel的函数同理。负责...

php怎么记住单词,如何快速精通python?

既然是毫无基础,那就简单介绍一下吧,python的入门其实只需要掌握几个关键点就行,新手要先学会安装环境、了解数据结构、函数这些东西,再配合实操进行实践,基本就可以入门了。

提示!!文章内容较长,大约需要半个小时,可以先收藏再慢慢看,目录如下:

一、了解环境

二、了解数据结构

三、了解基本函数

四、了解Nuypm计算包

——————正文警告!!——————

一、环境

Python的编写环境,用Anaconda足矣。Anaconda是专业的数据科学计算环境,已经集成绝大部分包和工具,不需要多余的安装和调试。

Python版本建议3.0以上,不要选择2.7的版本,否则你会被无尽的中文编码问题困扰。

Anaconda在官网下载,选择最新版本,约400MB。

完成安装后,Win版本会多出几个程序,Mac版本只有一个Navigator导航。数据分析最常用的程序叫Jupyter,以前被称为IPython Notebook,是一个交互式的笔记本,能快速创建程序,支持实时代码、可视化和Markdown语言。

点击Jupyter进入,它会自动创建一个本地环境localhost。

点击界面右上角的new,创建一个python文件。

开始你的Python

界面上部是工具栏,编辑撤回运行等,下面是快捷操作,大家以后会熟悉的。页面正中便是脚本执行的地方,我们输入自己第一行代码吧:

(我就不用hello world)灰色框是输入程序的地方,回车是换行,shift+回车执行灰色区域的代码,它的结果会直接在下面空白处出现。这就是Jupyter交互式的强大地方,将Python脚本分成片段式运行,尤其适合数据分析的摸索调整工作。

这里的print叫函数,和excel的函数同理,是程序执行的主体,负责将输入转化成输出(函数留在下一篇细讲)。这里将hello qinlu这段文字输出。新手可能会奇怪为什么要加引号,这种用引号括起来的文字在程序中叫字符串。

Python是一门计算机语言,它的逻辑和自然语言不一样,编程语言的目的是执行任务,所以它不能有歧义。为了规避各种歧义,人们创造了语法规则,只有正确的语法,才能被转换成CPU执行的机器码。

先了解Python语法中的数据类型。计算机最开始只被用于数值运算,后来被赋予了各种丰富的数据类型。

上面两个是小学生都会的四则运算,在计算机语言中可没有那么简单。它涉及了两个数值类型,整数int和浮点数float。整数和浮点数在计算机内部存储的方式是不同的,我们不用知道具体原理,明确一点,整数运算是永远精确的,浮点运算则可能有误差。

两种数据类型也可以互换,通过int函数和float函数。

有了数值,必然有文本,程序中叫字符串,用英文引号括起来表示。单引号和双引号没有区别,所以"qinlu"和'qinlu'是等价的,引号是边界,输出的时候不会包含它。当字符串内本身包含引号时,也不影响使用。

需要注意的是,不论单引号还是双引号,一旦混用很容易出现错误。因为程序并不知道它是字符串的边界还是符号。

解决方法有两种,一种是使用三引号,三引号代表整体引用,而且包含换行。第二种是引号前面加\,它是转义字符,表示这个引号就是单纯的字符。

三引号也可以用来注释,通常是大段的文字解释,如果一句话,我们更习惯用#,#后面的内容均不会作为程序执行。

时间是特殊的数值类型,它将结合datetime模块讲解。

还有两个常见的数据类型,布尔值和空值。布尔值是逻辑判断值,只有True和False。

布尔值在IF语句和数据清洗中经常使用,利用其过滤。布尔值能和布尔值运算,不过这里是and、not、or作为运算符,Ttue and True = True,False and True = False,False and False = False,not True = False,True or False = True等。

空值是一个特殊的值,表示为None,None不等于0,0具有数学意义而None没有,None更多表示该值缺失。

整数,浮点数,字符串,布尔值,空值就是Python常见的数据类型。Python3对中文的支持比较友好,所以大家可以用中文作为字符串试一下print。

数据类型构成了变量的基础,变量可以是任意的数据类型。想要用变量,必须先赋予变量一个值,这个过程叫赋值。

我首先给a赋予了一个整数值1,然后改变它为字符串abc,变量在Python中没有固定的数值类型,这是Python最大的优点,所以它在数据分析中很灵活。这也是它被称为动态语言的原因,相对应的叫静态语言。

Python是大小写敏感的语言,所以a和A是有区别的,这点请牢记。另外变量名尽可能使用英文,不要拼音,英文的可读性是优于拼音的。

变量有两种拼写风格,一种叫驼峰,一种叫下划线,以用户ID为例。驼峰命名法为userId,以一串英文词语user和id组成变量,第一个词语的首字母小写,第二个词语开始的首字母均大写。下划线命名法为user_id,全部小写,用_分割单词。

一个变量的值可以被赋予另外一个变量,如果b变量之前有另外一个值,那么会被1覆盖。呈从上而下的执行关系。

初看a = a + 1好像有逻辑问题,其实这涉及到了程序执行的先后顺序,程序是先计算a+1的值得到2,然后将其赋予(覆盖)了a。等号右边的计算先于左边,这是从右到左的逻辑关系。

有变量,自然有常量,常量是固定不变的量,可是在Python中没有真正意义的常量,一切皆可变,它更多是习惯上的叫法,即一旦赋值,就不再改变了。

Python的基础数学运算符号有+,-,*,/,//,%。前面四个就是加减乘除,其中除法的结果一定是浮点数。后面两个符号是除法的特殊形式,//代表除法中取整数,%代表除法中取余数。

到这里,新手部分已经讲解完成。再来讲讲数据结构。

二、数据结构

Python一共有三大数据结构,它是Python进行数据分析的基础,分别是tuple元组,list数组以及dict字典。本文通过这三者的学习,打下数据分析的基础。

1、数组

数组是一个有序的集合,他用方括号表示。

num就是一个典型的数组。数组不限定其中的数据类型,可以是整数也可以是字符串,或者是混合型。

数组可以直接用特定的函数,函数名和Excel相近。

sum是求和,len则是统计数组中的元素个数。

上述列举的函数是数组内整体元素的应用,如果我只想针对单一的元素呢?比如查找,这里就要用到数组的特性,索引。索引和SQL中的索引差不多,都是用来指示数据所在位置的逻辑指针。数组的索引便是元素所在的序列位置。

注意,索引位置是从0开始算起,这是编程语言的默认特色了。num[0]指数组的第一个元素,num[1]指数组的第二个元素。

我们用len()计算出了数组元素个数是5,那么它最后一个元素的索引是4。若是数组内的元素特别多呢?此时查找数组最后一位的元素会有点麻烦。Python有一个简易的方法,可以用负数表示,意为从最后一个数字计算索引。

这里的num[4]等价于num[-1],num[-2]则指倒数第二个的元素。

再来一个新问题,如何一次性选择多个元素?例如筛选出数组前三个元素。在Python中,用:表示范围。

num[0:3]筛选了前三个元素,方括号左边是闭区间,右边是开区间,所以这里是num[0],num[1]和num[2],并不包含num[3]。这个方法叫做切片。

上述是索引的特殊用法,[0:]表示从第0个索引开始,直到最后一个元素。[:3]表示从第一个元素开始,直到第3个索引。

负数当然也有特殊用法。[-1:]表示从最后一个元素开始,因为它已经是最后一个元素了,所以只返回它本身。[:-1]表示从第一个元素开始到最后一个元素。num[-2:-1]和num[-3:-1]大同小异。

数组的增删查

我们已经了解数组的基本概念,不过仍旧停留在查找,它不涉及数据的变化。工作中,更多需要操纵数组,对数组的元素进行添加,删除,更改。

数组通过insert函数插入,函数的第一个参数表示插入的索引位置,第二个表示插入的值。

另外一种方式是append,直接在数组末尾添加上元素。它在之后讲到迭代和循环时应用较多。

如果要删除特定位置的元素,用pop函数。如果函数没有选择数值,默认删除最后一个元素,如果有,则删除数值对应索引的元素。

更改元素不需要用到函数,直接选取元素重新赋值即可。

到这里,数组增删改查已经讲完,但这只是一维数组,一维数组之上还有多维数组。如果现在有一份数据是关于学生信息,一共有三个学生,要求包含学生的姓名,年龄,和性别,应该怎么用数组表示呢?

有两种思路,一种是用三个一维数组分别表示学生的姓名,年龄和性别。

学生属性被拆分成多个数组,利用索引来表示其信息,这里的索引有些类似SQL的主键,通过索引查找到信息。但是这种方法并不直观,实际应用会比较麻烦,更好的方法是表示成多维数组。

所谓多维数组,是数组内再嵌套数组,图中表示的是一个宽度为3,高度为3的二维数组。此时student[0]返回的是数组而不是单一值。这种方法将学生信息合并在一起,比第一个案例更容易使用。

如果想选择第一个学生的性别,应该怎么办呢?很简单,后面再加一个索引即可。

现在尝试快速创建一个多维数组。

[0]*3将快速生成3个元素值为0的数组,这是一种快捷操作,而[row]*4则将其扩展成二维数据,因为是4,所以是3*4的结构。

这里有一个注意点,当我们想更改多维数组中的某一个元素而不是数组时,这种方式会错误。

按照正常的想法,martix[1][0]将会改变第二个数组中的第一个值为1,但是结果是所有数组的第一个值都变成1。这是因为在matrix = [row] * 4操作中,只是创建3个指向row的引用,可以简单理解成四个数组是一体的。一旦其中一个改变,所有的都会变。

比较稳妥的方式是直接定义多维数组,或者用循环间接定义。多维数组是一个挺重要的概念,它也能直接表示成矩阵,是后续很多算法和分析的基础(不过在pandas中,它是另外一种形式了)。

2、元组

tuple叫做元组,它和数组非常相似,不过用圆括号表示。但是它最大的特点是不能修改。

当我们想要修改时就会报错。

而选择和数组没有差异。

元组可以作为简化版的数组,因为它不可更改的特性,很多时候可以作为常量使用,防止被篡改。这样会更安全。

3、字典

字典dict全称dictionary,以键值对key-value的形式存储。所谓键值,就是将key作为索引存储。用大括号表示。

图中的'qinlu'是key,18是value值。key是唯一的,value可以对应各种数据类型。key-value的原理不妨想象成查找字典,拼音是key,对应的文字是value(当然字典的拼音不唯一)。

字典和数组的差异在于,因为字典以key的形式存储和查找,所以它的查询速度非常快,毕竟翻字典的时候你只要知道拼音就能快速定位了。对dict数据结构,10个key和10万个key在查找对应的value时速度没有太大差别。

这种查找方式的缺点是占用内存大。数组则相反,查找速度随着元素的增加逐渐下降,这个过程想象成程序在一页页的翻一本没有拼音的字典,直到找到内容。数组的优点是占用的内存空间小。

所以数组和字典的优缺点相反,dict是空间换时间,list是时间换空间,这是编程中一个比较重要的概念。实际中,数据分析师的工作不太涉及工程化,选用数组或者字典没有太严苛的限制。

细心的读者可能已经发现,字典定义时我的输入顺序是qinlu,lulu,qinqin,而打印出来是lulu,qinlu,qinqin,顺序变了。这是因为定义时key的顺序和放在内存的key顺序没有关系,key-value通过hash算法互相确定,甚至不同Python版本的哈希算法也不同。这一点应用中要避免出错。

既然字典通过key-value对匹配查找,那么它自然不能不用数组的数值索引,它只能通过key值。

如果key不存在,会报错。通过in方法,可以返回True或False,避免报错。

dict和list一样,直接通过赋值更改value

能不能更改key的名字?不能,key一旦确定,就无法再修改,好比字典定好后,你能修改字的拼音么?

dict中删除key和list一样,通过pop函数。增加key则是直接赋予一个新的键值对。

dict的keys和values两个函数直接输出所有的key值和value值。如果要转换成数组,则再外面嵌套一个list函数

items函数,将key-value对变成tuple形式,以数组的方式输出。

字典可以通过嵌套应用更复杂的数据格式,和NoSQL与JSON差不多。

基础的数据类型差不多了,更多函数应用大家可以网上自行查阅文档,这块掌握了,在数据清洗过程中将会非常高效,尤其是读取Excel数据时。当然不要求滚瓜烂熟,因为后面将学习更加强大的Numpy和Pandas。

三、基本函数

1. 函数是什么

函数(Functions)是指可重复使用的程序片段。它们允许你为某个代码块赋予名字,允许你通过这一特殊的名字在你的程序任何地方来运行代码块,并可重复任何次数。这就是所谓的调用(Calling)函数。

在 Python 中,函数可以通过关键字 def 来定义。这一关键字后跟一个函数的标识符名称,再跟一对圆括号,其中可以包括一些变量的名称,再以冒号结尾,结束这一行。随后而来的语句块是函数的一部分。

在定义函数时给定的名称称作“形参”(Parameters),在调用函数时你所提供给函数的值称作“实参”(Arguments)。

2. 调用函数

要调用一个函数,需要知道函数的名称和参数。函数的参数只是输入到函数之中,以便我们可以传递不同的值给它,并获得相应的结果。

Python 内置的常用函数包括数据类型转换函数,比如int()函数可以把其他数据类型转换为整数。用input()读取用户的输入:

因为input()返回的数据类型是str,str不能直接和整数比较,必须先把str转换成整数。Python 提供了int()函数来完成这件事情:

函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:

如果函数调用出错,一定要学会看错误信息。

3.定义函数

在 Python 中,定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。

在 Python 交互环境中定义函数时,注意 Python 会出现...的提示。函数定义结束后需要按两次回车重新回到>>>提示符下:

如果你已经把my_abs()的函数定义保存为abstest.py文件了,那么,可以在该文件的当前目录下启动Python 解释器,用from abstest import my_abs来导入my_abs()函数,注意abstest是文件名(不含.py扩展名)。

定义一个什么事也不做的空函数,可以用pass语句:

pass语句什么都不做,实际上它可以用作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。

pass还可以用在其他语句里,比如:

缺少了pass,代码运行就会有语法错误。

数据类型检查可以用内置函数isinstance()实现。

Python 的函数返回多值其实就是返回一个tuple;Python 函数返回的是单一值时,返回值仍然是一个tuple。但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值。函数可以同时返回多个值,但其实就是一个tuple。

函数执行完毕也没有return语句时,自动return None。

4.函数的参数

Python 的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。

4.1 位置参数:

power(x, n)函数有两个参数:x和n,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数x和n。

4.2 默认参数:

对于一些函数来说,你可能为希望使一些参数可选并使用默认的值,以避免用户不想为他们提供值的情况。默认参数值可以有效帮助解决这一情况。你可以通过在函数定义时附加一个赋值运算符=来为参数指定默认参数值。要注意到,默认参数值应该是常数。更确切地说,默认参数值应该是不可变的。

n = 2 是默认参数

定义默认参数要牢记一点:默认参数必须指向不变对象。且只有那些位于参数列表末尾的参数才能被赋予默认参数值,意即在函数的参数列表中拥有默认参数值的参数不能位于没有默认参数值的参数之前。

4.3 可变参数:

有时你可能想定义的函数里面能够有任意数量的变量,也就是参数数量是可变的,这可以通过使用星号来实现。即传入的参数个数是可变的。

我们声明一个诸如 *param 的星号参数时,从此处开始直到结束的所有位置参数(Positional Arguments)都将被收集并汇集成一个称为param的元组(Tuple)。

类似地,当我们声明一个诸如 **param 的双星号参数时,从此处开始直至结束的所有关键字参数都将被收集并汇集成一个名为 param 的字典(Dictionary)。

4.4 关键字参数:

如果你有一些具有许多参数的函数,而你又希望只对其中的一些进行指定,那么你可以通过命名它们来给这些参数赋值——这就是关键字参数(Keyword Arguments)——我们使用命名(关键字)而非位置来指定函数中的参数。

关键字参数允许你传入 0 个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

举个例子,扩展函数的功能。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。

和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:

4.5 命名关键字参数:

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义函数并调用:

和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。

命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错。

使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个*作为特殊分隔符。如果缺少*,Python 解释器将无法识别位置参数和命名关键字参数,即缺少 *,city和job被视为位置参数。

4.6 参数组合:

在 Python 中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这 5 种参数都可以组合使用。

但是参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。虽然可以组合多达 5 种参数,但不要同时使用太多的组合,否则函数接口的可理解性很差。

通过一个tuple和dict,你也可以调用函数:

对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。

5. 递归函数

如果一个函数在内部调用自身本身,这个函数就是递归函数。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

通过下面的代码可以查看你的电脑最大算到多少:

解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。

尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

要改成尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中。Python 标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题

四、了解Mumpy包

Python数据分析绝对绕不过的四个包是numpy、scipy、pandas还有matplotlib。

numPy是Python数值计算最重要的基础包,大多数提供科学计算的包都是用numPy的数组作为构建基础。专门用来处理矩阵,它的运算效率比列表更高效。

1、NumPy 的 ndarray:多维数组对象

numpy的数据结构是n维的数组对象,叫做ndarray。可以用这种数组对整块数据执行一些数学运算,其语法跟标量元素之间的运算一样。

创建并操作多维数组:

"/>

这里没写 np.float64 只写了 float,但是NumPy会将 Python 类型映射到等价的dtype上。

数组的dtype的另一个用法:

"/>

u4(unit32):无符号的 32 位(4个字节)整型。

调用astype无论如何都会创建出一个新的数组(原始数据的一份拷贝)。

浮点数只能表示近似的分数值,在复杂计算中可能会积累一些浮点错误,因此比较操作只在一定小数位以内有效。

4、数组和标量之间的运算

数组:可对数据执行批量运算(不用编写循环即可)。这通常叫做矢量化(vectorization)。

大小相等的数组之间,它们之间任何的算术运算都会应用到元素级(每个元素都做这个运算了),数组与标量的算术运算也是。不同大小的数组之间的运算叫做广播(broadcasting)。

5、索引和切片

数据不会被复制,任何修改都直接改了原数组。

如果仅是要一份副本,则用 .copy()。

对二维数组单个元素的索引:

这两种方式等价。

若arr2d[2],则输出的是一维数组[7,8,9]。

2*2*3的数组(2组2行3列):

6、布尔型索引

需要先引入:from numpy.random import randn

或将代码改成:data = np.random.randn(7, 4)

布尔型数组的长度必须跟被索引的轴长度一致。每个名字对应 data 数组一行。

对条件进行否定的两种方式:

组合应用多个布尔条件,可使用&、|等布尔算术运算符

通过布尔型索引选取数组中的数组,将总是创建数据的副本,即使返回一模一样的数组也是一样。

通过布尔型数组设置值:

通过一维布尔数组设置整行或列的值:

7、花式索引

指利用整数数组进行索引。

np.empty((8,4))

Return a new array of given shape and type, without initializing entries.

for i in range(8):

arr[i] = i

Return an object that produces a sequence of integers from start (inclusive)

to stop (exclusive) by step

为了以特定顺序选取行的子集,只需传入一个用于指定顺序的整数列表或 ndarray,使用负数索引会从末尾开始选取行(最后一行是 -1)。

一次传入多个索引组,返回一个一维数组:

取整列的两种方法,相当于给列排了顺序:

花式索引跟切片不一样,总是将数据复制到新数组中。

数组转置和轴对换

转置返回的是源数据的视图,不进行任何复制操作。数组有 transpose 方法,还有一个 T 属性来完成转置:

8、高维数组

Transpose 要一个轴编号:

<img class="capture deal" src="//s3.pstatp.com/wenda/wenda_web/static/style/image/loading_a788ad0.gif" _src="data:image/svg+xml;utf8,<svg xmlns=" http:="" www.w3.org="" 2000="" svg'="" width="640" height="462">"/>

arr是 2 组 2 行 4 列的数组,transpose的参数表示shape的形状,对于这个例子来说,即2[0]、2[1]、4[2],transpose(1,0,2)转置后变为2[1]、2[0]、4[2],看起来仍是 2 组 2 行 4 列的形状,但数组内的元素经过转换后索引已经改变,也要遵循(1,0,2)的顺序。如转置前的数组arr[0,1,0]索引值为 4,转置后的数组arr'[1,0,0],索引值才为 4。其它同理。

ndarray 的 swapaxes 方法接受一对轴编号且返回源数据的视图:

"/>

np.meshgrid函数接受两个一维数组,并产生两个二维矩阵(对应于两个数组中所有的(x, y)对。

将条件逻辑表述为数组运算

np.wherea函数是三元表达式x if condition else y的矢量化版本。

np.where的第二个和第三个参数不必是数组,传递给where的数组大小可以不相等,甚至可以是标量值。在数据分析工作中,where通常用于根据另一个数组而产生一个新的数组。

用where表述出更复杂的逻辑:(where的嵌套)

"/>

10、排序

多维数组可以在任何一个轴向上进行排序,只需将轴编号传给sort:

顶级方法np.sort返回的数组已排序的副本,就地排序则会修改数组。

唯一化以及其他的集合逻辑

np.unique找出数组中的唯一值并返回已排序的结果

np.in1d用于测试一个数组的值在另一个数组的情况。

用爬虫技术能做到哪些有趣的事情?

看到这个问题必须来怒答一波~用python爬虫爬便宜机票了解一下?

喜欢旅行又怕吃土?让Python来爬取最便宜机票吧!

图源:

videoblocks.com

你喜欢旅行吗?

这个问题通常会得到一个肯定的答案,随后引出一两个有关之前冒险经历的故事。大多数人都认为旅行是体验新文化和开阔视野的好方法。但如果问题是“你喜欢搜索机票的过程吗?”也许话题就到此为止了……

可事实上,便宜的机票往往也很重要!本文将尝试构建一个网络爬虫,该爬虫对特定目的地运行并执行带有浮动日期(首选日期前后最多三天)的航班价格搜索。它会将结果保存为excel文件并发送一封包含快速统计信息的电子邮件。显然,这个爬虫的目的就是帮助我们找到最优惠的价格!

你可以在服务器上运行脚本(一个简单的Raspberry Pi就可以),每天运行一到两次。结果会以邮件形式发送,建议将excel文件存入Dropbox文件夹,以便随时随地查看。

因为爬虫以“浮动日期”进行搜索,所以它会搜索首选日期前后最多三天的航班信息。尽管该脚本一次仅运行一对目的地,但可以很容易地改写该爬虫使其每个循环运行多个目的地。最终甚至可能找到一些错误票价...那会很有意思!

另一个爬虫

某种意义上来讲,网络爬取是互联网“工作”的核心。

也许你认为这是一个十分大胆的说法,但谷歌就是从拉里·佩奇用Java和Python构建的网络爬虫开始的。爬虫不断地爬取信息,整个互联网都在试图为所有问题提供最佳的可能答案。网络爬取有不计其数的应用程序,即使更喜欢数据科学中的其他分支,你仍需要一些爬取技巧以获得数据。

这里用到的一些技术来自于最近新的一本佳作《Python网络数据采集》,书中包含与网络爬取相关的所有内容,并提供了大量简例和实例。甚至有一个特别有意思的章节,讲述如何解决验证码检验的问题。

Python的拯救

第一个挑战就是选择爬取信息的平台,本文选择了客涯(Kayak)。我们试过了Momondo, 天巡(Skyscanner), 亿客行(Expedia)和其它一些网站,但是这些网站上的验证码特别变态。

在那些“你是人类吗?”的验证中,尝试了多次选择交通灯、十字路口和自行车后,客涯似乎是最好的选择,尽管短时间内加载太多页面它会跳出安全检查。

我们设法让机器人每4到6个小时查询一次网站,结果一切正常。虽然说不定哪个部分偶尔会出点小问题,但是如果收到验证码,既可以手动解决问题后启动机器人,也可以等待几小时后的自动重启。

如果你是网络爬取新手,或者不知道为何有些网站花费很大力气阻止网络爬取,那么为构建爬虫写下第一行代码前,你一定要多加努力。

谷歌的“网络爬取规范”:

http://lmgtfy.com/?q=web+scraping+etiquette

系紧安全带...

导入并打开Chrome浏览器标签页后,会定义一些循环中会用到的函数。这个架构的构思大概是这样的:

· 一个函数用于启动机器人程序,表明想要搜索的城市和日期。

· 这个函数获得首轮搜索结果,按“最佳”航班排序,然后点击“加载更多结果”。

· 另一个函数会爬取整个页面,并返回一个dataframe数据表。

· 随后重复步骤2和步骤3,得出按“价格”和“航行时间”排序的结果。

· 发送一封简要总结价格(最低价和平均价)的邮件,并将带有这三种排序类型的dataframe数据表保存为一份excel文件。

· 以上所有步骤会在循环中重复,每X小时运行一次。

每个Selenium项目都以一个网页驱动器开始。我们使用Chromedriver驱动器,但还有其它选择。PhantomJS和Firefox也很受欢迎。下载Chromedriver后,将其置于一个文件夹中即可。第一行代码会打开一个空白Chrome标签页。

from time import sleep, strftime

from random import randint

import pandas as pd

from selenium import webdriver

from selenium.webdriver.common.keys import Keys

import smtplib

from email.mime.multipart import MIMEMultipart

# Change this to your own chromedriver path!

chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'

driver = webdriver.Chrome(executable_path=chromedriver_path) # This will open the Chrome window

sleep(2)

这些是将用于整个项目的包。使用randint函数令机器人在每次搜索之间随机睡眠几秒钟。这对任何一个机器人来说都是必要属性。如果运行前面的代码,应该打开一个Chrome浏览器窗口,机器人会在其中导航。

一起来做一个快速测试:在另一个窗口上访问客涯网(http://kayak.com),选择往返城市和日期。选择日期时,确保选择的是“+-3天”。由于在编写代码时考虑到了结果页面,所以如果只想搜索特定日期,很可能需要做一些微小的调整。

点击搜索按钮在地址栏获取链接。它应该类似于下面所使用的链接,将变量kayak定义为url,并从网页驱动器执行get方法,搜索结果就会出现。

无论何时,只要在几分钟内使用get命令超过两到三次,就会出现验证码。实际上可以自己解决验证码,并在下一次验证出现时继续进行想要的测试。从测试来看,第一次搜索似乎一直没有问题,所以如果想运行这份代码,并让它在较长的时间间隔后运行,必须解决这个难题。你并不需要十分钟就更新一次这些价格,对吧?

每个XPath都有陷阱

到目前为止,已经打开了一个窗口,获取了一个网站。为了开始获取价格和其他信息,需要使用XPath或CSS选择器,我们选择了XPath。使用XPath导航网页可能会令人感到困惑,即使使用从inspector视图中直接使用“复制XPath”,但这不是获得所需元素的最佳方法。有时通过“复制XPath”这个方法获得的链接过于针对特定对象,以至于很快就失效了。《Python网络数据采集》一书很好地解释了使用XPath和CSS选择器导航的基础知识。

接下来,用Python选择最便宜的结果。上面代码中的红色文本是XPath选择器,在网页上任意一处右键单击选择“inspect”就可以看到它。在想要查看代码的位置,可以再次右键单击选择“inspect”。

为说明之前所观察到的从“inspector”复制路径的缺陷,请参考以下差异:

1 # This is what the copymethod would return. Right click highlighted rows on the right side and select “copy> Copy XPath”//*[@id=“wtKI-price_aTab”]/div[1]/div/div/div[1]/div/span/span

2 # This is what I used todefine the “Cheapest” buttoncheap_results= ‘//a[@data-code = “price”]’

第二种方法的简洁性清晰可见。它搜索具有data-code等于price属性的元素a。第一种方法查找id等于wtKI-price_aTab的元素,并遵循第一个div元素和另外四个div和两个span。这次……会成功的。现在就可以告诉你,id元素会在下次加载页面时更改。每次页面一加载,字母wtKI会动态改变,所以只要页面重新加载,代码就会失效。花些时间阅读XPath,保证你会有收获。

不过,使用复制的方法在不那么“复杂”的网站上工作,也是很好的!

基于以上所展示的内容,如果想在一个列表中以几个字符串的形式获得所有搜索结果该怎么办呢?其实很简单。每个结果都在一个对象中,这个对象的类是“resultWrapper”。获取所有结果可以通过像下面这样的for循环语句来实现。如果你能理解这一部分,应该可以理解接下来的大部分代码。它基本上指向想要的结果(结果包装器),使用某种方式(XPath)获得文本,并将其放置在可读对象中(首先使用flight_containers,然后使用flight_list)。

前三行已展示在图中,并且可以清楚地看到所需的内容,但是有获得信息的更优选择,需要逐一爬取每个元素。

准备起飞吧!

最容易编写的函数就是加载更多结果的函数,所以代码由此开始。为了在不触发安全验证的前提下最大化所获取的航班数量,每次页面显示后,单击“加载更多结果”。唯一的新内容就是所添加的try语句,因为有时按钮加载会出错。如果它对你也有用,只需在前面展示的start_kayak函数中进行简要注释。

# Load more results to maximize the scraping

def load_more():

try:

more_results = '//a[@class = “moreButton”]'

driver.find_element_by_xpath(more_results).click()

# Printing these notes during the program helps me quickly check what it is doing

print('sleeping…..')

sleep(randint(45,60))

except:

pass

现在,经过这么长的介绍,已经准备好定义实际爬取页面的函数。

我们编译了下一个函数page_scrape中的大部分元素。有时这些元素会返回列表插入去程信息和返程信息之间。这里使用了一个简单的办法分开它们,比如在第一个 section_a_list和section_b_list变量中,该函数还返回一个flight_df数据表。所以可以分离在不同分类下得到的结果,之后再把它们合并起来。

def page_scrape():

“““This function takes care of the scraping part”““

xp_sections = '//*[@class=“section duration”]'

sections = driver.find_elements_by_xpath(xp_sections)

sections_list = [value.text for value in sections]

section_a_list = sections_list[::2] # This is to separate the two flights

section_b_list = sections_list[1::2] # This is to separate the two flights

# if you run into a reCaptcha, you might want to do something about it

# you will know there's a problem if the lists above are empty

# this if statement lets you exit the bot or do something else

# you can add a sleep here, to let you solve the captcha and continue scraping

# i'm using a SystemExit because i want to test everything from the start

if section_a_list == []:

raise SystemExit

# I'll use the letter A for the outbound flight and B for the inbound

a_duration = []

a_section_names = []

for n in section_a_list:

# Separate the time from the cities

a_section_names.append(''.join(n.split()[2:5]))

a_duration.append(''.join(n.split()[0:2]))

b_duration = []

b_section_names = []

for n in section_b_list:

# Separate the time from the cities

b_section_names.append(''.join(n.split()[2:5]))

b_duration.append(''.join(n.split()[0:2]))

xp_dates = '//div[@class=“section date”]'

dates = driver.find_elements_by_xpath(xp_dates)

dates_list = [value.text for value in dates]

a_date_list = dates_list[::2]

b_date_list = dates_list[1::2]

# Separating the weekday from the day

a_day = [value.split()[0] for value in a_date_list]

a_weekday = [value.split()[1] for value in a_date_list]

b_day = [value.split()[0] for value in b_date_list]

b_weekday = [value.split()[1] for value in b_date_list]

# getting the prices

xp_prices = '//a[@class=“booking-link”]/span[@class=“price option-text”]'

prices = driver.find_elements_by_xpath(xp_prices)

prices_list = [price.text.replace('$','') for price in prices if price.text != '']

prices_list = list(map(int, prices_list))

# the stops are a big list with one leg on the even index and second leg on odd index

xp_stops = '//div[@class=“section stops”]/div[1]'

stops = driver.find_elements_by_xpath(xp_stops)

stops_list = [stop.text[0].replace('n','0') for stop in stops]

a_stop_list = stops_list[::2]

b_stop_list = stops_list[1::2]

xp_stops_cities = '//div[@class=“section stops”]/div[2]'

stops_cities = driver.find_elements_by_xpath(xp_stops_cities)

stops_cities_list = [stop.text for stop in stops_cities]

a_stop_name_list = stops_cities_list[::2]

b_stop_name_list = stops_cities_list[1::2]

# this part gets me the airline company and the departure and arrival times, for both legs

xp_schedule = '//div[@class=“section times”]'

schedules = driver.find_elements_by_xpath(xp_schedule)

hours_list = []

carrier_list = []

for schedule in schedules:

hours_list.append(schedule.text.split('\n')[0])

carrier_list.append(schedule.text.split('\n')[1])

# split the hours and carriers, between a and b legs

a_hours = hours_list[::2]

a_carrier = carrier_list[1::2]

b_hours = hours_list[::2]

b_carrier = carrier_list[1::2]

cols = (['Out Day', 'Out Time', 'Out Weekday', 'Out Airline', 'Out Cities', 'Out Duration', 'Out Stops', 'Out Stop Cities',

'Return Day', 'Return Time', 'Return Weekday', 'Return Airline', 'Return Cities', 'Return Duration', 'Return Stops', 'Return Stop Cities',

'Price'])

flights_df = pd.DataFrame({'Out Day': a_day,

'Out Weekday': a_weekday,

'Out Duration': a_duration,

'Out Cities': a_section_names,

'Return Day': b_day,

'Return Weekday': b_weekday,

'Return Duration': b_duration,

'Return Cities': b_section_names,

'Out Stops': a_stop_list,

'Out Stop Cities': a_stop_name_list,

'Return Stops': b_stop_list,

'Return Stop Cities': b_stop_name_list,

'Out Time': a_hours,

'Out Airline': a_carrier,

'Return Time': b_hours,

'Return Airline': b_carrier,

'Price': prices_list})[cols]

flights_df['timestamp'] = strftime(“%Y%m%d-%H%M”) # so we can know when it was scraped

return flights_df

尽量让这些名字容易理解。记住变量a表示旅行的去程信息,变量b表示旅行的返程信息。接下来说说下一个函数。

等等,还有什么吗?

截至目前,已经有了一个能加载更多结果的函数和一个能爬取其他结果的函数。本可以在此结束这篇文章,而你可以自行手动使用这些函数,并在浏览的页面上使用爬取功能。但是前文提到给自己发送邮件和一些其他信息的内容,这都包含在接下来的函数start_kayak中。

它要求填入城市名和日期,并由此打开一个kayak字符串中的地址,该字符串直接跳转到“最佳”航班结果排序页面。第一次爬取后,可以获取价格的顶部矩阵,这个矩阵将用于计算平均值和最小值,之后和客涯(Kayak)的预测结果(页面左上角)一同发送到邮件中。这是单一日期搜索时可能导致错误的原因之一,因其不包含矩阵元素。

def start_kayak(city_from, city_to, date_start, date_end):

“““City codes it's the IATA codes!

Date format YYYY-MM-DD”““

kayak = ('https://www.kayak.com/flights/' + city_from + '-' + city_to +

'/' + date_start + '-flexible/' + date_end + '-flexible?sort=bestflight_a')

driver.get(kayak)

sleep(randint(8,10))

# sometimes a popup shows up, so we can use a try statement to check it and close

try:

xp_popup_close = '//button[contains(@id,”dialog-close”) and contains(@class,”Button-No-Standard-Style close “)]'

driver.find_elements_by_xpath(xp_popup_close)[5].click()

except Exception as e:

pass

sleep(randint(60,95))

print('loading more.....')

# load_more()

print('starting first scrape.....')

df_flights_best = page_scrape()

df_flights_best['sort'] = 'best'

sleep(randint(60,80))

# Let's also get the lowest prices from the matrix on top

matrix = driver.find_elements_by_xpath('//*[contains(@id,”FlexMatrixCell”)]')

matrix_prices = [price.text.replace('$','') for price in matrix]

matrix_prices = list(map(int, matrix_prices))

matrix_min = min(matrix_prices)

matrix_avg = sum(matrix_prices)/len(matrix_prices)

print('switching to cheapest results…..')

cheap_results = '//a[@data-code = “price”]'

driver.find_element_by_xpath(cheap_results).click()

sleep(randint(60,90))

print('loading more…..')

# load_more()

print('starting second scrape…..')

df_flights_cheap = page_scrape()

df_flights_cheap['sort'] = 'cheap'

sleep(randint(60,80))

print('switching to quickest results…..')

quick_results = '//a[@data-code = “duration”]'

driver.find_element_by_xpath(quick_results).click()

sleep(randint(60,90))

print('loading more…..')

# load_more()

print('starting third scrape…..')

df_flights_fast = page_scrape()

df_flights_fast['sort'] = 'fast'

sleep(randint(60,80))

# saving a new dataframe as an excel file. the name is custom made to your cities and dates

final_df = df_flights_cheap.append(df_flights_best).append(df_flights_fast)

final_df.to_excel('search_backups//{}_flights_{}-{}_from_{}_to_{}.xlsx'.format(strftime(“%Y%m%d-%H%M”),

city_from, city_to,

date_start, date_end), index=False)

print('saved df…..')

# We can keep track of what they predict and how it actually turns out!

xp_loading = '//div[contains(@id,”advice”)]'

loading = driver.find_element_by_xpath(xp_loading).text

xp_prediction = '//span[@class=“info-text”]'

prediction = driver.find_element_by_xpath(xp_prediction).text

print(loading+'\n'+prediction)

# sometimes we get this string in the loading variable, which will conflict with the email we send later

# just change it to “Not Sure” if it happens

weird = '¯\\_(ツ)_/¯'

if loading == weird:

loading = 'Not sure'

username = 'YOUREMAIL@hotmail.com'

password = 'YOUR PASSWORD'

server = smtplib.SMTP('smtp.outlook.com', 587)

server.ehlo()

server.starttls()

server.login(username, password)

msg = ('Subject: Flight Scraper\n\n\

Cheapest Flight: {}\nAverage Price: {}\n\nRecommendation: {}\n\nEnd of message'.format(matrix_min, matrix_avg, (loading+'\n'+prediction)))

message = MIMEMultipart()

message['From'] = 'YOUREMAIL@hotmail.com'

message['to'] = 'YOUROTHEREMAIL@domain.com'

server.sendmail('YOUREMAIL@hotmail.com', 'YOUROTHEREMAIL@domain.com', msg)

print('sent email…..')

虽然没有使用Gmail账户测试发送邮件,但是可以搜索到很多的替代方法,前文提到的那本书中也有其他方法来实现这一点。如果已有一个Hotmail账户,只要替换掉个人的详细信息,它就会开始工作了。

如果想探索脚本的某一部分正在做什么,可以将脚本复制下来并在函数外使用它。这是彻底理解它的唯一方法。

利用刚才创造的一切

在这些步骤之后,还可以想出一个简单的循环来使用刚创造的函数,同时使其持续运行。完成四个“花式”提示,写下城市和日期(输入)。因为测试时不想每次都输入这些变量,需要的时候可以使用以下这个清楚的方式进行替换。

如果已经做到了这一步,恭喜你!改进还有很多,比如与Twilio集成,发送文本消息而不是邮件。也可以使用VP*或更加难懂的方式同时从多个服务器上研究搜索结果。还有就是验证码的问题,验证码会时不时地跳出来,但对此类问题还是有解决办法的。不过,能走到这里已经是有很牢固的基础了,你可以尝试添加一些额外的要素。

使用脚本运行测试的示例

留言 点赞 关注

我们一起分享AI学习与发展的干货

欢迎关注全平台AI垂类自媒体 “读芯术”

那个APP可以学习到大学教材知识?

推荐几款我喜欢用的app吧。

一、文字资讯类

古诗词典:界面挺精致的,古诗词可分类,有利于利用零碎时间~

文言文:与上同一家公司出品。

一个:韩寒出品。

知乎:面有很多的话题,比如互联网话题,PPT话题,微信话题,在上面有很多的干货回答,值得我们学习。

果壳精选:科普必需。

BBC News:提供原汁原味英文阅读。

今日头条:无需赘言。

二、视频类

1.网易公开课:资源充足,涵盖面广,看英语练听力666,找名校演讲666,看纪录片666,听Ted演讲666。

2.Coursera:MOOC的主要根据地,不同于公开课的部分在于你不仅需要看lecture视频,更重要的是参与线上讨论,完成定期作业,成绩合格的最后还能获得证书喔~ 不过目前在移动设备上交作业仍不方便。

3.学堂在线:清华出品,国内高校推出的MOOC都在这啦

4.TED:相比于在网易公开课上看ted,我更喜欢在这个官方app上挑选,分类更加细致,新功能“surprise me”真的很棒!

三. 语言类

1.词典&记单词:这个建议把当前app市场上的都下载自己试手感,对词典内容、生词本,记单词方式等每个人需求会有不同吧。我用的是有道词典.

2.听力:每日英语听力、扇贝听力、唐僧英语等。经比较后比较喜欢唐僧英语,资源丰富,界面精美。扇贝新闻:有分级的新闻阅读,可以和BBC NEWS配合使用。

3.英语魔方秀/英语趣配音:英文电影配音,每个视频片段不超过两分钟,有些还支持选角色,录完一句话后会被打分,最后总体可与小伙伴们比较,类似唱吧的锻炼纯正口语的app~

4.新概念英语:以新概念教材为核心,内容丰富。

5.C语言学习手册、Java学习手册、Android手册:包含基础知识、算法、函数库等内容,供走向码农路上的各位参考。

6.编程助手:真的多种语言!PHP、C++、HTML5、JAVA等等!

四、时间管理类

1.Forest:控制使用手机时间的,比起我要当学霸,我喜欢它种树的概念。

2.爱今天:可以智能计算出你一天在各项事务上花费的时间。

3.番茄土豆:相信大家都用过,时间管理的元祖级app。

4.24Pi:设定开机自启后就能记录你一天手机的使用情况了,分析你各类app的使用情况和时间碎片化程度,临睡前查看一下进行反思再给第二天的自己设定一个目标吧><

五、技能类

驾考:驾考宝典、驾校一本通、2015新驾考宝典等。

会计:会计随身学、初级会计职称考试、会计从业万题库等

用APP来学习,我有以下标准:

第一,APP够小,不占内存

第二,免费

第三,没有广告,界面舒服

最最最最最最重要的是:真的能帮助我们学习呀!!

1.哔哩哔哩

你们不要以为我鬼畜了,哔哩哔哩虽然是弹幕最好玩、鬼畜最好玩的视频网站

但是像我们这种“优秀”的人也可以拿它学习的

哔站上面有大量高清的纪录片、美剧、英剧、电影资源(配合弹幕食用效果更佳)

还有很多英语、小语种的视频课程,尤其是教你如何正确发音的视频非常有用

并且可以设置1.5倍速播放!!

妈妈再也不用担心我看纪录片睡着啦

2.超级计算器

网易旗下目前国内最高级的计算器

可以计算各种高级数学问题,包括求导、积分、方程求解、三角函数、因式分解、阶乘等等。

广大初高中生知道了这个APP的话,数学作业就全解决了

3.网易公开课:有各行各业的名师讲堂课程。而且包含纪录片,TED 和翻译的名校的课程。

网易云课堂:实用技能的学习平台,学互联网课程、学设计创作技术、学摄影甚至可以学唱歌。特别PS(李涛大师的很全><)、AE教材之类的资源挺丰富。

4. 有道词典

一个中英,中法,中韩等语言互译的翻译软件。本人无论换了多少次手机,都是手机必备。

很多人知道,但能用到极致的。。。屈指可数

它不仅能查词,还能把单词收录自己账户的单词本中

单词本会根据记忆曲线帮你安排每天需要背的单词,简直是管家式服务

5.百词斩

扇贝单词和百词斩是背单词APP中的两座大山

两个APP我都用过,百词斩更得朕心

扇贝单词仅仅是普通的单词释义记忆,而百词斩用图片记忆法让人记得更牢,而且背单词的过程也变得更有趣。(学英语最最最重要的还是词汇呀!)

6.BBC NEWS

这是BBC官方新闻APP,是最地道的英文表达

同时看看外媒报道也能为你提供不同的看世界、看新闻的视角

在被封锁的墙内世界里看看国外的月亮到底圆不圆

7.提高英语听力:朗易思听

这是朗易思听资源分类下的类目,非常丰富,几乎你想要的都有!

从雅思、托福的听力原题,到各种美剧、电影、纪录片的原声,还有各种演讲等等等等。。。

它可以定位一句话,反复听,也可以录音跟读,录音和原音对比,就能听出自己哪方面不足,是读音不标准还是停顿错误,或者是语调不标准。

这就是我最喜欢它的一点,锻炼你说英语的能力!英语你还是要说,反复说反复纠正。

如何快速学习编程?

1. 我应该选择什么编程语言

可能困扰编程新手最多的一个问题是【我应该学什么编程语言】或者【我需要学习哪些课程才能做出一个web、一个app】,很多人一直纠结这个问题,陷入了东学一点、西看一点的死循环,到头来啥也没学好,这会很浪费时间。

刚上大一的时候,我也很想知道应该选择什么编程语言。我问了很多人,网上各种查资料,但所能得到的答案都很片面,多数对这个问题答非所问,总是回答说“某某编程语言难”,“某某编程语言性能好”。其实作为初学者,我们对计算机体系都不了解,就不要过多地去纠结性能,或者难易等因素,原因我等下再说。

如果你有明确的方向,那么很好选择。如果你想做算法、机器学习方向,那么python是最好的选择。如果你想做web开发,java、php等都可以。如果想做一些更底层的工作,那么就可以选c。当然这是建立在你有明确方向的基础上。可是,很多人都没怎么接触过计算机行业,特别是和我一样刚入学就被调剂到计算机专业的人。对这些同学来说,各个编程语言就只是个名字,除了叫法不一样,你根本不知道它们有什么差别。所以索性不要纠结了,我替你选一个吧。

如果你是在校大学生,那么你有大把连续的时间,就先学习c,然后再学c++。我个人是学c入门的,也许很多人不理解我为什么推荐学c,因为c和c++都很难、很复杂,看起来并不适合入门。然而正是它们的难和复杂才能让你更好地理解计算机系统【计算机系统不是指操作系统】。学习编程不是学习编程语言,而是学习一个计算机生态,即一个庞大的知识体系。只会编程语言而不理解整个计算机的体系,就像只会写字而写不出好文章。了解c/c++和了解计算机系统是极为贴合的,向下可以帮助你更容易地理解操作系统、编译原理、计算机网络、计算机组成原理,为什么呢?因为较为底层的东西很多都是用c实现的,和系统的贴合度极高,很多教材源码甚至教程,在讲述这些知识的时候都是用c或c++作为媒介。而向上,c++面向对象的机制,也可以做出一些应用,譬如五子棋游戏等,也不会显得那么枯燥。花个小半年时间了解c和c++,之后你就会觉得看书、看资料可以轻松很多。

如果你是一个上班族,但是刚刚学习编程,可能学c和c++对你来说有些复杂和困难,因为学习它们确实是很需要时间。你们不像在校生那样有大把的连续时间,而零碎的时间去学习一个比较复杂的东西效果不见得有那么好,所以可以先学一些【更容易见效】的编程语言,从python入手吧,至少能快速做出一些小应用,不至于丢失了兴趣,但是真的要入门编程又还得看看与计算机系统相关的书籍,这样才能更深层次地去编程,譬如【深入理解计算机系统】这一本书可以读很多遍,这本书把整个计算机系统给串起来了。

2.学习编程,我需要学习哪些课程?

我要学哪些课程?我为什么要学习如高数、离散数学、线性代数、概率论等课程?

这个问题也是之前困扰了我很久的问题。不过我现在想通了,对于【高数、离散、线性代数、概率论】等课程,很好解释,做算法的同学肯定知道为啥要学习这些课程。机器学习中会大量用到上述提到的课程,所以会比较好理解。对在校生而言,学校开设的很多课程我们不知道为什么要学,我们很疑惑,不知道学它有什么用,这个时候我们就会很纠结,还会产生抵触情绪。这很正常,因为我们学习得不够深入,自然不能理解它们的用处。

在我看来,大学本科课程更多的是面向“面”的教学,即什么课程都教给你一些,但是又讲得不那么深入;而工作或者读研,更多的则是面向“点”的学习,用到的知识更专。本科时,学校也不知道你以后是去搞算法、还是搞架构、还是搞服务器开发,甚至去搞硬件,所以学校需要你学很多课程,至少有个了解。对学生来说,一方面可以从中选择自己感兴趣的点;一方面也可以对未来的就业方向有些启发。所以即使像数电、模电等课程,虽然之后可能用不着,但是你也要学,并且会花费大量的时间。虽然你最后不一定去搞硬件,但是这些课程也会让你更容易去理解一些知识,比如cpu中的逻辑器件。

如果你在大一的时候就有一个明确的定位,知道自己今后想从事哪方面的工作,课程与课程之间是可以调一下优先级的。不过像大学物理,这种课程确实是对编程没有帮助,但是像我前面所说的,大学教育更注重广度,大物等课程可能就是为了给你普及生活常识吧。

其实,大学教育的问题是普遍存在的,我认为我们学习一项技能的时候,应该采取的是项目驱动式学习,即需要用到什么东西时不会了再去学,而不是先填鸭式的都填进脑子,并且在学习的过程中我们还不知道它这是干嘛用的,等之后用到了,甚至不记得自己学过,反而查资料才会想起:哦,原来我之前学的xx科目是这个用处啊,可是我当时并没有好好学。很多时候学生时间的浪费可能还是要怪老师、怪学校,他们一开始没给我们做好充分的课程介绍。所以,在经过比较多的编程和项目实践后,我认为一个比较好的学习方式是,改良版的项目驱动学习法。即:

学习一段时间,做个小项目,将做项目遇到的问题记下来,针对性地学习相关知识,然后再实践,再学一段时间理论,让知识成网状发射状地变大。当然,项目驱动式学习有一个弊端,就是每次学习的知识都是项目所需要的,很零碎、不成体系,所以需要改良,即在采取项目驱动学习法的时候每天抽一段时间去完整地读一本书,或者一个相关问题的完整介绍,这样就很容易把一些知识成体系地串起来。这样一段时间下来,慢慢的,你就知道我们为什么要学那么多科目,学这些科目能干什么。

为了表达地更加形象,我就举一个小例子,是我最近遇到的。我本身的工作是做Linux C++的,但不仅限于此。我个人对python、数据分析,以及机器学习等内容比较感兴趣,大家可以看到我最近也在我的专栏发布了很多文章。就从数据获取开始,我讲讲我这两个月做了什么东西。

谈到数据获取,可能最容易想到的是爬虫,爬虫是一个被说烂了的话题,所以我不想多说它是什么。很多时候有人觉得爬虫简单,为什么呢,因为有现成的框架,所以获取少量的数据就比较容易。但是当你需要爬取的数据很大的时候(比如我之前抓取了某网站500万用户的数据,在下班的时间、用自己家里普通的pc,计算机性能并不是那么好,比不上服务器,又要在不被封IP的情况下抓到这么大量的数据,然后对数据进行清洗,最后还要可视化展示),使用现成的爬虫框架就并不是那么容易实现了。况且,我需要抓很多数据源,并不是一锤子买卖。所以我选择去开发一个系统,即在现有的框架下进行二次开发,搭建一个属于自己的爬虫系统,并植入一些算法。我在系统中添加了很多中间件,直到现在,它还可以在10分钟内就部署一个能抓取大量数据的爬虫应用。当然,这个过程也遇到了不少麻烦,我就简单讲讲,怎么去攻克一个个问题。

下面先给出一个树形图,从上往下每一个圈都代表了学习过程中遇到的难点,如果你现在看不懂,没关系,我想告诉你的是一种梳理知识的方法:

如上图所示,就是一个项目驱动式学习的例子,我们的目的是为了获取数据,所以选择了爬虫:

爬虫可以理解为一个简单的过程:发送request,获取response,然后提取数据。这个过程会涉及到网络,是发送http还是https请求;目标网站是否需要登录,是post请求还是get请求,从这条线,衍生出了一条对网络进行学习的路径。获取到网页之后,如果不是结构化的数据,可能返回的是一个html源代码,那么可能就需要了解dom,或者html页面解析的知识,甚至需要了解一下前端开发。在抓取的过程中,经常会遇到数据中途不能被爬取的情况,一般是IP被封禁了,那么可能又要用上代理,代理是什么呢?http,https代理能不能混用呢?如何构建一个代理池呢?这里又有很多要学习的东西。还有可能遇到的情况是,抓下来的数据是加密的,需要通过js解密,这时候就要了解一下js,如何用爬虫模拟浏览器进行抓取。除此之外,如果抓取的频率不对,很多数据源会给你假数据,这就是一些经验问题了,本文不是技术文,所以就不多讨论。当解决了上述问题后,我们好像可以拿到一些数据了,但是当数据大起来,问题又复杂了,你可能需要使用分布式抓取了,这时候你可能需要了解一下redis,当request产生的速度大于其消费的速度之后,你的任务队列可能爆炸,所以这里又涉及到算法和数据结构的应用了。数据量上去之后,把数据写在文件里面是不靠谱的,这时候又涉及到存储了,到底是使用关系型数据库还是非关系型数据库呢,有什么区别呢?存进去的数据怎么去重呢?为什么insert操作越来越卡了呢?电脑怎么越来越热了呢?索引是什么,什么时候该建立索引呢?这里又牵扯到数据库原理相关的知识。遇到一些比较难处理的网站,比如有验证码识别该怎么办呢?其实对于很多纯数字和字母的验证码都很好解决,自己用深度学习训练即可。在TensorFlow的Demo中就要生成验证码自己训练的教程,然后制定个中间件放在爬虫系统中,这个问题就解决了。可是什么是深度学习呢?这里又引出一条对深度学习进行探索的例子,而我自己也是之前在学校的时候自学了小半年机器学习,有了一定的基础后,才能比较容易地上手TensorFlow框架。再往下就比较深了。上述六点简单讲了讲项目驱动式学习的介绍,其实,你看到的每一个小圆圈,深挖下去都大有文章。我们现在看到的只是冰山一角,任何一条学习路径学习下去都深无止境,我们不可能完全学会,可是项目驱动式学习最大的好处是让你知道你应该去学习什么,而不是先学一大堆知识,再去做一个项目。严格来说,项目驱动式学习的可视化路径是一张网,而不是一棵树,这里画成树状只是为了便于大家理解。

除了获得数据,还有清洗数据、分析数据,甚至挖掘数据,最后可视化数据并且展示数据,这里我就不一一介绍了。

3.学习编程是否需要制定计划?

学习编程是否需要制定计划,该制定什么样的计划呢?

我认为不只是编程需要制定计划,其他任何的学习和工作都需要制定计划。我从13年上大学就开始定期给自己制定计划,这个习惯也一直坚持到了现在,受益匪浅。当然也不只是制定学习计划,还可以列一些自己需要做的其他的事情。我最近在整理笔记的时候也发现了一些之前记录的计划和清单,可以给大家看看。比如下图就是我14年写的笔记,笔记上都留下了最后一次打开的时间。列举了一些自己需要看的文章,因为当时不太懂得规划,所以比较乱。

到了16年的时候,我做计划做得更加有条理了。下图是16年10月30日的计划,那时候我已经大四了,并且已经找到了工作、签了满意的offer,并且没有什么课,按理说可以放松放松了,不过我还是制定了一些学习计划,并且选择在11月去百度实习。从内容上看,主要是学习英语和计算机专业课,因为大一大二的时候我确实不明白为什么要学习专业课,到了大三下想清楚原因以后,我也就一直在重新学习,因为计算机专业课真的很重要!学好了这些课,能让你在日后的学习工作中轻松不少:

除了大四制定的计划外,大二的时候我也制定过较为详细的学习计划(如下图),把需要学习的内容进行了编号,存入表格,这样才能让你过得有条不紊。当然,很难完全按照计划去执行,不过制定相应的计划能让你清楚地知道自己应该干什么。

所以,如果你是在校生,那么好好制定一个计划吧,因为你有大把的时间。当然,如果你已经毕业了,没关系,我现在也在上班,同样也列举了自己最近要学习的内容,如下图(2月27日更新过),包括了短期和长期需要学习的内容:

4.编程是否需要做笔记和写博客?

我觉得,写不写博客无所谓,因为博客是要写出来给大家看的,可能要保证格式美观、语法也要尽量准确,最好比较有文采,我觉得太麻烦也就一直没写。而笔记是必须要做的,并且记笔记是一个长期的过程。在学习的过程中,我们一直都在追求一种最高效的学习方法,比如,同一个班的同学,他用他的学习方法考上了清华,而你用同样的方法就不行,为什么?因为他的方法对他自己而言是定制化的,可能且大概率不适合你,比如他的笔记你不一定能看懂,因为他可能设计了一套属于自己的符号。而就编程而言,很多同学说善用搜索引擎,是对的,可是搜索引擎搜出来的是别人的答案。你照搬过来,也许可以用,但是你没有记住,这些知识并不属于你,之后你可能还会遇到同样的问题,又要再搜索一遍,可能很难找到之前的那个答案了。但是记笔记就不一样,记笔记是定制化的,对你自己定制,你可以用自己最爽的表达方式来描述一个问题,是自己写给自己看的东西,看了几遍之后就能非常迅速和容易地理解。之后遇到相同的问题可以快速地通过找笔记解决。

举个例子,下图是我记录的一些关于gdb【linux下调试c++的工具】的使用的一些笔记。我只记录了我自己最常用的一些内容,也许你看着很乱,但是我就能很容易看懂,这就是我的定制化。

记笔记的习惯一定要坚持,等过个一年或者两年,这就是你巨大的财富,因为那是只有你才能看懂的东西。我已经记录了4年多、1G多的内容,现在的笔记基本已经形成了体系,可以给大家展示其中的一部分。

专业知识相关笔记:

开发相关的笔记:

一些类目:

5.有什么比较好的编程方法?

除了上述分享的一些方法,我认为在同一时间段不要学习太多类别的课程,比如你可以同时学习python和html/css,但是你不要同时学python、操作系统、编译原理、计算机组成、数据结构、网络,我曾经试过,一门课没学一会儿就学下一门,其实上一门根本学不到什么实际的知识。因为记忆知识是符合艾宾浩斯记忆曲线的。对于一门课,特别是很难的专业课,譬如操作系统,你每天看半小时,效果是比较差的,可能你热身就得半小时。所以宁可每天学两门,然后每一门学长一点的时间,比如两小时。【毕竟学校上课,一次课也得两小时】,要避免贪多,一口吃不成个胖子。

6.我需要刷oj么?

我认为刚开始编程的时候还是应该刷的,但是一定要注意,不要被你周围的“X神”给误导了。因为我上大学的时候,身边总是有很多搞计算机竞赛的人,他们之间都互相称对方为“X神”,某某神又使用一个牛逼的算法,将程序时间从1秒降低到了0.999秒。我要劝大家的是,刷题不是为了达到这个目的,不是说非要在竞赛中拿奖,除非你是特别喜欢,否则,没必要去背代码。我们刷题的目的是适应写代码的感觉,在这个过程中你会遇到编译错误,你会慢慢去记住一些语法、关键字,并理解一些概念,还可以自己去使用它,比如实现数据结构。慢慢的你就会变得有经验,知道一些错误产生的原因。我也是慢慢这样过来的,我现在在工作和下班以后写代码时,基本都不用IDE了,比如写c++,要么vim,要么就是sublime,而调试用的是我前面提到的工具gdb。即,有一个文本编辑器就能写代码,脱离了IDE的束缚。在写oj之后一段时间,在比较熟练了之后,就可以不去刷题了,可以去譬如github这样的网站上找点项目来看,然后自己跟着写一下,编程能力慢慢就提升了。就计算机专业来说,很多同学在大一上完编程课之后,就很少写代码了,这样是很不好的。刷题除了可以锻炼编程能力,对于找工作前突击也很有作用。比如,我之前投递过华为公司的研发岗位,校招的时候有笔试题。我就在16年国庆的时候刷了一下华为的oj,我记得笔试是600分的总分,过100就给面试机会,而我很轻松的就拿了500分,而当时也就刷了20多道华为的题。

7.看书还是看视频?

网上有不少人鄙视看视频学习的同学,我不知道为什么,因为我认为看视频是一个很好的学习方式。不过我们得明白看书和看视频分别有什么优缺点。

其实我是很建议看视频入门的,因为目前网上的应用型【非学术型:比如清华大学的操作系统,非常难】的视频都是很简单的,很多是面向初学者的,视频能用较短的时间告诉你你现在所学的技术可以干什么,可能需要先修哪些知识,可以帮助我们搭建一个项目驱动式学习的网络。可是视频也有个缺点:就是知识非常的杂,很不系统。虽然现在很多教学网站都提供了学习路径,但是这些路径中的视频很多时候都不是同一个老师录制的,只是按照知识的依赖关系排的顺序,所以,如果想通过视频去系统地学习一门知识,是比较困难的。【当然,一些学术型的视频还是很推荐的,比如斯坦福的机器学习,清华的操作系统、数据结构等课程,能坚持看完,绝对受益匪浅】。而应用型的,比如web开发等知识,还是得看书。书籍等特点就是系统化,由浅入深,你可以定制化地看自己薄弱的章节。所以一个比较好的学习方式是:

看视频入门,看书进阶。

8.多久能学会编程?

其实这个问题是没有答案的,如果只是想做出一个小应用,2个月足矣,而就我个人而言,我认为学习编程不是学习一种编程语言,而是学习一个生态,一个计算机系统,所以无止境。

9.我应该选择什么资料,看什么书?

其实这个问题也是很多编程新手容易困惑的问题。网络上拥有我们一辈子都看不完的教程和资料,所以现在应该不会存在找不着视频教程、找不着书看的问题。而问题就是我们不知道看什么视频、看什么书。从开始学编程到现在,我也买了上百本书,而真正适合自己的好书并不多。而视频教程的问题就更严重了,东看一点、西看一点,知识很难组织成网络。所以学习编程的过程中,我们遇到的最大的问题是:当我们遇到问题的时候,在大量资料面前,我们不知道选择什么资料去学习。即使我们使用项目驱动式学习的方法找到了我们的方向,但是同一个路径下,也有很多资料。前文列举的项目驱动式学习的图中,我们是自上而下的去发现问题,然后再解决问题。如果能有人帮我们组织好学习路径,然后自下而上地去学习,那么效率可能会提高很多。

不过不用担心,我已经尽我所能,将我看过或者我认为好的课程和书本资料给串了串,整理好上传了,所有学习资料均免费,无任何收费课程。

资料百度云地址: https://pan.baidu.com/s/11Pk-TAKuiyKaY9Llxpdj2Q 提取码: w4mk

另外,很多新手反馈不会搭环境,那我给大家推荐一款可以在线运行代码的网站,那个背单词app百词斩推出的学习编程的网站,在线编辑,在线运行,避免了你搭环境的痛苦,直接运行即可,这个环境使用是免费的,点击FreeCode,选择“代码练习”新建文件就可以了:

这个网站对初学者比较友好,不过目前只有Python环境,其他的环境得再等等。

百词斩一直走的都是黑科技路线,并且出的课程也是采用的及时反馈的模式,也正是在他们家自己搭建的环境上,进行的一个练习,对电脑的配置也没有什么要求,甚至平板和手机等终端上都可以使用,也是按背单词那套来设计的,感兴趣的同学也可以去看看,也可以去公众号回复「免费教程」能白嫖免费的课程,感觉也还挺扎实的,实在不感兴趣的,也可以免费薅一下他家的编程环境,对电脑配置真的没有什么要求。

不会英文学习一门PHP语言会难吗?

(关注@九言堂 ,探讨不一样的思路)

首先观点要明确:不会英语,对编程本身几乎没有影响,影响的是进一步学习英文文档。

英语好,对编程有没有帮助?

这好像是一句废话,英语好不光对编程有帮助,对看电影、玩游戏、听音乐、出国旅游等等等等,都有帮助。在当今全球化的时代,英语这个国际语言的作用毋庸置疑。

学编程,一定要会英语?

不一定。编程中是会碰到英语,但反反复复就那几个,有些中文编程语言号称不用英文,其实就是换汤不换药。无非是用汉字“如果”代替英文的“if”罢了,没有任何意义。难道就差这几个单词吗?你反过来想,把关键词替换成中文,难道没学过编程的人,就能看得懂程序吗?母语是英语的人,是不是都能看懂英文程序呢?显然答案都是否定的。

另外,现在翻译软件各式各样,完全能够解决你的日常编程使用。

编程靠的是思想,不管你说的是什么自然语言,不管你用的是哪种编程语言,核心思想都一样。

会英语对编程真正有帮助的地方在哪?

看英文文档。整个计算机行业,绝大部分文档,都是英文。如果不会英文,就失去了接触业界大部分知识的机会。就好比你不懂普通话,只会方言。在老家看不出问题,但到了外地上大学,你就只能和很小一部分老乡交流,失去了和大部分人交流的机会。所以英文好,在学习业界知识方面有很大优势。

但是,话又说回来,也不是天天要看英文文档,况且文档一般都不会很复杂,四级水平完全足够,实在不行,翻译软件也能帮忙,没什么好担心的。

总之一句话:会英文,对学编程有帮助;但学编程,不一定要会英文。

不过,如果你26个字母都不认识,那确实有些费劲?

本文转载自互联网,如有侵权,联系删除