Python:基础&爬虫

2024-06-17 1116阅读

Python爬虫学习(网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。)

python学习资料已打包好,需要的小伙伴可以戳这里【python资料】

一、基础知识准备

Python在各个编程语言中比较适合新手学习,Python解释器易于扩展,可以使用C、C++或其他可以通过C调用的语言扩展新的功能和数据类型。Python也可用于可定制化软件中的扩展程序语言。Python丰富的标准库,提供了适用于各个主要系统平台的源码或机器码。

1 条件判断语句

score = 60
if score >=90 and score =70 and score >n,则a' =a//(2^n),左移 n 位相当于原操作数整除 2^n,原操作数不发生变化。 
>>> 2>>1     # 移动一位,相当于是2//2
1            
>>> 2>>2     # 相当于先左移一位得到1,结果1再除以2等于0
0
>>> 2>>3     # 相当于2//8
0  
          
>>> -8>>2    # 移动2位,相当于-8//4
-2
>>> -8>>3    # 移动3位,相当于是用结果-2再除以2
-1
>>> -8>>4    # 移动4位,相当于是用结果-1再除以2
-1

如果操作数是正数,那么对之不停进行右移操作,最终结果一定可以得到 0;如果操作数是负数,对之不停进行右移操作,最终结果一定可以得到 -1。

匿名函数lambda

匿名函数 lambda 是指一类无需定义标识符(函数名)的函数或子程序。

lambda 函数可以接收任意多个参数 (包括可选参数) 并且返回单个表达式的值。

语法:

lambda [arg1,arg2,.....argn]:expression

冒号前是参数,可以有多个,用逗号隔开,冒号右边的为表达式(只能为一个)。其实lambda返回值是一个函数的地址,也就是函数对象。

lambda arg: print("hello,world",arg)

lambda表达式限制只能包含一行代码,但是其实可以利用元组或列表强行让其包含多行。(但是这么做会严重影响可读性,除非万不得已不要使用)

lambda :(
    print("hello"),
    print("world"),           
)
切片

Python:基础&爬虫

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:

myTuple = ("Bill", "Steve", "Elon")
x = "#".join(myTuple)
print(x)
'''
输出:
Bill#Steve#Elon
'''

实例2:

myDict = {"name": "Bill", "country": "USA"}
mySeparator = "TEST"
x = mySeparator.join(myDict)
print(x)
'''
输出:
nameTESTcountry
'''

注释:在使用字典作为迭代器时,返回的值是键,而不是值。

split(separator, max)

将字符串拆分为列表,您可以指定分隔符,默认分隔符是任何空白字符。若指定 max,列表将包含指定数量加一的元素。

实例1:

txt = "welcome to China"
x = txt.split()
print(x)
'''
输出:
['welcome', 'to', 'China']
'''

实例2:

txt = "apple#banana#cherry#orange"
# 将 max 参数设置为 1,将返回包含 2 个元素的列表!
x = txt.split("#", 1)
print(x)
'''
输出:
['apple', 'banana#cherry#orange']
'''
内置函数
enumerate()

用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

实例1:

>>>seq = ['one', 'two', 'three']
>>> for i, element in enumerate(seq):
...     print i, element
... 
0 one
1 two
2 three

实例2:

>>>seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1))       # 下标从 1 开始
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
exec()

exec 执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码。

# 单行语句字符串
>>>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库的使用,不然你无法看懂下面代码

2 爬取网页

2.1 爬取豆瓣top250第一页数据
#-*- coding =utf-8 -*-
import requests
def 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) #get方式发送请求
    html = r.text #接收数据
    print(html)  
    return html
if __name__ == "__main__": # main函数用于测试程序
    askUrl("https://movie.douban.com/top250?start=") #调用函数

可以看到成功的爬取到豆瓣top250第一页的数据

Python:基础&爬虫

2.2 爬取豆瓣top250前10页数据
#-*- coding =utf-8 -*-
import requests
#爬取一个页面
def 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)
# 爬取所有页面
def getData(baseurl):
    for i in range(0, 10):
        url = baseurl + str(i * 25)
        html = askUrl(url)
if __name__ == "__main__": # main函数用于测试程序
    baseurl = "https://movie.douban.com/top250?start="
    getData(baseurl)

可以看到排名250的梦之安魂曲也被成功爬取到

Python:基础&爬虫

image-20200327115150029

3 BeautifulSoup4库

BeautifulSoup4和 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。

假设有这样一个baidu.html,放在py文件目录下,下面的例子都基于该html,具体内容如下:



    
    
    
    
    百度一下,你就知道 


  
    
        
          
            
            新闻
            hao123
            地图
            视频
            贴吧
            更多产品 
          
        
    
  


3.1 快速使用案例
# 导入模块
from bs4 import BeautifulSoup
# 读取html文件信息(在真实代码中是爬取的网页信息)
file = open("./baidu.html",'rb') #解析器
content = f.read()
f.close()
# 创建解析器
bs = BeautifulSoup(content,"html.parser")
# 输出网页内容:注:此内容已被缩进格式化(自动更正格式),其实这个是在上一步实例化时就已完成
print(bs)
#输出网页中title标签中的内容
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语言库
html5libBeautifulSoup(markup, “html5lib”)最好的容错性,以浏览器的方式解析文档,生成HTML5格式的文档速度慢、不依赖外部扩展
3.2 BS4四大对象种类

BeautifulSoup4将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种

  • Tag
  • NavigableString
  • BeautifulSoup
  • Comment
    3.2.1 Tag

    Tag通俗点讲就是为了获取HTML中的一个个标签

    from bs4 import BeautifulSoup 
    file = open('./baidu.html', 'rb') 
    content = file.read() 
    bs = BeautifulSoup(content,"html.parser") 
    # 获取title标签的所有内容
    print(bs.title)  #百度一下,你就知道 
    # 获取head标签的所有内容
    print(bs.head) 
    # 获取第一个a标签的所有内容
    print(bs.a) 
    # 类型
    print(type(bs.a)) # 
    #bs 对象本身比较特殊,它的 name 即为 [document] 
    print(bs.name) # [document]
    # head #对于其他内部标签,输出的值便为标签本身的名称
    print(bs.head.name)  # head
    # 获取a标签里的所有属性,打印输出来,得到的类型是一个字典。 
    print(bs.a.attrs) 
    # {'class': ['mnav'], 'href': 'http://news.baidu.com', 'name': 'tj_trnews'}
    #还可以利用get方法,传入属性的名称,二者是等价的
    print(bs.a['class']) # 等价 bs.a.get('class') 
    # 可以对这些属性和内容等等进行修改
    bs.a['class'] = "newClass"
    print(bs.a) 
    # 还可以对这个属性进行删除
    del bs.a['class'] 
    print(bs.a)
    
    3.2.2 NavigableString

    既然我们已经得到了标签的内容,那么问题来了,我们要想获取标签内部的文字怎么办呢?很简单,用 .string 即可,例如

    from bs4 import BeautifulSoup 
    file = open('./baidu.html', 'rb') 
    content = file.read() 
    bs = BeautifulSoup(content,"html.parser") 
    #获取title标签中的字符串
    print(bs.title.string) #百度一下,你就知道 
    # 类型
    print(type(bs.title.string)) 
    #
    
    3.3.3 BeautifulSoup

    BeautifulSoup对象表示的是一个文档的内容。大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性,例如:

    from bs4 import BeautifulSoup 
    file = open('./baidu.html', 'rb') 
    content = file.read() 
    bs = BeautifulSoup(content,"html.parser") 
    #获取整个文档
    print(bs)
    print(type(bs)) #
    
    3.3.4 Comment

    Comment 对象是一个特殊类型的 NavigableString 对象,其输出的内容不包括注释符号。

    from bs4 import BeautifulSoup 
    file = open('./baidu.html', 'rb') 
    content = file.read() 
    bs = BeautifulSoup(content,"html.parser") 
    print(bs.a)
    # a标签如下:
    # 
    print(bs.a.string) # 新闻  #不会输出上面a标签中的注释符号
    print(type(bs.a.string)) 
    # 
    
    3.3 遍历文档数

    .contents:获取Tag的所有子节点,返回一个list

    from bs4 import BeautifulSoup 
    file = open('./baidu.html', 'rb') 
    content = file.read() 
    bs = BeautifulSoup(content,"html.parser") 
    print(bs.head.contents)  #获取head下面的所有直接子节点,返回列表 
    print(bs.head.contents[1 #用列表索引来获取它的某一个元素
    

    .children:获取Tag的所有子节点,返回一个生成器

    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是否包含属性
    3.4 文档的搜索find_all()
    name参数
    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)
    #正则表达式过滤:如果传入的是正则表达式,那么BeautifulSoup4会通过search()来匹配内容
    import re
    t_list = bs.find_all(re.compile("a"))
    print(t_list)
    
    函数参数
    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")#搜索包含name的标签
    t_list = bs.find_all(name_is_exists)
    for item in t_list: #打印列表内容
         print(item)
    
    keyword参数
    from bs4 import BeautifulSoup 
    file = open('./baidu.html', 'rb') 
    content = file.read() 
    bs = BeautifulSoup(content,"html.parser") 
    #搜索id=head的内容
    t_list = bs.find_all(id="head")
    for item in t_list:
        print(item)
        
    #搜索class=manav的内容    
    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参数
    from bs4 import BeautifulSoup 
    file = open('./baidu.html', 'rb') 
    content = file.read() 
    bs = BeautifulSoup(content,"html.parser") 
    import re
    #t_list = bs.find_all(text="hao123")
    #t_list = bs.find_all(text=["hao123","贴吧","地图"])
    t_list = bs.find_all(text=re.compile("\d"))#查找包含数字的文本
    for item in t_list: 
         print(item)
    
    limit 参数
    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选择器
    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')
    #通过id(#表示id)来查找
    t_list = bs.select('#u1')
    #通过属性来查找 查找a标签中class=bri的内容
    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()) #拿到t_list中的文本
    

    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次ab(2}c表示abbc
    扩展前一个字符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等
      import re
      pat=re.compile("A")
      m=pat.search("CBA") #等价于 re.search(A,CBA)
      print(m)#  表示匹配到了
      m=pat.search("CBD")
      print(m)  #None 表示没匹配到
      
      4.2.2 search()
      • 在字符串中寻找模式
      • 格式:re.search(pattern, string[, flags=0])
      • re.search函数会在字符串内查找模式匹配,只要找到第一个匹配然后返回,如果字符串没有匹配,则返回None。
          import re
          m = re.search("asd" , "ASDasd" )
          print(m)#   #匹配到了,返回MatchObject(True)
          
          
          m = re.search("asd" , "ASDASD" )
          print(m)                                 #没有匹配到,返回None(False)
        
        4.2.3 match()
        • 在字符串开始处匹配模式
        • 格式:re.match(pattern, string[, flags=0])
          import re
          pat=re.compile( "a" ) 
          print(pat.match( "Aasd" )) #输出None
          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])
                import re
                #前面字符串是规则(正则表达式),后面字符串是被校验的字符串
                print(re.findall("a","ASDaDFGAa"))      
                #[a,a] 	#列表形式返回匹配到的字符串
                
                p = re.compile(r'\d+')
                print(p.findall('o1n2m3k4'))
                #执行结果如下:
                #['1', '2', '3', '4']
                
                print(re.findall("[A-Z]","ASDaDFGAa"))
                #[ A , S , D , D , F , G , A ] 
                
                print(re.findall("[A-Z]+","ASDaDFGAa"))
                #[ ASD , DFGA ]
                
                pat = re.compile("[A-Za-z]")
                print(pat.findall("ASDcDFGAa"))
                #[ A , S , D , c , D , F , G , A , a ]
              
              4.2.4 re. split()
              • 按照能够匹配的子串将string分割后返回列表。
              • 可以使用re.split来分割字符串,如:re.split(r’\s+’, text);将字符串按空格分割成一个单词列表。
              • 格式:re.split(pattern, string[, maxsplit])
                • maxsplit: 用于指定最大分割次数,不指定将全部分割。
                print(re.split('\d+','one1two2three3four4five5'))
                # 执行结果如下:
                # ['one', 'two', 'three', 'four', 'five', '']
                
                4.2.5 finditer()
                • 搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。

                • 找到 RE 匹配的所有子串,并把它们作为一个迭代器返回。

                • 格式:re.finditer(pattern, string[, flags=0])

                  import re
                  iter = re.finditer(r'\d+','12 drumm44ers drumming, 11 ... 10 ...')
                  for i in iter:
                      print(i)
                      print(i.group())
                      print(i.span())
                  '''
                  # 执行结果如下:
                  
                  12
                  (0, 2)
                  
                  44
                  (8, 10)
                  
                  11
                  (24, 26)
                  
                  10
                  (31, 33)
                  '''
                  
                  4.2.6 sub()
                  • 格式:re.sub(pattern, repl, string, count)

                  • 用repl替换 pattern匹配项

                    import re
                    print(re.sub(a,A,abcasd)) #找到a用A替换,后面见和group的配合使用
                    #AbcAsd  #第四个参数指替换个数。默认为0,表示每个匹配项都替换。
                    text = "JGood is a handsome boy, he is cool, clever, and so on..."
                    print(re.sub(r'\s+', '-', text)) #\s:空格
                    #JGood-is-a-handsome-boy,-he-is-cool,-clever,-and-so-on...
                    
                    4.3 模式修正符

                    所谓模式修正符,即可以在不改变正则表达式的情况下,通过模式修正符改变正则表达式的含义,从而实现一些匹配结果的调整等功能。

                    修饰符描述
                    re.I使匹配对大小写不敏感
                    re.L做本地化识别(locale-aware)匹配
                    re.M多行匹配,影响 ^ 和 $
                    re.S使 . 匹配包括换行在内的所有字符
                    re.U根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
                    re.X该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。
                    import re
                    string = "Python"
                    pat = "pyt"
                    rst = re.search(pat,string,re.I) # 第三个参数
                    print(rst)#
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]