数据采集与处理 课件全套 刘珍 项目1-7 Python基础实战- Scrapy爬虫框架实战_第1页
数据采集与处理 课件全套 刘珍 项目1-7 Python基础实战- Scrapy爬虫框架实战_第2页
数据采集与处理 课件全套 刘珍 项目1-7 Python基础实战- Scrapy爬虫框架实战_第3页
数据采集与处理 课件全套 刘珍 项目1-7 Python基础实战- Scrapy爬虫框架实战_第4页
数据采集与处理 课件全套 刘珍 项目1-7 Python基础实战- Scrapy爬虫框架实战_第5页
已阅读5页,还剩352页未读 继续免费阅读

下载本文档

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

文档简介

项目1

Python基础实战数据采集与处理掌握了解掌握熟悉掌握

Python的语法、数据结构、运算符、表达式、流程控制12掌握

列表、字典、函数、模块和包、类与对象、文件操作、异常处理等熟悉

Python3及PyCharm集成开发环境的安装34了解

Python的特点、版本、应用领域、语法规范等学习目标目录任务1 Python开发环境搭建任务2 从HTML文档提取特定字符串任务3 用列表、字典组织数据任务4 基于正则表达式提取图片链接文本任务5 从JSON文件中加载数据Windows系统中安装PythonLinux系统中安装Python安装PyCharm集成开发环境任务1 Python开发环境搭建Python语言因简洁的语法、出色的开发效率以及强大的功能,迅速在多个领域占据一席之地,成为最符合人类期待的编程语言。Python简介Python的特点简单易学免费开源可移植性面向对象丰富的库Python语言之所以能够迅速发展,受到程序员的青睐,与它具有的特点密不可分。目前市场上Python

2和Python

3两个版本并行,2021开始官方不在维护Python2Python的版本Python作为一门功能强大且简单易学的编程语言得到了广泛应用,它主要应用在以下领域Web开发科学计算与数据分析自动化运维网络爬虫游戏开发人工智能Python的应用领域(1)访问Python官网,选择Downloads→Windows:1.1

Windows系统中安装Python(2)运行下载的对应版本的Python安装程序(如:python-3.10.7-amd64.exe),按步骤执行安装:1.1

Windows系统中安装Python(3)安装完成后,打开Windows系统的命令提示符窗口测试是否安装成功:1.1

Windows系统中安装Python(1)通过wget命令下载minicoda安装脚本:1.2

Linux系统中安装Python(2)运行安装脚本,根据提示进行对应操作:sh

Miniconda3-latest-Linux-x86_64.sh(3)通过source命令执行activate脚本,进入虚拟环境:source/usr/local/miniconda3/bin/activate(4)窗口输入python,进入python运行环境后,输入命令:python-V1.2

Linux系统中安装Python(1)访问jetbrains官网,下载PyCharm安装包:1.3

安装PyCharm集成开发环境(2)运行下载好的PyCharm安装程序,按步骤执行安装:1.3安装PyCharm集成开发环境(3)安装完成后的启动界面:1.3安装PyCharm集成开发环境任务2

从HTML文档提取特定字符串数值类型与变量字符串运算符流程控制2.1

数值类型与变量(1)

数值类型包括整型(int)、浮点型(float)、复数类型(complex)

、布尔类型(bool)整型浮点型复数类型布尔型123 123.45 123+4j True注1:

布尔类型只有两个取值:True和False注2:Python中的任何对象都可以转换为布尔类型,若要进行转换,符合以下条件的数据都会被转换为False:None、任何为0的数值类型、任何空对象

(如:空序列“”

、()、[]

,空字典{}等)2.1

数值类型与变量(2)

变量l

Python程序运行的过程中随时可能产生一些临时数据,程序会将这些数据保存在内存单元中,并使用不同的标识符来标识各个内存单元。这些具有不同标识、存储临时数据的内存单元称为变量,标识内存单元的符号为变量名(或称为标识符),内存单元中存储的数据就是变量的值。l

变量名由字母、数字和下划线组成,且不能以数字开头;区分大小写l

变量要先声明后使用2.1

数值类型与变量说明:图中的print()为Python的输出函数,作用是输出一段文本;type()为Python中的函数,作用是返回给定对象的类型Python采用空格和缩进来区分语句的层次关系,同一层次的代码行左侧需严格对齐,否则代码将不能通过编译上图中第13行和15行中以符号“#”开始的那段文本是Python中的注释,通常是对某些代码块的解释说明文本,Python在运行时会自动忽略这部分内容l

示例2.2

字符串(1)字符串是一种用来表示文本的数据类型,它是由符号或者数值组成的一个连续序列。l

Python3的字符串是由Unicode字符组成的不可变序列l

Python支持使用一对单引号、一对双引号或三对引号来定义字符串2.2

字符串(2)Python通过索引或切片的方式来访问字符串中的部分内容正向索引反向索引#

用于截取字符串中的某个字符#

用于从字符串中截取子字符串注:采用切片的方式来截取字符串的子串时,切片选取的区间为左闭右开型,截取的子串包含起始下标处对应的字符,但不包含结束下标处对应的字符,如果省略了起始下标,表示从字符串的开始位置处截取,如果省略了结束下标,表示截取到字符串的最后一个字符;步长值用于指定截取时的间隔区间,可以为正整数或负整数,如果省略了步长值,则默认步长值为1。索引方式:l 字符串变量[索引下标]l 字符串变量[起始下标:结束下标:步长值]2.2

字符串(3)字符串的常用操作:l 字符串拼接:使用“+”运算符实现l 字符串替换:str.replace(old,new,count=None)

l 字符串分割:str.split(sep=None,maxsplit=-1)

l 字符串两侧空格去除:str.strip(chars=None)、lstrip()、

rstrip()

l 字符串格式化输出:占位符方式、format()方法、f-strings方式2.3

运算符l 算术运算符:+、-、*、/、//

(整除)

、%

(取余)

、**

(幂)l 比较运算符:

==、!=、>、<、>=、<=l 赋值运算符:=,“=”还可与算术运算符组合成复合赋值运算符l 逻辑运算符:or(或)、

and(与)、not

(非)l 位运算符:<<

(按位左移)、>>

(按位右移)、&

(按位与)、I

(按位或)、^(按位异或)、~(按位取反)2.3

运算符l 运算符的优先级:Python支持使用多个不同的运算符连接简单表达式,实现相对复杂的功能。此时需注意运算符之间的优先级,或者使用圆括号“()”来改变表达式的执行顺序从上往下,优先级增高2.3

运算符2.3

运算符(1)if语句:

用于实现分支结构2.4

流程控制if单分支双分支if-else多分支if-elif-else(2)match语句:

Python3.10中引入,类似于C语言中的switch语句,

但功能更强大2.4

流程控制(3)

for语句:

Python循环语句的一种2.4

流程控制2.4

流程控制(4)while语句:

可配合使用break,continue语句实现对循环的灵活控制任务3

用列表、字典等组织数据列表(list)元组(tuple)字典(dict)集合(set)函数3.1

列表(list)列表是Python中有序序列之一,它可以存储任意类型的元素。(1)列表的创建:l

使用中括号“[]”创建: list_1=[1,2,

3]l

通过函数list()创建: list_2

=

list(‘数据采集与处理’)3.1

列表(list)l

使用索引方式访问: list_2[1]l

使用切片方式访问: list_2[1:6:2]#

取出第二个元素:据#

得到三个元素的子列表:

['据',

'集',

'处'](2)列表元素的访问:list_2

=

list(‘数据采集与处理’)3.1

列表(list)(3)列表的遍历:list_2

=

list(‘数据采集与处理’)l

使用for循环遍历:l

列表推导式:l

Range函数:3.1

列表(list)3.2

元组(tuple)(1)元组的创建:l

使用小括号“()”创建:tup_1=(), tup_2=(1,),tup_3=

(1,2,3)l

通过函数tuple()创建: tup_4

=

tuple(), tup_5=tuple(“Python”)注1:当使用小括号“()”创建元组时,如果元组中只包含一个元素,那么要在该元素的后面添加逗号,以保证Python解释器能够识别其为元组注2:元组中的元素是不允许修改的3.2

元组(tuple)(2)元组元素的访问及遍历与列表类似:3.2

元组(tuple)3.3

字典(dict)字典是Python中的映射类型(每个元素都是一个键值对,key-value)(1)字典的创建:l

使用大括号“{}”创建: dict_1

=

{id:1001,name:“张三”}l

通过函数dict()创建: dict_2

=

dict(id=1002,name=“李四”)注1:一个字典中的键(key)须唯一,不能重复,可以使用字符或数值等类型注2:一个字典中的值(value)可以重复,可以是任意类型注3:字典与序列类型不同,字典中的元素是无序的,因此不能象列表或元组那样通过索引来读取元素或进行切片操作,字典只能通过键来获取对应的值3.3

字典(dict)(1)Python中的集合分为可变集合与不可变集合:3.4

集合(set)由set()函数创建,集合中的元素可以动态地增加或删除。可变集合由frozenset()函数创建,集合中的元素不可改变。不可变集合注1:集合与字典类似,也是无序的数据类型,因此也不能象列表或元组那样通过索引来读取元素或进行切片操作注2:集合与数学上的集合概念类似,集合所包含的元素是唯一的、不可重复3.4

集合(set)3.5

函数(1)函数指封装起来的、完成某个具体任务并能够被复用的一段代码。Python安装包、标准库中自带的函数统称为内置函数,用户自己编写的函数称为自定义函数(2)Python中使用关键字

def定义函数:(3)函数的调用:函数名([参数列表])3.5

函数(3)函数的参数:l 位置参数:传参时按定义时的顺序对应传递l 关键字参数:以“参数名=值”的方式传递l 默认值参数:函数定义时指定了默认值,如果调用时不给它传值,则用定义时指定的默认值l 不定长参数:定义时参数名前加上“*”或“**”,表示可接收任意数量的位置参数或关键字参数3.5

函数(4)变量的作用域:l 局部变量:在函数内部定义的变量,仅在函数内部可见l

全局变量:在函数外部定义的变量,在整个脚本内可见。函数内可以引用全局变量,但是如果要在函数内修改全局变量的值,需先在函数内使用关键字“global”进行声明。3.5

函数(5)匿名函数:l 匿名函数是无需函数名标识的函数,它的函数体只能是单个表达式。Python中使用关键字lambda定义匿名函数:lambda[arg1

[,arg2,argn]]:expressionl 为方便使用匿名函数,应使用变量记录这个函数:3.5

函数模块和包异常处理正则表达式任务4

基于正则表达式提取图片链接文本4.1

模块和包(1)每个.py的python文件都被视为一个模块,python文件的名称就是模块名(2)一个文件夹包含一个名为:“

init

.py”的文件(可以是一个没有任何内容的空文件),则这个文件夹便是Python中的包,包名就是文件夹的名称,包中可以包含多个模块或子包(3)Python中的模块可分为三类,分别是内置模块、第三方模块和自定义模块:4.1

模块和包内置模块Python的官方模块,可直接导入程序供开发人员使用。由非官方制作发布的、供给大众使用的Python模块,在使用之前需要开发人员先自行安装第三方模块开发人员在程序编写的过程中自行编写的、存放功能性代码的.py文件。自定义模块4.1

模块和包(4)模块的使用:l 使用import导入:l 使用from…import…导入:注1:在from

import

方式下,可以用通配符‘*’

,表示导入所有成员注2:在from

import

方式下,可能会出现模块的成员名称重名的问题注3:使用第三方模块之前,需要使用包管理工具下载和安装第三方模块:如:pipinstallpandas4.1

模块和包(5)

name

属性:l Python为模块提供了

name

属性l

name

属性通常与if条件语句一起使用l 若当前模块是启动模块,则其

name

的值为“

main

”l 若该模块被其它程序导入,则

name

的值为文件名4.1

模块和包(6)包的使用:l

使用import导入:使用import导入包中的模块时,需要在模块名的前面加上包名,格式为

“包名.模块名”;若要使用已导入模块中的函数时,需要通过“包名.模块名.函数名”实现l 使用from…import…导入:此方式下导入包中模块包含的内容,若需要使用导入模块中的函数,需要通过“模块名.函数名”实现注:包中的

init

.py文件有两个作用:标识当前文件夹是一个Python的包;模糊导入:如果

init

文件中没有声明

all

属性,那么使用

from包名 import

*

导入的内容为空4.2

异常处理(1)相关概念:l 语法错误:不符合Python语法格式的代码所引起的错误l 逻辑错误:语法格式正确的Python代码在运行时产生的错误l 异常:程序运行期间检测到的错误称为异常若异常不被处理,默认会导致程序崩溃而终止运行(2)Python的异常类:4.2

异常处理注:Python允许程序开发人员自定义异常,只需创建一个类,让它继承自Exception类或其它异常类即可(3)Python的异常处理结构:4.2

异常处理注:也可以在程序中通过raise语句主动抛出异常,语法如下:raise

异常类名或raise

异常对象4.2

异常处理(4)异常处理的执行过程:l 执行try下面的代码块;l 如果没有异常出现,则忽略所有的except块,如果定义了else块,则跳转到else块执行,如果没有else块,但是定义了finally块,则跳转到finally块执行;l 如果出现异常,则忽略try块中没有执行的剩余代码,跳转到能捕获对应异常类的except块执行,忽略其他的except块;l else块或except块执行完后,如果定义了finally块,则跳转到finally块执行。4.2

异常处理(5)with语句:l with语句适用于对资源进行访问的场合,无论资源在使用过程中是否发生异常,都可以使用with语句保证执行释放资源操作。with

上下文表达式

[as

资源对象]:语句体注:上下文表达式会返回一个上下文管理器对象,若指定了as子句,则将上下文管理器对象的

enter

()方法的返回值赋值给资源对象。4.2

异常处理4.3

正则表达式(1)基本概念:l 用于描述字符串匹配规则的文本字符串称为正则表达式l 一个正则表达式也称为一个模式(pattern)l 在正则表达式中具有特殊含义的专用字符称为元字符l Python中的正则表达式模块是re模块(2)常用元字符:4.3

正则表达式(3)re模块的常用函数:4.3

正则表达式4.3

正则表达式类和对象文件与目录操作JSON任务5从JSON文件中加载数据5.1

类和对象(1)基本概念:l 类(Class):类是具有相同属性和行为的一类对象的集合,它提供了一个抽象的共性描述l 对象(Object):对象是类的一个具体实例,是数据(描述事物的属性)和动作(体现事物的行为)的结合体l 抽象(Abstract):抽象是从许多事物中抽取它们的共同特征,形成概念的过程l 封装(Encapsulation):封装就是将对象的属性和行为封装起来,不需要让外界知道具体实现细节,类把自己的属性和行为只让可信的其它类或者对象操作,对不可信的类或对象隐藏信息l 继承(Inheritance):继承描述的是类与类之间的关系,通过继承,一个新类可以使用现有类的相关功能,并且在无须重新编写原来的类的情况下对这些功能进行扩展。继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”、“超类”l 多态(Polymorphism):多态指一个类实例的相同方法在不同情形下有不同的表现形式,即同一个属性或行为在父类和子类中具有不同的语义5.1

类和对象(2)类的定义:Python使用关键字class定义类注1:如果在类成员名之前添加双下划线(

),则该成员为类的私有成员,它限定只能类的内部访问注2:类的实例方法的定义与函数类似,只是方法中的第一参数须为:self,只能由对象调用5.1

类和对象(3)对象的创建及使用:l 创建对象对象名

=

类名()l 成员引用对象名.属性对象名.方法()5.1

类和对象(3)类的构造方法与析构方法:l 构造方法:

init

()如果定义类时显式地定义

init

()方法,那么创建对象时Python解释器会调用显式定义的

init

()方法;如果定义类时没有显式定义

init

()方法,那么Python解释器会调用默认的

init

()方法在构造方法中定义的属性是实例属性,只能通过对象进行访问在类中定义的属性是类属性,可以通过对象或类进行访问l 析构方法:

del

()当一个对象的引用计数器数值为0时,就会调用析构方法

del

()5.1

类和对象(4)类的继承及方法重写:l 类的继承:单继承:class

子类(父类)多继承:class

子类(父类1,父类2,...)l 方法重写:子类可以继承父类的属性和方法,若父类的方法不能满足子类的要求,子类可以重写父类的方法,以实现子类的特定功能。重写就是在子类重新定义一个与父类的方法名及参数完全相同,但是方法体的具体实现不同的方法。如果子类重写了父类的方法,但仍希望调用父类中的方法,那么可以使用super()函数实现:super().方法名()5.2

文件与目录操作(1)文件操作:l 打开文件:open(file,mode='r',encoding=None)file:待打开文件的文件名mode:文件的打开模式,如右图所示:encoding:文件的编码方式函数调用成功会返回一个文件对象l 关闭文件:文件对象.close()close()方法用于关闭文件,该方法没有参数,直接调用即可也可以使用with语句打开文件,自动实现文件的关闭操作l 读文件:read()、readline()、readlines()l 写文件:write()、writelines()5.2

文件与目录操作5.2

文件与目录操作(2)目录操作:

Python提供了多个内置模块实现对目录的操作l os.path模块中的目录操作常用函数:5.2

文件与目录操作(2)目录操作:

Python提供了多个内置模块实现对目录的操作l os模块中的目录操作常用函数:5.2

文件与目录操作(2)目录操作:

Python提供了多个内置模块实现对目录的操作l shutil模块中的目录操作常用函数:5.2

文件与目录操作5.3

JSON(1)JSON(JavaScriptObjectNotation)是一种简单、清晰的数据交换格式,它采用完全独立于编程语言的文本格式来存储和表示数据,其语法规则如下:l

使用键值对(

key

:

value

)表示对象属性和值;l

使用逗号(,)分隔多条数据;l

使用大括号{

}包含对象;l

使用方括号[

]表示数组(2)JSON对象为

{key:

value,

key:

value,…}的键值对结构。key为对象的属性,value为对应的属性值,通过“对象.key”的方式来获取对应的属性值。一个表示学生信息的JSON对象示例如下:5.3

JSON5.3

JSON(3)Python提供了内置模块:json模块用于JSON数据的处理l

dump(obj,fp):把Python对象obj转换为JSON字符串并写入fp文件中l

dumps(obj):把Python对象obj转换为JSON字符串l

load(fp):文件中读取JSON数据然后转换为Python对象l

loads(s):把JSON字符串s转换为Python对象(通常为字典)注1:在dump()、dumps()函数中,可以通过参数indent来设置转换时的格式化空格数量注2:在load()、loads()函数中,可以通过参数cls来设置使用的转换类(JSONCoder类的子类),如果不设置,则默认使用JSONCoder类小结l 在本项目中主要介绍了一些Python的基础知识,包括Python的特点、版本、应用领域、Python开发环境的搭建、Python的数据结构、运算符、流程控制、字符串、列表、元组、字典、集合、函数、类和对象、异常处理、文件及目录操作、模块和包、正则表达式、JSON数据处理等。l 通过本项目的学习,希望能够独立搭建Python开发环境,能掌握Python的数据结构、流程控制、函数及类的定义及使用、模块、文件操作、异常处理、正则表达式、JSON数据处理等相关知识,对Python程序开发有一个初步认识,为后续的爬虫学习做好铺垫。项目2

网页数据采集实战数据采集与处理2023年11月10日星期五掌握了解掌握熟悉学习目标掌握

urllib库、requests库的使用12掌握

基本的反爬策略、网页数据的请求方法等熟悉

通用爬虫工具的使用、爬虫的基本流程34了解

爬虫产生的背景、爬虫的基本概念目录任务1 利用工具爬取电商网页任务2 基于urllib库爬取一个电商网页任务3 urllib处理发送GET/POST请求任务4 请求头伪装与代理服务器应用任务5 网络异常处理任务6 基于requests库爬取电商网页任务1 利用工具爬取电商网页任务1 利用工具爬取电商网页(1)爬虫的定义:l

网络爬虫,又称为网页蜘蛛、网络机器人,是一种按照一定的规则(爬虫算法),自动请求互联网上的相关网页并提取页面中相关数据的程序或脚本海量销售数据大数据分析啤酒和尿布的关联度极高调整销售策略销量增加(2)爬虫产生的背景:l 啤酒和尿布的故事l从互联网自有数据不够用时,如何获取更多的数据?任务1 利用工具爬取电商网页(3)爬虫的分类l

按使用场景分:通用爬虫、聚焦爬虫l

按爬取数据的形式分:累积式网络爬虫、增量式网络爬虫l

按爬取数据的存在方式分:表层网络爬虫、深层网络爬虫通用爬虫工作原理聚焦爬虫工作原理任务1 利用工具爬取电商网页(3)与爬虫相关的网站文件:l

robots.txt:l robots.txt是网站基于Robots

协议(网络爬虫排除标准,又称爬虫协议、机器人协议等)制定的爬虫规则文本l robots.txt

文件必须放置在站点的根目录下,而且文件名必须全部小写l Robots

协议是一种建议,网络爬虫可以选择不遵守它,

但可能会存在法律风险l

Sitemap.xml:l 文件列出了网站中的网址及每个网址的其他元数据,以便于爬虫可以更加智能地爬取网站l 要谨慎对待,注意文件的内容缺失或过期的问题任务1 利用工具爬取电商网页(4)常用反爬虫策略:l

设置

User-Agent:l User-agent表示用户代理,通过User-agent伪装,可降低被反爬机制检测到的概率l

使用代理

IP:l 使用多个代理IP,可以降低单个IP地址的访问量l

降低访问频率:l 降低访问网站的频率,防止对方从访问量上认出爬虫的身份l

验证码识别:l 对于某些网站访问过程中需动态输入验证码才能继续访问的情况,爬虫需采用技术识别出正确验证码,并能自动填写,完成验证任务1 利用工具爬取电商网页(5)爬虫的合法性:l

爬虫所采集的数据是指互联网上公开的并且可以访问到的网页信息,而不是网站的后台信息(没有权限访问),更不是用户注册的信息(非公开的)。l

恶意利用爬虫技术抓取数据,以获取不正当竞争的优势,或牟取不法利益的,则可能触犯法律任务1 利用工具爬取电商网页(6)基于八爪鱼工具爬取数据示例l

下载工具,并安装:任务1 利用工具爬取电商网页(6)基于八爪鱼工具爬取数据示例l

打开工具,创建爬虫任务:任务1 利用工具爬取电商网页(6)基于八爪鱼工具爬取数据示例l

任务设置任务1 利用工具爬取电商网页(6)基于八爪鱼工具爬取数据示例l

设置数据采集规则:任务1 利用工具爬取电商网页(6)基于八爪鱼工具爬取数据示例l

采集规则设置完成后,保存并开始采集:任务1 利用工具爬取电商网页(6)基于八爪鱼工具爬取数据示例l

数据自动采集:任务2

基于urllib库爬取一个电商网页任务2

基于urllib库爬取一个电商网页模块功能request请求处理模块,用于打开和读取URL,可以模拟浏览器的一个请求发起过程error异常处理模块,为request模块所引发的异常定义了异常类parse用于解析

URLrobotparser用于解析

robots.txt

文件(1)urllib库l

Python自带的标准库,直接导入项目中便可使用。主要模块如下:任务2

基于urllib库爬取一个电商网页参数说明url网页的URL地址,可以是一个字符串,或一个urllib.request.Request对象data一个对象,包含要发送到服务器的附加数据,若不需要发送数据则为

None。当data不为None时,发送请求的方式需为POSTtimeout可选参数,用于指定阻塞操作(如连接尝试)的超时时间,单位为秒cafileHTTPS

请求时,指定CA

证书文件capathHTTPS

请求时,指定

CA

证书的路径cadefault未使用context该参数若提供,则必须是一个

ssl.SSLContext

实例,用于描述各种

SSL

参数(2)urllib.request.urlopen():l

Urllib库的request模块中用于访问给定URL的函数,主要参数说明如下:任务2

基于urllib库爬取一个电商网页(3)urllib.request.Request:l

URL

请求对象的抽象类。当使用urlopen()方法发送一个请求时,可通过该类的对象来添加额外的data参数,自定义请求头(header)信息等:参数说明url网页的URL地址,与urllib.request.urlopen方法中的url参数一致data与urllib.request.urlopen方法中的data参数一致,目前唯一用到

data

的只有

HTTP

请求headers一个字典,用于设置request请求头信息,通常被用于

"伪装"

User-Agent

标头值。如果给出了

data参数,则应当包含合适的

Content-Type

头部信息;若未提供且

data

不是

None,则会把

Content-Type:

application/x-www-form-urlencoded

加入作为默认值origin_req_host发起初始会话的请求主机,默认值为

"http.cookiejar.request_host(self)

",表示是用户发起初始请求的主机名或

IP

地址unverifiable用于标示出请求是否无法验证method字符串,表示要采用的

HTTP

请求方法,如"GET"、"POST"

等任务2

基于urllib库爬取一个电商网页(4)http.client.HTTPResponse:l

使用urlopen()向网站发送请求时,如果是

HTTP

/

HTTPS请求

URL,urlopen()将返回一个http.client.HTTPResponse

的对象,它提供了提供获取网站相关响应内容的方法:l read()、readline()、readlines():对响应内容的数据读取操作;l info():获取远程服务器返回的头信息,python3.9后建议用headers代替;l getcode():返回HTTP状态码,python3.9后建议用status代替;l geturl():返回请求的URL,python3.9后建议用url代替(5)基于urllib爬取当当网页面:任务2

基于urllib库爬取一个电商网页任务3

urllib处理发送GET/POST请求任务3

urllib处理发送GET/POST请求(1)HTTP请求l

GET请求:用于向服务器请求数据,直接在URL中附加上所有请求参数l

POST请求:通常用于向服务器提交数据,它的请求参数或提交的数据不会出现在URL地址中,是一种比较主流也比较安全的数据传递方式l

PUT请求:请求服务器存储一个资源,通常要指定存储的位置l

DELETE请求:请求服务器删除一个资源l

HEAD请求:请求获取对应的HTTP报头信息l

OPTIONS请求:可以获得当前URL所支持的请求类型任务3

urllib处理发送GET/POST请求(2)GET请求l

GET请求一般用于向服务器请求数据,请求的参数通过URL地址传递到服务器,即可以直接在URL中写上要传递的参数,也可以由表单进行传递l

GET请求要传递的参数跟在页面地址中的问号(?)后面,每个参数以“参数名=值”的格式表示,多个参数之间用符号

&

拼接/?key=%C5%C0%B3%E6&act=input任务3

urllib处理发送GET/POST请求(3)URL编解码l

GET请求的参数包含中文或其他特殊字符时,需要对这些参数进行URL编码,可通过

urllib.parse.urlencode()方法实现l

与URL编码类似,也可通过

urllib.parse.unquote()方法进行URL解码URL编码示例URL解码示例任务3

urllib处理发送GET/POST请求(4)POST请求l

POST请求通过表单的方式向服务器提交数据,它的请求参数或提交的数据不会出现URL地址中l

当urlopen()函数的data参数不为None(空)时,则

HTTP请求的方式应为POST方式通过Chrome浏览器的DevTools查看HTTP请求方式任务3

urllib处理发送GET/POST请求(5)基于urllib库的POST请求实现思路:l

设置请求的URL网址l

构建表单数据,并使用urllib.parse.urlencode()对数据进行编码l

创建Request对象,参数包括URL地址和要传递的数据l

使用addheader()添加头信息,模拟浏览器进行爬取l

使用urllib.request.urlopen()打开对应的Request对象,完成信息的传递l

后续处理,如读取网页内容、将从网页中提取到的数据写入文件等操作任务3

urllib处理发送GET/POST请求(6)urllib库发送GET请求示例任务3

urllib处理发送GET/POST请求(7)urllib库发送POST请求示例注:示例中的data、headers变量中的相关数据须根据自已的浏览器调度工具查看到的相关内容做相应修改。任务4 请求头伪装与代理服务器应用任务4 请求头伪装与代理服务器应用(1)请求头伪装:加入特定的Headers请求头信息,将爬虫发出的请求伪装成一个从浏览器发出请求l

通过urllib.request.Request进行设置l

通过Request.add_header()来添加请求头信息任务4请求头伪装与代理服务器应用(2)代理服务器:l

urlopen()方法不支持代理、Cookies等HTTP/HTTPS高级功能,在urllib库下使用代理服务器,需要通过urllib.request中的OpenerDirector类的对象来实现,步骤如下:l

使用相关的Handler处理器创建特定功能的处理器对象l

通过build_opener()方法使用Handler对象创建自定义的opener对象l

使用自定义的opener对象,调用open()方法发送请求(2)代理服务器示例:任务4请求头伪装与代理服务器应用(3)豆瓣电影Top

250排行榜爬取示例1任务4请求头伪装与代理服务器应用(3)豆瓣电影Top

250排行榜爬取示例2任务4请求头伪装与代理服务器应用(3)豆瓣电影Top

250排行榜爬取示例3任务4请求头伪装与代理服务器应用(3)豆瓣电影Top

250排行榜爬取示例4任务4请求头伪装与代理服务器应用(3)豆瓣电影Top

250排行榜爬取示例5任务4请求头伪装与代理服务器应用任务5 网络异常处理任务5 网络异常处理(1)URLError:l

继承自Python的OSError,定义了三个参数:args、reason、filenamel

URLError产生的原因:l 没有连接网络l 服务器连接失败l 找不到指定的服务器URLError示例任务5 网络异常处理(2)HTTPError:l

继承自URLError,它的对象拥有一个整型的属性code,表示服务器返回的错误代码,如:404表示请求的页面不存在l

urllib.request.urlopen()方法请求页面时,如果返回的响应码表示无法处理请求内容时,urlopen()会抛出HTTPError任务5 网络异常处理(1)HTTPError示例:任务6 基于requests库爬取电商网页任务6 基于requests库爬取电商网页(1)Requests:l

Python第三方库,使用前需先安装l

基于urllib的基础上进行了高度的封装,提供了比urllib更丰富的功能,更方便的调用方式任务6 基于requests库爬取电商网页(2)requests库的常用请求函数:l

request()函数是最基础的函数,其它函数是对request()函数的快捷封装l

request(method,url,**kwargs)requests库的常用请求函数方法说明requsts.request()构造一个请求,最基本的HTTP请求实现方法requsts.get()获取网页,对应HTTP中的GET方法requsts.post()向网页提交信息,对应HTTP中的POST方法requsts.head()获取网页的头信息,对应HTTP中的HEAD方法requsts.put()向网页提交put方法,对应HTTP中的PUT方法requsts.patch()向网页提交局部修改的请求,对应HTTP中的PATCH方法requsts.delete()向网页提交删除请求,对应HTTP中的DELETE方法任务6 基于requests库爬取电商网页(3)requests.Request:l

请求对象,用于将一个请求发送到服务器l

Request对象的生命周期针对一个客户端请求,一旦请求发送完毕,该请求包含的内容就会被释放掉l

Request对象的请求完成后会将响应结果封装在Response类的响应对象中返回,其中包含服务器对HTTP请求的响应。任务6 基于requests库爬取电商网页(4)requests.Session:l

表示请求会话,提供了持久性、连接池等相关配置l

Session类的对象可以跨越多个页面,它的生命周期同样针对的是一个客户端,当关闭这个客户端的浏览器时,只要是在预先设置的会话周期内,这个会话包含的内容会一直存在,不会被马上释放掉任务6 基于requests库爬取电商网页(5)requests.Response:l

响应对象,包含服务器对HITP请求的响应l

当request()函数得到服务器返回的响应后,会创建并返回一个Response类对象,该对象包含了服务器返回的所有信息,也包括原来创建的Request对象。Response对象的常用属性属性说明status_codeHTTP请求返回的状态码,200表示成功textHTTP响应的字符串形式,即url对应的网页内容encoding从HTTP

header中获取的响应内容编码方式apparent_encoding从内容中推断的响应内容编码方式(备选)contentHTTP响应内容的二进制形式任务6 基于requests库爬取电商网页(6)Requests库中的异常类:异常类说明requests.ConnectionError网络连接异常,如DNS查询失败,拒绝连接等requests.HTTPErrorHTTP错误异常requests.URLRequiredURL缺失异常requests.TooManyRedirects超过最大重定向次数,产生重定向异常requests.ConnectTimeout连接远程服务器超时异常requests.Timeout请求URL超时异常任务6 基于requests库爬取电商网页(7)豆瓣电影排行版TOP250的数据爬取小结l 在本项目中主要介绍了爬虫的基本概念及两个爬虫请求的常用库:包括爬虫的概念、产生背景、爬虫的分类、爬虫的合法性、爬虫的基本流程、通用爬虫工具的使用、基于urllib库及requests库的网页数据采集、基本的反爬策略应用等。l 通过本项目的学习,希望能够了解爬虫的相关概念,能基于urllib库及requests库实现对特定网页的采集,能掌握基本的反爬策略,能在爬虫程序中进行请求头伪装及代理服务器设置实现对网页的爬取,为后续的爬虫学习做好铺垫。任务3.4.1

Beautiful

Soup数据采集与处理任务3.4.1

Beautiful

SoupBeautifulSoup库l

是一个python的HTML或XML的第三方解析库,使用前需先安装:pipinstall

beautifulsoup4l

它将复杂HTML文档转换成一个树形结构,每个节点都是Python对象,所有对象可以归纳为4种:bs4.element.Tag类:表示标签,它有两个非常重要的属性:name,表示标签名;attributes,表示标签的属性,可通过字典的引用方式或“.”成员引用符的方式来引用bs4.element.NavigableString类:表示HTML中标签的文本(非属性字符串)bs4.BeautifulSoup类:表示一个文档的全部内容。大部分时候,可以把它当作

Tag

对象,支持遍历文档树和搜索文档树的大部分方法bs4.element.Comment类:表示文档内的注释部分(2)Beautiful

Soup4网页数据解析流程任务3.4.1

Beautiful

Soup(2.1)BeautifulSoup对象l

使用Beautiful

Soup进行网页数据解析,首先要创建一个BeatutifulSoup类的对象,其构造方法的参数说明如下:说明markup待解析的HTML字符串或类文件对象features用于指定解析器,

可以是特定解析器的名称(“

l

xml

”,“

l

xml

-

x

m

l

、“html.parser

”或“html5lib”),或者是要使用的标记类型(“html”、“html5”、“xml”)builder使用自定义的TreeBuilder子类替换features中的默认实现parse_only指定文档中需解析的部分,其他未指定部分将会被忽略from_encoding指定要使用的编码格式exclude_encodings指定要排除的编码格式任务3.4.1

Beautiful

Soup(2.2)查找目标节点l

Beautiful

Soup定义了很多搜索节点的方法,如find_XXX()系列方法、select()方法[可使用CSS选择器的语法]等、参数说明name指定要查找的标签名,如果传入的是字符串,查找与字符串完全匹配的内容;如果传入的是正则表达式,则通过re模块的match()函数进行匹配;如果传入一个列表,则会返回与列表中任一元素相匹配的内容。attrs字典结构的过滤器,用于设置查找时的过滤条件recursive指定是否检索当前节点的所有子孙节点,如果为False,则只检索当前节点的直接子节点string搜索文档中的字符串内容。与

name

参数的可选值一样,string

参数也接受字符串、正则表达式、

列表Truelimit限制返回结果的数量,一旦搜索到结果的数量达到了limit的限制时,就会停止搜索kwargs关键字参数,如果某个指定名字的参数不是方法中内置的参数名,那么在进行搜索时,会把该参数当作指定名称的标签中的属性来fin搜d索_X。XX()系列方法常用参数任务3.4.1

Beautiful

Soup(2.3)解析信息l

通过相关方法获取到了目标节点对象后,便可通过该对象的相关属性或方法来获取相应的信息:如果一个节点对象只有一个

NavigableString

类型子节点,那么这个节点对象可以使用.string方法获取子节点;如果一个节点对象包含了多个子节点,则节点对象就无法确定

.string

方法应该调用哪个子节点的内容,这种情况下,

.string

的输出结果是

None

;如果一个节点对象中包含多个字符串,可以使用

.strings

.stripped_strings来循环获取,.stripped_strings

可以去除多余空白内容;如果只想得到一个节点对中包含的文本内容,则可以使用

get_text()

方法,该方法获取到节点中包含的所有文本内容,包括子孙节点中的内容,并将结果作为Unicode字符串返回。任务3.4.1

Beautiful

Soup项目4 并发技术实战数据采集与处理2023年11月10日星期五掌握了解掌握熟悉学习目标掌握

多线程技术、基于multiprocessing库的多进程并发编程12掌握

多线程技术、协程技术、基于threading、gevent的并发编程熟悉

并发爬虫的开发34了解

并发原理、Python实现并发的方式目录任务1 基于进程的并发爬虫任务2 基于queue模块的多线程爬虫任务3 基于协程的并发爬虫任务4 历史天气并发爬取任务1 基于进程的并发爬虫任务1 基于进程的并发爬虫(1)并发原理l

用户在同一时间内可以同时运行多个应用程序称为多任务处理,每个应用程序被称作一个任务l

多任务处理的两种形式:并发和并行并行:两个或者多个任务在同一时刻发生并发:两个或者多个任务在同一时间间隔内发生任务1 基于进程的并发爬虫(2)进程(Process)l

进程是一个实体。它是有特定功能的程序在一个数据集上一次动态执行的过程,它是系统进行资源分配的基本单位l

每一个进程都有自己的地址空间,进程占据的内存空间由控制块、程序段和数据段三个部分组成:控制块(Proscessing

Control

Block,PCB):描述进程的数据结构,(是进程在计算机中的唯一标识(含有标识信息),计算机通过查看PCB来感知进程的存在。程序段:用于存放程序执行代码的一块内存区域。数据段:存储变量和进程执行期间产生的中间数据及最终数据的一块内存区域。任务1 基于进程的并发爬虫(3)进程的生命周期任务1 基于进程的并发爬虫multiprocessing模块l

Python用于实现多进程并发编程的标准模块l

模块中的Process类用于创建进程对象,当进程对象创建后,通过调用start()方法来启动进程,构造方法如下:

init

(self,group=None,target=None,name=None,args=(),kwargs={},*,daemon=None)参数说明如下:group:必须为None,它仅用于兼容

threading.Thread;target:由

run()

方法调用的可调用对象,表示子进程的功能函数,用于为子进程分派任务;Ø

name:表示当前进程的名称。若没有指定,默认为Process-N,N为从1开始递增的整数;Ø

args:以元组形式传递给target指定函数的位置参数;kwargs:以字典形式传递给target指定函数的关键字参数;daemon:表示是否将进程设为守护进程(在后台运行的一类特殊进程,用于执行特定的系统任务)。如果为默认值(

None),则表示从父进程中继承该标志位。任务1 基于进程的并发爬虫(5)

Process对象的常用方法方法说明start()启动子进程,等待CPU调度is_alive()判断进程实例是否还有效join(timeout=None)在timeout秒内,等待子进程结束;若为timeout为默认值None,则表示一直等待terminate

()结束当前进程,进程的后代进程将不会被终止kill

()同terminate()run()在父进程中直接运行target参数引用的可执行对象,不会启动子进程close()关闭Process对象,释放所有资源任务1 基于进程的并发爬虫(6)基于Process类的多进程并发编程示例通过Process类的子类创建多进程通过Process类创建多进程任务1 基于进程的并发爬虫(7)进程池(Pool类)l

若需要创建大量进程时,手动创建的方式不仅低效繁琐,而且工作量巨大。另外,进程的创建、运行、销毁均会增加任务的额外开销,降低多进程任务的执行效率。此时,可使用multiprocessing模块中提供的Pool(进程池)类,批量创建并管理子进程。Pool类的构造方法如下:

init

(self,processes=None,initializer=None,initargs=(),maxtasksperchild=None,

context=None)参数说明如下:processes:指定进程池中并行执行的子进程数量。若processes参数设为None,则会使用os.cpu_count()返回的CPU核心数initializer:若不为

None,则每个工作进程将会在启动时调用

initializer(*initargs)initargs:传递给initializer的参数maxtasksperchild,一个工作进程在它退出或被一个新的工作进程代替之前能完成的任务数量,以释放闲置资源context,用于设定工作进程启动时的上下文任务1 基于进程的并发爬虫(7)Pool类对象的常用方法方法说明apply(func,args,

kwds)以阻塞方式调用func函数,args、kwds是分别以元组或字典方式传递给func函数的参数apply_async(func,args,kwds,callback=None,

error_callback=None)以非阻塞方式调用func函数,args、kwds是分别以元组或字典方式传递给func函数的参数,callback、error_callback分别是回调函数及错误回调函数map(func,iterable,chunksize=None)以阻塞方式将func函数应用到interable可迭代对象的每个元素上,可迭代对象按chunksize分为多个任务提交给子进程处理。map_async(func,iterable,chunksize=None,

callback=None,error_callback=None)以非阻塞方式将func函数应用到interable可迭代对象的每个元素上,可迭代对象按chunksize分为多个任务提交给子进程处理。callback、error_callback分别是回调函数及错误回调函数close()关闭Pool对象,不再接受新任务terminate()不管任务是否完成,立即终止Pool对象join()在close()方法后使用,阻塞主进程,直到子进程全部结束任务1 基于进程的并发爬虫(7)基于Pool类的多进程并发编程示例任务1 基于进程的并发爬虫(8)进程间的通信l

进程拥有自己独立的用户空间(代码+数据),一般情况下,无法与其他进程共享l

multiprocessing模块中提供了Queue(队列)类和Pipe(管道)类来实现进程间的通信:Queue(队列)类:用于创建和管理存储共享资源的先入先出队列Pipe(管道)类:由操作系统内核提供的高效的进程间通信方式,从通信效率上来说,管道比队列要更加高效任务1 基于进程的并发爬虫(8)进程间通信示例基于Pipe的进程通信示例基于Queue的进程通信示例任务1 基于进程的并发爬虫(8)基于多进程并发爬虫采集豆瓣电影Top

250排行榜任务1 基于进程的并发爬虫(8)基于多进程并发爬虫采集豆瓣电影Top

250排行榜任务1 基于进程的并发爬虫(8)基于多进程并发爬虫采集豆瓣电影Top

250排行榜任务2 基于queue模块的多线程爬虫任务2 基于queue模块的多线程爬虫(1)线程(Thread)l

线程是操作系统执行运算调度的最小单位。线程不能独立存在,必须包含在某个进程中,是进程的实际运行单位l

线程由线程ID、当前指令指针(PC)、寄存器集合和堆栈组成,它不能独立拥有系统资源,同一进程中的多个线程将共享该进程中的全部系统资源,因此线程间的数据通信要比进程间的数据通信更容易、高效l

同一进程内的每个线程有自己的独立堆栈和寄存器等,在线程切换时,只需保存和恢复自己的堆栈和寄存器,无须涉及进程的内存空间,因此线程的创建、调度和销毁工作比进程要高效,需要的计算资源也比进程要少任务2 基于queue模块的多线程爬虫threading模块l

Python用于实现多线程并发编程的标准模块l

模块中的Thread类用于创建线程对象,线程对象创建后,通过调用start()方法来启动线程,构造方法(与进程的类似)如下:

init

(self,group=None,target=None,name=None,args=(),kwargs={},*,daemon=None)参数说明如下:group:默认为None,用于向后兼容ThreadGroup;target:由

run()

方法调用的可调用对象,表示子线程的功能函数,用于为子线程分派任务;name:表示当前线程的名称。若没有指定,默认为Thread-N,N为从1开始递增的整数;args:以元组形式传递给target指定函数的位置参数;kwargs:以字典形式传递给target指定函数的关键字参数;daemon:表示是否将进程设为守护线程。如果为默认值(

None),则表示从父线程中继承该标志位。任务2 基于queue模块的多线程爬虫(3)

Thread对象的常用属性和方法方法说明start()启动子线程,等待CPU调度is_alive()判断进程实例是否还有效join(timeout=None)在timeout秒内,等待子线程结束;若为timeout为默认值None,则表示一直等待getName()获取线程的名字run()直接运行target参数引用的可执行对象属性说明name线程名字,由实例化参数name指定ident线程标识符daemon是否是一个daemon线程,若不设置,默认值为FalseThread对象的常用属性Thread对象的常用方法任务2 基于queue模块的多线程爬虫(4)线程同步l

多个线程访问同一共享数据,可能会引发竞争问题,这种多个线程同时访问共享数据造成的混乱情况,称为多线程数据竞争l

为了解决多线程数据竞争问题,需引入线程同步机制,以保证多个线程可以有序的访问共享数据l

threading模块提供了多种线程同步机制:Lock、RLock、Semaphore、Condition、Event等。其基本原理均是给共享数据“加锁”任务2 基于queue模块的多线程爬虫(4)线程同步示例任务2 基于queue模块的多线程爬虫(5)基于多线程并发爬虫采集豆瓣电影Top

250排行榜任务3 基于协程的并发爬虫任务3 基于协程的并发爬虫(1)协程(coroutine)l

是一种用户态的轻量级线程,也称为“轻量级线程”、“微线程”、“纤程(fiber)”等l

由单一线程内部发出控制信号进行调度,而非受到操作系统管理,因此协程没有切换开销和同步锁机制,具有极高的执行效率l

协程拥有自己的寄存器上下文和栈,它的调度完全由用户控制,基本没有内核切换的开销,可以不加锁的访问全局变量,上下文的切换非常快l

Python是提供原生协程支持的语言之一l

gevent是一个基于协程的Python第三方库,使用前需先安装,常用方法:gevent.spawn():创建并启动协程; gevent.joinall():等待所有协程执行完毕任务3 基于协程的并发爬虫(2)进程、线程、协程的不同特点l

一个线程可以拥有多个协程,一个进程也可以单独拥有多个协程l

线程和进程都是同步机制,而协程则是异步机制l

协程能保留上一次调用时的状态,每次重入时,就相当于进入上一次调用的状态l

单进程下协程和多线程区别不大,相较之下,协程更省资源、效率更高、更安全l

协程的切换不像多线程调度那样耗费资源,所以不用严格限制协程的数量任务3 基于协程的并发爬虫(3)基于协程的并发爬虫采集豆瓣电影Top

250排行榜任务4 历史天气并发爬取任务4 历史天气并发爬取(1)CPU密集型与I/O密集型l

CPU

密集型(CPU-bound):CPU

密集型也叫做计算密集型,此类任务主要消耗CPU资源。I/O操作在很短时间内就可以完成,其耗时对任务的完成影响不大,任务所需的资源主要集中在CPU的大量计算和处理上,特点是CPU

占用率相当高l

I/O密集型(I/O

bound):此类任务的特点是CPU消耗很少,由于I/O读写的速度远远低于CPU和内存的速度,任务运行时大部分的状况是CPU

在等I/O(硬盘,内存)读写操作的完成,CPU

占用率较低,对于I/O密集型任务,在一定的任务总量下,任务越多,CPU效率越高。任务4 历史天气并发爬取(1)多进程并发,多线程并发、协程并发的对比l

多进程并发:可以利用多核CPU

进行并行计算,是真正的并发;缺点是:占用的资源最多,可以启动的数目比线程要少。适用于CPU密集型计算l

多线程并发:与多进程相比,更加轻量级,占用资源较少;缺点是:与多进程并发技术相比,多线程并发技术只能够并发执行,不能够利用多CPU;与协程技术相比,线程的启动数量有限,占用内存资源,有线程切换开销。适用于I/O密集行型计算l

协程并发:内存开销最小,启动协程数目最多;缺点是:支持的库有限制,代码实现复杂。适用于I/O

密集型计算任务4 历史天气并发爬取(2)基于多线程的历史天气并发爬取l

页面分析:历史天气预报查询页面任务4 历史天气并发爬取(2)基于多线程的历史天气并发爬取l

页面分析:具体月份的历史天气预报页面任务4 历史天气并发爬取(2)基于多线程的历史天气并发爬取l

数据定位:各月份的历史天气预报页面链接任务4 历史天气并发爬取(2)基于多线程的历史天气并发爬取l

数据定位:具体的历史天气预报数据任务4 历史天气并发爬取(2)基于多线程的历史天气并发爬取l

代码实现1:任务4 历史天气并发爬取(2)基于多线程的历史天气并发爬取l

代码实现2:任务4 历史天气并发爬取(2)基于多线程的历史天气并发爬取l

代码实现3:任务4 历史天气并发爬取(2)基于多线程的历史天气并发爬取l

代码实现4:任务4 历史天气并发爬取(2)基于多线程的历史天气并发爬取l

爬虫代码运行时截图:小结l 在本项目中主要介绍了基于Pyth

温馨提示

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

评论

0/150

提交评论