一、爬虫
说到爬虫不得不说 python,因为我学 python 很大一部分原因是因为爬虫,爬虫之前有新闻报道,说爬虫犯法了,但不能不说爬虫是个好东西,只要我们在法律允许的范围内,合理使用爬虫就好了
而我近期学习爬虫,将学习过程记录了下来,算是我学习爬虫的一个笔记吧,我看的是北京理工大学,嵩天副教授的 python网络爬虫与信息提取 的国家精品在线开放课程,讲的十分详细,我感觉不错
二、Requests 模块
1、安装requests
我们使用pip安装request模块,为了下载比较快,我们使用清华开源镜像库:https://pypi.tuna.tsinghua.edu.cn/simple/
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple/
或者用pycharm打开project interpreter
搜索requests,选中后进行安装
2、HTTP协议
HTTP:超文本传输协议,是基于“请求与响应”模式的、无状态的应用层协议,采用URL作为定位网络资源的标识
URL: 格式http://host[:port][path]
1 | * host: 合法的Internet主机域名或IP地址 |
HTTP的URL理解:
URL是通过HTTP协议存取资源的Internet路径,一个URL对应一个数据资源
HTTP协议对资源的操作:
方法 | 说明 |
---|---|
GET | 请求获取URL位置的资源 |
HEAD | 请求获取URL位置资源的响应消息报告,即获取该资源的头部信息 |
POST | 请求向URL的位置资源后附加新的数据 |
PUT | 请求向URL位置存储一个资源,覆盖原URL未知的资源 |
PATCH | 请求局部更新URL未知的资源,即改变该处资源的部分内容 |
DELETD | 请求删除URL位置存储的资源 |
requests 模块的主要方法
方法 | 说明 |
---|---|
requests.request() | 构造一个请求,支撑以下各方法的基础方法 |
requests.get(url) | 获取HTML网页的主要方法,对应HTTP的GET,获取网络资源 |
requests.head(url,get) | 获取HTML网页头部形式,对应HTTP的HEAD,以很少的流量获取资源的概要信息 |
requests.post(url, post,date) | 向HTML网页提交POST请求,对应HTTP的POST,向服务器提交新增数据 |
requesrs.put(url,put,date) | 向HTML网页提交PUT请求,对应HTTP的PUT,向服务器提交数据,覆盖旧的数据 |
requests.patch() | 向HTML网页提交局部修改请求,对应HTTP的PATCH |
requests.delete() | 向HTML页面提交删除请求,对应HTTP的DELETE |
requests 模块中的方法一一对应 http 协议对于资源的操作
3、requests模块主要方法
1 | requests.request(method,url,**kwargs) |
- method : 请求方式,对应“GET”,”POST”,”PUT”,”DELETE”,”HEAD”,PATCH”,OPTIONS”7种
- url:拟获取页面的URL链接
- **kwargs:控制访问参数,共13个
控制参数 | 说明 |
---|---|
params | 字典或者字节序列,作为参数增加到URL中 |
data | 字典或者字节序列或文件对象,作为Request的内容 |
headers | 字典,HTTP定制头 |
cookies | 字典或者CookieJar,Request中的cookie |
auth | 元组,支持HTTP认证功能 |
files | 字典类型,传输文件 |
timeout | 设定超时时间,秒为单位 |
proxies | 字典类型,设定访问代理服务器,可以增加登录认证 |
allow_redirrects | True/Flase,默认为True,重定向开关 |
stream | True/Flase,默认为True,获取内容立即下载开关 |
verify | True/Flase,默认为True,认证SSL证书开关 |
cert | 本地SSL证书路径 |
json | JSON格式的数据,作为Request的内容 |
1 | requests.get(url,params = None,**kwargs) |
- url : 拟获取页面的url连接
- params : url的额外参数,字典或字节流格式,可选
- **kwargs : 12个控制参数
1 | requests.head(url,**kwargs) |
- url :拟获取页面的url
- **kwargs:13个控制访问参数
1 | requests.post(url,data=None,json=None,**kwargs) |
- url :拟更新页面的URL
- data:字典、字节序列或文件,request的内容
- json:JSON格式的数据,request的内容
- **kwargs:11个控制访问参数
1 | requests.put(url,data=None,**kwargs) |
- url:拟更新页面的url连接
- data:字典、字节序列或文件,request的内容
- **kwargs:12个控制访问参数
1 | request.patch(url,data=None,**kwargs) |
- url:拟更新页面的URL
- data:字典,字节序列或文件,request的内容
- **kwargs:12个控制访问参数
1 | request.delete(url,**kwargs) |
- URL :拟删除页面的URL连接
- **kwargs:13个控制访问参数xs
4、request模块的 get() 方法
获取一个网页最简单的方法是使用
r = request.get(url)
给定get方法和url构造一个向服务器请求资源的request对象 ,返回包含服务器资源的Response对象,Response包含了所有资源
request.get(url,params=None,**kwargs)
- url : 拟获取页面的URL连接
- params :URL中的额外参数,字典或字节流格式,可选
- **kwargs : 12个控制访问的参数,可选
Response 对象属性:
属性 | 说明 |
---|---|
r.status_code | HTTP 请求的返回状态,200表示连接成功,404表示连接失败 |
r.text | HTTP 相应内容的字符形式,即,url对应的页面内容 |
r.encoding | HTTP header 中猜测的响应内容的编码格式 |
r.apparent_encoding | 从内容中分析出的响应内容编码方式(备选编码方式) |
r.content | HTTP 响应内容的二进制形式 |
r.encoding:如果header中不存在charset ,则认为编码为 ISO-8859-1
r.apparent_encoding: 根据网页内容分析出的编码方式
4、爬取网页的通用代码
爬取网页的通用代码框架
1 | def getHTMLText(url): |
5、request 模块异常
异常 | 说明 |
---|---|
requests.ConnectionError | 网络连接异常,如DNS查询失败、拒绝连接等 |
requests.HTTPError | HTTP 错误异常 |
requests.URLRequired | URL缺失异常 |
requests.TooManyRedirects | 超过最大重定向次数,产生重定向异常 |
requests.ConnectTimeout | 连接远程服务器超时异常 |
requests.Timeout | 请求URL超时,产生超时异常 |
理解Requests模块的异常
异常 | 说明 |
---|---|
r.raise_for_status() | 如果不是200,产生requests.HTTPError |
三、BeautifulSoup 模块
1、安装BeautifulSoup 模块
使用命令pip install beautifulsoup4
安装,或者使用pycharm安装
BeautifulSoup 是解析、遍历、维护“标签树”的功能库
引用 BeautifulSoup 常用from bs4 import BeautifulSoup
2、信息标记的三种形式
信息的标记
- 标记后的信息可形成信息组织结构,增加了信息维度
- 标记后的信息可用于通信、存储或展示
- 标记的结构与信息一样具有重要价值
- 标记后的信息跟有利于程序的理解和运用
HTML(hyper text markup language) 超文本标记语言,是WWW(world wide wed)的信息组织方式,能将声音 图像 视频等超文本信息嵌入到文本信息中。HTML通过预定义的<>...</>
标签形式组织不同类型的信息
信息标记的三种形式:XML、JSON、YAML
XML:XML(eXtensible Markkup Language) :扩展标记语言与HTML类似,以标签为主来构建信息表达信息的方式,如果有信息就用一对<name>...</name>
来表达信息,如果没有内容可以用</name>
来表示,也可以用<!-- -->
来表示注释
JSON:JSON(JavaScript Object Notation):JavaScript 语言中对面向对象信息的一种表达形式,简单来讲就是有类型的键值对 key: value,来构建的信息表达方式。“key”:”value”,是有数据类型的信息,当同一个键值有多个key时,可以使用"key":["value1","value2"...]
,键值对可以嵌套使用"key":{"newkey":"value"}
YAML:YAML(YAML Ain’t Markup Language):采用无类型的键值对来构建信息,key: value
,用缩进来表达所属关系,如:
1 | key : |
YAML 用-
表示并列关系,如:
1 | key : |
YAML 用|
来表示整块数据 #
表示注释
三种信息标记形式的比较
XML 用<name>...</name>
来表达信息,JSON用有类型的键值对来表示信息的,YAML是用无类型的键值对来表达信息的
XML:
1 | <person> |
JSON:
1 | { |
YAML:
1 | fristName: Tim |
XML:最早的通用信息标记语言,可扩展性好,但繁琐,在Internet上的信息交互于传递
JSON:信息有类型,适合程序处理(js),较XML简洁,用在移动应用云端和节点的信息通信中,没有注释,用在程序对接口的处理中
YAML:信息无类型,文本信息比例最高,可读性好,用在各类系统的配置文件中,有注释易读
3、信息提取的一般方法
第一种:完整解析信息的标记形式,再提取关键信息,XML,JSON,YAML,需要标记解析器,例如bs4库的标签树遍历,优点是信息解析准确,缺点是提取过程繁琐,速度慢
第二种:无视标记形式,直接搜索关键信息,对信息的文本利用查找函数查找就可以,优点是提取过程简单,缺点是提取结果准确性与信息内容相关,
第三种:融合方法,结合形式解析与搜索方法,提取关键信息,需要标记解析器及文本查找函数
4、BeautifulSoup解析
将获得到的网页资源进行解析,转换成容易识别的、标签类型的信息标记,为后续爬取做准备工作
解析器 | 使用方法 | 条件 |
---|---|---|
bs4的HTML解析器 | BeautifulSoup(mk,”html.parser”) | 安装bs4 |
lxml的HTML解析器 | BeautifulSoup(mk,”lxml”) | pip install lxml |
lxml的XML解析器 | BeautifulSoup(mk,”xml”) | pip install lxml |
html5lib的解析器 | BeautifulSoup(mk,”html5lib”) | pip install html5lib |
基于bs4库的HTML格式化和编码
1 | soup = BeautifulSoup(f.text,"html.parser") |
prettify()将HTML的标签进行换行等处理,使得标签十分清晰,
bs4库将读入的HTML和其他字符串的编码转换为utf-8
1 | import requests |
5、BeautifulSoup类的基本元素
基本元素 | 说明 |
---|---|
Tag | 标签,最基本的信息组织单元,分别用<>和</>标明开头和结尾 |
Name | 标签的名字,<p>...</p> 的名字是’p’,格式:<tag>.name |
Attributes | 标签的属性,字典形式的组织,格式:<tag>:attrs |
NavigableString | 标签内非属性字符串,<>...</> 中字符串,格式:<tag>.string |
Comment | 标签内字符串的注释部分,一种特殊的comment类型 |
1 | import requests |
运行结果:
1 | a |
6、基于bs4模块的HTML内容遍历
标签树的下行遍历
属性 | 说明 |
---|---|
.contents | 子节点的列表,将<tag> 所有儿子节点存入列表 |
.children | 子节点的迭代类型,与.contents类型,用于循环遍历儿子节点 |
.descendants | 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历 |
1 | soup = BeautifulSoup(f.text,"html.praser") |
1 | for child in soup.body.children: #遍历儿子标签 |
1 | for descendants in soup.body.descendants: # 遍历子孙节点 |
标签树的上行遍历
属性 | 说明 |
---|---|
.parent | 节点的父亲标签 |
.parents | 节点先辈标签的迭代类型,用于循环遍历先辈标签 |
1 | soup = BeautifulSoup(f.text,"html.parser") |
遍历先辈标签
1 | soup = BeautifulSoup(f.text,"html.parser") |
标签树的平行遍历
属性 | 说明 |
---|---|
.next_sibling | 返回按照HTML文本顺序的下一个平行节点标签 |
.previous_sibling | 返回按照HTML文本顺序的上一个平行节点标签 |
.next_siblings | 迭代类型,返回按照HTML文本顺序的后续所有平行节点标签 |
.previous_siblings | 迭代类型,返回按照HTML文本顺序的前序所有平行节点标签 |
平行遍历发生在同一个父节点的各节点间,各平行标签不一定都是标签,有可能是 NavigableString
1 | soup = BeautifulSoup(f.test,"html.parser") |
标签树的平行遍历
1 | for sibling in soup.a.next_siblings: # 遍历 a 标签的下一个平行标签 |
1 | for sibiling in soup.a.previous_siblings: # 遍历 a 标签的上一个标签 |
7、基于bs4库的HTML内容查找方法
1 | soup.find_all("a") # 查找 a 标签 |
1 | import re |
<>.find_all(name,attrs,recursive,string,**kwargs)
返回一个列表类型,存储查找结果
name :对标签名称的检索字符串
attrs:对标签属性值的检索字符串,可标注属性检索
recursive:是否对子孙全部检索,默认为True
string:<>...</>
中字符串区域的检索字符
1 | soup.fina_all('p','course') # 查找还有course属性的P标签 |
1 | import re |
1 | soup.find_all("a",recursive = Flase) # 从soup 的根节点开始,儿子节点中没有 a 标签 |
1 | soup.find_all(string="basic pyhton") # 从soup中检索basic Pyhton |
1 | import re |
<tag>(..)
等价于<tag>.find_all(..)
soup(..)
等价于soup.find_all(..)
soup的find方法扩展
方法 | 说明 |
---|---|
<>.find() | 搜索且只返回一个结果,字符串类型,同.find_all()参数 |
<>.find_parents() | 在先辈节点中搜索,返回列表类型,同.find_all()参数 |
<>.find_parent() | 在先辈节点中返回一个结果,字符串类型,同.find()参数 |
<>.find_next_siblings() | 在后续平行节点中搜索,返回列表类型,同.find_all()参数 |
<>.find_next_sibling() | 在后续平行节点中返回一个结果,字符串类型,同.find()参数 |
<>.find_previous_siblings() | 在前序平行节点中搜索,返回列表类型,同.find_all()参数 |
<>.find_previous_sibling() | 在前序平行节点中返回一个结果,字符串类型,同.find()参数 |
1 | # 爬取最好大学网站的大学排名信息 |
输出结果:
1 | 排名 学校名称 总分 |
四、RE模块
1、正则表达式
正则表达式(regular expression regex RE):正则表达式是用来简洁表达一组字符串的表达式
1 | py+ 表示p后面有几个或多个y的字符串 |
正则表达式特点:
通用的字符串表达框架
简洁表达一组字符串的表达式
针对字符串表达“简洁”和“特征”思想的工具
判断某字符串的特征归属
正则表达式在文本处理中十分常用
- 表达文本类型的特征(病毒、入侵等)
- 同时查找或替换一组字符串
- 匹配字符串的全部或部分区域
正则表达式的使用:
编译:将符合正则表达式语法的字符串转换为正则表达式特征
正则表达式的语法:
1 | P(Y|YT|YTH|YTHON)?N |
正则表达式由字符和操作符构成
操作符 | 说明 | 实例 |
---|---|---|
. | 表示任何单个字符 | |
[] | 字符集,对单个字符给出排除范围 | [abc]表示a,b,c,[a |
[^ ] | 非字符集,对单个字符给出排除范围 | [^abc]表示非a或非b或非c的单个字符串 |
* | 前一个字符0次或无限次扩展 | abc*表示ab,abc,abcc,abccc等 |
+ | 前一个字符1次或 无限次扩展 | abc+表示abc,abcc,abccc等 |
? | 前一个字符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] |
经典正则表达式实例
1 | ^[a-za-z]+$ 由26个字母组成的字符串 |
匹配IP地址的正则表达式:IP地址字符串形式的正则表达式 IP地址分4段,每段0-255,\d+.\d+.\d+.\d+
\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}
,这两个不精确,精确的(([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5]).){3}(1-9)?\d|1\d{2}|2[0-4]\d|25[0-5]
2、Re库的使用
Re库是Pyhton的标准库,主要用于字符串匹配,正则表达式是raw string类型(原生字符串类型),表示为r’text’,原生字符串类型是不包含转义符的字符串,也可以用string类型的,但更为复杂繁琐(因为有转义符)
Re库主要功能函数
函数 | 说明 |
---|---|
re.search() | 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象 |
re.match | 从一个字符串的开始位置起匹配正则表达式,返回match对象 |
re.findall() | 搜索字符串,以列表形式返回全部能匹配的子串 |
re.split() | 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型 |
re.finditer() | 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象 |
re.sub() | 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串 |
1 | re.search(pattern,string,flags=0) |
- 在一个字符串类型中搜索匹配正则表达式的第一个位置,返回match对象
- pattern:正则表达式的字符串或原生字符串表示
- string:待匹配的字符串
- flags :正则表达式使用时的控制标记
flags正则表达式使用时常用的控制标记
常用标记 | 说明 |
---|---|
re.I re.IGNORECASE | 忽略正则表达式的大小写,[A-Z]能匹配小写字符 |
re.M re.MULTILINE | 正则表达式中的^操作符能够将给定字符串的每行当做匹配开始 |
re.S re.DOTALL | 正则表达式中的 . 操作符能够匹配所有中字符,默认匹配除换行外的所有字符 |
1 | import re |
1 | re.match(pattern,string,flags=0) |
- 从一个字符串的开始位置起匹配正则表达式,返回match对象
- pattern:正则表达式的字符串或原生字符串表示
- string:待匹配的字符串
- flags :正则表达式使用时的控制标记
1 | import re |
1 | re.findall(pattern,string,flags=0) |
- 搜索字符串,以列表形式返回 全部能匹配的子串
- pattern:正则表达式的字符串或原生字符串表示
- string:待匹配的字符串
- flags :正则表达式使用时的控制标记
1 | import re |
1 | re.split(pattern,string,maxsplit=0,flags=0) |
- 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型
- pattern:正则表达式的字符串或原生字符串表示
- string:待匹配的字符串
- maxsplit:最大分割数,剩余部分作为最后一个元素输出
- flags :正则表达式使用时的控制标记
1 | import re |
返回
1 | ['BIT','TUS',' '] |
1 | re.finditer(pattern,string,falgs=0) |
- 搜索字符串,返回一个匹配结果的迭代类型,每个迭代类型元素是match对象
- pattern:正则表达式的字符串或原生字符串表示
- string:待匹配的字符串
- flags :正则表达式使用时的控制标记
1 | import re |
1 | re.sub(pattern,repl,string,count=0,flags=0) |
- 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串
- pattern:正则表达式的字符串或原生字符串表示
- repl:替换匹配字符串的字符串
- string:待匹配的字符串
- count:匹配的最大替换次数
- flags :正则表达式使用时的控制标记
1 | import re |
返回
1 | 'BIT:zipcode TUS:zipcode' |
Re的另一种等价用法
1 | rst = re.search(r'[1-9]\d{5}','BIT 100081') #函数式用法,一次调用 |
1 | regex = re.compile(pattern,flags=0) |
- 将正则表达式的字符串形式编译成正则表达式对象
- pattern:正则表达式的字符串或原生字符串表示
- flags:正则表达式使用时的控制标记
函数 | 说明 |
---|---|
regex.search() | 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象 |
regex.match | 从一个字符串的开始位置起匹配正则表达式,返回match对象 |
regex.findall() | 搜索字符串,以列表形式返回全部能匹配的子串 |
regex.split() | 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型 |
regex.finditer() | 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象 |
regex.sub() | 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串 |
3、match对象类型
1 | match = rst = re.search(r'[1-9]\d{5}','BIT 100081') |
运行结果:
1 | 100081 |
match 对象属性:
属性 | 说明 |
---|---|
.string | 带匹配的文本 |
.re | 匹配时使用的pattern对象(正则表达式) |
.pos | 正则表达式搜索文本的开始位置 |
.endpos | 正则表达式搜索文本的结束位置 |
match 对象的方法:
方法 | 说明 |
---|---|
.group() | 获取匹配后的字符串 |
.start() | 匹配字符串在原始字符串的开始位置 |
.end() | 匹配字符串在原始字符串的结束位置 |
.span() | 返回(.start()),(.end()) |
1 | import re |
运行结果:
1 | 'BIT100081 TSU100084' |
4、re库的贪婪匹配和最小匹配
re库默认采用贪婪匹配,即输出匹配最长的子串
1 | match = re.search(r'PY.*N','PYANBNCNDN') |
输出结果:
1 | 'PYANBNCNDN' |
最小匹配:
1 | match = re.search(r'PY.*?N','PYANBNCNDN') |
输出结果:
1 | 'PYN' |
最小匹配操作符
操作符 | 说明 |
---|---|
*? | 前一个字符0次或无限次扩展,最小匹配 |
+? | 前一个字符串1次或无限次扩展,最小匹配 |
?? | 前一个字符0次或1次扩展,最小匹配 |
{m,n}? | 扩展前一个字符m至n次(含n),最小匹配 |
五、selenium 模块
1、安装selenium模块
使用 pip install
命令安装selenium 模块,或者在 pycharm 里面安装,安装步骤和安装 requests 模块相同
2、安装Chromedriver
Chromedriver 是谷歌出的一款无头浏览器,我可可以用 python 中的 selenium 模块调用无头浏览器,模拟认为操作浏览器
查看你浏览器的版本,下载的无头浏览器要和你的浏览器版本相对应,打开浏览器,找到 帮助->关于 Google Chrome 查看浏览器版本,下载对应的无头浏览器
下载完成后,将无头浏览器添加到你 python 的目录中,或者在调用的时候指明无头浏览器的路径
3、selenium模块使用
Chromedriver 的设置
1 | from selenium import webdriver |
- 设置chrome 二进制文件的位置
- 添加启动参数(add_argument)
- 添加扩展应用 (add_extension, add_encoded_extension)
- 添加实验性质的设置参数 (add_experimental_option)
- 设置调试器地址 (debugger_address)
使用
1 | option = webdriver.ChromeOptions() |
这样就会打开无头浏览器
Chromedriver浏览网页
1 | dr.get(url) |
这样就会通过url 打开网页
Chromedriver定位标签
- find_element_by_id() #通过id查找获取
- find_element_by_name() #通过name属性查找
- find_element_by_class_name() #通过class属性查找
- find_element_by_tag_name() #通过标签名字查抄
- find_element_by_link_text() #通过浏览器中可点击的文本查找
- find_element_by_xpath() #通过xpath表达式查找
- find_element_by_css_selector() #通过css选择器查找
1 | driver.find_element_by_id('01') #找到标签 ID 为 01 的标签 |
Chromedriver控制页面
- 设置浏览器窗口大小:driver.set_window_size(480, 800)
- 回退到上一个访问页面:driver.back()
- 前进到下一个访问页面:driver.forward()
- 退出浏览器:driver.quit()
webdriver常用操作
- click() 点击
- send_key(value) 输入值
- clear() 清空输入
- size 元素对应的大小
- text 获取对应元素的文字
1 | driver.find_element_by_id('01').click() |
webdrive中ActionChains类的鼠标动作
- perform():执行所有ActionChains中存储的所有行为
- context_click():右击
- double_click():双击
- drag_and_drop():拖动
- move_to_element():悬浮
- click_and_hold():鼠标按住不松手
- move_to_lelment():拖动到某元素
- move_by_offset(xoffset=50,yoffset=60):按坐标移动
首先我们需要定位到响应元素, 然后使用ActionChains(实例化的浏览器)中鼠标操作(带操作的元素)的各种方法
1 | from selenium.webdriver.common.action_chains import ActionChains |