Python语言程序设计课件 第12章 网络爬虫专题_第1页
Python语言程序设计课件 第12章 网络爬虫专题_第2页
Python语言程序设计课件 第12章 网络爬虫专题_第3页
Python语言程序设计课件 第12章 网络爬虫专题_第4页
Python语言程序设计课件 第12章 网络爬虫专题_第5页
已阅读5页,还剩63页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

专题四

网络爬虫引言网络爬虫作为一种自动化程序,能够模拟人类的浏览行为,抓取互联网上的信息,并将其转化为可供分析和应用的数据。这不仅为数据分析、市场调研和学术研究提供了便利,也为企业决策和产品开发带来了新的机遇。网络爬虫涉及网络协议、数据解析和存储等多个技术领域,其复杂性和灵活性使得掌握网络爬虫的技能尤为重要。在本节中,我们将深入探讨网络爬虫的基本原理,学习使用Python编写爬虫程序,并通过实际案例,体验从网络获取数据的全过程。CATALOGUE目录01网络爬虫概述02Requests库03BeautifulSoup库04Selenium库05反爬机制网络爬虫模拟真实用户在浏览器中访问网页的行为,可以代替用户发出请求并获取网页返回的信息12.1网络爬虫概述1.定义通用网络爬虫聚焦网络爬虫增量式网络爬虫深层网络爬虫2.分类12.1网络爬虫概述3.安全许可法律框架网站的使用条款道德与行业规范robots.txt协议12.1网络爬虫概述Robots协议Robots协议(或称为RobotsExclusionProtocol)是一种用于指导网络爬虫(或称为网络机器人)访问网站内容的标准。它通过在网站根目录下放置一个名为robots.txt的文件,来告知爬虫哪些页面可以抓取,哪些页面不可以。这一协议有助于保护网站的隐私和资源,同时避免过度抓取对服务器的负担。12.1网络爬虫概述Robots协议User-agent:[爬虫名称]Disallow:[不允许抓取的路径]Allow:[允许抓取的路径]Sitemap:[网站地图URL]12.1网络爬虫概述Robots协议以豆瓣的robots文件为例:User-agent:*Disallow:/subject_searchDisallow:/amazon_searchDisallow:/searchDisallow:/group/searchDisallow:/event/search...Allow:/ads.txtSitemap:/sitemap_index.xmlSitemap:/sitemap_updated_index.xml#Crawl-delay:5User-agent:WandoujiaSpiderDisallow:/User-agent:Mediapartners-GoogleDisallow:/subject_search...Disallow:/j/12.1网络爬虫概述4.爬虫库介绍库

名作

用应用场景Requests简化HTTP请求,支持GET和POST等请求方式爬取静态网页,发送API请求,处理网络交互BeautifulSoup解析HTML和XML文档,提供灵活的查找和导航功能从HTML中提取数据,如标题、链接、段落等Scrapy一个强大的爬虫框架,支持多线程和分布式爬取大规模数据抓取,构建爬虫项目,进行数据采集和处理lxml高效的HTML和XML解析库,支持XPath和CSS选择器解析复杂的HTML文档,进行高性能数据提取Selenium自动化浏览器操作,支持JavaScript渲染的网页抓取爬取需要登录或动态加载内容的网站,进行自动化测试PyQuery使用jQuery语法解析HTML文档,简化数据提取流程快速提取HTML元素,支持类似jQuery的选择器语法pandas数据处理和分析库,可用于处理抓取到的数据数据清洗和分析,处理CSV、Excel等格式的数据12.2Requests库1.简介Requests库是Python中一个简单易用的HTTP库,广泛应用于发送HTTP请求和处理响应。2.用法pipinstallrequests12.2Requests库2.用法方

法介

绍应用场景get(url,params=None,headers=None,cookies=None,verify=True,proxies=None,timeout=None)发送GET请求,获取指定URL的内容。支持查询参数、请求头和Cookie设置。用于爬取静态网页、获取API数据。post(url,data=None,json=None,headers=None,cookies=None,verify=True,proxies=None,timeout=None)发送POST请求,向指定URL提交数据。支持表单数据和JSON格式的数据。提交表单、上传数据、与API交互。put(url,data=None,json=None,headers=None,cookies=None,verify=True,proxies=None,timeout=None)发送PUT请求,更新指定URL的资源。支持表单数据和JSON格式的数据。更新服务器上的资源,如用户信息等。delete(url,headers=None,cookies=None,verify=True,proxies=None,timeout=None)发送DELETE请求,删除指定URL的资源。用于删除服务器上的资源,如用户账户等。head(url,headers=None,cookies=None,verify=True,proxies=None,timeout=None)发送HEAD请求,仅获取响应头,不获取响应体。检查URL的有效性、获取页面元信息而不下载内容。options(url,headers=None,cookies=None,verify=True,proxies=None,timeout=None)发送OPTIONS请求,获取服务器支持的请求方法和其他选项。了解API的支持情况,或用于跨域请求的预检。session()创建会话对象,能够跨请求保持某些参数,如Cookie和请求头。当需要多次请求同一网站时,保持登录状态和其他参数。12.2Requests库2.用法(1)get()该函数会根据传入的URL构建一个请求,并将该请求发送给服务器。请求头描

述User-Agent标识客户端的类型、操作系统和浏览器版本等信息。用于模拟浏览器请求。Accept告诉服务器,客户端可处理的内容类型(如

text/html、application/json等)。Content-Type指定请求体中数据的类型,通常用于

POST请求(如application/x-www-form-urlencoded、application/json等)。Authorization用于传递身份验证信息,常见于

API请求,需要提供BearerToken或BasicAuth。Cookie用于向服务器发送客户端存储的

Cookie信息,帮助保持会话状态。Referer指定请求来源的

URL,通常用于跟踪用户来源和分析流量。Accept-Language指定客户端能够理解的语言,用于请求本地化内容。12.2Requests库2.用法(1)get()例如:importrequestsurl='/search'params={'q':'requests','page':1}headers={'User-Agent':'Mozilla/5.0'}response=requests.get(url,params=params,headers=headers)print(response.text)#输出网页内容12.2Requests库2.用法(2)post()POST请求通过调用post()函数发送,用于向服务器提交数据。importrequestsurl='/api'data={'username':'user','password':'pass'}headers={'User-Agent':'Mozilla/5.0'}response=requests.post(url,data=data,headers=headers)print(response.status_code)#输出响应状态码print(response.json())#输出响应的JSON数据12.2Requests库2.用法(3)responseResponse类的对象封装了服务器返回的响应信息text描述:该属性返回响应的内容,类型为字符串。内容是以Unicode格式解码的,适用于获取文本类型的响应数据。用法示例:response=requests.get('')print(response.text)#输出网页的HTML内容12.2Requests库2.用法(3)responsecontent描述:该属性返回响应的原始内容,类型为字节序列(bytes)。适用于获取二进制数据,如图像或PDF文件。用法示例:response=requests.get('/image.jpg')withopen('image.jpg','wb')asf:f.write(response.content)#保存图像文件12.2Requests库2.用法(3)responsejson()描述:该方法将响应内容解析为JSON格式,并返回Python对象。如果响应的内容不是有效的JSON格式,会引发ValueError异常。用法示例:response=requests.get('/data')data=response.json()#将响应内容解析为字典print(data)12.2Requests库2.用法(3)responseheaders描述:该属性返回响应头信息,类型为字典。通过该属性可以获取与响应相关的所有头部信息,例如内容类型、服务器信息等。用法示例:response=requests.get('')print(response.headers['Content-Type'])#输出响应的内容类型12.2Requests库2.用法(3)responsecookies描述:该属性返回一个RequestsCookieJar对象,包含服务器返回的Cookies。可以通过该属性获取、设置或删除Cookies。用法示例:response=requests.get('')print(response.cookies)#输出响应中的Cookies12.2Requests库2.用法(3)responseurl描述:该属性返回请求的URL,类型为字符串。可以用来验证最终访问的URL,尤其是在发生重定向时。用法示例:response=requests.get('/somepage')print(response.url)#输出请求的URL12.2Requests库2.用法(3)responseencoding描述:该属性用于设置或获取响应内容的编码方式,默认为None。可以通过该属性手动指定响应的字符编码,以确保text属性正确解码内容。用法示例:response=requests.get('')response.encoding='utf-8'#设置响应的编码方式print(response.text)#输出解码后的内容12.2Requests库实例12-1:爬取网页内容以豆瓣电影的首页为例,爬取首页所有内容并保存。12.2Requests库实例12-1:爬取网页内容参考代码12345678910111213141516importrequests#定义目标URLurl='/'#设置请求头,模拟浏览器访问headers={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/91.0.4472.124Safari/537.36'}#发送GET请求response=requests.get(url,headers=headers)#检查响应状态码ifresponse.status_code==200:#将网页内容保存到txt文件

withopen('douban_movies_content.txt','w',encoding='utf-8')asf:f.write(response.text)print("网页内容已保存到douban_movies_content.txt")else:print(f"请求失败,状态码:{response.status_code}")12.2Requests库实例12-1:爬取网页内容爬取后的结果12.3BeautifulSoup库介绍用于解析和处理HTML和XML文档的库。它提供了简单而灵活的API,能够轻松提取网页中的数据。通过BeautifulSoup,开发者可以快速找到感兴趣的元素,并进行相应的操作,如提取文本、修改标签、查找特定的元素等。结合Requests库,BeautifulSoup通常用于获取网页内容后,对HTML结构进行解析,提取文本、链接、图像等信息12.3BeautifulSoup库12.3.1HTML结构介绍HTML(超文本标记语言)是构建网页的基础,使用标签来定义网页的内容和结构。理解HTML的结构有助于有效地提取和操作网页数据。12.3BeautifulSoup库12.3.1HTML结构介绍1234567891011121314151617<!DOCTYPEhtml><htmllang="zh"><head><metacharset="UTF-8"><title>示例网页</title></head><body><h1>欢迎来到示例网页</h1><p>这是第一段内容。</p><p>这是第二段内容。</p><divclass="container"><h2>子标题</h2><pclass="text">这是一个带类名的段落。</p><ahref="">链接到示例网站</a></div></body></html>12.3BeautifulSoup库12.3.2BeautifulSoup的使用pipinstallbeautifulsoup4lxml使用BeautifulSoup时,首先需要创建一个BeautifulSoup对象,它可以接收HTML或XML文档并解析它。其主要参数如下:soup=BeautifulSoup(markup,features="html.parser",builder=None)12.3BeautifulSoup库12.3.2BeautifulSoup的使用基本用法如下12345frombs4importBeautifulSoup#将12.3.1中的HTML代码记为html_doc#创建BeautifulSoup对象soup=BeautifulSoup(html_doc,'lxml')print(soup)12.3BeautifulSoup库12.3.2BeautifulSoup的使用在上述小节中,将爬取的HTML代码存储为html_doc.txt,并进行读取。1234567frombs4importBeautifulSoup#从1.txt中读取HTML代码

withopen('html_doc.txt,'r',encoding='utf-8')asfile:html_doc=file.read()#读取文件内容#创建BeautifulSoup对象soup=BeautifulSoup(html_doc,'lxml')print(soup)12.3BeautifulSoup库12.3.2BeautifulSoup的使用以下是BeautifulSoup中一些常用的方法及其说明。方

法说

明find(tag,attrs)查找第一个符合条件的标签,tag是标签名称,attrs是属性字典。find_all(tag,attrs)查找所有符合条件的标签,返回列表。get_text()获取标签内的所有文本内容,自动处理标签嵌套。select(selector)使用CSS选择器语法查找标签,返回符合条件的标签列表。parent获取当前标签的父标签。children获取当前标签的所有子标签,返回一个生成器。next_sibling获取当前标签的下一个兄弟标签。previous_sibling获取当前标签的上一个兄弟标签。12.3BeautifulSoup库12.3.2BeautifulSoup的使用1.find方法find方法用于查找符合条件的第一个标签。如果找到了符合条件的标签,它将返回该标签的BeautifulSoup对象;如果未找到,则返回None。基本语法:soup.find(name,attrs,recursive,string,limit,**kwargs)12.3BeautifulSoup库12.3.2BeautifulSoup的使用1.find方法例如:利用find函数寻找标签div下面的:<pclass="text">这是一个带类名的段落。</p>文字内容,可使用以下语句:1234#查找具有class为"container"的div元素div_tag=soup.find('div',class_='container')#查找具有class为"text"的p元素p_tag=soup.find('p',class_='text')结果为:这是一个带类名的段落。12.3BeautifulSoup库12.3.2BeautifulSoup的使用1.find更多方法123456789101112131415161718#查找第一个<h1>标签h1_tag=soup.find('h1')print(h1_tag.text)

#输出:欢迎来到示例网页#查找包含特定文本的<p>标签p_tag=soup.find('p',string="这是一个带类名的段落。")print(p_tag)#输出:<pclass="text">这是一个带类名的段落。</p>#查找class="container"的<div>标签中的第一个<p>标签p_tag=soup.find('div',class_="container").find('p')print(p_tag.text)#输出:这是一个带类名的段落。#查找具有特定属性title="Example"的<a>标签a_tag=soup.find('a',attrs={'title':'Example'})print(a_tag['href'])#输出:#使用正则表达式查找href属性中包含"example"的<a>标签a_tag=soup.find('a',href=pile(r"example"))print(a_tag['href'])#输出:/page1#查找具有多个属性的<a>标签a_tag=soup.find('a',href="")print(a_tag.text)#输出:链接到示例网站12.3BeautifulSoup库12.3.2BeautifulSoup的使用2.find_all方法find_all方法用于查找所有符合条件的标签,返回一个列表。与find()只返回第一个符合条件的标签不同,find_all()可以提取多个元素。其基本语法类似于find(),只是返回结果的形式不同。基本语法:soup.find_all(name,attrs,recursive,string,limit,**kwargs)12.3BeautifulSoup库12.3.2BeautifulSoup的使用2.find_all方法查找所有的`<p>`标签>>>p_tags=soup.find_all('p')>>>forpinp_tags:>>>print(p.text)查找所有带有`class`属性为"text"的`<p>`标签>>>p_tags=soup.find_all('p',class_='text')>>>forpinp_tags:>>>print(p.text)运行结果输出:这是第一段内容。这是第二段内容。这是一个带类名的段落。运行结果输出:这是一个带类名的段落。12.3BeautifulSoup库12.3.2BeautifulSoup的使用2.find_all方法查找所有的`<a>`标签,并提取`href`属性>>>a_tags=soup.find_all('a')>>>foraina_tags:>>>print(a['href'])通过`attrs`参数查找带有特定属性的标签>>>div_tags=soup.find_all('div',attrs={'class':'container'})>>>fordivindiv_tags:>>>print(div.h2.text)运行结果输出:运行结果输出:子标题12.3BeautifulSoup库12.3.2BeautifulSoup的使用2.find_all方法通过`string`参数查找包含特定文本的标签>>>p_tags=soup.find_all('p',string="这是第一段内容。")>>>forpinp_tags:>>>print(p.text)使用正则表达式查找`<a>`标签中的`href`属性包含"example"的链接>>>importre>>>a_tags=soup.find_all('a',href=pile(r"example"))>>>foraina_tags:>>>print(a['href'])运行结果输出:这是第一段内容。运行结果输出:12.3BeautifulSoup库12.3.2BeautifulSoup的使用2.find_all方法限制返回的标签数量>>>p_tags=soup.find_all('p',limit=2)>>>forpinp_tags:>>>print(p.text)查找嵌套在特定标签中的元素>>>div_tag=soup.find('div',class_='container')>>>p_tags=div_tag.find_all('p')>>>forpinp_tags:>>>print(p.text)运行结果输出:这是第一段内容。这是第二段内容运行结果输出:这是一个带类名的段落。12.3BeautifulSoup库12.3.2BeautifulSoup的使用2.find_all方法查找具有多个条件的元素>>>a_tags=soup.find_all('a',href="",class_="link")此例可以通过多个条件查找符合所有条件的元素,这在查找页面元素时非常有用。12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息以豆瓣电影《觉醒时代》(/subject/30228394/comments?status=P)为例,在12-1.py的基础上,使用BeautifulSoup库对短评页面进行解析,将评分和评论内容,保存到movie、.csv文件中。12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息(1)构造headers:12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息点击“检查”按钮headers={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/Safari/537.36Edg/'}12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息(2)使用requests库获取网页内容1234567891011121314importrequests#定义目标URLdefget_url(url):#设置请求头,模拟浏览器访问

headers={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/Safari/537.36Edg/'}

response=requests.get(url,headers=headers)

ifresponse.status_code==200:

returnresponse.textelse:print(f"请求失败,状态码:{response.status_code}")url='/subject/30228394/comments?status=P'print(get_url(url))12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息(3)使用bs4对网页内容进行解析12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息仔细查看相关代码:可以看到每一个短评是以divclass=“comment-item”进行标识的。12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息div_tags=soup.find_all('div',attrs={'class':'comment-item'})comment_cont=div.find('p',attrs={'class':'comment-content'})comment_text=comment_cont.find('span',class_="short").text同样的,我们可以获取评分:#使用正则表达式匹配包含'allstar'的class属性rating_span=comment_info.find('span',class_=pile(r'allstar\d+'))ifrating_span:rating_class=rating_span['class'][0]#获取class名字rating=re.search(r'\d+',rating_class).group()#使用正则提取数字部分rating_value=int(rating)//10#提取的数值是两位,除以10得到星级12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息因此通过bs4库和requests库进行页面爬取解析具体代码如下:importrequestsfrombs4importBeautifulSoupimportreimportcsvdefget_url(url):headers={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/Safari/537.36Edg/'}response=requests.get(url,headers=headers)ifresponse.status_code==200:returnresponse.textelse:print(f"请求失败,状态码:{response.status_code}")returnNone12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息因此通过bs4库和requests库进行页面爬取解析具体代码如下:defparse(html_doc,csv_writer):soup=BeautifulSoup(html_doc,'lxml')div_tags=soup.find_all('div',attrs={'class':'comment-item'})fordivindiv_tags:comment_info=div.find('span',class_="comment-info")rating_span=comment_info.find('span',class_=pile(r'allstar\d+'))ifrating_span:rating_class=rating_span['class'][0]#获取class名字rating=re.search(r'\d+',rating_class).group()#使用正则提取数字部分rating_value=int(rating)//10#提取的数值是两位,除以10得到星级comment_cont=div.find('p',attrs={'class':'comment-content'})comment_text=comment_cont.find('span',class_="short").textifcomment_contelse'无评论'print(f"评分:{rating_value},评论:{comment_text}")csv_writer.writerow([rating_value,comment_text])break12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息因此通过bs4库和requests库进行页面爬取解析具体代码如下:if__name__=="__main__":url='/subject/30228394/'html_doc=get_url(url)ifhtml_doc:withopen('movie.csv',mode='w',newline='',encoding='utf-8-sig')asfile:csv_writer=csv.writer(file)csv_writer.writerow(['Rating','Comment'])parse(html_doc,csv_writer)print("数据已保存到movie.csv文件中")12.3BeautifulSoup库12.3.3综合实例12-1:解析《觉醒时代》影评信息最终结果:12.3BeautifulSoup库大家也可以考虑如下问题:如何完善代码使之可以爬取多页,提示:可解析“后页”,或者点击后页,查看网址构造。除此之外,还可以结合前面的数据分析等专题对爬取的数据进一步处理,在本章节就不再阐述。12.4Selenium库简介Selenium是一个强大的自动化测试工具,可以用来模拟用户在浏览器上的操作,适用于动态网页的爬取。在网络爬虫领域,Selenium的主要优势在于能够处理JavaScript渲染的内容,这是传统静态网页爬取工具(如requests)所无法完成的任务。12.4Selenium库Selenium基本用法Selenium的基本用法要使用Selenium进行网页爬取,通常需要以下几步:(1)安装Selenium和对应的浏览器驱动。(2)启动浏览器,访问目标网页。(3)模拟用户行为,如点击、滚动、输入等操作。(4)获取页面数据,并使用BeautifulSoup或其他解析工具提取信息。12.4Selenium库Selenium基本用法首先,需要安装selenium库:pipinstallseleniumSelenium支持不同的浏览器(如Chrome、Firefox)。以Chrome为例,我们可以通过help->Aboutchrome查看具体版本:12.4Selenium库Selenium基本用法下载Chrome浏览器的驱动chromedriver。你可以根据自己的Chrome版本从

官方页面(https://googlechromelabs.github.io/chrome-for-testing/#stable))下载相应版本的chromedriver。12.4Selenium库Selenium基本用法并将其路径加入到系统环境变量中。(右键点击“此电脑”>“属性”>“高级系统设置”>“环境变量”。在“系统变量”中找到Path,选择它并点击“编辑”。点击“新建”,添加chromedriver.exe所在的目录(例如C:\ProgramFiles)。保存更改。12.4Selenium库Selenium基本用法在Python代码中使用Selenium时,确保指定chromedriver的路径(如果未添加到PATH):>>>fromseleniumimportwebdriverdriver=webdriver.Chrome(executable_path='C:\\path\\to\\chromedriver.exe')测试成功如下:12.4Selenium库Selenium基本用法以下是使用Selenium启动Chrome浏览器并访问某个网页的简单示例:12345678910111213fromseleniumimportwebdriverfrommon.byimportByfromselenium.webdriver.chrome.serviceimportServicefrommon.keysimportKeysimporttime#设置chromedriver的路径chrome_driver_path='/path/to/chromedriver'#修改为chromedriver的实际路径service=Service(chrome_driver_path)driver=webdriver.Chrome(service=service)driver.get("")time.sleep(3)print(driver.title)driver.quit()12.4Selenium库Selenium基本用法1.定位网页元素Selenium提供了多种方式来定位网页元素,包括按ID、按类名、按标签名等。常用的定位方法包括:find_element(By.ID,'id_name'):按元素的ID查找。find_element(By.CLASS_NAME,'class_name'):按元素的类名查找。find_element(By.XPATH,'xpath'):按元素的XPath查找。12.4Selenium库Selenium基本用法2.模拟用户操作Selenium能够模拟多种用户操作,例如:输入文字:element.send_keys('text')点击按钮:element.click()清除文本框内容:element.clear()模拟键盘操作:如回车element.send_keys(Keys.RETURN)12.4Selenium库Selenium

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论