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

2.3 函数和类

C、C++、Java、Ruby、Perl、Lisp……在笔者所知的编程语言之中,所有的程序都是由函数和类组成的。可以说任何程序里面包含的不是函数就是类,Pyther当然也不例外。

2.3.1 函数

曾经有一句非常出名的话是In Unix Everything Is A File,在Unix中所有的一切都是文件。在这里可以借鉴一下,In Python Everything Is A Function,在Python程序中,所有的一切都是函数。这是典型的C语言写法,把所需的功能都写成一个一个的函数,然后由函数调用函数。依次类推,最终完成整个程序的功能。

还记得上节提过的暴力破解吗?不管用什么工具,暴力破解都少不了一个合适的字典(此字典非彼字典,这里的字典指的是一个包含密码的文件,也就是一个密码集,而不是Python的变量类型)。当然网上有很多的密码字典可供下载,但它们要么太大遍历一次需要太多的时间,要么没有针对性根本就不包含所需的密码。如果已知了一些可能是密码的字符串,完全可以根据已知条件用程序编写有针对性的字典出来,这样会节省很多时间。

【示例2-13】现在来编写一个简单的程序makePasswordFileFunction.py,创建一个有针对性的专用密码字典。打开Putty连接到Linux,执行命令:

        cd code/crawler
        vi makePasswordFileFunction.py

makePasswordFileFunction.py的代码如下:

          1 #! /usr/bin/env python
          2 #-*- coding: utf-8-*-
          3 __author__ = 'hstking hstking@hotmail.com'
          4
          5 import os
          6 import platform
          7 import itertools
          8 import time
          9
         10 def main():
         11     ''’主程序 '''
         12     global rawList #原始数据列表
         13     rawList = []
         14     global denyList #非法单词列表
         15     denyList = [' ', '', '@']
         16     global pwList #最终的密码列表
         17     pwList = []
         18     global minLen #密码的最小长度
         19     minLen = 6
         20     global maxLen #密码的最大长度
         21     maxLen = 16
         22     global timeout
         23     timeout = 3
         24     global flag
         25     flag = 0
         26     run = {
         27 '0':exit,  #退出
         28 '1':getRawList,  #创建原始列表
         29 '2':addDenyList,  #添加不可能出现的元素
         30 '3':clearRawList,  #清空列表
         31 '4':setRawList,  #原始列表排序
         32 '5':modifyPasswordLen,  #修改最终的密码长度
         33 '6':createPasswordList,  #创建最终的字典列表
         34 '7':showPassword,  #显示密码
         35 '8':createPasswordFile  #创建密码文件
         36 }
         37
         38    while True:
         39       mainMenu()
         40          op = raw_input(’输入选项:')
         41          if op in map(str, range(len(run))):
         42             run.get(op)()
         43          else:
         44             tipMainMenuInputError()
         45             continue
         46
         47 def mainMenu():
         48     ''’主菜单 '''
         49     global denyList
         50     global rawList
         51     global pwList
         52     global flag
         53     clear()
         54     print(u'||'),
         55     print(u'='*40),
         56     print(u'||')
         57     print(u'|| 0:退出程序’)
         58     print(u'|| 1:输入密码原始字符串’)
         59     print(u'|| 2:添加非法字符到列表’)
         60     print(u'|| 3:清空原始密码列表’)
         61     print(u'|| 4:整理原始密码列表’)
         62     print(u'|| 5:改变默认密码长度(%d-%d)' %(minLen, maxLen))
         63     print(u'|| 6:创建密码列表’)
         64     print(u'|| 7:显示所有密码’)
         65     print(u'|| 8:创建字典文件’)
         66     print(u'||'),
         67     print(u'='*40),
         68     print(u'||')
         69     print(u’当前非法字符为:%s' %denyList)
         70     print(u’当前原始密码元素为:%s' %rawList)
         71     print(u’共有密码%d个’ %len(pwList))
         72     if flag:
         73        print(u"已在当前目录创建密码文件dic.txt")
         74      else:
         75        print(u"尚未创建密码文件")
         76
         77 def clear():
         78     ''’清屏函数 '''
         79     OS = platform.system()
         80     if (OS == u'Windows'):
         81        os.system('cls')
         82     else:
         83        os.system('clear')
         84
         85 def tipMainMenuInputError():
         86     ''’错误提示 '''
         87     clear()
         88     print(u"只能输入0-7的整数,等待%d秒后重新输入" %timeout)
         89     time.sleep(timeout)
         90
         91 def getRawList():
         92     ''’获取原始数据列表 '''
         93     clear()
         94     global denyList
         95     global rawList
         96     print(u"输入回车后直接退出")
         97     print(u"当前原始密码列表为:%s" %rawList)
         98     st = None
         99     while not st == '':
        100        st = raw_input("请输入密码元素字符串:")
        101        if st in denyList:
        102           print(u"这个字符串是预先设定的非法字符串")
        103           continue
        104        else:
        105          rawList.append(st)
        106          clear()
        107          print(u"输入回车后直接退出")
        108          print(u"当前原始密码列表为:%s" %rawList)
        109
        110 def addDenyList():
        111     ''’添加非法词 '''
        112     clear()
        113     global denyList
        114     print(u"输入回车后直接退出")
        115     print(u"当前非法字符为:%s" %denyList)
        116     st = None
        117     while not st == '':
        118        st = raw_input("请输入需要添加的非法字符串:")
        119        denyList.append(st)
        120        clear()
        121        print(u"输入回车后直接退出")
        122        print(u"当前非法字符列表为:%s" %denyList)
        123
        124 def clearRawList():
        125     ''’清空原始数据列表 '''
        126     global rawList
        127     rawList = []
        128
        129 def setRawList():
        130     ''’整理原始数据列表 '''
        131     global rawList
        132     global denyList
        133     a = set(rawList)
        134     b = set(denyList)
        135     rawList = []
        136     for str in set(a - b):
        137        rawList.append(str)
        138
        139 def modifyPasswordLen():
        140     ''’修改默认密码的长度 '''
        141     clear()
        142     global maxLen
        143     global minLen
        144     while True:
        145        print(u"当前密码长度为%d-%d" %(minLen, maxLen))
        146        min = raw_input("请输入密码最小长度:")
        147        max = raw_input("请输入密码最大长度:")
        148        try:
        149           minLen = int(min)
        150           maxLen = int(max)
        151        except ValueError:
        152           print(u"密码长度只能输入数字[6-18]")
        153           break
        154        if minLen not in xrange(6,19) or  maxLen not in xrange(6,19):
        155           print(u"密码长度只能输入数字[6-18]")
        156           minLen = 6
        157           maxLen = 16
        158           continue
        159        if minLen == maxLen:
        160           res = raw_input("确定将密码长度设定为%d吗?(Yy/Nn)" %minLen)
        161           if res not in list('yYnN'):
        162              print(u"输入错误,请重新输入")
        163              continue
        164           elif res in list('yY'):
        165               print(u"好吧,你确定就好")
        166               break
        167           else:
        168               print(u"给个机会,改一下吧")
        169               continue
        170       elif minLen > maxLen:
        171          print(u"最小长度比最大长度还大,可能吗?请重新输入")
        172          minLen = 6
        173          maxLen = 16
        174          continue
        175       else:
        176          print(u"设置完毕,等待%d秒后回主菜单" %timeout)
        177          time.sleep(timeout)
        178          break
        179
        180 def createPasswordList():
        181     ''’创建密码列表 '''
        182     global rawList
        183     global pwList
        184     global maxLen
        185     global minLen
        186     titleList = []
        187     swapcaseList = []
        188     for st in rawList:
        189        swapcaseList.append(st.swapcase())
        190        titleList.append(st.title())
        191    sub1 = []
        192    sub2 = []
        193    for st in set(rawList + titleList + swapcaseList):
        194       sub1.append(st)
        195    for i in xrange(2, len(sub1) + 1):
        196       sub2 += list(itertools.permutations(sub1, i))
        197    for tup in sub2:
        198       PW = ''
        199       for subPW in tup:
        200          PW += subPW
        201       if len(PW) in xrange(minLen, maxLen + 1):
        202          pwList.append(PW)
        203       else:
        204          pass
        205
        206 def showPassword():
        207     ''’显示创建的密码 '''
        208     global pwList
        209     global timeout
        210     for i in xrange(len(pwList)):
        211        if i%4 == 0:
        212           print("%s\n" %pwList[i])
        213        else:
        214           print("%s\t" %pwList[i]),
        215     print('\n')
        216     print(u"显示%d秒,回到主菜单" %timeout)
        217     time.sleep(timeout)
        218
        219 def createPasswordFile():
        220     ''’创建密码字典文件 '''
        221     global flag
        222     global pwList
        223     print(u"当前目录下创建字典文件:dic.txt")
        224     time.sleep(timeout)
        225     with open('./dic.txt', 'w+') as fp:
        226        for PW in pwList:
        227           fp.write(PW)
        228           fp.write('\n')
        229     flag = 1
        230
        231
        232 if __name__ == '__main__':
       233      main()

输入:wq,保存makePasswordFileFunction.py。makePasswordFileFunction.py稍微复杂一点点,它的作用就是根据用户输入的“密码元素”来创建一个字典列表。该脚本将输入的元素根据一定的规则修改、添加后当作新元素添加到元素列表中去。最后将元素列表排列组合得到最后的字典列表。执行命令:

        python makePasswordFileFunction.py

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

图2-19 run makePasswordFileFunction.py

纯C语言的写法好处就是关系简单明了,函数调用一目了然。但如果调用的函数过多,就难免有些混乱了。简单功能的程序还无妨,稍大一点项目就有些吃力了。

提示

不要添加太多的“密码元素”,这个程序只是利用了Python的模块,没有优化算法。如果输入的“密码元素”超过了20个,那么创建密码字典的时间会非常的长。

2.3.2 类

既然有了In Python Everything Is A Fcuntion,当然会有In Python Everything Is A Class。这种C++的写法就是把所有相似的功能都封装到一个类里。最理想的情况是一个程序只有一个主程序,然后在主程序里实例化类。

【示例2-14】还是以编写密码字典为例,将makePasswordFileFunction.py改编成makePasswordFileClass.py。打开Putty连接到Linux,执行命令:

        cd code/crawler
        vi makePasswordFileClass.py

makePasswordFileClass.py的代码如下:

    1 #! /usr/bin/env python
    2 #-*- coding: utf-8-*-
    3 __author__ = 'hstking hstking@hotmail.com'
    4
    5 import os
    6 import platform
    7 import itertools
    8 import time
    9
   10 class MakePassword(object):
   11      def __init__(self):
   12           self.rawList = []
   13           self.denyList = ['', ' ', '@']
   14           self.pwList = []
   15           self.minLen = 6
   16           self.maxLen = 16
   17           self.timeout = 3
   18           self.flag = 0
   19           self.run = {
   20 '0':exit,
   21 '1':self.getRawList,
   22 '2':self.addDenyList,
   23 '3':self.clearRawList,
   24 '4':self.setRawList,
   25 '5':self.modifyPasswordLen,
   26 '6':self.createPasswordList,
   27 '7':self.showPassword,
   28 '8':self.createPasswordFile
   29 }
   30           self.main()
   31
   32      def main(self):
   33           while True:
   34                self.mainMenu()
   35              op = raw_input(’输入选项:')
   36                if op in map(str, range(len(self.run))):
   37                     self.run.get(op)()
   38                else:
   39                     self.tipMainMenuInputError()
   40                     continue
   41
   42      def mainMenu(self):
   43           self.clear()
   44           print(u'||'),
   45           print(u'='*40),
   46           print(u'||')
   47          print(u'|| 0:退出程序’)
   48          print(u'|| 1:输入密码原始字符串’)
   49          print(u'|| 2:添加非法字符到列表’)
   50          print(u'|| 3:清空原始密码列表’)
   51          print(u'|| 4:整理原始密码列表’)
   52          print(u'|| 5:改变默认密码长度(%d-%d)' %(self.minLen, self.maxLen))
   53          print(u'|| 6:创建密码列表’)
   54          print(u'|| 7:显示所有密码’)
   55          print(u'|| 8:创建字典文件’)
   56           print(u'||'),
   57           print(u'='*40),
   58           print(u'||')
   59          print(u’当前非法字符为:%s' %self.denyList)
   60          print(u’当前原始密码元素为:%s' %self.rawList)
   61          print(u’共有密码%d个’ %len(self.pwList))
   62           if self.flag:
   63              print(u"已在当前目录创建密码文件dic.txt")
   64           else:
   65              print(u"尚未创建密码文件")
   66
   67      def clear(self):
   68           OS = platform.system()
   69           if (OS == u'Windows'):
   70                os.system('cls')
   71           else:
   72                os.system('clear')
   73
   74      def tipMainMenuInputError(self):
   75           self.clear()
   76          print(u"只能输入0-7的整数,等待%d秒后重新输入" %timeout)
   77           time.sleep(timeout)
   78
   79      def getRawList(self):
   80           self.clear()
   81          print(u"输入回车后直接退出")
   82          print(u"当前原始密码列表为:%s" %self.rawList)
   83           st = None
   84           while not st == '':
   85              st = raw_input("请输入密码元素字符串:")
   86                if st in self.denyList:
   87                  print(u"这个字符串是预先设定的非法字符串")
   88                     continue
   89                else:
   90                     self.rawList.append(st)
   91                     self.clear()
   92                  print(u"输入回车后直接退出")
   93                  print(u"当前原始密码列表为:%s" %self.rawList)
   94
   95      def addDenyList(self):
   96           self.clear()
   97          print(u"输入回车后直接退出")
   98          print(u"当前非法字符为:%s" %self.denyList)
   99           st = None
   100           while not st == '':
   101              st = raw_input("请输入需要添加的非法字符串:")
   102                self.denyList.append(st)
   103                self.clear()
   104              print(u"输入回车后直接退出")
   105              print(u"当前非法字符列表为:%s" %self.denyList)
   106
   107      def clearRawList(self):
   108           self.rawList = []
   109
   110      def setRawList(self):
   111           a = set(self.rawList)
   112           b = set(self.denyList)
   113           self.rawList = []
   114           for str in set(a - b):
   115                self.rawList.append(str)
   116
   117      def modifyPasswordLen(self):
   118           self.clear()
   119           while True:
   120              print(u"当前密码长度为%d-%d" %(self.minLen, self.maxLen))
   121              min = raw_input("请输入密码最小长度:")
   122              max = raw_input("请输入密码最大长度:")
   123                try:
   124                     self.minLen = int(min)
   125                     self.maxLen = int(max)
    126                except ValueError:
   127                  print(u"密码长度只能输入数字[6-18]")
   128                     break
   129                if self.minLen not in xrange(6,19) or  self.maxLen not in
 xrange(6,19):
   130                  print(u"密码长度只能输入数字[6-18]")
   131                     self.minLen = 6
   132                     self.maxLen = 16
   133                     continue
   134                if self.minLen == self.maxLen:
   135                  res = raw_input("确定将密码长度设定为%d吗?(Yy/Nn)"
 %self.minLen)
   136                     if res not in list('yYnN'):
   137                      print(u"输入错误,请重新输入")
   138                         continue
   139                     elif res in list('yY'):
   140                      print(u"好吧,你确定就好")
   141                         break
   142                     else:
   143                      print(u"给个机会,改一下吧")
   144                         continue
   145                elif self.minLen > self.maxLen:
   146                  print(u"最小长度比最大长度还大,可能吗?请重新输入")
   147                     self.minLen = 6
   148                     self.maxLen = 16
   149                     continue
   150                else:
   151                  print(u"设置完毕,等待%d秒后回主菜单" %self.timeout)
   152                     time.sleep(self.timeout)
   153                     break
   154
   155      def createPasswordList(self):
   156           titleList = []
    157           swapcaseList = 
   158           for st in self.rawList:
   159                swapcaseList.append(st.swapcase())
   160                titleList.append(st.title())
   161           sub1 = []
   162           sub2 = []
   163           for st in set(self.rawList + titleList + swapcaseList):
   164                sub1.append(st)
   165           for i in xrange(2, len(sub1) + 1):
   166                sub2 += list(itertools.permutations(sub1, i))
   167           for tup in sub2:
   168                PW = ''
   169                for subPW in tup:
   170                     PW += subPW
   171                if len(PW) in xrange(self.minLen, self.maxLen + 1):
   172                     self.pwList.append(PW)
   173                else:
   174                     pass
   175
   176      def showPassword(self):
   177           for i in xrange(len(self.pwList)):
   178                if i%4 == 0:
   179                     print("%s\n" %self.pwList[i])
   180                else:
   181                     print("%s\t" %self.pwList[i]),
   182           print('\n')
   183          print(u"显示%d秒,回到主菜单" %self.timeout)
   184           time.sleep(self.timeout)
   185
   186      def createPasswordFile(self):
   187          print(u"当前目录下创建字典文件:dic.txt")
   188           time.sleep(self.timeout)
   189           with open('./dic.txt', 'w+') as fp:
   190                for PW in self.pwList:
   191                     fp.write(PW)
   192                     fp.write('\n')
   193           self.flag = 1
   194
   195
   196 if __name__ == '__main__':
   197      mp = MakePassword()

输入:wq,保存makePasswordFileClass.py。makePasswordFileClass.py和makePasswordFileFunction.py实质上没什么区别,只是一个使用的是C语言风格的函数调用,一个使用的是C++风格的类实例化。执行命令:

        python makePasswordFileClass.py

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

图2-20 run makePasswordFileClass.py

执行结果完全一样。这种C++的写法好处就是调用过程简单,不再关心类具体的实现过程,只需要调用其功能即可;但随之而来就是类的继承、函数重载等麻烦。这种写法在写大项目时可能非常有用。个人写小程序也行,那就没那么多优势了。

提示

这个密码还有个问题就是在创建密码文件前并没有估算磁盘剩余空间是否足够。一般的解决办法是先估算密码文件的大小,然后创建一个大小相同的空文件。能创建成功则继续运行程序,不能则抛出异常。