《Python网络爬虫从入门到实践(第2版)》学习之二:编写第一个网络爬虫

第2章   编写第一个网络爬虫

        读完本章就可以体会到通过完成一个简单的Python网络爬虫而带来的乐趣。本章主要介绍如何安装Python和编辑器Jupyter、Python的一些基础语法以及编写一个最简单的Python网络爬虫。

2.1 搭建Python平台

        Python是一种计算机程序语言,由于其简洁性、易学性和可扩展性,已成为最受欢迎的程序语言之一。在2016年最受欢迎的编程语言中,Python已经超过C++排名第3位。另外,由于Python拥有强大而丰富的库,因此可以用来处理各种工作。
        在网络爬虫领域,由于Python简单易学,又有丰富的库可以很好地完成工作,因此很多人选择Python进行网络爬虫。

2.1.1 Python的安装

        Python的安装主要有两种方式:一是直接下载Python安装包安装,二是使用Anaconda科学计算环境下载Python。
        根据笔者的经验,这两种方式也对应着用Python来爬虫的两类人群:如果你希望成为Python开发人员或者爬虫工程师,推荐你直接下载Python安装包,配合着Pycharm编辑器,这将提升你的开发效率;如果你希望成为数据分析师或者商业分析师,爬虫只是方便之后做数据分析,推荐你使用Anaconda,配合着自带的Jupyter Notebook,这会提升你的分析效率。
        由于网络爬虫需要较多的代码调试,因此我推荐初学者使用Anaconda。因为Anaconda除了包含了Python安装包,还提供了众多科学计算的第三方库,如Numpy、Scipy、Pandas和Matplotlib等,以及机器学习库,如Scikit-Learn等。而且它并不妨碍你之后使用Pycharm开发。
请读者选择一种下载,不要两种都用,不然会带来Python版本管理的混乱。
        第一种方法:Anaconda的安装十分简单,只需两步即可完成。下面将介绍在Windows下安装Anaconda的步骤,在Mac下的安装方法与此类似。

        第二种方法:使用Python安装包方法也非常简单。下面将介绍在Windows下安装的步骤,在Mac下的安装方法类似。

  • 步骤一:下载Python。打开Python下载页面Download Python | Python.org ,下载最新版的Python
  • 步骤二:安装Python。双击打开Python安装文件,选择 Add Python 3.7 to PATH,之后单击InstallNow安装即可

2.1.2 使用pip安装第三方库

        pip是Python安装各种第三方库(package)的工具。
        对于第三方库不太理解的读者,可以将库理解为供用户调用的代码组合。在安装某个库之后,可以直接调用其中的功能,使得我们不用自己写代码也能实现某个功能。这就像你为计算机杀毒时,会选择下载一个杀毒软件,而不是自己写一个杀毒软件,直接使用杀毒软件中的杀毒功能来杀毒就可以了。这个比方中的杀毒软件就像是第三方库,杀毒功能就是第三方库中可以实现的功能,你可以调用第三方库实现某个功能。
        由于Anaconda或者Python安装包自带了pip,因此不用再安装pip。
在下面的例子中,我们将介绍如何用pip安装第三方库bs4,它可以使用其中的BeautifulSoup解析网页。

  • 步骤一:打开cmd.exe,在Windows中为cmd,在Mac中为terminal。在Windows中,cmd是命令提示符,输入一些命令后,cmd.exe可以执行对系统的管理。单击“开始”按钮,在“搜索程序和文件”文本框中输入cmd后按回车键,系统会打开命令提示符窗口,如图2-5所示。在Mac中,可以直接在“应用程序”中打开terminal程序。
  • 步骤二:安装bs4的Python库。在cmd中键入pip install bs4后按回车键,如果出现successfully installed,就表示安装成功

       除了bs4这个库,之后还会用到requests库、lxml库等其他第三方库,帮助我们更好地进行网络爬虫。正因为这些第三方库的存在,才使得Python在爬虫领域越来越方便、越来越活跃。

2.1.3 使用编辑器Jupyter 编程

        如果你使用Anaconda安装的Python,那么可以使用Anaconda自带的Jupyter Notebook编程;如果你使用Python安装包下载的Python,下一节会介绍Pycharm的安装方法。为了方便大家学习和调试代码,本书推荐使用Anaconda自带的Jupyter Notebook。下面将介绍Jupyter Notebook的使用方法。

  • 步骤一:通过cmd打开Jupyter。打开cmd,键入jupyter notebook后按回车键,浏览器启动Jupyter界面,地址默认为http://localhost:8888/tree
  • 步骤二:创建Python文件。这时浏览器会开启一个页面,在页面中选择想创建文件的文件夹,单击右上角的New按钮,从下拉列表中选择Python 3作为希望启动的Notebook类型。
  • 步骤三:在新创建的文件中编写Python程序。键入print('hello world!')后,可以按Shift + Enter快捷键执行刚刚的代码。

为什么本书使用Jupyter Notebook学习和编写Python脚本呢?
        首先,Jupyter Notebook的交互式编程可以分段运行Python,对于网络爬虫这种分阶段(获取网页-解析网页-存储数据)运行的脚本来说,在写代码和测试阶段可以边看边写,可以加快调试代码的速度,非常适合debug(代码纠错)。
        其次是展示,Jupyter Notebook能够把运行和输出的结果保存下来,下次打开这个Notebook时也可以看到之前运行的结果。除了可以编写代码外,Jupyter还可以添加各种元素,比如图片、视频、链接等,同时还支持Markdown。
        在完成代码之后,还可以在Jupyter左上角点击File > Download as > Python,下载为.py文件,就可以放到其他编辑器里运行了。
        如果你对Python的其他自定义功能有要求的话,推荐下载Jupyter的插件nbextensions。具体指引可以到笔者知乎或本书官网www.santostang.com 了解。

2.1.4 使用编辑器Pycharm编程

如果你使用Python安装包下载的Python,推荐选择Pycharm编辑器。

  • 步骤一:下载Pycharm。打开Pycharm下载页面JetBrains: Essential tools for software developers and teams pycharm/download ,下载Community版本。
  • 步骤二:安装Pycharm。双击打开Pycharm安装文件,根据自己电脑选择32bit还是64bit,记得在Create Associations勾选 .py,安装即可。
  • 步骤三:打开Pycharm。在开始页面,选择自己喜欢的主题。
  • 步骤四:随后点击Create New Project创建一个新的项目。
  • 步骤五: 选择好存储项目的位置,这里我给项目起的名称是“WebScraping”,你可以按照自己的需求存放项目地址。
  • 步骤六:进入Pycharm页面后,会看到如下页面。这时,点击File > New > Python File,并填上python文件名,例如“test”。创建完test.py文件后,打开test.py,键入print('hello world!')。选中代码,右键选择 Run ‘test’,即可得到结果。

2.2 Python 使用入门

        本节主要介绍Python的一些基础语法。如果你已经学会使用Python,可以跳过这一节,直接开始编写第一个Python网络爬虫。

2.2.1 基本命令

        Python是一种非常简单的语言,最简单的就是print,使用print可以打印出一系列结果。例如,键入print("Hello World!"),打印的结果如下:

        In [1]:print ("Hello World!")

Hello World!
另外,Python要求严格的代码缩进,以Tab键或者4个空格进行缩进,代码要按照结构严格缩进,例如:     

In [2]:x = 1
    if x == 1:
        print ("Hello World!")

Hello World!
如果需要注释某行代码,那么可以在代码前面加上“#”,例如:
In [3]:# 在前面加上#,代表注释

In [3]:# 在前面加上#,代表注释
    print ("Hello World!")

Hello World!

2.2.2 数据类型

        Python是面向对象(object oriented)的一种语言,并不需要在使用之前声明需要使用的变量和类别。下面将介绍Python的4种数据类型。
1. 字符串(string)
        字符串是常见的数据类型,一般用来存储类似“句子”的数据,并放在单引号(')或双引号(")中。如果要连接字符串,那么可以简单地加起来。

In [4]: string1 = 'Python Web Scriping'
        string2 = "By Santos"
        string3 = string1 + " " + string2
        print(string3)

Python Web Scraping by Santos
        如果字符串包含单引号(')和双引号("),应该怎么办?可以在前面加上右斜杠(),例如以下案例:

In [5]:string = “I\'m Santos. I love \"Python\"."
        print (string)

I'm Santos. I love "python".
2. 数字(Number)
        数字用来存储数值,包含两种常用的数字类型:整数(int)和浮点数(float),其中浮点数由整数和小数部分组成。两种类型之间可以相互转换,如果要将整数转换为浮点数,就在变量前加上float;如果要将浮点数转换为整数,就在变量前加上int。

float1 = 7.5

trans_int = int(float1) 


        还有其他两种复杂的数据类型,即长整数和复数,由于不常用到,感兴趣的读者可以自己学习。
3. 列表(list)
        如果需要把上述字符串和数字囊括起来,就可以使用列表。列表能够包含任意种类的数据类型和任意数量。创建列表非常容易,只要把不同的变量放入方括号中,并用逗号分隔即可。

list1 = ["python","web","Scarpy"]

list2 =[1,2,3,4,5]

list3 = ["a", 2 ."e",4]

        怎么访问列表中的值呢?可以在方括号中标明相应的位置索引进行访问,与一般认知不一样的是,索引从0开始

print("List1[0] :“,list1[0])

print (List2[1:3] :“,list2[1:3])

list1[0]: Python
list2[1:3]: [2, 3]


如何修改列表中的值呢?可以直接为列表中的相应位置赋予一个新值,例如:

List1[1] = "new"

print (list1)

打印结果:['Python', 'new', 'Scrappy']
如果想要给列表添加值呢?可以用append()方法,例如:

List1.append(“By Santos”)

print(list1)

输出结果:['Python', 'new', 'Scrappy', 'by Santos']
4. 字典(Dictionaries)
字典是一种可变容器模型,正如其名,字典含有“字”(直译为键值,key)和值(value),使用字典就像是自己创建一个字典和查字典的过程。每个存储的值都对应着一个键值key,key必须唯一,但是值不需要唯一。值也可以取任何数据类型,例如:

namebook = {“Name”:“Alex”,"Age":7,"Class":"First" }

print(namebook["Name"])   #可以把相应的键值放入方括号,提取值

print(namebook)          #也可以直接输出整个字典

输出结果:

Alex
{'Name': 'Alex', 'Age': 7, 'Class': 'First'}
如何遍历访问字典中的每一个值呢?这里需要用到字典和循环的结合,例如:

#循环提取整个dictionary的Key和Value

for key,value in namebook.item():

        print(key,value)

输出结果:

Name Alex
Age 7
Class First
如果想修改字典中的值或者加入新的键值呢?可以直接修改和加入,例如:

#直接修改值和添加一个键值

namebook["Name"]  = "Tom"

namebook["Gender"]  = "M"

print(namebook)

输出结果:

{'Name': 'Tom', 'Age': 7, 'Class': 'First', 'Gender':'M'}

2.2.3 条件语句和循环语句

条件语句可以使得当满足条件的时候才执行某部分代码。条件为布尔值,也就是只有True和False两个值。当if判断条件成立时才执行后面的语句;当条件不成立的时候,执行else后面的语句,例如:

book = “Python”   #定义字符串book

if book == “Python”:    #判断变量是否为‘Python’

        print ("You are studying python.")  #条件成立时输出

else:

        print(“Wrong.”) #条件不成立时输出

输出结果:

        You are studying python.
如果需要判断的有多种条件,就需要用到elif,例如:

book = “java”    #定义字符串book

if book == “Python”:    #判断变量是否为‘Python’

        print ("You are studying python.")  #条件成立时输出

elif book == “java”   #判断变量是否为‘java’

        print ("You are studying java.")  #条件成立时输出

else:

        print(“Wrong.”) #条件不成立时输出

输出结果:

You are studying java.
Python的条件语句注意不要少了冒号(:)。
循环语句能让我们执行一个代码片段多次,循环分为for循环和while循环。
for循环能在一个给定的顺序下重复执行,例如:

citylist = ["Beijing","Shaihai","Guangzhou"]

for eachcity in citylist:

        print(eachcity)

输出结果:

Beijing
Shanghai
Guangzhou
除了对列表进行直接循环,有时我们还会使用range()进行循环,首先用len(citylist)得到列表的长度为3,然后range(3)会输出列表[0,1,2],从而实现循环,得到和上面一样的结果。例如:

citylist = ["Beijing","Shaihai","Guangzhou"]

for i in range(len(citylist)):

        print(eachcity[i])

输出结果:

Beijing
Shanghai
Guangzhou
while循环能不断重复执行,只要能满足一定条件,例如:

count = 0

while count <3:

        print(count)  #打印出0,1,2

        count +=1  #与count = count +1 一样

执行结果:

0
1
2

2.2.4 函数

在代码很少的时候,我们按照逻辑写完就能够很好地运行。但是如果代码变得庞大复杂起来,就需要自己定义一些函数(Functions),把代码切分成一个个方块,使得代码易读,可以重复使用,并且容易调整顺序。
其实Python就自带了很多函数,例如下面的sum()和abs()函数,我们可以直接调用。

print(sum([1,2,3,4]))  #对系列进行求和计算

print(abs(-1))    #返回数字的绝对值

执行结果:

10
1
此外,我们也可以自己定义函数。一个函数包括输入参数和输出参数,Python的函数功能可以用y = x +1的数学函数来理解,在输入x=2的参数时,y输出3。但是在实际情况中,某些函数输入和输出参数可以不用指明。下面定义一个函数:

#定义函数

def calulus(x):

        y = x + 1

        return y

#定义函数

result = calulus(2)

print(result)

执行结果:

3
参数必须要正确地写入函数中,函数的参数也可以为多个,也可以是不同的数据类型,例如可以是两个参数,分别是字符串和列表型的。

#定义函数

def fruit function(fruit1,fruit2)

        fruits = fruit1 + " " + fruit2[0] + " " + fruit2[1]

        return fruits

#调用函数

result = fruit function("apple",["banana","orange"])

print(result)

执行结果:

apple banana orange

2.2.5 面向对象编程

在介绍面向对象编程之前先说明面向过程编程。面向过程编程的意思是根据业务逻辑从上到下写代码,这个容易被初学者接受,按照逻辑需要用到哪段代码写下来即可。
随着时间的推移,在编程的方式上又发展出了函数式编程,把某些功能封装到函数中,需要用时可以直接调用,不用重复撰写。这也是上面提到的函数式编程,函数式的编程方法好处是节省了大量时间。
接下来,又出现了面向对象编程。面向对象编程是把函数进行分类和封装后放入对象中,使得开发更快、更强。例如:

class Person:#创建类

        #__init__()方法称为类的构造方法,注意是左右是两个下划线

        def __init__(self,name,age):

                self.name =name

                self.age = age

        def detail(self):    #通过self调用被封装的内容

                print(self.name)

                print(self.age)

        obj1 = Person('santos',18)

        obj1.detail()  #Python将obj1传输self参数,即:

                                #obji1.detail(obj1).此时内部self=obj1 

执行结果:

santos
18
看到这里,也许你有疑问,要实现上述代码的结果,使用函数式编程不是比面向对象编程更简单吗?例如,如果我们使用函数式编程,可以写成:

def detail(name,age):

        print(name)

        print(age)

obj1 = detail('santos',18)

执行结果:

santos
18
此处确实是函数式编程更容易。使用函数式编程,我们只需要写清楚输入和输出变量并执行函数即可;而使用面向对象的编程方法,首先要创建封装对象,然后还要通过对象调用被封装的内容,岂不是很麻烦?
但是,在某些应用场景下,面向对象编程能够显示出更大的优势。
如何选择函数式编程和面向对象编程呢?可以这样进行选择,如果各个函数之间独立且无共用的数据,就选用函数式编程;如果各个函数之间有一定的关联性,那么选用面向对象编程比较好。
下面简单介绍面向对象的两大特性:封装和继承。
1. 封装
封装,顾名思义就是把内容封装好,再调用封装好的内容。封装分为两步:
第一步为封装内容。
第二步为调用被封装的内容。
(1)封装内容
下面为封装内容的示例。

class Person:  #创建类

        def __init__(self,name,age):

                self.name = name

                self.age = age

        obj1 = Person('santos',18)

        #将“santos”和18分别封装到obj1及self的name和age属性

        self在这里只是一个形式参数,当执行obj1 = Person('santos', 18 )时,self等于obj1,此处将santos和18分别封装到obj1及self的name和age属性中。结果是obj1有name和age属性,其中name="santos",age=18。
(2)调用被封装的内容
        调用被封装的内容时有两种方式:通过对象直接调用和通过self间接调用。
        通过对象直接调用obj1对象的name和age属性,代码如下:

     

class Person:  #创建类

        def __init__(self,name,age):

                self.name = name

                self.age = age

        obj1 = Person('santos',18)     #将“santos”和18分别封装

                                                        #obj1 及self的name和age属性

        print(obj1.name)   #直接调用obj1对象的name属性

        print(obj1.age)   #直接调用obj1对象的age属性

执行结果:    

santos
18
通过self间接调用时,Python默认会将obj1传给self参数,即obj1.detail(obj1)。此时方法内部的self = obj1,即self.name='santos',self.age =18,代码如下:

class Person:  #创建类

        def __init__(self,name,age):

                self.name = name

                self.age = age

          def detail(self):    #通过self调用被封装的内容

                print(self.name)

                print(self.age)

        obj1 = Person('santos',18) 

        obj1.detail()  #Python将obj1传输 self参数,即obj1.detail(obj1),

                            #此时内部self=obj1            

执行结果:    

santos
18
上述例子定义了一个Person的类。在这个类中,可以通过各种函数定义Person的各种行为和特性,要让代码显得更加清晰有效,就要在调用Person类各种行为的时候也可以随时提取。这比仅使用函数式编程更加方便。
面对对象的编程方法不会像平时按照执行流程去思考,在这个例子中,是把Person这个类型视为一个对象,它拥有name和age两个属性,在调用过程中,让自己把自己打印出来。
综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或self间接获取被封装的内容。
2. 继承
        继承是以普通的类为基础建立专门的类对象。面向对象编程的继承和现实中的继承类似,子继承了父的某些特性,例如:
        猫可以:喵喵叫、吃、喝、拉、撒
        狗可以:汪汪叫、吃、喝、拉、撒
        如果我们要分别为猫和狗创建一个类,就需要为猫和狗实现他们所有的功能,代码如下,这里为伪代码,无法在python执行:

class 猫:

        def 喵喵叫(self):

                print(“喵喵叫”)

        def 吃(self):

                # do something

        def 喝(self):

                # do something

        def 拉(self):

                # do something

        def 撒(self):

                # do something

class 狗:

        def 汪汪叫(self):

                print(“汪汪叫”)

        def 吃(self):

                # do something

        def 喝(self):

                # do something

        def 拉(self):

                # do something

        def 撒(self):

                # do something

        从上述代码不难看出,吃、喝、拉、撒是猫狗共同的特性,我们没有必要在代码中重复编写。如果用继承的思想,就可以写成:
        动物:吃喝拉撒
        猫:喵喵叫(猫继承动物的功能)
        狗:汪汪叫(狗继承动物的功能)

class Animal:

        def eat(self):

                print(“%s 吃” %self.name)

        def drink(self):

                print(“%s 喝” %self.name)

        def shit(self):

                print(“%s 拉” %self.name)

        def pee(self):

                print(“%s 撒” %self.name)

class Cat(Animal):

        def __init__(self,name):

                self.name = name

        def cry(self):

                print(“喵喵叫”)

class Dog(Animal):

        def __init__(self,name):

                self.name = name

        def cry(self):

                print(“汪汪叫”)

c1 = Cat("小白家的小黑猫")

c1.eat()

c1.cry()

d1 = Dog("胖子家的小瘦狗")

d1.eat()

d1.cry()

运行结果:

       小白家的小黑猫 吃
        喵喵叫
        胖子家的小瘦狗 吃
        汪汪叫
        对于继承来说,其实就是将多个类共有的方法提取到父类中,子类继承父类中的方法即可,不必一一实现每个方法。

2.2.6 错误处理

        在编程过程中,我们不免会遇到写出来的程序运行错误,所以程序员经常戏称自己是在“写bug(错误)而非写程序”。这些错误一般来说会使得整个程序停止运行,但是在Python中,我们可以用try/except语句来捕获异常。
        try/except使用try来检测语句块中的错误,如果有错误的话,except则会执行捕获异常信息并处理。以下是一个实例:

try:

        result = 5/0  #除以0会产生运算错误

except Exception as e:  #出现错误会执行except

        print(e)   #把错误打印出来

执行结果:

        division by zero
        上述代码首先执行try里面的语句,除以0产生运算错误后,会执行except里的语句,将错误打印出来。在网络爬虫中,它可以帮我们处理一些无法获取到数据报错的情况。
此外,如果我们并不想打印错误,就可以用pass空语句。

try:

        result = 5/0  #除以0会产生运算错误

except Exception as e:  #出现错误会执行except

        pass   #空语句,不做任何事情

2.3 编写第一个简单的爬虫

        当了解了Python的基础语法后,就算你是编程小白,也可以轻松爬取一些网站了。
为了方便大家练习Python网络爬虫,笔者专门搭建了一个博客网站用于爬虫的教学,本书教学部分的爬虫全部基于爬取笔者的个人博客网站(www.santostang.com )。一方面,由于这个网站的设计和框架不会更改,因此本书的网络爬虫代码可以一直使用;另一方面,由于这个网站由笔者拥有,因此避免了一些法律上的风险。
        下面以爬取笔者的个人博客网站为例获取第一篇文章的标题名称,教大家学会一个简单的爬虫。

2.3.1 第一步:获取页面

#!/usr/bin/python

# coding: utf-8

import requests #引入包requests

link = “http://www.santostang.com”    #定义link为目标网页地址

#定义请求头的浏览器代理,伪装成浏览器

headers ={“User-Agent”:“Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US;rv:1.9.1.6) Gecko/20091201 FireFox/3.5.6”)

r = requests.get(link,headers =headers)  #请求网页

print(r.text)    #r.text 是获取的网页内容代码

上述代码就能获取博客首页的HTML代码,HTML是用来描述网页的一种语言,也就是说网页呈现的内容背后都是HTML代码。如果你对HTML不熟悉的话,可以先去w3school (HTML 教程 ) 学习一下,大概花上几个小时就可以了解HTML。
在上述代码中,首先import requests引入包requests,之后获取网页。
(1)首先定义link为目标网页地址。
(2)之后用headers来定义请求头的浏览器代理,进行伪装。
(3)r是requests的Response回复对象,我们从中可以获取想要的信息。r.text是获取的网页内容代码。

2.3.2 第二步:提取需要的数据

#!/usr/bin/python

# coding: utf-8

import requests

from bs4 import BeautifulSoup    # 从bs4这个库中导入BeautifulSoup

link = “http://www.santostang.com”

headers ={“User-Agent”:“Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US;rv:1.9.1.6) Gecko/20091201 FireFox/3.5.6”)

r = requests.get(link,headers =headers) 

soup = BeautifuSoup(r,text,"html.parser")  #使用BeautifulSoup解析

#找到第一篇文章标题,定位到Class是“post-title”的和h1元素,提取a,提取a里面的字符串,strip()去除左右空格

title = soup.find("h1",class= "post-title",a.text.strip()

print(r.text) 

        在获取整个页面的HTML代码后,我们需要从整个网页中提取第一篇文章的标题。
这里用到BeautifulSoup这个库对页面进行解析,BeautifulSoup将会在第4章进行详细讲解。首先需要导入这个库,然后把HTML代码转化为soup对象,接下来用soup.find("h1", class_="post-title").a.text.strip()得到第一篇文章的标题,并且打印出来。
soup.find("h1", class_="post-title").a.text.strip()的意思是,找到第一篇文章标题,定位到class是"post-title"的h1元素,提取a元素,提取a元素里面的字符串,strip()去除左右空格。
对初学者来说,使用BeautifulSoup从网页中提取需要的数据更加简单易用。
那么,我们怎么从那么长的代码中准确找到标题的位置呢?
        这里就要隆重介绍Chrome浏览器的“检查(审查元素)”功能了。下面介绍找到需要元素的步骤。
        步骤一:使用Chrome浏览器打开博客首页www.santostang.com 。右击网页页面,在弹出的快捷菜单中单击“检查”命令。

        步骤二:出现如图2-18所示的审查元素页面。单击左上角的鼠标键按钮,然后在页面上单击想要的数据,下面的Elements会出现相应的code所在的地方,就定位到想要的元素了。

        步骤三:在代码中找到标蓝色的地方,为

学习笔记(2) – 同一页面多图表。我们可以用soup.find("h1", class_="post-title").a.text.strip()提取该博文的标题。

2.3.3 第三步:存储数据

import requests

from bs4 import BeautifulSoup    # 从bs4这个库中导入BeautifulSoup

link = “http://www.santostang.com”

headers ={“User-Agent”:“Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US;rv:1.9.1.6) Gecko/20091201 FireFox/3.5.6”)

r = requests.get(link,headers =headers) 

soup = BeautifuSoup(r,text,"html.parser")  #使用BeautifulSoup解析

title = soup.find("h1",class= "post-title",a.text.strip()

print(r.text) 

#打开一个空白txt,然后使用f.write写入刚刚的字符串title

with open('title_test.txt','a+') as f

        f.write(title)

        存储到本地的txt文件非常简单,在第二步的基础上加上2行代码就可以把这个字符串保存在text中,并存储到本地。txt文件地址应该和你的Python文件放在同一个文件夹。
返回文件夹,打开title_test.txt文件。

文件里的内容:

        echarts学习笔记(2)-同一页面多图表

2.4 Python实践:基础巩固

        学习完基础知识,做完第一个爬虫例子后,是不是觉得网络爬虫并没有想象中那么难呢?本书的目标就是希望你可以快速上手Python和爬虫,然后在后面的实战中学习。但是Python爬虫入门简单,一步步深入学习后,你会发现坑越来越多。只有认真阅读、反复练习,才能熟能生巧。
为了巩固大家学习Python网络爬虫的成果,第2章~第7章的结尾都提供了一个实践项目。这些实践的目的一是让读者从实践中检验自己学习了多少知识,二是进一步巩固在该章节中学习的知识。这些实践项目的完整代码都在书中,你也可以从本书配书资源的下载地址下载。除此之外,章末还提供了一个进阶问题供感兴趣的读者思考。
        如果你是一个编程新手,在进一步学习Python编程之前需要记得以下3点:
(1)实践是最快的学习方式。如果你打算通过阅读本书而学会Python爬虫,就算读上100遍可能也不会达到很好的效果,最有效的方法就是:手输代码,反复练习。这也是为什么本书均通过项目案例来讲解Python网络爬虫的原因。
(2)搜索引擎是最好的老师。如果遇到不明白的问题,请学会使用百度或谷歌引擎搜索。就笔者自己的体验而言,谷歌的有效信息检索速度比百度快,较新的回答很有可能是英文的,但是如果你的英文阅读能力不行,就另当别论了。记得使用谷歌搜索时,找到Stack Overflow网站上的回答可以非常快地解决你的问题。
(3)请不要复制、粘贴代码。复制、粘贴代码除了可以让你在短时间内完成任务之外,没有任何好处。只有通过亲自输入代码,并不断重复、不断加快速度,才会提升你的编程能力和编程效率。否则给你一张白纸,你会什么代码都写不出。
本章实践的项目主要是帮助Python的初学者巩固之前学过的知识,如果你已经对Python有所了解,可以跳过以下部分。为了达到最好的效果,请先自行完成下面的题目。每一题后面都会提供答案,这些答案并不是唯一解,也不是让你不思考直接复制、粘贴运行的,而是用来对比思路,巩固Python基础内容的。

2.4.1 Python基础试题

试题1:请使用Python中的循环打印输出从1到100的所有奇数。

试题2:请将字符串“你好$$$我正在学Python@#@#现在需要&&&修改字符串”中的符号变成一个空格,需要输出的格式为:“你好 我正在学Python 现在需要 修改字符串”。

试题3:输出9×9乘法口诀表。

试题4:请写出一个函数,当输入函数变量月利润为I时,能返回应发放奖金的总数。例如,输出“利润为100 000元时,应发放奖金总数为10 000元”。
其中,企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%;利润在20万元到40万元之间时,高于20万元的部分可提成5%;利润在40万元到60万元之间时,高于40万元的部分可提成3%;利润在60万元到100万元之间时,高于60万元的部分可提成1.5%;利润高于100万元时,超过100万元的部分按1%提成。

试题5:用字典的值对字典进行排序,将{1: 2, 3: 4, 4:3, 2:1, 0:0}按照字典的值从大到小进行排序。

试题6:请问以下两段代码的输出分别是什么?

a = 1

def fun(a):

        a = 2

fun(a)

print (a)

a= []

def fun(2):

        a.append(1)

fun(a)

print(a)

试题7:请问以下两段代码的输出分别是什么?

class Person:

        name = "aaa"

p1=Person()

p2=Person()

p1.name="bbb"

print(p1.name)

print(p2.name)

print(Person.name)

class Person:

        name = []

p1=Person()

p2=Person()

p1.name.append(1)

print(p1.name)

print(p2.name)

print(Person.name)

2.4.2 参考答案

试题1答案:

for i in range(1,101):

        if i % 2 == 1:

                print(i)

        在上述代码中,range(1,101) 返回的是从1到100所有整数的列表list,然后使用循环判断这个数字除以2的余数是否为1,i % 2返回的是i除以2的余数。如果余数等于1,就输出该数字。

试题2答案:

str1 ="你好$$$我正在学习Python@#@#现在需要&%&%&修改字符串"

str2 = str1.repalce('$$$',‘   ‘).replace('@#@#'.'   ').replace('&%&%&','   ')

print(str2)

在上述代码中,使用replace方法可以将字符串中的一些字符替换成想要的字符。例如,str1.replace('$$$','   ‘) 就是把 str1中的'$$$'替换成空格。
其实还可以采用另一种更加简单的方法:

import re

str1 ="你好$$$我正在学习Python@#@#现在需要&%&%&修改字符串"

str2 =re.sub('[$@#&%]+',‘   ‘,str1)

print(str2)

        这里用到一个库re(正则表达式),使用其中的re.sub可以进行替换。正则表达式的功能将在第5章进行详细说明。
试题3答案:

for i in range(1,10)

        for j in range(1,i+1):

                print("%dx%d=%d\t"%(j,i,i*j),end="")

                print("")

运行上述代码,得到的结果如图2-20所示。

1x1=1

1x2=2 2x2=4

1x3=3 2x3=6 3x3=9

1x4=4 2x4=8 3x4=12 4x4=16

1x5=5 2x5=10 3x5=15 4x5=20 5x5=25

1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36

1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49

1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64

1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81

        上述代码使用了两个循环的嵌套,在第一个循环中i为1,在第二个循环中j为1。当j完成循环后,i会加1,变成2,j又从1开始一个新的循环,从而得到输出的这个9×9乘法表。
试题4答案:

def calcute profit(I):

        I=I/1000

        if I<=10:

                a=I*0.1

                return a *10000

        elif I<=20 and I>10:

                b= 0.25 + I * 0.075

                 return b*10000

        elif I<=40 and I>20:

                c= 0.75 + I * 0.05

                return c *10000

        elif I<=60 and I>40:

               d = 1.55 + I * 0.03

                return d*10000

        elif I<=80 and I>60:

                e= 2.45 + I * 0.015

                return e *10000

        else:

                f= 2.95 + I * 0.01

                return f *10000

I = int(input('净利润:'))

profit = calcute_profit(I)

print('利润为%d元时,应发奖金总数为%d元' %(I,profit))

在上述代码中,计算应发奖金时,我们对不同的情况使用if和elif进行了不同的处理。
还可以使用一个比较简洁的方式:

def calcute_profit(I):

        arr = [1000000,600000,400000,200000,100000,0]

        #这应该是各个分界值,把他们放在列表中方便访问

        rat = [0.01,0.015,0.03,0.05,0.075,0.1]

        #这是各个分界值所对应的奖金比例值

        r =0   # 这是总奖金的初始值

        for idx in range(0,6):有六个分界,值当然要循环8次

                if I > arr[idx]:

                        r = r + (I - arr[idx])*rat[idx]

                        I = arr[idx]

        return r

I = int(input('净利润:'))

profit = calcute_profit(I)

print('利润为%d元时,应发奖金总数为%d元' %(I,profit))

试题5答案:

import operator

x ={1:2,3:4.4:3,2:1,0:0}

sorted_x = sorted(x.items()),key=operator.itemgetter(1))

print(sorted_x)

运行上述代码,输出的结果是:
[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]
对字典进行排序是不可能的,只有把字典转换成另一种方式才能排序。字典本身是无序的,但是如列表元组等其他类型是有序的,所以需要用一个元组列表来表示排序的字典。

试题6答案:
第一段代码输出的结果是:1
第二段代码输出的结果是:[1]
从结果发现,在第一段代码中,a为数字int,函数改变不了函数以外a的值,输出结果仍然为1;而在第二段代码中,a为列表,函数将函数以外的a值改变了。
这是因为在Python中对象有两种,即可更改(mutable)与不可更改(immutable)对象。在Python中,strings字符串、tuples元组和numbers数字是不可更改对象,而list列表、dict字典等是可更改对象。
在第一段代码中,当一个引用传递给函数时,函数自动复制一份引用。函数里和函数外的引用是不一样的。
在第二段代码中,函数内的引用指向的是可变对象列表a,函数内的列表a和函数外的列表a是同一个。

试题7答案:
第一段代码输出的结果是:bbb aaa aaa
第二段代码输出的结果是:[1] [1] [1]
代码中的p1.name="bbb"表示实例调用了类变量,其实就是函数传参的问题。p1.name一开始指向类变量name="aaa",但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了,所以第一个答案是bbb。而后面的两个答案还是调用类变量name="aaa",所以还是aaa。
第二段的答案因为正如上面所言,列表和字典是可更改对象,因此修改一个指向的对象时会把类变量也改变了。

2.4.3 自我实践题

读者若有时间,可以从W3school的Python 100例中学习Python的各种应用基础知识,网址是:Python 100例_w3cschool 。

为了给初学者实际可以运行的代码实例:(确保能运行和调试的)可在下面的链接下载

https://download.csdn.net/download/jyl_sh/89393741

  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jyl_sh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值