zoukankan      html  css  js  c++  java
  • python 基础知识点整理 和详细应用

    Python教程


    Python是一种简单易学,功能强大的编程语言。它包含了高效的高级数据结构和简单而有效的方法,面向对象编程。Python优雅的语法,动态类型,以及它天然的解释能力,使其成为理想的语言,脚本和应用程序高速开发在大多数平台上的很多领域。

    Python解释器及其扩展标准库的源代码和编译版本号能够从Python的Web网站,http://www.python.org/全部主要平台可自由查看,而且能够自由公布。该网站上也包括了分配和指针到非常多免费的第三方Python模块,程序,工具,以及附加的文档。

    Python的解释器非常easy扩展新的功能,并在C或C ++(或由C来调用其它语言)实现的数据类型。 Python也非常适于作为定制应用的一种扩展语言。

    本教程向读者介绍了非正式的Python语言和系统的基本概念和功能。它有助于理解Python和实战练习,当然全部的样例都是自包括的,所以这本手冊能够离线阅读为好。

    有关标准对象和模块的具体介绍,请參见Python标准库。Python语言參考给出了语言的更正式的定义。须要编写C或C + +扩展,请阅读扩展和嵌入Python解释器和Python/C的API參考手冊。也有几本书涵盖了各个深度的Python。

    本教程并不试图全面,涵盖每个功能,甚至每个经常使用功能。相反,它介绍了很多Python中最引人注目的功能,会给Python语言的韵味和风格是一个好開始。看完之后,你就能够阅读和编写Python模块和程序,将准备进一步了解Python标准库描写叙述的各种Python库模块

    Python概述

    Python是一种高层次的,解释性的,交互式和面向对象的脚本语言。Python被设计成具有非常强的可读性,它使用英语如其它语言经常使用空白作为标点符号,它比其它语言语法结构更少。

    • Python被解析:这意味着它是在执行时由解释器处理,你并不须要在执行前编译程序。这类似于Perl和PHP。

    • Python是互动:这意味着你能够在Python的提示和解释器进行交互,直接写出你的程序。

    • Python是面向对象的:这意味着Python支持面向对象的方式或程序,它封装了对象中的代码的技术。

    • Python是刚開始学习的人的语言:Python是为0基础程序猿一种伟大的语言,并支持广泛的应用,从简单的文本处理,WWW浏览器,以游戏开发。

    Python的历史:

    Python是由Guido van Rossum在八十年代末和九十年代初在全国研究所数学与计算机科学在荷兰开发。

    Python从更多语言,包含ABC,Modula-3语言,C语言,C+ +,Algol-68,Smalltalk和unix的shell等脚本语言得到參考开发。

    Python是有版权的。比方Perl,Python源码如今是GNU通用公共许可证(GPL)下提供。

    Python的如今是由一个核心开发团队在维护,尽管Guido van Rossum仍然持有在指导其进展至关关键的数据。

    Python的特点:

    Python的功能亮点包含:

    • 易于学习:Python有相对较少的keyword,结构简单,明白的语法。这让学生学习的时间相对较短。

    • 易于阅读:Python代码是更加明白,可见。

    • 易于维护:Python的成功在于它的源码是相当easy维护。

    • 广泛的标准库:Python的最大长处是体积库非常方便,在UNIX,Windows和Macintosh跨平台兼容。

    • 交互模式:支持交互模式中,能够从终端输入结果正确的语言,让交互測试的代码片段和调试。

    • 便携式:Python能够在多种硬件平台上执行,而且对全部的平台上使用同样的接口。

    • 扩展:能够加入低级别的模块在Python解释器。这些模块使程序猿能够加入或自己定义自己的工具来提高效率。

    • 数据库:Python提供接口给全部基本的商业数据库。

    • GUI编程:Python支持,能够创建并移植到很多系统调用,库和Windows系统,如Windows MFC,Macintosh和Unix的X Window系统的GUI应用程序。

    • 可扩展性:Python提供了一个更好的结构,并支持比shell脚本大型程序。

    除了上面提到的功能,Python也有非常好的功能,几个列举例如以下:

    • 支持功能和结构化的编程方法,以及面向对象。

    • 它能够作为一种脚本语言,或者能够被编译为字节码建立大型的应用程序。

    • 很高的动态数据类型,而且支持动态类型检查。

    • 支持自己主动垃圾收集。

    • 它能够用C,C + +,COM和ActiveX,CORBA和Java非常easy地集成。

    Python环境安装

    本地环境设置

    假设愿意设置您的Python环境,让我们了解怎样建立Python环境。 Python可在各种平台,包含Linux和Mac OS X,可尝试打开一个终端窗体并输入“python”,以检查是否已经安装了python,什么版本号,假设已经有安装。

    • Unix (Solaris, Linux, FreeBSD, AIX, HP/UX, SunOS, IRIX, etc.)

    • Win 9x/NT/2000

    • Macintosh (Intel, PPC, 68K)

    • OS/2

    • DOS (multiple versions)

    • PalmOS

    • Nokia 手机

    • Windows CE

    • Acorn/RISC OS

    • BeOS

    • Amiga

    • VMS/OpenVMS

    • QNX

    • VxWorks

    • Psion

    • Python也可被移植到Java和.NET 虚拟机

    获得Python

    最新源码,二进制文件,文档,新闻等可在Python的官方站点:

    Python官方站点:http://www.python.org/

    能够从下面网站下载Python文档。文件格式是HTML,PDF和PostScript。

    Python文档站点: www.python.org/doc/

    安装Python:

    Python发行版适用于各种平台。你仅仅须要下载适用于您的平台的二进制代码并安装Python。

    假设二进制代码针对您的平台无法使用,你须要一个C编译器来手动编译源码。编译源码提供了选择,为安装功能方面更大的灵活性。

    这里是在各种平台上安装Python的高速概览:

    UNIX和Linux的安装方式:

    以下是简单的步骤,在Unix/ Linux机器上安装Python。

    • 打开Web浏览器并转至http://www.python.org/download/

    • 依照链接下载压缩的源码在Unix/ Linux操作系统。

    • 下载并解压文件。

    • 编辑模块/安装文件,假设你想自己定义一些选项。

    • 运行./configure 脚本

    • make

    • make install

    这将安装python的标准位置在 /usr/local/bin文件夹和它的库安装在/usr/local/lib/pythonXX,当中XX是Python使用的版本号。

    Windows上安装:

    以下是Windows机器上安装Python的步骤。

    • 打开Web浏览器并转至 http://www.python.org/download/

    • 依照链接到Windows安装python-XYZ.msi文件,当中XYZ是你要安装的版本号。

    • 要使用此安装程序python-XYZ.msi,Windows系统必须支持Microsoft安装程序2.0。仅仅需安装程序文件保存到本地计算机,然后执行它,看看是否你的机器支持MSI。

    • 通过双击它在Windows中执行下载的文件。这将出Python的安装向导,这些都非常easy使用。仅仅需接受默认设置,等到安装完毕后。

    Macintosh上安装:

    最新的Mac电脑配备安装了Python,但可能好几年前的机器没有安装。见http://www.python.org/download/mac/上获得的最新版本号以及额外的工具来支持在Mac上开发的指令。对于老的Mac OS的Mac OS X10.3之前(2003年推出),MacPython上是可用的。“

    仅仅要到这个链接,完整Mac OS安装安装细节。

    设置PATH:

    程序和其它可运行文件能够住在很多文件夹,所以操作系统提供,列出文件夹的操作系统搜索可运行文件的搜索路径。

    路径被存储在环境变量,这是由操作系统维护的命名字符串。这些变量包括可用于命令行解释器和其它程序的信息。

    路径变量名为Path的Unix或路径在Windows(UNIX是区分大写和小写的,Windows是没有)。

    在Mac OS中,安装程序处理的道路细节。调用不论什么特定文件夹Python解释器,必须Python的文件夹加入到您的路径。

    设置路径,在Unix/Linux上:

    将Python文件夹加入到在Unix系统中的特定会话的路径:

    • 在csh shell: 输入
      SETENV PATH "$PATH:/usr/local/bin/python"  然后按回车键。

    • 在 bash shell (Linux): 输入
      export PATH="$PATH:/usr/local/bin/python" 然后按回车键。

    • 在 sh 或  ksh shell: 输入 
      PATH="$PATH:/usr/local/bin/python" 然后按回车键。

    注: /usr/local/bin/python 为Python文件夹的路径

    设置路径Windows系统:

    以Python文件夹加入到了 Windows 特定会话的路径:

    • 在命令提示符下: 输入 
      path %path%;C:Python 然后按Enter键。

    注意:C:Python 是Python文件夹的路径

    Python环境变量:

    这里是重要的环境变量,其能够被Python确认:

    变量 描写叙述
    PYTHONPATH 有类似路径的作用。这个变量告诉Python解释器在哪里能够找到导入到程序中的模块文件。 PYTHONPATH应包括Python源码库文件夹,包括Python源码的文件夹。 PYTHONPATH是由Python安装程序有时会预设。
    PYTHONSTARTUP 包括了在每次启动的解释器(类似于Unix.profile或.login文件)时运行Python源码的初始化文件的路径。这个文件通常命名为.pythonrc.py。在Unix中,通常包括载入有用程序或改动PYTHONPATH命令。
    PYTHONCASEOK 在Windows中使用,以指示Python找到一个import语句,第一个不区分大写和小写的匹配。将此变量设置为随意值来激活它。
    PYTHONHOME 备选模块搜索路径。它通常嵌入在PYTHONSTARTUP或PYTHONPATH文件夹,以使交换模块库的简单。

    执行Python:

    有三种不同的方式来启动Python:

    (1) 交互式解释器:

    能够输入python,并在開始通过命令行启动在交互式解释器它编码的时候。从UNIX,DOS或其它系统提供了一个命令行解释器或shell窗体。

    $python             # Unix/Linux
    
    or 
    
    python%             # Unix/Linux
    
    or 
    
    C:>python           # Windows/DOS
    

    以下是全部可用的命令行选项的列表:

    选项 描写叙述
    -d 提供调试输出
    -O 生成优化代码(结果为.pyo文件)
    -S 不执行导入站点,在启动时查找Python路径
    -v 具体输出(在导入语句具体的跟踪)
    -X 禁止基于类内置异常(仅仅使用字符串);開始1.6版本号过时
    -c cmd 作为cmd 字符串执行Python脚本发送
    file 从给定的文件执行Python脚本

    (2) 脚本的命令行:

    Python脚本能够在命令行中通过调用应用程序中的解释,如以下的运行:

    $python  script.py          # Unix/Linux
    
    or 
    
    python% script.py           # Unix/Linux
    
    or 
    
    C:>python script.py         # Windows/DOS
    

    注意:请确保该文件的权限模式能够运行。

    (3)集成开发环境

    您能够从图形用户界面(GUI)环境中执行Python。全部须要的是一个支持Python系统的GUI应用程序。

    • UNIX:IDLE也是早期的UNIX系统为Python的IDE。

    • Windows:PythonWin是第一个Windows界面的Python和一个GUI的IDE。

    • Macintosh:Python的的Macintosh版本号随着闲置的IDE可从主站下载,不是MACBINARY就是BinHex'd文件。

    在继续到下一个章节前,请确保您的环境已正确设置及全然正常工作。假设不可以建立正常的环境,那么可以又一次安装配置。 
     
    全部在以后的章节中给出的样例已经运行了可在Linux CentOS 上的 Python2.7.3版本号。

    Python基本的语法

    Python与Perl,C和Java语言等有很多相似之处。只是,也有语言之间有一些明白的差别。本章的目的是让你迅速学习Python的语法。

    第一个Python程序:

    交互模式编程:

    调用解释器不经过脚本文件作为參数,显示下面提示:

    $ python
    Python 2.6.4 (#1, Nov 11 2014, 13:34:43)
    [GCC 4.1.2 20120704 (Red Hat 5.6.2-48)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>>
    

    键入下列文字在Python提示符,然后按Enter键:

    >>> print "Hello, Python!";
    

    假设您执行的是新的Python版本号,那么须要使用打印语句括号像print ("Hello, Python!");。可是在Python版本号2.6.4,这将产生下面结果:

    Hello, Python!
    

    脚本模式编程:

    调用解释器及脚本作为參数,并開始运行的脚本,并一直持续到脚本完毕。当脚本完毕时,解释器不再是活动的。

    让我们在脚本中编写一个简单的Python程序。全部的Python文件将具有.py扩展。所以,把以下的代码写在一个test.py文件。

    print "Hello, Python!";

    在这里,我如果你已经在PATH变量中设置Python解释器。如今,尝试例如以下执行这个程序:

    $ python test.py

    这将产生下面结果:

    Hello, Python!
    

    让我们尝试还有一种方式来运行Python脚本。以下是改动后的test.py文件:

    #!/usr/bin/python
    
    print "Hello, Python!";

    在这里,如果Python解释器在/usr/bin文件夹中可用。如今,尝试例如以下执行这个程序:

    $ chmod +x test.py     # This is to make file executable
    $./test.py
    

    这将产生下面结果:

    Hello, Python!
    

    Python标识符:

    Python标识符是用来标识一个变量,函数,类,模块或其它对象的名称。一个标识符開始以字母A到Z或a〜z或后跟零个或多个字母下划线(_),下划线和数字(0〜9)。

    Python中标识符内不同意标点符号,如@,$和%。 Python是一种区分大写和小写的编程语言。因此,Manpower 和manpower在Python中是两个不同的标识符。

    这里有Python标识符命名约定:

    • 类名以大写字母以及全部其他标识符以小写字母。

    • 开头单个前导下划线的标识符表示由该标识符约定意思是私有的。

    • 开头两个前导下划线的标识符表示一个强烈的私有的标识符。

    • 假设标识符末尾还具有两个下划线结束时,该标识符是一个语言定义的特殊名称。

    保留字:

    以下列出了在Python中的保留字。这些保留字不能够被用作常量或变量,或不论什么其他标识符。全部Pythonkeyword仅仅包括小写字母。

    and exec not
    assert finally or
    break for pass
    class from print
    continue global raise
    def if return
    del import try
    elif in while
    else is with
    except lambda yield

    行和缩进:

    一个程序猿学习Python时,遇到的第一个须要注意的地方是,不使用括号来表示代码的类和函数定义块或流程控制。代码块是由行缩进,这是严格运行表示方式。

    在缩进位的数目是可变的,可是在块中的全部语句必须缩进同样的量。在这个样例中,两个功能块都非常好使用:

    if True:
        print "True"
    else:
      print "False"

    然而,在本实施例中的第二块将产生一个错误:

    if True:
        print "Answer"
        print "True"
    else:
        print "Answer"
      print "False"

    因此,在Python中全部的连续线缩进的空格数相同的会结成块。下面是各种语句块中的样例:

    注意:不要试图理解所使用的逻辑或不同的功能。仅仅要确定你明确,即使他们各种模块无需括号。

    #!/usr/bin/python
    
    import sys
    
    try:
      # open file stream
      file = open(file_name, "w")
    except IOError:
      print "There was an error writing to", file_name
      sys.exit()
    print "Enter '", file_finish,
    print "' When finished"
    while file_text != file_finish:
      file_text = raw_input("Enter text: ")
      if file_text == file_finish:
        # close the file
        file.close
        break
      file.write(file_text)
      file.write("
    ")
    file.close()
    file_name = raw_input("Enter filename: ")
    if len(file_name) == 0:
      print "Next time please enter something"
      sys.exit()
    try:
      file = open(file_name, "r")
    except IOError:
      print "There was an error reading file"
      sys.exit()
    file_text = file.read()
    file.close()
    print file_text

    多行语句:

    Python语句通经常使用一个新行结束。 可是,Python同意使用续行字符()来表示,该行应该继续下去(跨行)。比如:

    total = item_one + 
            item_two + 
            item_three

    包括在[],{}或()括号内的陈述并不须要使用续行符。比如:

    days = ['Monday', 'Tuesday', 'Wednesday',
            'Thursday', 'Friday']

    Python引號:

    Python接受单引號('),双引號(“)和三(''或”“”)引用,以表示字符串常量,仅仅要是同一类型的引號開始和结束的字符串。

    三重引號能够用于跨越多个行的字符串。比如,全部下列是合法的:

    word = 'word'
    sentence = "This is a sentence."
    paragraph = """This is a paragraph. It is
    made up of multiple lines and sentences."""

    Python凝视:

    一个井号(#),这不是一个字符串文字开头的凝视。“#”号之后字符和到物理行是凝视的一部分,Python解释器会忽略它们。

    #!/usr/bin/python
    
    # First comment
    print "Hello, Python!";  # second comment

    这将产生下面结果:

    Hello, Python!
    

    凝视可能会在声明中表达或同一行之后:

    name = "Madisetti" # This is again comment

    你能够使用多行凝视例如以下:

    # This is a comment.
    # This is a comment, too.
    # This is a comment, too.
    # I said that already.

    使用空行:

    一行仅仅含有空格,可能带有凝视,假设是空行那么Python全然忽略它。

    在交互式解释器会话中,必须输入一个空的物理行终止多行语句。

    等待用户:

    程序的以下一行显示的提示,按回车键退出,等待用户按下回车键:

    #!/usr/bin/python
    
    raw_input("
    
    Press the enter key to exit.")

    在这里,“ 已”被用来显示实际行之前创建两个换行。一旦用户按下键时,程序结束。这是一个非常好的技巧,保持一个控制台窗体打开,直到用户完毕应用程序执行。

    在一行中多个语句:

    分号( ; ) 同意在单行写入多条语句,无论语句是否启动一个新的代码块。以下是使用分号演示样例:

    import sys; x = 'foo'; sys.stdout.write(x + '
    ')

    多个语句组作为套件:

    一组单独的语句,在Python单一的代码块被称为序列。复杂的语句,如if, while, def, and class,那些须要一个标题行和套件。

    标题行開始的声明(与keyword),并终止与冒号(:)),接着是一个或多个线构成该套件。比如:

    if expression : 
       suite
    elif expression : 
       suite 
    else : 
       suite

    命令行參数:

    我们可能已经看到了,比方,非常多程序能够执行,它们提供有关怎样执行的一些基本信息。 Python中能够使用 -h 做到这一点:

    $ python -h
    usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...
    Options and arguments (and corresponding environment variables):
    -c cmd : program passed in as string (terminates option list)
    -d     : debug output from parser (also PYTHONDEBUG=x)
    -E     : ignore environment variables (such as PYTHONPATH)
    -h     : print this help message and exit
    
    [ etc. ]
    

    您也能够设定您的脚本,它应该以这种方式接受各种选项。  命令行參数是一个高级主题并在以后学习,当您通过其他的Python概念后。

    Python变量类型

    变量是仅仅只是保留的内存位置用来存储值。这意味着,当创建一个变量,那么它在内存中保留一些空间。

    依据一个变量的数据类型,解释器分配内存,并决定怎样能够被存储在所保留的内存中。因此,通过分配不同的数据类型的变量,你能够存储整数,小数或字符在这些变量中。

    变量赋值:

    Python的变量不必显式地声明保留的存储器空间。当分配一个值给一个变量的声明将自己主动发生。等号(=)来赋值给变量。

    操作数=操作符的左边是变量,操作数=操作符的右側的名称在变量中存储的值。比如:

    #!/usr/bin/python
    
    counter = 100          # An integer assignment
    miles   = 1000.0       # A floating point
    name    = "John"       # A string
    
    print counter
    print miles
    print name

    在这里,分配值100,1000.0和“John”分别给变量counter,miles和respectively。当执行这个程序,这将产生下面结果:

    100
    1000.0
    John
    

    多重赋值:

    Python同意您同一时候指定一个值给几个变量。比如:

    a = b = c = 1

    这里,整数对象创建的值1,而且全部三个变量被分配到同样的内存位置。您也能够将多个对象分别到多个变量。比如:

    	a, b, c = 1, 2, "john"

    这里,两个整对象用值1和2分配给变量a和b,而且值为“john”的字符串对象被分配到变量c。

    标准的数据类型:

    存储在内存中的数据能够是多种类型的。比如,一个人的年龄被存储为一个数字值和他的地址被存储为字母数字字符。Python用于对每一个人的操作的各种标准类型定义在存储方法。

    Python有五个标准的数据类型:

    • 数字

    • 字符串

    • 列表

    • 元组

    • 字典

    Python数字:

    数字数据类型存储数值。它们是不可变的数据类型,这意味着改变一个新分配的对象的数字数据类型的结果值。

    当分配一个值给他们创建的对象。比如:

    var1 = 1
    var2 = 10

    也能够使用del语句删去有关一些对象。 del语句的语法是:

    del var1[,var2[,var3[....,varN]]]]

    也能够使用del语句删除单个或多个对象。比如:

    del var
    del var_a, var_b

    Python支持四种不同的数值类型:

    • int (有符号整数)

    • long (长整数[也能够以八进制和十六进制表示])

    • float (浮点实数值)

    • complex (复数)

    比如:

    这里是数字的一些样例:

    int long float complex
    10 51924361L 0.0 3.14j
    100 -0x19323L 15.20 45.j
    -786 0122L -21.9 9.322e-36j
    080 0xDEFABCECBDAECBFBAEl 32.3+e18 .876j
    -0490 535633629843L -90. -.6545+0J
    -0x260 -052318172735L -32.54e100 3e+26J
    0x69 -4721885298529L 70.2-E12 4.53e-7j
    • Python同意使用一个小写L表示长整型,但建议您仅仅使用一个大写的L到避免和数字1 长得一样不easy分辨,Python显示长整数用一个大写L。

    • 复数包括一个有序对表示为a + bj,当中,a是实部,b是复数的虚部实浮点数。

    Python字符串:

    在Python中的字符串被确定为一组连续的字符在引號之间。 Python同意在不论什么对单引號或双引號。串的子集,能够使用切片操作符可採用([]和[:]),索引从0開始的字符串的開始和结束(-1)。

    加号(+)符号的字符串连接操作符,而星号(*)表示反复操作。比如:

    #!/usr/bin/python
    
    str = 'Hello World!'
    
    print str          # Prints complete string
    print str[0]       # Prints first character of the string
    print str[2:5]     # Prints characters starting from 3rd to 5th
    print str[2:]      # Prints string starting from 3rd character
    print str * 2      # Prints string two times
    print str + "TEST" # Prints concatenated string

    这将产生下面结果:

    Hello World!
    H
    llo
    llo World!
    Hello World!Hello World!
    Hello World!TEST
    

    Python列表:

    列表是最通用的Python复合数据类型。列表中包括以逗号分隔,并在方括号([])包括的项目。在一定程度上,列表相似C语言中的数组,它们之间的一个差别是,全部属于一个列表中的项目能够是不同的数据类型的。

    存储在一个列表中的值能够使用切片操作符来訪问([]和[:])用索引从0開始,在列表的開始位置和结束为-1。加号(+)符号列表连接运算符,星号(*)反复操作。比如:

    #!/usr/bin/python
    
    list = [ 'abcd', 786 , 2.23, 'john', 70.2 ]
    tinylist = [123, 'john']
    
    print list          # Prints complete list
    print list[0]       # Prints first element of the list
    print list[1:3]     # Prints elements starting from 2nd till 3rd 
    print list[2:]      # Prints elements starting from 3rd element
    print tinylist * 2  # Prints list two times
    print list + tinylist # Prints concatenated lists

    这将产生下面结果:

    ['abcd', 786, 2.23, 'john', 70.200000000000003]
    abcd
    [786, 2.23]
    [2.23, 'john', 70.200000000000003]
    [123, 'john', 123, 'john']
    ['abcd', 786, 2.23, 'john', 70.200000000000003, 123, 'john']
    

    Python元组:

    元组是类似于列表中的序列数据类型。一个元组由数个逗号分隔的值。不同于列表,只是,元组圆括号括起来。

    列表和元组之间的主要差别是:列表括在括号([])和它们的元素和大小是能够改变的,而元组在圆括号(),不能被更新。元组能够被觉得是仅仅读列表。比如:

    #!/usr/bin/python
    
    tuple = ( 'abcd', 786 , 2.23, 'john', 70.2  )
    tinytuple = (123, 'john')
    
    print tuple           # Prints complete list
    print tuple[0]        # Prints first element of the list
    print tuple[1:3]      # Prints elements starting from 2nd till 3rd 
    print tuple[2:]       # Prints elements starting from 3rd element
    print tinytuple * 2   # Prints list two times
    print tuple + tinytuple # Prints concatenated lists

    这将产生下面结果:

    ('abcd', 786, 2.23, 'john', 70.200000000000003)
    abcd
    (786, 2.23)
    (2.23, 'john', 70.200000000000003)
    (123, 'john', 123, 'john')
    ('abcd', 786, 2.23, 'john', 70.200000000000003, 123, 'john')
    

    下面是元组无效的,由于我们尝试更新一个元组,这是不同意的。类似的操作在列表中是能够的:

    #!/usr/bin/python
    
    tuple = ( 'abcd', 786 , 2.23, 'john', 70.2  )
    list = [ 'abcd', 786 , 2.23, 'john', 70.2  ]
    tuple[2] = 1000    # Invalid syntax with tuple
    list[2] = 1000     # Valid syntax with list

    Python字典:

    Python字典是一种哈希表型。他们像关联数组或哈希在Perl中一样,由键 - 值对组成。字典键差点儿能够是不论什么Python类型,但一般是数字或字符串。值能够是随意Python的对象。

    字典是由花括号括号({}),可分配值,并用方括号([])訪问。比如:

    #!/usr/bin/python
    
    dict = {}
    dict['one'] = "This is one"
    dict[2]     = "This is two"
    
    tinydict = {'name': 'john','code':6734, 'dept': 'sales'}
    
    
    print dict['one']       # Prints value for 'one' key
    print dict[2]           # Prints value for 2 key
    print tinydict          # Prints complete dictionary
    print tinydict.keys()   # Prints all the keys
    print tinydict.values() # Prints all the values

    这将产生下面结果:

    This is one
    This is two
    {'dept': 'sales', 'code': 6734, 'name': 'john'}
    ['dept', 'code', 'name']
    ['sales', 6734, 'john']
    

    字典有元素顺序的概念。它的元素是无序的。

    数据类型转换:

    有时候,可能须要运行的内置类型之间的转换。类型之间的转换,仅仅需使用类名作为函数。

    有几个内置的功能,从一种数据类型进行转换为还有一种。这些函数返回一个表示转换值的新对象。

    函数 描写叙述

    int(x [,base])

    将x转换为一个整数。基数指定为base,假设x是一个字符串。

    long(x [,base] )

    将x转换为一个长整数。基数指定为base,假设x是一个字符串。

    float(x)

    将x转换到一个浮点数。

    complex(real [,imag])

    创建一个复数。

    str(x)

    转换对象x为字符串表示形式。

    repr(x)

    对象x转换为一个表达式字符串。

    eval(str)

    计算一个字符串,并返回一个对象。

    tuple(s)

    把s转换为一个元组。

    list(s)

    把s转换为一个列表。

    set(s)

    把s转换为一个集合。

    dict(d)

    创建一个字典。 d必须的(键,值)元组序列。

    frozenset(s)

    把s转换为冻结集。

    chr(x)

    整数转换为一个字符。

    unichr(x)

    整数转换为一个Unicode字符。

    ord(x)

    转换单个字符为整数值。

    hex(x)

    将整数转换为十六进制字符串。

    oct(x)

    将整数转换为以八进制的字符串。

    Python 3开发网络爬虫(一)

    选择Python版本号

    有2和3两个版本号, 3比較新, 听说修改大. 依据我在知乎上搜集的观点来看, 我还是倾向于使用”在趋势中将会越来越火”的版本号, 而非”眼下已经非常稳定并且非常成熟”的版本号. 这是个人喜好, 并且预測不一定准确. 可是假设Python3无法像Python2那么火, 那么整个Python语言就不可避免的随着时间的推移越来越落后, 因此我想事实上选哪个的最坏风险都一样, 可是最好回报却是Python3的大. 事实上两者差别也能够说大也能够说不大, 终于都不是什么大问题. 我选择的是Python 3.

     

    选择參考资料

    因为我是一边学一边写, 而不是我全然学会了之后才開始非常有条理的写, 所以參考资料就非常重要(本来应该是个人开发经验非常重要, 但我是零基础).

    写到这里的时候, 上面第二第三个链接的票数第一的回答已经看完了, 他们提到的有些部分(比方爬行的路线不能有回路)我就不写了。
     

    一个简单的伪代码

    下面这个简单的伪代码用到了set和queue这两种经典的数据结构, 集与队列. 集的作用是记录那些已经訪问过的页面, 队列的作用是进行广度优先搜索.

    queue Q
    set S
    StartPoint = "http://jecvay.com"
    Q.push(StartPoint)  # 经典的BFS开头
    S.insert(StartPoint)  # 訪问一个页面之前先标记他为已訪问
    while (Q.empty() == false)  # BFS循环体
      T = Q.top()  # 而且pop
      for point in PageUrl(T)  # PageUrl(T)是指页面T中全部url的集合, point是这个集合中的一个元素.
        if (point not in S)
          Q.push(point)
          S.insert(point)
    

    这个伪代码不能运行,  我认为我写的有的不伦不类, 不类Python也不类C++.. 可是我相信看懂是没问题的, 这就是个最简单的BFS结构. 我是看了知乎里面的那个伪代码之后, 自己用我的风格写了一遍. 你也须要用你的风格写一遍.

    这里用到的Set其内部原理是採用了Hash表, 传统的Hash对爬虫来说占用空间太大, 因此有一种叫做Bloom Filter的数据结构更适合用在这里替代Hash版本号的set. 我打算以后再看这个数据结构怎么使用, 如今先跳过, 由于对于零基础的我来说, 这不是重点.

    代码实现(一): 用Python抓取指定页面

    我使用的编辑器是Idle, 安装好Python3后这个编辑器也安装好了, 小巧轻便, 按一个F5就能执行并显示结果. 代码例如以下:

    #encoding:UTF-8
    import urllib.request
     
    url = "http://www.baidu.com"
    data = urllib.request.urlopen(url).read()
    data = data.decode('UTF-8')
    print(data)
    

    演示样例图片

    urllib.request是一个库, 隶属urllib. 点此打开官方相关文档. 官方文档应该怎么使用呢? 首先点刚刚提到的这个链接进去的页面有urllib的几个子库, 我们临时用到了request, 所以我们先看urllib.request部分. 首先看到的是一句话介绍这个库是干什么用的:

    The urllib.request module defines functions and classes which help in opening URLs (mostly HTTP) in a complex world — basic and digest authentication, redirections, cookies and more.

    然后把我们代码中用到的urlopen()函数部分阅读完.

    urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False)

    重点部分是返回值, 这个函数返回一个 http.client.HTTPResponse 对象, 这个对象又有各种方法, 比方我们用到的read()方法, 这些方法都能够依据官方文档的链接链过去. 依据官方文档所写, 我用控制台执行完成上面这个程序后, 又继续执行例如以下代码, 以更熟悉这些乱七八糟的方法是干什么的.

    >>> a = urllib.request.urlopen(full_url)
    >>> type(a)
    <class ‘http.client.HTTPResponse’>

    >>> a.geturl()
    ‘http://www.baidu.com/s?word=Jecvay’

    >>> a.info()
    <http.client.HTTPMessage object at 0x03272250>

    >>> a.getcode()
    200

    代码实现(二): 用Python简单处理URL

    假设要抓取百度上面搜索关键词为Jecvay Notes的网页, 则代码例如以下

    import urllib
    import urllib.request
     
    data={}
    data['word']='Jecvay Notes'
     
    url_values=urllib.parse.urlencode(data)
    url="http://www.baidu.com/s?"
    full_url=url+url_values
     
    data=urllib.request.urlopen(full_url).read()
    data=data.decode('UTF-8')
    print(data)
    

    data是一个字典, 然后通过urllib.parse.urlencode()来将data转换为 ‘word=Jecvay+Notes’的字符串, 最后和url合并为full_url, 其余和上面那个最简单的样例相同. 关于urlencode(), 相同通过官方文档学习一下他是干什么的. 通过查看

    1. urllib.parse.urlencode(query, doseq=False, safe=”, encoding=None, errors=None)
    2. urllib.parse.quote_plus(string, safe=”, encoding=None, errors=None)
    大概知道他是把一个通俗的字符串, 转化为url格式的字符串。

    Python 3开发网络爬虫(二)

    上一回, 我学会了

    1. 用伪代码写出爬虫的主要框架;
    2. 用Python的urllib.request库抓取指定url的页面;
    3. 用Python的urllib.parse库对普通字符串转符合url的字符串.

    这一回, 開始用Python将伪代码中的全部部分实现. 因为文章的标题就是”零基础”, 因此会先把用到的两种数据结构队列集合介绍一下. 而对于”正則表達式“部分, 限于篇幅不能介绍, 但给出我比較喜欢的几个參考资料.

    Python的队列

    在爬虫程序中, 用到了广度优先搜索(BFS)算法. 这个算法用到的数据结构就是队列.

    Python的List功能已经足够完毕队列的功能, 能够用 append() 来向队尾加入元素, 能够用类似数组的方式来获取队首元素, 能够用 pop(0) 来弹出队首元素. 可是List用来完毕队列功能事实上是低效率的, 由于List在队首使用 pop(0) 和 insert() 都是效率比較低的, Python官方建议使用collection.deque来高效的完毕队列任务.

    from collections import deque
    queue = deque(["Eric", "John", "Michael"])
    queue.append("Terry")           # Terry 入队
    queue.append("Graham")          # Graham 入队
    queue.popleft()                 # 队首元素出队
    #输出: 'Eric'
    queue.popleft()                 # 队首元素出队
    #输出: 'John'
    queue                           # 队列中剩下的元素
    #输出: deque(['Michael', 'Terry', 'Graham'])

    (以上样例引用自官方文档)

    Python的集合

    在爬虫程序中, 为了不反复爬那些已经爬过的站点, 我们须要把爬过的页面的url放进集合中, 在每一次要爬某一个url之前, 先看看集合里面是否已经存在. 假设已经存在, 我们就跳过这个url; 假设不存在, 我们先把url放入集合中, 然后再去爬这个页面.

    Python提供了set这样的数据结构. set是一种无序的, 不包括反复元素的结构. 一般用来測试是否已经包括了某元素, 或者用来对众多元素们去重. 与数学中的集合论相同, 他支持的运算有交, 并, 差, 对称差.

    创建一个set能够用 set() 函数或者花括号 {} . 可是创建一个空集是不能使用一个花括号的, 仅仅能用 set() 函数. 由于一个空的花括号创建的是一个字典数据结构. 下面相同是Python官网提供的演示样例.

    >>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
    >>> print(basket)                      # 这里演示的是去重功能
    {'orange', 'banana', 'pear', 'apple'}
    >>> 'orange' in basket                 # 高速推断元素是否在集合内
    True
    >>> 'crabgrass' in basket
    False
     
    >>> # 以下展示两个集合间的运算.
    ...
    >>> a = set('abracadabra')
    >>> b = set('alacazam')
    >>> a                                  
    {'a', 'r', 'b', 'c', 'd'}
    >>> a - b                              # 集合a中包括元素
    {'r', 'd', 'b'}
    >>> a | b                              # 集合a或b中包括的全部元素
    {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
    >>> a & b                              # 集合a和b中都包括了的元素
    {'a', 'c'}
    >>> a ^ b                              # 不同一时候包括于a和b的元素
    {'r', 'd', 'b', 'm', 'z', 'l'

    事实上我们仅仅是用到当中的高速推断元素是否在集合内的功能, 以及集合的并运算.

    Python的正則表達式

    在爬虫程序中, 爬回来的数据是一个字符串, 字符串的内容是页面的html代码. 我们要从字符串中, 提取出页面提到过的全部url. 这就要求爬虫程序要有简单的字符串处理能力, 而正則表達式能够非常轻松的完毕这一任务.

    參考资料

    尽管正則表達式功能异常强大, 非常多实际上用的规则也非常巧妙, 真正熟练正則表達式须要比較长的实践锻炼. 只是我们仅仅须要掌握怎样使用正則表達式在一个字符串中, 把全部的url都找出来, 就能够了. 假设实在想要跳过这一部分, 能够在网上找到非常多现成的匹配url的表达式, 拿来用就可以.

     

    Python网络爬虫Ver 1.0 alpha

    有了以上铺垫, 最终能够開始写真正的爬虫了. 我选择的入口地址是Fenng叔的Startup News, 我想Fenng叔刚刚拿到7000万美金融资, 不会介意大家的爬虫去光临他家的小站吧. 这个爬虫尽管能够勉强执行起来, 可是因为缺乏异常处理, 仅仅能爬些静态页面, 也不会分辨什么是静态什么是动态, 碰到什么情况应该跳过, 所以工作一会儿就要败下阵来.

    import re
    import urllib.request
    import urllib
     
    from collections import deque
     
    queue = deque()
    visited = set()
     
    url = 'http://news.dbanotes.net'  # 入口页面, 能够换成别的
     
    queue.append(url)
    cnt = 0
     
    while queue:
      url = queue.popleft()  # 队首元素出队
      visited |= {url}  # 标记为已訪问
     
      print('已经抓取: ' + str(cnt) + '   正在抓取 <---  ' + url)
      cnt += 1
      urlop = urllib.request.urlopen(url)
      if 'html' not in urlop.getheader('Content-Type'):
        continue
     
      # 避免程序异常中止, 用try..catch处理异常
      try:
        data = urlop.read().decode('utf-8')
      except:
        continue
     
      # 正則表達式提取页面中全部队列, 并推断是否已经訪问过, 然后增加待爬队列
      linkre = re.compile('href="(.+?)"')
      for x in linkre.findall(data):
        if 'http' in x and x not in visited:
          queue.append(x)
          print('增加队列 --->  ' + x)

    这个版本号的爬虫使用的正則表達式是

    'href="(.+?)"'

    所以会把那些.ico或者.jpg的链接都爬下来. 这样read()了之后碰上decode(‘utf-8′)就要抛出异常. 因此我们用getheader()函数来获取抓取到的文件类型, 是html再继续分析当中的链接.

    if 'html' not in urlop.getheader('Content-Type'):
        continue

    可是即使是这样, 依旧有些站点执行decode()会异常. 因此我们把decode()函数用try..catch语句包围住, 这样他就不会导致程序中止. 程序执行效果图例如以下:

    webbugv1ahpha

    爬虫是能够工作了, 可是在碰到连不上的链接的时候, 它并不会超时跳过. 并且爬到的内容并没有进行处理, 没有获取对我们有价值的信息, 也没有保存到本地. 下次我们能够完好这个alpha版本号.

    Python3网络爬虫(三): 伪装浏览器

    上一次我自学爬虫的时候, 写了一个简陋的勉强能执行的爬虫alpha. alpha版有非常多问题. 比方一个站点上不了, 爬虫却一直在等待连接返回response, 不知道超时跳过; 或者有的站点专门拦截爬虫程序, 我们的爬虫也不会伪装自己成为浏览器正规部队; 而且抓取的内容没有保存到本地, 没有什么作用. 这次我们一个个解决这些小问题.

    此外, 在我写这系列文章的第二篇的时候, 我还是一个对http的get和post以及response这些名词一无所知的人, 可是我认为这样是写不好爬虫的. 于是我參考了 <<计算机网络–自顶向下方法>> 这本书的第二章的大部分内容. 假设你也一样对http的机制一无所知, 我也推荐你找一找这方面的资料来看. 在看的过程中, 安装一个叫做Fiddler的软件, 边学边实践, 观察浏览器是怎样訪问一个站点的, 怎样发出请求, 怎样处理响应, 怎样进行跳转, 甚至怎样通过登录认证. 有句老话说得好, 越会用Fiddler, 就对理论理解更深刻; 越对理论理解深刻, Fiddler就用得越顺手. 最后我们在用爬虫去做各种各样的事情的时候, Fiddler总是最得力的助手之中的一个.

    加入超时跳过功能

    首先, 我简单地将

    urlop = urllib.request.urlopen(url)

    改为

    urlop = urllib.request.urlopen(url, timeout = 2)

    执行后发现, 当发生超时, 程序由于exception中断. 于是我把这一句也放在try .. except 结构里, 问题解决.

     

    支持自己主动跳转

    在爬 http://baidu.com 的时候, 爬回来一个没有什么内容的东西, 这个东西告诉我们应该跳转到 http://www.baidu.com . 可是我们的爬虫并不支持自己主动跳转, 如今我们来加上这个功能, 让爬虫在爬 baidu.com 的时候可以抓取 www.baidu.com 的内容.

    首先我们要知道爬 http://baidu.com 的时候他返回的页面是怎么样的, 这个我们既能够用 Fiddler 看, 也能够写一个小爬虫来抓取. 这里我抓到的内容例如以下, 你也应该尝试一下写几行 python 来抓一抓.

    <html>
    <meta http-equiv=”refresh” content=”0;url=http://www.baidu.com/”>
    </html>

    看代码我们知道这是一个利用 html 的 meta 来刷新与重定向的代码, 当中的0是等待0秒后跳转, 也就是马上跳转. 这样我们再像上一次说的那样用一个正則表達式把这个url提取出来就能够爬到正确的地方去了. 事实上我们上一次写的爬虫已经能够具有这个功能, 这里仅仅是单独拿出来说明一下 http 的 meta 跳转.

    伪装浏览器正规军

    前面几个小内容都写的比較少. 如今具体研究一下怎样让站点们把我们的Python爬虫当成正规的浏览器来訪. 由于假设不这么伪装自己, 有的站点就爬不回来了. 假设看过理论方面的知识, 就知道我们是要在 GET 的时候将 User-Agent 加入到header里.

    假设没有看过理论知识, 依照下面keyword搜索学习吧 :D

    • HTTP 报文分两种: 请求报文响应报文
    • 请求报文的请求行首部行
    • GETPOST, HEAD, PUT, DELETE 方法

    我用 IE 浏览器訪问百度首页的时候, 浏览器发出去的请求报文例如以下:

    GET http://www.baidu.com/ HTTP/1.1
    Accept: text/html, application/xhtml+xml, */*
    Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3
    User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
    Accept-Encoding: gzip, deflate
    Host: www.baidu.com
    DNT: 1
    Connection: Keep-Alive
    Cookie: BAIDUID=57F4D171573A6B88A68789EF5DDFE87:FG=1; uc_login_unique=ccba6e8d978872d57c7654130e714abd; BD_UPN=11263145; BD

    然后百度收到这个消息后, 返回给我的的响应报文例如以下(有删节):

    HTTP/1.1 200 OK
    Date: Mon, 29 Sep 2014 13:07:01 GMT
    Content-Type: text/html; charset=utf-8
    Connection: Keep-Alive
    Vary: Accept-Encoding
    Cache-Control: private
    Cxy_all: baidu+8b13ba5a7289a37fb380e0324ad688e7
    Expires: Mon, 29 Sep 2014 13:06:21 GMT
    X-Powered-By: HPHP
    Server: BWS/1.1
    BDPAGETYPE: 1
    BDQID: 0x8d15bb610001fe79
    BDUSERID: 0
    Set-Cookie: BDSVRTM=0; path=/
    Set-Cookie: BD_HOME=0; path=/
    Content-Length: 80137

    <!DOCTYPE html><!–STATUS OK–><html><head><meta http-equiv=”content-type” content=”text/html;charset=utf-8″><meta http-equiv=”X-UA-Compatible” content=”IE=Edge”><link rel=”dns-prefetch” href=”//s1.bdstatic.com”/><link rel=”dns-prefetch” href=”//t1.baidu.com”/><link rel=”dns-prefetch” href=”//t2.baidu.com”/><link rel=”dns-prefetch” href=”//t3.baidu.com”/><link rel=”dns-prefetch” href=”//t10.baidu.com”/><link rel=”dns-prefetch” href=”//t11.baidu.com”/><link rel=”dns-prefetch” href=”//t12.baidu.com”/><link rel=”dns-prefetch” href=”//b1.bdstatic.com”/><title>百度一下,你就知道</title><style index=”index” > ……….这里省略两万字……………. </script></body></html>

    假设可以看懂这段话的第一句就OK了, 别的可以以后再配合 Fiddler 慢慢研究. 所以我们要做的就是在 Python 爬虫向百度发起请求的时候, 顺便在请求里面写上 User-Agent, 表明自己是浏览器君.

    在 GET 的时候加入 header 有非常多方法, 以下介绍两种方法.

    第一种方法比較简便直接, 可是不好扩展功能, 代码例如以下:

    import urllib.request
     
    url = 'http://www.baidu.com/'
    req = urllib.request.Request(url, headers = {
        'Connection': 'Keep-Alive',
        'Accept': 'text/html, application/xhtml+xml, */*',
        'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'
    })
    oper = urllib.request.urlopen(req)
    data = oper.read()
    print(data.decode())	
    

     

    另外一种方法使用了 build_opener 这种方法, 用来自己定义 opener, 这样的方法的优点是能够方便的拓展功能, 比如以下的代码就拓展了自己主动处理 Cookies 的功能.

    import urllib.request
    import http.cookiejar
     
    # head: dict of header
    def makeMyOpener(head = {
        'Connection': 'Keep-Alive',
        'Accept': 'text/html, application/xhtml+xml, */*',
        'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'
    }):
        cj = http.cookiejar.CookieJar()
        opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
        header = []
        for key, value in head.items():
            elem = (key, value)
            header.append(elem)
        opener.addheaders = header
        return opener
     
    oper = makeMyOpener()
    uop = oper.open('http://www.baidu.com/', timeout = 1000)
    data = uop.read()
    print(data.decode())
    

    上述代码执行后通过 Fiddler 抓到的 GET 报文例如以下所看到的:

    GET http://www.baidu.com/ HTTP/1.1
    Accept-Encoding: identity
    Connection: close
    Host: www.baidu.com
    User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
    Accept: text/html, application/xhtml+xml, */*
    Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3

    可见我们在代码里写的东西都加入到请求报文里面了.

    保存抓回来的报文

    顺便说说文件操作. Python 的文件操作还是相当方便的. 我们能够讲抓回来的数据 data 以二进制形式保存, 也能够经过 decode() 处理成为字符串后以文本形式保存. 修改一下打开文件的方式就能用不同的姿势保存文件了. 以下是參考代码:

    def saveFile(data):
        save_path = 'D:\temp.out'
        f_obj = open(save_path, 'wb') # wb 表示打开方式
        f_obj.write(data)
        f_obj.close()
     
    # 这里省略爬虫代码
    # ...
     
    # 爬到的数据放到 dat 变量里
    # 将 dat 变量保存到 D 盘下
    saveFile(dat)
    

     

    下回我们会用 Python 来爬那些须要登录之后才干看到的信息. 在那之前, 我已经对 Fiddler 略微熟悉了. 希望一起学习的也提前安装个 Fiddler 玩一下.

    Python3网络爬虫(四): 登录

    今天的工作非常有意思, 我们用 Python 来登录站点, 用Cookies记录登录信息, 然后就能够抓取登录之后才干看到的信息. 今天我们拿知乎网来做示范. 为什么是知乎? 这个非常难解释, 可是肯定的是知乎这么大这么成功的站点全然不用我来帮他打广告. 知乎网的登录比較简单, 传输的时候没有对username和password加密, 却又不失代表性, 有一个必须从主页跳转登录的过程.

    不得不说一下, Fiddler 这个软件是 Tpircsboy 告诉我的. 感谢他给我带来这么好玩的东西.

    第一步: 使用 Fiddler 观察浏览器行为

    在开着 Fiddler 的条件下执行浏览器, 输入知乎网的网址 http://www.zhihu.com 回车后到 Fiddler 中就能看到捕捉到的连接信息. 在左边选中一条 200 连接, 在右边打开 Inspactors 透视图, 上方是该条连接的请求报文信息, 下方是响应报文信息.

    当中 Raw 标签是显示报文的原文. 下方的响应报文非常有可能是没有经过解压或者解码的, 这样的情况他会在中间部位有一个小提示, 点击一下就能解码显示出原文了.

    QQ截图20141003151553

     

    以上这个截图是在未登录的时候进入 http://www.zhihu.com 得到的. 如今我们来输入username和password登陆知乎网, 再看看浏览器和知乎server之间发生了什么.

    QQ截图20141003152308

     

    点击登陆后, 回到 Fiddler 里查看新出现的一个 200 链接. 我们浏览器携带者我的帐号password给知乎server发送了一个 POST, 内容例如以下:

    POST http://www.zhihu.com/login HTTP/1.1
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    Accept: */*
    X-Requested-With: XMLHttpRequest
    Referer: http://www.zhihu.com/#signin
    Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/5.0 (Windows NT 6.4; WOW64; Trident/7.0; rv:11.0) like Gecko
    Content-Length: 97
    DNT: 1
    Host: www.zhihu.com
    Connection: Keep-Alive
    Pragma: no-cache
    Cookie: __utma=51854390.1539896551.1412320246.1412320246.1412320246.1; __utmb=51854390.6.10.1412320246; __utmc=51854390; __utmz=51854390.1412320246.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmv=51854390.000–|3=entry_date=20141003=1

    _xsrf=4b41f6c7a9668187ccd8a610065b9718&email=此处涂黑%40gmail.com&password=此处不可见&rememberme=y

    截图例如以下:

    QQ截图20141003153243

    我的浏览器给 http://www.zhihu.com/login 这个网址(多了一个/login) 发送了一个POST, 内容都已经在上面列出来了, 实username, 有password, 有一个”记住我”的 yes, 当中这个 WebForms 标签下 Fiddler 可以比較井井有条的列出来 POST 的内容. 所以我们用 Python 也发送同样的内容就能登录了. 可是这里出现了一个 Name 为 _xsrf 的项, 他的值是 4b41f6c7a9668187ccd8a610065b9718. 我们要先获取这个值, 然后才干给他发.

    浏览器是怎样获取的呢, 我们刚刚是先訪问了 http://www.zhihu.com/ 这个网址, 就是首页, 然后登录的时候他却给 http://www.zhihu.com/login 这个网址发信息. 所以用侦探一般的思维去思考这个问题, 就会发现肯定是首页把 _xsrf 生成发送给我们, 然后我们再把这个 _xsrf 发送给 /login 这个 url. 这样一会儿过后我们就要从第一个 GET 得到的响应报文里面去寻找 _xsrf

    截图下方的方框说明, 我们不仅登录成功了, 并且server还告诉我们的浏览器怎样保存它给出的 Cookies 信息. 所以我们也要用 Python 把这些 Cookies 信息记录下来.

    这样 Fiddler 的工作就基本结束了!

    第二步: 解压缩

    简单的写一个 GET 程序, 把知乎首页 GET 下来, 然后 decode() 一下解码, 结果报错. 细致一看, 发现知乎网传给我们的是经过 gzip 压缩之后的数据. 这样我们就须要先对数据解压. Python 进行 gzip 解压非常方便, 由于内置有库能够用. 代码片段例如以下:

    import gzip
    def ungzip(data):
        try:        # 尝试解压
            print('正在解压.....')
            data = gzip.decompress(data)
            print('解压完成!')
        except:
            print('未经压缩, 无需解压')
        return data
    

    通过 opener.read() 读取回来的数据, 经过 ungzip 自己主动处理后, 再来一遍 decode() 就能够得到解码后的 str 了

    第二步: 使用正則表達式获取沙漠之舟

    _xsrf 这个键的值在茫茫无际的互联网沙漠之中指引我们用正确的姿势来登录知乎, 所以 _xsrf 可谓沙漠之舟. 假设没有 _xsrf, 我们也许实username和password也无法登录知乎(我没试过, 只是我们学校的教务系统确实如此) 如上文所说, 我们在第一遍 GET 的时候能够从响应报文中的 HTML 代码里面得到这个沙漠之舟. 例如以下函数实现了这个功能, 返回的 str 就是 _xsrf 的值.

    import re
    def getXSRF(data):
        cer = re.compile('name="_xsrf" value="(.*)"', flags = 0)
        strlist = cer.findall(data)
        return strlist[0]
    

     

    第三步: 发射 POST !!

    集齐 _xsrf, id, password 三大法宝, 我们能够发射 POST 了. 这个 POST 一旦发射过去, 我们就登陆上了server, server就会发给我们 Cookies. 本来处理 Cookies 是个麻烦的事情, 只是 Python 的 http.cookiejar 库给了我们非常方便的解决方式, 仅仅要在创建 opener 的时候将一个 HTTPCookieProcessor 放进去, Cookies 的事情就不用我们管了. 以下的代码体现了这一点.

    import http.cookiejar
    import urllib.request
    def getOpener(head):
        # deal with the Cookies
        cj = http.cookiejar.CookieJar()
        pro = urllib.request.HTTPCookieProcessor(cj)
        opener = urllib.request.build_opener(pro)
        header = []
        for key, value in head.items():
            elem = (key, value)
            header.append(elem)
        opener.addheaders = header
        return opener
    

    getOpener 函数接收一个 head 參数, 这个參数是一个字典. 函数把字典转换成元组集合, 放进 opener. 这样我们建立的这个 opener 就有两大功能:

    1. 自己主动处理使用 opener 过程中遇到的 Cookies
    2. 自己主动在发出的 GET 或者 POST 请求中加上自己定义的 Header

    第四部: 正式执行

    正式执行还差一点点, 我们要把要 POST 的数据弄成 opener.open() 支持的格式. 所以还要  urllib.parse 库里的 urlencode() 函数. 这个函数能够把 字典 或者 元组集合 类型的数据转换成 & 连接的 str.

    str 还不行, 还要通过 encode() 来编码, 才干当作 opener.open() 或者 urlopen() 的 POST 数据參数来使用. 代码例如以下:

    url = 'http://www.zhihu.com/'
    opener = getOpener(header)
    op = opener.open(url)
    data = op.read()
    data = ungzip(data)     # 解压
    _xsrf = getXSRF(data.decode())
     
    url += 'login'
    id = '这里填你的知乎帐号'
    password = '这里填你的知乎密码'
    postDict = {
            '_xsrf':_xsrf,
            'email': id,
            'password': password,
            'rememberme': 'y'
    }
    postData = urllib.parse.urlencode(postDict).encode()
    op = opener.open(url, postData)
    data = op.read()
    data = ungzip(data)
     
    print(data.decode())  # 你能够依据你的喜欢来处理抓取回来的数据了!
    

    代码执行后, 我们发现自己关注的人的动态(显示在登陆后的知乎首页的那些), 都被抓取回来了. 下一步做一个统计分析器, 或者自己主动推送器, 或者内容分级自己主动分类器, 都能够.

    完整代码例如以下:

    import gzip
    import re
    import http.cookiejar
    import urllib.request
    import urllib.parse
     
    def ungzip(data):
        try:        # 尝试解压
            print('正在解压.....')
            data = gzip.decompress(data)
            print('解压完成!')
        except:
            print('未经压缩, 无需解压')
        return data
     
    def getXSRF(data):
        cer = re.compile('name="_xsrf" value="(.*)"', flags = 0)
        strlist = cer.findall(data)
        return strlist[0]
     
    def getOpener(head):
        # deal with the Cookies
        cj = http.cookiejar.CookieJar()
        pro = urllib.request.HTTPCookieProcessor(cj)
        opener = urllib.request.build_opener(pro)
        header = []
        for key, value in head.items():
            elem = (key, value)
            header.append(elem)
        opener.addheaders = header
        return opener
     
    header = {
        'Connection': 'Keep-Alive',
        'Accept': 'text/html, application/xhtml+xml, */*',
        'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko',
        'Accept-Encoding': 'gzip, deflate',
        'Host': 'www.zhihu.com',
        'DNT': '1'
    }
     
    url = 'http://www.zhihu.com/'
    opener = getOpener(header)
    op = opener.open(url)
    data = op.read()
    data = ungzip(data)     # 解压
    _xsrf = getXSRF(data.decode())
     
    url += 'login'
    id = '这里填你的知乎帐号'
    password = '这里填你的知乎密码'
    postDict = {
            '_xsrf':_xsrf,
            'email': id,
            'password': password,
            'rememberme': 'y'
    }
    postData = urllib.parse.urlencode(postDict).encode()
    op = opener.open(url, postData)
    data = op.read()
    data = ungzip(data)
     
    print(data.decode())
    

  • 相关阅读:
    我理解的优秀软件工程师
    Hello 博客园!
    线程安全与可重入函数之间的区别与联系
    linux-粘滞位的使用
    死锁产生的四个必要条件及处理死锁的策略
    数据结构—位图
    Linux下进度条的简单实现
    Linux-find命令
    Linux文件3个时间点(access time,modify time,change time)
    各种排序算法的实现、总结
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4088800.html
Copyright © 2011-2022 走看看