Python 网络爬虫实战
上QQ阅读APP看书,第一时间看更新

2.2 Python语句

说到语句,回想一下C、C++、Java、Perl等,似乎所有的编程语言都有类似的语句。条件判断、有限循环、无限循环,这几个是最基本的,也是必不可少的。每个编程语言都差不多。熟悉了这几个语句后,即使是一门从未接触过的语言,稍微了解一下格式语法就可以用新的语言解决一般的小问题了。

2.2.1 条件语句——if else

似乎所有的条件语句都使用if……else……。它的作用可以简单地概括为非此即彼。满足条件A则执行A的语句,否则执行B语句。Python的if.……else……功能更加强大,在if和else之间添加数个elif,有更多的条件选择。其表达形式如下:

        if判断条件1:
        执行语句1
        elif判断条件2:
        执行语句2
        elif判断条件3:
        执行语句3
        else:
        执行语句4

【示例2-6】编写testIfRemainder7.py熟悉一下Python下的if语句。testIfRemainder7.py用来检验输入数字能否被7整除。打开Putty连接到Linux,执行命令:

        cd code/crawler
        vi testIfRemainder7.py

testIfRemainder7.py的代码如下:

          1 #! /usr/bin/env python
          2 #-*- coding: utf-8-*-
          3 __author__ = 'hstking hstking@hotmail.com'
          4
          5
          6 def isEvenNum(num):
          7    if num%7 == 0:
          8       print(u"%d可以被7整除" %num)
          9    else:
         10       print(u"%d不可被7整除" %num)
         11
         12 if __name__ == '__main__':
         13    numStr = raw_input("请输入一个整数:")
         14    try:
         15       num = int(numStr)
         16    except ValueError:
         17       print(u"输入错误,要求输入一个整数")
         18       exit()
         19
         20    isEvenNum(num)

输入:wq,保存testIfRemainder7.py。testIfRemainder7.py要求用户输入一个整数,然后判断这个数能否被7整除,基本就是一个最基本的非此即彼的判断。执行命令:

        python testIfRemainder7.py

得到的结果如图2-12所示。

图2-12 run testIfRemainder7.py

非常简单。按照格式,照猫画虎就可以解决类似的问题了。

Case switch是C语言中经典的条件语句之一。可惜的是Python中并没有Case语句。不过没关系,if elif else完全可以替代case语句。如果原意开动脑筋,Python中还有很多可以替代Case语句的方案,例如利用字典什么的,这里就不再一一赘述了。

2.2.2 有限循环——for

在编程时,总会遇到这种事情,把某个过程重复N次。这是每个编程语言都不可避免的。好在几乎所有的编程语言都提供for语句。它的作用是将一个语句块、函数等重复执行有限的次数。

for循环表达形式如下:

        for Var in Sequence:
        执行语句

比如从1加到100。大数学家高斯(Johann Karl Friedrich Gauss)10岁时就给出了计算的公式。虽然已经有了简单的方法,用笨方法验算一下也不错。

【示例2-7】编写testForGauss10.py,打开Putty连接到Linux,执行命令:

        cd code/crawler
        vi testForGauss10.py

testForGauss10.py的代码如下:

          1 #! /usr/bin/env python
          2 #-*- coding: utf-8-*-
          3 __author__ = 'hstking hstking@hotmail.com'
          4
          5 def cumulative(num):
          6    sum = 0
          7    for i in xrange(1, num+1):
          8       sum += i
          9    return sum  #累加函数,返回累加后的值
         10
         11 def main():
         12    while True:
         13       print(u"===========================")
         14       print(u"输入exit退出程序:")
         15       str_num = raw_input("从1累加到:")
         16       if str_num == 'exit':
         17          break
         18       try:
         19          sum = cumulative(int(str_num))
         20       except ValueError:
         21          print(u"除非退出输入exit,只能输入数字")
         22          continue
         23       print(u"从1累加到%d的总数是%d" %(int(str_num), sum))
         24
         25
         26 if __name__ == '__main__':
         27    main()

输入:wq,保存testForGauss10.py。testForGauss.py将使用最笨的方法求从1加到100的和,使用for循环一个数一个数地叠加。执行命令:

        python testForGauss10.py

得到的结果如图2-13所示。

图2-13 run testForGauss10.py

经过验算,聪明办法和笨办法得到的结果一致。for循环用于数字循环时有2种方法生成Sequence,一种是range(1,100),另一种是xrange(1,100)。在使用for循环时,这两种方法生成的Sequence几乎没有区别。但如果循环数比较大的情况下建议使用xrange,因为range是直接生成了一个列表,而xrange则是生成了一个生成器。

仔细看下for循环的表达式,Sequence是一个序列,说明for循环不仅仅适用于数字形式的循环,比如可以将文件放入列表中作为一个序列,然后对文件进行操作。

2.2.3 无限循环——while

既然有有限循环,当然就有无限循环了。无限循环的作用是,只要不满足某种条件,就一直循环下去,直到满足条件为止。While循环表达形式如下:

        while Boolean expression:
        执行语句

【示例2-8】Linux终端登录就是一个类似while循环的示例。下面模拟Linux终端登录,编写testWhileSimulateLogin.py。打开Putty连接到Linux,执行命令:

        cd code/crawler
        vi testWhileSimulateLogin.py

testWhileSimulateLogin.py的代码如下:

          1 #! /usr/bin/env python
          2 #-*- coding: utf-8-*-
          3 __author__ = 'hstking hstking@hotmail.com'
          4
          5 import getpass
          6
          7 class FakeLogin(object):
          8    def __init__(self):
          9        self.name = 'king'
         10        self.password = 'haha, no pw'
         11        self.banner = 'hello, you have login system'
         12        self.run()
         13
         14    def  run(self):
         15        ''’仿Linux终端登录窗口’''
         16        print(u"不好意思,只有一个用户king")
         17        print(u"偷偷的告诉你,密码是6个8哦")
         18        while True:
         19            print(u"Login:king")
         20            pw = getpass.getpass("Password:")
         21            if pw == '88888888':
         22               print(u"%s" %self.banner)
         23               print(u"退出程序")
         24               exit()
         25            else:
         26               if len(pw) > 12:
         27                  print(u"密码长度应该小于12")
         28                  continue
         29               elif len(pw) < 6:
         30                  print(u"密码长度大于6才对")
         31                  continue
         32               else:
         33                  print(u"可惜,密码错误。继续猜")
         34                  continue
         35
         36
         37 if __name__ == '__main__':
         38    fl = FakeLogin()

输入:wq,保存testWhileSimulateLogin.py。testWhileSimulateLogin.py脚本模拟Linux登录,如果输入了正确的密码才退出程序,输入了错误的密码则给出相应的提示,直到输入正确为止。因为不知道会输入多少次才会退出,所以这里使用while循环正好。执行命令:

        python testWhileSimulateLogin.py

得到的结果如图2-14所示。

图2-14 run testWhileSimulateLogin.py

实际上目前的终端登录都有次数限制,不可能这样无限地输入密码进行测试,否则就会被暴力破解。正好这个程序没有限制,有兴趣的可以自行编写程序,实验一下暴力破解密码。

2.2.4 中断循环——continue、break

continue和break语句都只能作用于循环之中,只对循环起作用。continue的作用是,从continue语句开始到循环结束,之间所有的语句都不执行,直接从下一次循环重新开始;而break语句的作用是退出循环,该循环结束。

【示例2-9】用continue、break来做一个随机猜数字的游戏。先给定一个数值范围,系统在给定的范围内随机选取一个数,然后来猜这个随机数是多少,猜对了直接退出,猜错了系统则提示猜的数字与随机数相比是大了还是小了。打开Putty连接到Linux,执行命令:

        cd code/crawler
        vi guessNum.py

guessNum.py的代码如下:

          1 #! /usr/bin/env python
          2 #-*- coding: utf-8-*-
          3 __author__ = 'hstking hstking@hotmail.com'
          4
          5 import random
          6
          7 class GuessNum(object):
          8     ''’这个类用于猜随机数 '''
          9     def __init__(self):
         10         print(u"随机产生一个0-100的随机数")
         11         self.num = random.randint(0,101)
         12         self.guess()
         13
         14     def guess(self):
         15        i = 0
         16        while True:
         17            print(u"猜这个随机数,0-100")
         18            strNum = raw_input("输入你猜的数字:")
         19            i += 1
         20            try:
         21               print("****************")
         22               if int(strNum) < self.num:
         23                  print(u"你猜得太小了")
         24                  continue
         25               elif int(strNum) > self.num:
         26                  print(u"你猜得太大了")
         27                  continue
         28               else:
         29                  print(u"你总算是猜对了")
         30                  print(u"你总共猜了%d次" %i)
         31                  break
         32           except ValueError:
         33                  print(u"只能输入数字,继续猜吧")
         34                  continue
         35           print(u"如果没有continue或break,就会显示这个,要不要试试?")
         36
         37
         38 if __name__ == '__main__':
         39    gn = GuessNum()

输入:wq,保存guessNum.py。guestNum先指定了一个1~100的随机数。然后开始猜这个随机数是多少,一般来说猜5次左右就可以猜出来。如果能一次猜到这个随机数,有这么逆天的运气还是赶紧买几注彩票试试吧。执行命令:

        python guestNum.py

得到的结果如图2-15所示。

图2-15 run guessNum.py

试一下,要猜多少次才会猜对这个随机数。

提示

一般来说,纯粹只有循环而没有中断循环的情况很少见(特别是在while循环中)。大多都是配对出现的,所以熟悉了循环还必须掌握中断循环的方法。

2.2.5 异常处理——try except

要求输入的数据不符合要求,访问列表、元组下标超出范围,根据key访问字典中的key值却发现这个key不存在……编程时总会遇上种种意外。有些编程语言在碰到程序执行意外错误时,系统提示错误,然后退出程序。当然,Python也是这样处理的,但不同的是Python还给出了其他的选择。

在Python中,用try来测试可能出现异常的语句,然后用except来处理可能出现的异常。try except的表达形式如下:

        try:
        语句
        except [exception, [data…]]:
         Do something
        except [exception, [data…]]:
         Do something
        except [exception, [data…]]:
         Do something

意思是,尝试执行语句,如果出现某个异常则怎么做。因为同一个语句可能出现不同的异常,所以也会给出不同的解决方法。另外,try还可以配以else、finally语句一起使用,不过这种情况比较少,有兴趣的朋友可以自行google用法。

【示例2-10】以常见的输入数据异常为例,编写testTryInput.py,打开Putty连接到Linux,执行命令:

        cd code/crawler
        vi testTryInput.py

testTryInput.py的代码如下:

          1 #! /usr/bin/env python
          2 #-*- coding: utf-8-*-
          3 __author__ = 'hstking hstking@hotmail.com'
          4
          5 class TryInput(object):
          6      def __init__(self):
          7         self.len = 10
          8         self.numList = self.createList()
          9         self.getNum()
         10
         11     def createList(self):
         12        print(u"创建一个长度为%d的数字列表" %self.len)
         13        numL = []
         14        while len(numL) < 10:
         15           n = raw_input("请输入一个整数:")
         16           try:
         17              num = int(n)
         18           except ValueError:
         19              print(u"输入错误,要求是输入一个整数")
         20              continue
         21           numL.append(num)
         22           print(u"现在的列表为:"),
         23           print(numL)
         24           return numL
         25
         26       def getNum(self):
         27           print(u"当前列表为"),
         28           print(self.numList)
         29           inStr = None
         30           while inStr ! = 'EXIT':
         31              print(u"输入EXIT退出程序")
         32              inStr = raw_input("输入列表下标[-10,9]:")
         33              try:
         34                 index = int(inStr)
         35                 num = self.numList[index]
         36                 print(u"列表中下标为%d的值为%d" %(index, num))
         37              except ValueError:
         38                 print(u"输入错误,列表下标是一个整数")
         39                 continue
         40              except IndexError:
         41                 print(u"下标太大,访问列表超出范围")
         42                 continue
         43
         44
         45 if __name__ == '__main__':
         46    ti = TryInput()

输入:wq,保存testTryInput.py。testTryInput.py目的是创建一个数字列表,在创建过程中尝试各种异常。执行命令:

        python testTryInput.py

得到的结果如图2-16所示。

图2-16 run testTryInput.py

这个程序就是针对输入出现的异常和访问列表越界的异常给出了解决方案。编程过程中总会遇上各种各样的异常。考虑周到一点,思维缜密一点,善用try一点,程序的健壮性就不止强一点点。

2.2.6 导入模块——import

个人看来,Python最大的优点不是简单易学,而是其强大的模块功能。前面写的一个程序,后面就可以将它当成一个模块导入现在的程序,取其精华弃其糟粕地随意使用。最理想的情况是任何一个功能,只要写一次,以后所有人都可以任其调用。代码重用性高得可怕,而且Python还可以根据需求将C、C++、Java等程序作为模块,随意取用。这是为什么Python被称之为胶水语言的原因。

Python2的标准模块(一般也叫Python标准库)是安装Python时自带的模块,具体请参考网页https://docs.python.org/2.7/py-modindex.html。它包含了几乎所有的常用功能。如果觉得不够,没关系,可以用pip来安装第三方的模块,这个模块库就已经非常强大了。如果还不够,也没关系,还有强大的github,全世界的Pyther在背后支持你。找到适用的功能程序导入到自己程序里就可以了。对别人程序极度不放心,非要自力更生也行,那就辛苦一下,自己写个程序做自己独有的模块吧。

模块导入的几种方式如下,可根据需要自行选择:

        #同时导入多个模块
        import module1[, module2[, ... moduleN]
        #导入模块中的某个函数、类、变量
        from modname import name1[, name2[, ... nameN]]
        #导入某个模块中所有
        from modname import *

每次使用print打印时,总是同一个颜色。能不能使用不同的颜色打印呢?当然可以,第三方模块库里就有相关的模块。只需要使用pip安装即可,github仔细找找应该也能找得到。在这里自力更生,自己动手写一个最符合自己要求的彩色打印的print。

【示例2-11】编写testImportColorPrint.py,将它作为模块导入到其他的python程序中使用。打开Putty,连接到Linux,执行命令:

        cd code/crawler
        vi testImportColorPrint.py

testImportColorPrint.py的代码如下:

          1 #! /usr/bin/env python
          2 #-*- coding: utf-8-*-
          3 __author__ = 'hstking hstking@hotmail.com'
          4
          5 import sys
          6
          7 class ColorPrint(object):  #带颜色的打印
          8    def __init__(self, color, msg):
          9        self.color = color
         10        self.msg = msg
         11        self.cPrint(self.color, self.msg)
         12
         13    def cPrint(self, color, msg):
         14       colors = {
         15 'black' :   '\033[1;30;47m',  #黑色
         16 'red'   :   '\033[1;31;47m',  #红色
         17 'green' :   '\033[1;32;47m',  #绿色
         18 'yellow':   '\033[1;33;47m',  #黄色
         19 'blue'  :   '\033[1;34;47m',  #蓝色
         20 'white' :   '\033[1;37;47m'}  #白色
         21       if color not in colors.keys():
         22          print(u"输入的颜色暂时没有,按系统默认配置的颜色打印")
         23       else:
         24          print(u"输入的颜色有效,开始彩色打印")
         25          print(u"%s" %colors[color])
         26          print(msg)
         27          print(u"\033[0m")
         28
         29
         30 if __name__ == '__main__':
         31    cp = ColorPrint(sys.argv[1], sys.argv[2])

输入:wq,保存testImportColorPrint.py。这里只写入了黑色、红色、绿色、黄色、蓝色和白色这几种颜色。如需添加其他的颜色请自行google一下。执行命令:

        python testImportColorPrint.py  black "I'm black"
        python testImportColorPrint.py  red "I'm red"
        python testImportColorPrint.py  green "I'm green"
        python testImportColorPrint.py  yellow "I'm yellow"
        python testImportColorPrint.py  blue "I'm blue"
        python testImportColorPrint.py  white "I'm white"

得到的结果如图2-17所示。

图2-17 run testImportColorPrint.py

彩色打印已经实现了(白色打印时因为背景色也是白色,所以显示不明显),下面是将testImportColorPrint.py当作模块导入到其他Python程序中供其使用。

【示例2-12】无须太复杂,写个最简单的testImport.py,只要能将testImportColorPrint.py当成模块导入使用即可。执行命令:

        cd code/crawler
        vi testImport.py

testImport.py的代码如下:

          1 #! /usr/bin/env python
          2 #-*- coding: utf-8-*-
          3 __author__ = 'hstking hstking@hotmail.com'
          4
          5 from testImportColorPrint import ColorPrint
          6 #这里的testImportColorPrint模块就是从当前目录下导入的
    testImportColorPrint.py程序
          7
          8 if __name__ == '__main__':
          9    p_black = ColorPrint('black', 'I am black print')
         10    p_black = ColorPrint('red', 'I am red print')
         11    p_black = ColorPrint('green', 'I am green print')
         12    p_black = ColorPrint('yellow', 'I am yellow print')
         13    p_black = ColorPrint('blue', 'I am blue print')
         14    p_black = ColorPrint('white', 'I am white print')

输入:wq,保存testImport.py。testImport.py尝试调用testImportColorPrint.py脚本作为模块,调用该脚本的类放到自己的脚本中执行。执行命令:

得到的结果如图2-18所示。

图2-18 导入模块测试

提示

将Python程序当成模块导入的先决条件是,这两个程序在同一目录下。或者将模块化的程序(这里就是testImportColorPrint.py)路径加入到Python的系统路径中。是不是很简单呢?其实Python就是这么简单。