Python:基础&爬虫
Python爬虫学习(网络爬虫(又称为网页蜘蛛 ,网络机器人,在FOAF 社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网 信息的程序 或者脚本 。另外一些不常使用的名字还有蚂蚁、自动索引 、模拟程序 或者蠕虫。)
一、基础知识准备
Python在各个编程语言中比较适合新手学习,Python解释器易于扩展,可以使用C 、C++ 或其他可以通过C调用的语言扩展新的功能和数据类型 。 [4] Python也可用于可定制化软件中的扩展程序语言。Python丰富的标准库,提供了适用于各个主要系统平台的源码 或机器码 。
1 条件判断语句
1 2 3 4 5 6 7 score = 60 if score >=90 and score <=100 : print ("本次考试等级为A" ) elif score >=70 and score <90 : print ("本次考试等级为B" ) else : print ("本次考试等级为C" )
2 循环语句
2.1 for循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 for i in range (5 ): print (i) for i in range (0 ,10 ,3 ): print (i) for i in range (-10 ,-100 ,-30 ): print (i) name="chengdu" for x in name: print (x) a = ["a" , "b" , "c" ] for i in range (len (a)): print (i, a[i])
2.2 while循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 i=0 while i<3 : print ("这是第%d次循环:" %(i+1 )) print ("i=%d" %i) i+=1 '''#output: 这是第1次循环: i=0 这是第2次循环: i=1 这是第3次循环: i=2 '''
1 2 3 4 5 6 7 8 9 10 11 12 13 count = 0 while count<3 : print (count,"小于3" ) count +=1 else : print (count,"大于或等于3" ) '''#output: 0 小于3 1 小于3 2 小于3 3 大于或等于3 '''
3 字符串
1 2 3 4 5 6 7 8 9 10 11 12 str ="chengdu" print (str ) print (str [0 ]) print (str [0 :5 ]) print (str [1 :7 :2 ]) print (str [5 :]) print (str [:5 ]) print (str +",hello" ) print (str *3 ) print ("hello\nchengdu" ) print (r"hello\nchengdu" ) print ("-" *30 )
4 列表-List
列表中的每个元素都分配一个数字 - 它的位置或索引,第一个索引是0,第二个索引是1,依此类推。
4.1 列表定义
1 2 3 namelist = ["小张" ,"小王" ,"小李" ] testlist = [1 ,"测试" ,"str" ] testlist = [["a" ,"b" ],["c" ,"d" ],["e" ,"f" ,"g" ]]
4.2 列表元素输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 namelist = ["小张" ,"小王" ,"小李" ] print (namelist[1 ]) for name in namelist: print (name) '''output 小张 小王 小李 ''' for i,name in enumerate (namelist): print (i,name) '''output 0 小张 1 小王 2 小李 '''
4.3 列表元素切片
如下所示:L=[‘Google’, ‘Python’, ‘Taobao’]
Python
表达式 结果
描述
L[2]
‘Taobao’
读取第三个元素
L[-1]
‘Taobao’
读取最后一个元素
L[1:]
[‘Python’, ‘Taobao’]
输出从第二个元素开始后的所有元素
L[:-1]
[‘Google’, ‘Python’]
输出从第一个到倒数第一个的所有元素
L[-2:]
[‘Python’, ‘Taobao’]
输出从倒数第二个到末尾的所有元素
4.4 列表元素追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 a = [1 ,2 ] b = [3 ,4 ] a.append(b) print (a) a = [1 ,2 ] b = [3 ,4 ] a.extend(b) print (a) a=[1 ,2 ,4 ] a.insert(2 ,3 ) print (a)
4.5 列表元素删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 a = ["小张" ,"小王" ,"小李" ] del a[2 ] print (a) a = ["小张" ,"小王" ,"小李" ] a.pop() print (a) a = ["小张" ,"小王" ,"小李" ] a.remove("小李" ) print (a)
4.6 列表元素修改
1 2 3 a = ["小张" ,"小王" ,"小李" ] a[2 ] = "小红" print (a)
4.7 列表元素查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 a = ["小张" ,"小王" ,"小李" ] findName = input ("请输入你要查找的学生姓名:" ) if findName in a: print ("找到" ) else : print ("未找到" ) a = ["小张" ,"小王" ,"小李" ] print (a.index("小王" ,0 ,2 )) print (a.index("小李" ,0 ,2 )) print (a.count("小王" ))
4.8 列表元素反转和排序
1 2 3 4 5 6 7 8 9 a = [1 ,4 ,2 ,3 ] a.reverse() print (a) a.sort() print (a) a.sort(reverse=True ) print (a)
5 前段知识综合练习
Topic: 将8个老师随机分配到3个办公室
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import randomoffices = [[],[],[]] teachers = ["A" ,"B" ,"C" ,"D" ,"E" ,"F" ,"G" ,"H" ] for teacher in teachers: index = random.randint(0 ,2 ) offices[index].append(teacher) i=1 for office in offices: print ("office%d的人数为:%d" %(i,len (office))) i += 1 for name in office: print ("%s" %name,end="\t" ) print ("\n" ) print ("-" *20 )
6 元组-Tuple
元组与列表类似,不同之处在于元组的元素不能修改。
元组使用小括号,列表使用方括号。
6.1 元组定义
1 2 3 4 tup1=() tup2=(5 ) tup2=(5 ,) tup3 = ('Google' , 'Python' , 1997 , 2000 )
6.2 元组元素切片
1 2 3 4 tup=(1 ,2 ,3 ) print (tup[0 ]) print (tup[-1 ]) print (tup[0 :2 ])
6.3 元组元素增加(连接)
1 2 3 4 tup1 = (12 ,34 ,56 ) tup2 = ("ab" ,"cd" ,"ef" ) tup3 = tup1+tup2 print (tup3)
6.4 元组元素删除
1 2 3 tup1 = (12 ,34 ,56 ) del tup1
6.5 元组元素不能修改
1 2 tup1 = (12 ,34 ,56 ) tup1[0 ] = 72
7 字典-dict
字典使用键值对(key=>value)存储;键必须是唯一的,但值则不必。
7.1 字典定义
1 2 dict = {key1 : value1, key2 : value2 }info = {"name" :"简简" ,"age" :18 }
7.2 字典访问
1 2 3 4 5 6 7 8 info = {"name" :"简简" ,"age" :18 } print (info["name" ])print (info["age" ])print (info["sex" ]) print (info.get("sex" )) print (info.get("sex" ,"没有" ))
7.3 字典键值增加
1 2 3 info = {"name" :"简简" ,"age" :18 } info["sex" ]="man" print (info)
7.4 字典键值删除
1 2 3 4 5 6 7 8 9 10 11 12 info = {"name" :"简简" ,"age" :18 } del info["name" ] print (info) del info print (info) info = {"name" :"简简" ,"age" :18 } info.clear() print (info)
7.5 字典键值修改
1 2 3 info = {"name" :"简简" ,"age" :18 } info["age" ]=20 print (info)
7.6 字典键值查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 info = {"name" :"简简" ,"age" :18 } print (info.keys()) print (info.values()) print (info.items()) for key in info.keys(): print (key) for value in info.values(): print (value) for key,value in info.items(): print ("(key=%s,value=%s)" %(key,value))
8 函数
8.1 函数定义和使用
1 2 3 4 5 def printinfo (a,b ): c =a + b print (c) printinfo(1 ,2 )
8.2 带返回值的函数
1 2 3 4 5 def info (a,b ): c =a + b return c print (info(1 ,2 ))
8.3 返回多个值的函数
1 2 3 4 5 6 7 def divid (a,b ): shang = a//b yushu = a%b return shang,yushu sh,yu = divid(5 ,2 ) print ("商:%d 余数:%d" %(sh,yu))
9 文件操作
9.1 打开文件(open)
用法:对象=open(文件名,访问模式)
1 f = open ('test.txt' , 'w' )
模式
说明
r
以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
w
打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a
打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
rb
以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
wb
以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
ab
以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
r+
打开一个文件用于读写。文件指针将会放在文件的开头。
w+
打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a+
打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
rb+
以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
wb+
以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
ab+
以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
9.2 关闭文件(close)
用法:对象.close()
9.3 写数据(write)
用法:对象.write()
1 2 3 f=open ("test.txt" ,"w" ) f.write("hello,world" ) f.close()
9.4 读数据(read)
用法:对象.read()
1 2 3 4 f=open ("test.txt" ,"r" ) content=f.read(5 ) print (content)f.close()
9.5 读一行数据(readline)
用法:对象.readline()
1 2 3 4 5 6 f = open ('test.txt' , 'r' ) content = f.readline() print ("1:%s" %content)content = f.readline() print ("2:%s" %content)f.close()
9.6 读多行数据(readlines)
用法:对象.readlines()
1 2 3 4 5 6 7 8 9 10 f=open ("test.txt" ,"r" ) content=f.readlines() print (content) i=1 for temp in content: print ("%d:%s" % (i, temp)) i += 1 f.close()
9.7 OS模块
os模块中的函数:
序号
函数名称
描述
格式
1
getcwd()
获取当前的工作目录
格式:os.getcwd() 返回值:路径字符串
2
chdir()
修改当前工作目录
格式:os.chdir() 返回值:None
3
listdir()
获取指定文件夹中的 所有文件和文件夹组成的列表
格式:os.listdir(目录路径) 返回值:目录中内容名称的列表
4
mkdir()
创建一个目录/文件夹
格式:os.mkdir(目录路径) 返回值:None
5
makedirs()
递归创建文件夹
格式:os.makedirs(路径)
6
rmdir()
移除一个目录(必须是空目录)
格式:os.rmdir(目录路径) 返回值:None
7
removedirs()
递归删除文件夹
格式:os.removedirs(目录路径) 返回值:None 注意最底层目录必须为空
8
rename()
修改文件和文件夹的名称
格式:os.rename(源文件或文件夹,目标文件或文件夹) 返回值:None
9
stat()
获取文件的相关 信息
格式:os.stat(文件路径) 返回值:包含文件信息的元组
10
system()
执行系统命令
格式:os.system() 返回值:整型 慎用! 玩意来个rm -rf 你就爽了!
11
getenv()
获取系统环境变量
格式:os.getenv(获取的环境变量名称) 返回值:字符串
12
putenv()
设置系统环境变量
格式:os.putenv(‘环境变量名称’,值) 返回值:无 注意:无法正常的getenv检测到。
13
exit()
推出当前执行命令,直接关闭当前操作
格式:exit() 返回值:无
10 异常处理
10.1 异常简介
1 2 3 print '-----test--1---' open ('123.txt' ,'r' ) print '-----test--2---'
image-20200323100011079
打开一个不存在的文件123.txt,当找不到123.txt 文件时,就会抛出给我们一个IOError类型的错误,No such file or directory:123.txt (没有123.txt这样的文件或目录)
10.2 捕获异常
1 2 3 4 5 6 try : print ('-----test--1---' ) open ('123.txt' ,'r' ) print ('-----test--2---' ) except IOError: pass
此程序看不到任何错误,因为用except 捕获到了IOError异常,并添加了处理的方法
pass 表示实现了相应的实现,但什么也不做;如果把pass改为print语句,那么就会输出其他信息
总结:
image-20200323100335376
把可能出现问题的代码,放在try中
把处理异常的代码,放在except中
1 2 3 4 try : print num except IOError: print ('产生错误了' )
上例程序,已经使用except来捕获异常,但是还会看到错误的信息提示
except捕获的错误类型是IOError,而此时程序产生的异常为 NameError ,所以except没有生效
1 2 3 4 try : print num except NameError: print ('产生错误了' )
Python的一些內建异常:
异常
描述
Exception
常规错误的基类
AttributeError
对象没有这个属性
IOError
输入/输出操作失败
IndexError
序列中没有此索引(index)
KeyError
映射中没有这个键
NameError
未声明/初始化对象 (没有属性)
SyntaxError
Python 语法错误
TypeError
对类型无效的操作
ValueError
传入无效的参数
ZeroDivisionError
除(或取模)零 (所有数据类型)
更多可以参考:http://blog.csdn.net/gavin_john/article/details/50738323
10.3 捕获多个异常
1 2 3 4 5 6 7 8 try : print ('-----test--1---' ) open ('123.txt' ,'r' ) print ('-----test--2---' ) print (num) except (IOError,NameError):
10.4 获取异常的信息描述
image-20200323102016442
image-20200323102023020.png
10.5 try…finally…
在程序中,如果一个段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用finally。 比如文件关闭,释放锁,把数据库连接返还给连接池等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import timetry : f = open ('test.txt' ) try : while True : content = f.readline() if len (content) == 0 : break time.sleep(2 ) print (content) except : pass finally : f.close() print ('关闭文件' ) except : print ("没有这个文件" )
test.txt文件中每一行数据打印,但是我有意在每打印一行之前用time.sleep方法暂停2秒钟。这样做的原因是让程序运行得慢一些。在程序运行的时候,按Ctrl+c中断(取消)程序。
我们可以观察到KeyboardInterrupt异常被触发,程序退出。但是在程序退出之前,finally从句仍然被执行,把文件关闭。
11 Python知识
除法
除 /
整除 //
求余 %
商和余数的元组 divmod
移位操作
左移(<<)
`a<,左移 n 位相当于原操作数乘以 2^n,原操作数不发生变化。
1 2 3 4 >>> 3 <<1 6 >>> -3 <<2 -12
右移(>>)
a>>n,则a' =a//(2^n)
,左移 n 位相当于原操作数整除 2^n,原操作数不发生变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> 2 >>1 1 >>> 2 >>2 0 >>> 2 >>3 0 >>> -8 >>2 -2 >>> -8 >>3 -1 >>> -8 >>4 -1
如果操作数是正数,那么对之不停进行右移操作,最终结果一定可以得到 0
;如果操作数是负数,对之不停进行右移操作,最终结果一定可以得到 -1
。
匿名函数lambda
匿名函数 lambda 是指一类无需定义标识符(函数名)的函数或子程序。
lambda 函数可以接收任意多个参数 (包括可选参数) 并且返回单个表达式的值。
语法:
1 lambda [arg1,arg2,.....argn]:expression
冒号前是参数,可以有多个,用逗号隔开,冒号右边的为表达式(只能为一个)。其实lambda返回值是一个函数的地址,也就是函数对象。
1 lambda arg: print ("hello,world" ,arg)
lambda表达式限制只能包含一行代码,但是其实可以利用元组或列表强行让其包含多行。(但是这么做会严重影响可读性,除非万不得已不要使用)
1 2 3 4 lambda :( print ("hello" ), print ("world" ), )
切片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 t=[1 ,2 ,3 ,4 ,5 ] print (t[1 :]) 取第二个到最后一个元素结果:[2 3 4 5 ] print (t[:]) 取所有元素结果:[1 2 3 4 5 ] print (t[1 :3 ]) 取t[1 ]-t[2 ]结果:[ 2 3 ] print (t[:-1 ]) 除了最后一个取全部结果:[ 1 2 3 4 ] print (t[::-1 ]) 取从后向前(相反)的元素结果:[ 5 4 3 2 1 ] print (t[2 ::-1 ]) 取从下标为2 的元素翻转读取结果:[ 3 2 1 ]
字符串方法
join(iterable)
获取可迭代对象(iterable)中的所有项目,并将它们连接为一个字符串。
实例1:
1 2 3 4 5 6 7 8 myTuple = ("Bill" , "Steve" , "Elon" ) x = "#" .join(myTuple) print (x)''' 输出: Bill#Steve#Elon '''
实例2:
1 2 3 4 5 6 7 8 9 myDict = {"name" : "Bill" , "country" : "USA" } mySeparator = "TEST" x = mySeparator.join(myDict) print (x)''' 输出: nameTESTcountry '''
注释:在使用字典作为迭代器时,返回的值是键,而不是值。
split(separator, max)
将字符串拆分为列表,您可以指定分隔符,默认分隔符是任何空白字符。若指定 max,列表将包含指定数量加一的元素。
实例1:
1 2 3 4 5 6 7 8 txt = "welcome to China" x = txt.split() print (x)''' 输出: ['welcome', 'to', 'China'] '''
实例2:
1 2 3 4 5 6 7 8 9 txt = "apple#banana#cherry#orange" x = txt.split("#" , 1 ) print (x)''' 输出: ['apple', 'banana#cherry#orange'] '''
内置函数
enumerate()
用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
实例1:
1 2 3 4 5 6 7 >>>seq = ['one' , 'two' , 'three' ] >>> for i, element in enumerate (seq):... print i, element... 0 one1 two2 three
实例2:
1 2 3 4 5 >>>seasons = ['Spring' , 'Summer' , 'Fall' , 'Winter' ] >>> list (enumerate (seasons))[(0 , 'Spring' ), (1 , 'Summer' ), (2 , 'Fall' ), (3 , 'Winter' )] >>> list (enumerate (seasons, start=1 )) [(1 , 'Spring' ), (2 , 'Summer' ), (3 , 'Fall' ), (4 , 'Winter' )]
exec()
exec 执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 >>>exec ('print("Hello World")' ) Hello World >>> exec ("""for i in range(5): ... print ("iter time: %d" % i)... """ )iter time: 0 iter time: 1 iter time: 2 iter time: 3 iter time: 4
二、Python爬虫
下面的学习方式是以爬取豆瓣top250 网页进行开展的
基本流程: 爬取网页—>解析数据—>保存数据
1 requests库
Requests
是一个简单方便的HTTP 库
。比Python
标准库中的urllib2
模块功能强大。Requests 使用的是 urllib3,因此继承了它的所有特性。Requests 支持使用cookie
保持会话,支持文件上传,支持自动确定响应内容的编码,支持URL
和 POST
数据自动编码。帮助我们轻松解决关于HTTP
的大部分问题。
爬取网页首先要学习requests库或者urllib库的使用,不然你无法看懂下面代码
学习requests库,请看我另外一篇文章,里面对requests库进行了详细的讲解Python模块-Requests学习
2 爬取网页
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import requestsdef askUrl (url ): head = { "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.69 Safari/537.36 Edg/81.0.416.34" } html="" r = requests.get(url, headers = head) html = r.text print (html) return html if __name__ == "__main__" : askUrl("https://movie.douban.com/top250?start=" )
可以看到成功的爬取到豆瓣top250第一页的数据
image-20200327113957848
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import requestsdef askUrl (url ): head = { "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.69 Safari/537.36 Edg/81.0.416.34" } r = requests.get(url, headers = head) html = r.text print (html) def getData (baseurl ): for i in range (0 , 10 ): url = baseurl + str (i * 25 ) html = askUrl(url) if __name__ == "__main__" : baseurl = "https://movie.douban.com/top250?start=" getData(baseurl)
可以看到排名250的梦之安魂曲也被成功爬取到
image-20200327115150029
3 BeautifulSoup4库
BeautifulSoup4和 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。
以下只涉及基础使用,详情请看中文文档:Beautiful Soup 4.4.0 文档
假设有这样一个baidu.html,放在py文件目录下,下面的例子都基于该html,具体内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <!DOCTYPE html > <html > <head > <meta content ="text/html;charset=utf-8" http-equiv ="content-type" /> <meta content ="IE=Edge" http-equiv ="X-UA-Compatible" /> <meta content ="always" name ="referrer" /> <link href ="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel ="stylesheet" type ="text/css" /> <title > 百度一下,你就知道 </title > </head > <body link ="#0000cc" > <div id ="wrapper" > <div id ="head" > <div class ="head_wrapper" > <div id ="u1" > <a class ="mnav" href ="http://news.baidu.com" name ="tj_trnews" > </a > <a class ="mnav" href ="http://news.baidu.com" name ="tj_trnews" > 新闻</a > <a class ="mnav" href ="https://www.hao123.com" name ="tj_trhao123" > hao123</a > <a class ="mnav" href ="http://map.baidu.com" name ="tj_trmap" > 地图</a > <a class ="mnav" href ="http://v.baidu.com" name ="tj_trvideo" > 视频</a > <a class ="mnav" href ="http://tieba.baidu.com" name ="tj_trtieba" > 贴吧</a > <a class ="bri" href ="//www.baidu.com/more/" name ="tj_briicon" style ="display: block;" > 更多产品 </a > </div > </div > </div > </div > </body > </html >
3.1 快速使用案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from bs4 import BeautifulSoupfile = open ("./baidu.html" ,'rb' ) content = f.read() f.close() bs = BeautifulSoup(content,"html.parser" ) print (bs)print (bs.title.string)
3.2 BeautifulSoup4主要解析器
解析器
使用方法
优势
劣势
Python标准库
BeautifulSoup(markup, “html.parser”)
Python的内置标准库,执行速度适中,文档容错能力强
Python 2.7.3 or 3.2.2前的版本中文档容错能力差
lxml HTML 解析器
BeautifulSoup(markup, “lxml”)
速度快 文档容错能力强
需要安装C语言库
lxml XML 解析器
BeautifulSoup(markup, [“lxml-xml”]) BeautifulSoup(markup, “xml”)
速度快 唯一支持XML的解析器
需要安装C语言库
html5lib
BeautifulSoup(markup, “html5lib”)
最好的容错性,以浏览器的方式解析文档,生成HTML5格式的文档
速度慢、不依赖外部扩展
3.2 BS4四大对象种类
BeautifulSoup4将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种
Tag
NavigableString
BeautifulSoup
Comment
3.2.1 Tag
Tag通俗点讲就是为了获取HTML中的一个个标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) print (bs.title) print (bs.head) print (bs.a) print (type (bs.a)) print (bs.name) print (bs.head.name) print (bs.a.attrs) print (bs.a['class' ]) bs.a['class' ] = "newClass" print (bs.a) del bs.a['class' ] print (bs.a)
3.2.2 NavigableString
既然我们已经得到了标签的内容,那么问题来了,我们要想获取标签内部的文字怎么办呢?很简单,用 .string 即可,例如
1 2 3 4 5 6 7 8 9 10 11 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) print (bs.title.string) print (type (bs.title.string))
3.3.3 BeautifulSoup
BeautifulSoup对象表示的是一个文档的内容。大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性,例如:
1 2 3 4 5 6 7 8 9 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) print (bs)print (type (bs))
Comment 对象是一个特殊类型的 NavigableString 对象,其输出的内容不包括注释符号。
1 2 3 4 5 6 7 8 9 10 11 12 13 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) print (bs.a)print (bs.a.string) print (type (bs.a.string))
3.3 遍历文档数
.contents:获取Tag的所有子节点,返回一个list
1 2 3 4 5 6 7 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) print (bs.head.contents) print (bs.head.contents[1
.children:获取Tag的所有子节点,返回一个生成器
1 2 3 4 5 6 7 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) for child in bs.body.children: print (child)
.descendants
获取Tag的所有子孙节点
.strings
如果Tag包含多个字符串,即在子孙节点中有内容,可以用此获取,而后进行遍历
.stripped_strings
与strings用法一致,只不过可以去除掉那些多余的空白内容
.parent
获取Tag的父节点
.parents
递归得到父辈元素的所有节点,返回一个生成器
.previous_sibling
获取当前Tag的上一个节点,属性通常是字符串或空白,真实结果是当前标签与上一个标签之间的顿号和换行符
.next_sibling
获取当前Tag的下一个节点,属性通常是字符串或空白,真是结果是当前标签与下一个标签之间的顿号与换行符
.previous_siblings
获取当前Tag的上面所有的兄弟节点,返回一个生成器
.next_siblings
获取当前Tag的下面所有的兄弟节点,返回一个生成器
.previous_element
获取解析过程中上一个被解析的对象(字符串或tag),可能与previous_sibling相同,但通常是不一样的
.next_element
获取解析过程中下一个被解析的对象(字符串或tag),可能与next_sibling相同,但通常是不一样的
.previous_elements
返回一个生成器,可以向前访问文档的解析内容
.next_elements
返回一个生成器,可以向后访问文档的解析内容
.has_attr
判断Tag是否包含属性
详情请看中文文档:Beautiful Soup 4.4.0 文档
3.4 文档的搜索find_all()
name参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) t_list = bs.find_all("a" ) t_list = bs.find_all("title" ) print (t_list)import ret_list = bs.find_all(re.compile ("a" )) print (t_list)
函数参数
1 2 3 4 5 6 7 8 9 10 11 12 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) def name_is_exists (tag ): return tag.has_attr("name" ) t_list = bs.find_all(name_is_exists) for item in t_list: print (item)
keyword参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) t_list = bs.find_all(id ="head" ) for item in t_list: print (item) t_list = bs.find_all(class_="mnav" ) for item in t_list: print (item) t_list = bs.find_all(href="http://news.baidu.com" ) for item in t_list: print (item)
text参数
1 2 3 4 5 6 7 8 9 10 11 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) import ret_list = bs.find_all(text=re.compile ("\d" )) for item in t_list: print (item)
limit 参数
1 2 3 4 5 6 7 8 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) t_list = bs.find_all("a" ,limit=3 ) for item in t_list: print (item)
3.5 css选择器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from bs4 import BeautifulSoup file = open ('./baidu.html' , 'rb' ) content = file.read() bs = BeautifulSoup(content,"html.parser" ) t_list = bs.select('title' ) t_list = bs.select('.mnav' ) t_list = bs.select('#u1' ) t_list = bs.select("a[class='bri']" ) t_list=bs.select("head > title" ) t_list=bs.select(".mnav ~ .bri" ) for item in t_list: print (item) print (t_list[0 ].get_text())
4 re库
正则表达式(Regular Expression)通常被用来匹配
、检索
、替换
和分割
那些符合某个模式(规则)的文本。
4.1 正则表达式常用操作符
操作符
说明
实例
.
表示除 “\n” 之外的任何单个字符。
[ ]
宇符集,对单个字符给出取值范围
[abc]表示a,b,c;[a-z]表示a到z单个字符
[^ ]
非字符集,对单个字符恰给出排除范围
[^abc]表示非a或非b或c的单个字符
*
前一个字符0次或无限次扩展
abc* 表示ab、abc、abcc、abcc等
+
前一个字符1次或无限次扩展
abc+ 表示abc、abcc、abcc等
?
前一个字符0次或1攻扩展
abc? 表示ab、abc
|
左右表达式任意一个
abc|def 表示abc、def
{m}
扩展前一个字符m次
ab(2}c表示abbc
{m,n}
扩展前一个字符m至n次(含n)
ab{1,2}c表示abc、abbc
^
匹配字符串开头
^abc表示abc且在一个字符串的开头
$
匹配字符串结尾
abc$表示abc且在一个字符串的结尾
( )
分组标记,内部只能使用|操作符
(abc)表示abc ,(abc|def)表示abc、def
\d
数字,等价于[0-9]
\w
单词字符,等价于[A-Za-z0-9_ ]
4.2 re库常用函数
函数
说明
re.compile()
返回一个正则对象的模式。
re. search()
在一个字符串中搜素匹配正则表达式的第一个位置 ,返回match对象
re. match()
从一个字符串的开始位置起匹配正则表达式,返回match对象
re. findall()
搜索字符串,以列表类型返回全部能匹配的子串
re. split()
将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
re. finditer()
擅索字符串。返回一个匹配结果的迭代类型,每个选代元素是match对象
re. sub()
在一个字符串中普换所有匹配正则表达式的子串,返回替换后的字符申
4.2.1 compile()
格式:re.compile(pattern[,flags=0])
pattern
: 编译时用的表达式字符串。
flags
: 编译标志位,用于修改正则表达式的匹配方式,如:re.I、re.S等
1 2 3 4 5 6 7 import repat=re.compile ("A" ) m=pat.search("CBA" ) print (m)m=pat.search("CBD" ) print (m)
4.2.2 search()
在字符串中寻找模式
格式:re.search(pattern, string[, flags=0])
re.search函数会在字符串内查找模式匹配,只要找到第一个匹配然后返回,如果字符串没有匹配,则返回None。
1 2 3 4 5 6 7 import rem = re.search("asd" , "ASDasd" ) print (m)m = re.search("asd" , "ASDASD" ) print (m)
4.2.3 match()
在字符串开始处匹配模式
格式:re.match(pattern, string[, flags=0])
1 2 3 4 5 6 import repat=re.compile ( "a" ) print (pat.match ( "Aasd" )) pat=re.compile ( "A" ) print (pat.match ( "Aasd" ))
注:match和search一旦匹配成功,就是一个match object对象,而match object对象有以下方法:
group() 返回被 RE 匹配的字符串
start() 返回匹配开始的位置
end() 返回匹配结束的位置
span() 返回一个元组包含匹配 (开始,结束) 的位置
4.2.3 findall()
列表形式返回匹配项
格式:re.findall(pattern, string[, flags=0])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import reprint (re.findall("a" ,"ASDaDFGAa" )) p = re.compile (r'\d+' ) print (p.findall('o1n2m3k4' ))print (re.findall("[A-Z]" ,"ASDaDFGAa" ))print (re.findall("[A-Z]+" ,"ASDaDFGAa" ))pat = re.compile ("[A-Za-z]" ) print (pat.findall("ASDcDFGAa" ))
4.2.4 re. split()
按照能够匹配的子串将string分割后返回列表。
可以使用re.split来分割字符串,如:re.split(r’\s+’, text);将字符串按空格分割成一个单词列表。
格式:re.split(pattern, string[, maxsplit])
maxsplit
: 用于指定最大分割次数,不指定将全部分割。
1 2 3 4 print (re.split('\d+' ,'one1two2three3four4five5' ))
4.2.5 finditer()
搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。
找到 RE 匹配的所有子串,并把它们作为一个迭代器返回。
格式:re.finditer(pattern, string[, flags=0])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import reiter = re.finditer(r'\d+' ,'12 drumm44ers drumming, 11 ... 10 ...' )for i in iter : print (i) print (i.group()) print (i.span()) ''' # 执行结果如下: <_sre.SRE_Match object; span=(0, 2), match='12'> 12 (0, 2) <_sre.SRE_Match object; span=(8, 10), match='44'> 44 (8, 10) <_sre.SRE_Match object; span=(24, 26), match='11'> 11 (24, 26) <_sre.SRE_Match object; span=(31, 33), match='10'> 10 (31, 33) '''
4.2.6 sub()
4.3 模式修正符
所谓模式修正符,即可以在不改变正则表达式的情况下,通过模式修正符改变正则表达式的含义,从而实现一些匹配结果的调整等功能。
修饰符
描述
re.I
使匹配对大小写不敏感
re.L
做本地化识别(locale-aware)匹配
re.M
多行匹配,影响 ^ 和 $
re.S
使 . 匹配包括换行在内的所有字符
re.U
根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X
该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。
1 2 3 4 5 import restring = "Python" pat = "pyt" rst = re.search(pat,string,re.I) print (rst)