——王宇阳—根据mooc课程总结记录笔记(Code_boy)
Requests库:自动爬去HTML页面、自动网络请求提交
robots.txt:网络爬虫排除标准
Beautiful Soup库:解析HTML页面(pycharm中安装bs4即可)
re正则表达式:对HTML数据分析选择
requests.get(url[,params = None,**kwargs])
url:获取网页的url链接
params:url中的额外参数,字典或字节流格式,可选
**kwargs:12个控制访问的参数
Response对象的属性{0.0.py}
属性 | 说明 |
---|---|
r.status_code | HTTP请求的返回状态,200表示连接成功,404表示失败 |
r.text | HTTP响应内容的字符串形式,(即:url对应的页面内容) |
r.encoding | 从HTTP header中猜测的响应内容编码方式 |
r.apparent_encoding | 从内容中分析出的响应内容编码方式(备选编码方式) |
r.content | HTTP响应内容的二进制形式 |
实例:
1 r = requests.get("http://www.baidu.com")
2 print(r.status_code) #返回状态码(内容值为‘200’表示访问成功)
3 #结果: 200
4 print(r.text) #返回响应内容的字符串形式
5 #结果:【即-baidu主页的源代码内容(存在乱码字符)】
6 print(r.encoding) #从HTTP header中猜测的响应内容编码方式
7 #结果: ISO-8859-1 即baidu页面的编码标准(方式 )
8 print(r.apparent_encoding) #从内容中分析出的响应内容编码方式[备选编码方式]
9 #结果: utf-8
10 print(r.content) #内容的二进制形式
11 #结果: 总之看不懂
12
13 r.encoding = 'utf-8' #(utf-8为r.apparent_encoding得到的结果)
14 print(r.text)
15 #结果就为人可看懂的响应内容 主要是通过r.encoding的重新定义类型,而r.text是参照encoding的编码方式
通用爬虫代码框架:
1 def getHTMLText(url):
2 try:
3 r = requests.get(url,timeout = 30)
4 r.raise_for_status() #如果状态不是200 则抛出异常:requests.HTTPError(r.status_code)
5 r.encoding = r.apparent_encoding
6 return r.text
7 except:
8 return "产生异常"
9
10 if __name__ == "__main__":
11 url = 'http://www.baidu.com'
12 print(getHTMLText(url))
Requests库的异常
异常 | 说明 |
---|---|
requests.ConnectionError | 网络连接错误异常,(如DNS查询失败,拒绝连接等) |
requests.HTTPError | HTTP错误异常 |
requests.URLequired | URL缺失异常 |
requests.TooManyRedirects | 超过最大重定向次数,产生重定向异常 |
requests.ConnectTimeout | 连接远程服务器超时异常 |
requests.Timeeout | 请求URL超时,产生超时异常 |
r.raise_for_status | 如果不是200,产生异常: requetst.HTTPError |
通用代码框架实例:
1 def getHTMLText(url):
2 try:
3 r = requests.get(url,timeout = 30)
4 r.raise_for_status() #如果状态不是200 则抛出异常:requests.HTTPError(r.status_code)
5 r.encoding = r.apparent_encoding
6 return r.text
7 except:
8 return "产生异常"
9
10 if __name__ == "__main__":
11 url = 'http://www.baidu.com'
12 print(getHTMLText(url))
Requests库的7个主要方法
方法 | 说明 |
---|---|
requests.request() | 构造一个请求,支撑以下的各方法的基础方法 |
requests.get() | 获取HTML网页的只要方法,对应于HTTP的GET |
requests.head() | 获取HTML网页头信息的方法,对应于HTTP的HEAD |
requests.post() | 向HTML网页提交POST请求的方法,对应于HTTP的POST |
requests.put() | 向HTML网页提交PUT请求的方法,对应于HTTP的PUT |
requests.patch() | 向HTML网页提交局部修改请求,对应于HTTP的PATCH |
requests.delete() | 向HTML网页提交删除请求,对应于HTTP的DELETE |
HTTP协议:
HTTP,Hypertext Transfer Protocol,超文本传输协议
HTTP是一个基于“请求与响应”模式的、无状态的应用层协议。
HTTP采用URL定位网络资源的标识
URL格式: http://host[:port][path]
host:合法的Internet主机域名或IP地址
port:端口号(缺省端口为80)
path:请求资源的路径
HTTP协议对资源的操作:
方法 | 说明 |
---|---|
GET | 请求获取URL位置的资源 |
HEAD | 请求获取URL位置资源的响应信息报告,即获得该资源的头部信息 |
POST | 请求向URL位置的资源后附加新的数据 |
PUT | 请求向URL位置存储一个资源,覆盖原URL位置的资源 |
PATCH | 请求局部更新URL位置的资源,即改变该出资源的部分内容 |
DELETE | 请求删除URL位置存储的资源 |
理解PATCH和PUT的区别
假设URL位置有一组数据UserInfo,包括UserID、UserName等20个字段
需求:用户修改了UserName,其他不变
*采用PATCH,仅向URL提交UserName的局部更新请求【追加】
*采用PUT,必须将所有20个字段一并提交到URL,未提交字段被删除【覆盖追加】
1 #requests库的head()方法 (请求获取URL位置资源的响应信息报告,即获得该资源的头部信息)
2 r = requests.head('http://httpbin.org/get')
3 print(r.headers)
4 '''
5 结果:
6 {'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0',
7 'Date': 'Thu, 22 Nov 2018 03:52:22 GMT', 'Content-Type':
8 'application/json', 'Content-Length': '268',
9 'Access-Control-Allow-Origin': '*',
10 'Access-Control-Allow-Credentials': 'true', 'Via': '1.1 vegur'}
11 '''
12 #requests库的post()方法(请求向URL位置的资源后附加新的数据)
13 payload = {'key1':'value1','key2':'value2'}
14 r = requests.post('http://httpbin.org/post',data = payload)
15 #向URL POST一个字典,自动编码未form(表单)
16 print(r.text)
17 '''
18 结果:
19 {
20 "args": {},
21 "data": "",
22 "files": {},
23 "form": {
24 "key1": "value1",
25 "key2": "value2"
26 },
27 "headers": {
28 "Accept": "*/*",
29 "Accept-Encoding": "gzip, deflate",
30 "Connection": "close",
31 "Content-Length": "23",
32 "Content-Type": "application/x-www-form-urlencoded",
33 "Host": "httpbin.org",
34 "User-Agent": "python-requests/2.20.1"
35 },
36 "json": null,
37 "origin": "106.111.147.213",
38 "url": "http://httpbin.org/post"
39 }
40 '''
41 r = requests.post('http://httpbin.org/post',data = 'abc')
42 #向URL POST一个字符串自动编码为 data
43 print(r.text)
44 '''
45 结果:
46 {
47 "args": {},
48 "data": "abc",
49 "files": {},
50 "form": {},
51 "headers": {
52 "Accept": "*/*",
53 "Accept-Encoding": "gzip, deflate",
54 "Connection": "close",
55 "Content-Length": "3",
56 "Host": "httpbin.org",
57 "User-Agent": "python-requests/2.20.1"
58 },
59 "json": null,
60 "origin": "106.111.147.213",
61 "url": "http://httpbin.org/post"
62 }
63 '''
64 #requests库的put()方法 (请求向URL位置存储一个资源,覆盖原URL位置的资源)
65 payload = {'key1':'value1','key2':'value2'}
66 r = requests.put('http://httpbin.org/put',data = payload)
67 #向URL PUT一个字典,自动编码未form(表单)[和post的区别是会覆盖原有内容]
68 print(r.text)
69 '''
70 结果:
71 {
72 "args": {},
73 "data": "",
74 "files": {},
75 "form": {
76 "key1": "value1",
77 "key2": "value2"
78 },
79 "headers": {
80 "Accept": "*/*",
81 "Accept-Encoding": "gzip, deflate",
82 "Connection": "close",
83 "Content-Length": "23",
84 "Content-Type": "application/x-www-form-urlencoded",
85 "Host": "httpbin.org",
86 "User-Agent": "python-requests/2.20.1"
87 },
88 "json": null,
89 "origin": "106.111.147.213",
90 "url": "http://httpbin.org/put"
91 }
92 '''
requests库的方法功能及使用:
requests.request(method,url,**kwargs)
method:请求方法,对应get/put/post等7种方法
r = requests.request(‘GET’,url,**kwargs)
r = requests.request(‘HEAD,’url,**kwargs)
r = requests.request(‘POST’,url,**kwargs)
r = requests.request(‘PUT’,url,**kwargs)
r = requests.request(‘PATCH’,url,**kwargs)
r = requests.request(‘delete’,url,**kwargs)
r = requests.request(‘OPTIONS’,url,**kwargs)#向服务获取相关的参数
url:拟获取页面的url链接
**kwargs:控制访问的参数,共13个(可选)
params:字典或字节序列,作为参数增加到url中(链接部分系统会添加一个‘?’)
data:字典、字节序列或文件对象,作为Request的内容
json:json格式的数据,作为Request的内容
headers:字典,HTTP定制头
cookies:字典或CookieJar,Request中的cookie *
auth:元组,支持HTTP认证功能 *
files:字典类型,传输文件
timeout:设定超时时间,单位:秒(时间内为返回,则返回异常)
proxies:字典类型,设定访问代理服务器,可以增加登陆认证
pxs={'http':'http://user:pass@10.10.10.1:1234'} r = requests.request('GET','http://www.baidu.com',proxies = pxs)
allow_redirects:True/False,默认为True,重定向开关
stream:True/False,默认为True,获取内容立即下载开关
verify:True/False,默认为True,认证SSL证书开关
cert:本地SSL证书路径
requests.get(url,params=None,**kwargs)*
url:页面的url链接
params:url中额外的参数,字典或字节流格式,可选
**kwargs:12个控制访问的参数
requests.head(url,**kwargs)
url:页面的url链接
**kwargs:13个控制访问的参数
requests.post(url,data=None,json=None,**kwargs)
url:页面的url链接
data:字典、字节序列或文件,Request的内容
json:JSON格式的数据,Request的内容
**kwargs:11个控制访问参数(data、json已使用)
requests.put(url,data=None,**kwargs)
url:页面的url链接
data:字典、字节序列或文件,Request的内容
**kwargs:12个控制访问的参数
requests.patch(url,data=None,**kwargs)
url:页面的url链接
data:字典、字节序列或文件,Request的内容
**kwargs:12个控制访问的参数
requests.delete(url,**kwargs)
url:删除页面的url链接
**kwargs:13个控制访问的参数
网络爬虫的尺寸:
样式 | 爬取网页,玩转网页 | 爬取网站,爬取系列网站 | 爬取全网 |
---|---|---|---|
规模 | 小规模,数据量小的,爬取速度不敏感(>=90%) | 中规模,数据规模较大,爬取速度敏感 | 大规模,搜索引擎,爬取速度关键 |
常用库 | requests库 | scrapy库 | 定制开发 |
网络爬虫的骚扰:
爬虫利用快速功能访问web服务器,服务器很难提供适应高速度爬虫的资源
受限于编写水平的目的,网络爬虫将会为web服务器带来巨大的资源开销
网络爬虫的法律风险:
服务器上的数据有产权归属
网络爬虫获取的数据后牟利将带来法律风险
网络爬虫==”爬亦有道”
限制爬虫条件:
>>>来源审查:判断 User-Agent 进行限制
·检查来访HTTP协议头的User-Agent域,只响应浏览器或友好爬虫的访问
>>>发布公告:Robots协议*
Robots Exclusion Standard 网络爬虫排除标准
作用:告知所有爬虫网站的爬取策略,要求爬虫遵守
形式:在网站根目录下的robots.txt文件
京东Robots协议:(https://www.jd.com/robots.txt)
User-agent: * Disallow: /?* Disallow: /pop/*.html Disallow: /pinpai/*.html?* User-agent: EtaoSpider Disallow: / User-agent: HuihuiSpider Disallow: / User-agent: GwdangSpider Disallow: / User-agent: WochachaSpider Disallow: / | 任何爬虫均应该遵守 不允许访问?开头的路径 不允许访问 /pop/*.html 不允许访问 /pinpai/*.html?* EtaoSpider, HuihuiSpider, GwdangSpider, WochachaSpider 被认定为恶意爬虫 被禁止爬取任何内容 |
---|
Robots协议基本语法:
# | 注释 |
---|---|
* | 代表所有 |
/ | 代表根目录 |
User-agent:* | 指定爬虫(*表示所有爬虫) |
Disallow:/ | 不允许爬虫访问的目录 |
爬虫基础1实例:(代码+总结){0.1.py}
1 #实例:爬取京东商品
2 import requests
3 import os
4 '''
5 r = requests.get('https://item.jd.com/36826348085.html?jd_pop=14982c1c-64d9-4bab-ac5c-e40af7ce62a2&abt=0')
6 print(r.status_code)
7 print(r.encoding)
8 print(r.text[:1000])
9 #实例:爬取亚马逊
10
11 kv = {'User-Agent':'mozilla/5.0'} #打造访问头(伪造成一个浏览器)
12 r=requests.get('https://www.amazon.cn/dp/B073LJR2JF/ref=cngwdyfloorv2_recs_0/459-8865541-1481366?pf_rd_m=A1AJ19PSB66TGU&pf_rd_s=desktop-2&pf_rd_r=AT12APC0BDJ216Y55VSR&pf_rd_r=AT12APC0BDJ216Y55VSR&pf_rd_t=36701&pf_rd_p=d2aa3428-dc2b-4cfe-bca6-5e3a33f2342e&pf_rd_p=d2aa3428-dc2b-4cfe-bca6-5e3a33f2342e&pf_rd_i=desktop',headers=kv)
13 print(r.status_code)
14 print(r.text[:1000])
15 print(r.request.headers)
16 '''
17 #实例:百度搜索
18 #百度搜索的搜索url:http://www.baidu.com/s?wd:{搜索内容}
19 '''
20 keyword = 'python'
21 try:
22 kv = {'wd':keyword}
23 r = requests.get('http://www.baidu.com/s',params = kv)
24 print(r.request.url)
25 r.raise_for_status()
26 print(len(r.text))
27 except:
28 print("爬取失败")
29 '''
30 #案例:网络图片爬取和存储
31 '''
32 url = "http://img0.dili360.com/pic/2018/09/21/5ba4a6af2af103q29157813_t.jpg"
33 rot = "E://"
34 path = rot + url.split('/')[-1] #指定图片保存名为url地址中的后缀名(url)
35 try:
36 if not os.path.exists(rot): #查看有没有rot目录,没有则创建
37 os.mkdir(rot) #创建rot目录
38 if not os.path.exists(path): #查看path目录下有没有path文件
39 r = requests.get(url) #链接url地址
40 #with open(path,'wb') as f: #另一种文件打开和存储方式(???)
41 f = open(path,'wb') #打开文件
42 f.write(r.content) #r.content 响应url的二进制内容
43 f.close()
44 print("保存成功")
45 else:
46 print("文件已存在")
47 except:
48 print("爬取失败")
49 '''
50 #爬单个视频
51 '''
52 url = "http://video.pearvideo.com/mp4/adshort/20181121/cont-1480150-13269168_adpkg-ad_hd.mp4"
53 rot = "E://"
54 path = rot + url.split('/')[-1] #指定视频保存名为url地址中的后缀名(url)
55 try:
56 if not os.path.exists(rot): #查看有没有rot目录,没有则创建
57 os.mkdir(rot) #创建rot目录
58 if not os.path.exists(path): #查看path目录下有没有path文件
59 r = requests.get(url) #链接url地址
60 #with open(path,'wb') as f: #另一种文件打开和存储方式(???)
61 f = open(path,'wb') #打开文件
62 f.write(r.content) #r.content 响应url的二进制内容
63 f.close()
64 print("保存成功")
65 else:
66 print("文件已存在")
67 except:
68 print("爬取失败")
69 '''
70 #IP地址归属地的自动查询
71 #IP168查询链接url:http://ip138.com/ips138.asp?ip=123.125.6.1&action=2
72 #由上得出:IP查询url:http://ip138.com/ips138.asp?ip=ip地址
73
74 url = "http://ip138.com/ips138.asp"
75 ip = {"ip":"106.111.147.213"}
76 try:
77 r = requests.get(url,params=ip)
78 r.raise_for_status()
79 print(r)
80 #print(r.status_code)
81 r.encoding = r.apparent_encoding
82 print(r.text)
83 except:
84 print("爬取失败")
小总结:
实例中可以看出,在搜索方便我们需要掌握url接口(会找、会用):中间利用到了params()函数来在url后添加新的内容,在调用该函数时最后url会在两者的结合处添加一个“?”符号
在网页爬取方面我们也看见了r.text[]的格式出现,这表示我们需要爬取网页源码的字符数;
实例中看见了图片和视频的爬取,并采用二进制的形式保存!
Beautiful Soup库:(pycharm中安装bs4){0.2 bs.py}
库的使用:
1 form bs4 import BeautifulSoup
2
3 soup = BeautifulSoup( ‘ <p>data</p> ‘ , ‘ html.parser ‘ )
Beautiful Soup库的理解:是解析、遍历、维护“标签树”的功能库
*Beautiful Soup库,也叫Beautiful Soup4或者bs4
目前常用的引用方法: for bs4 impor tBeautiful Soup
Beautiful Soup类 <<<等价>>> 标签树 <<<等价>>> HTML<\>
因为形成等价,所以通过Beautiful Soup类使得标签树形成了一个变量;
简单比喻:Beautiful Soup对应HTML/XML文档的全部内容
Beautiful Soup库解析器:
解析器 | 使用方法 | 条件 |
---|---|---|
bs4的HTML解析器 | Beautiful Soup ( mk , ‘html.parser‘ ) | 安装bs4库 |
lxml的HTML解析器 | Beautiful Soup ( mk , ‘lxml’ ) | pip install lxml |
lxml的XML解析器 | Beautiful Soup ( mk , ‘xml’ ) | pip install lxml |
html5lib的解析器 | Beautiful Soup ( mk , ‘html5lib’ ) | pip install html5lib |
Beautiful Soup库的基本元素:
基本元素 | 说明 |
---|---|
Tag | 标签,最基本的信息组织单元,分别用<>和</>标明开头和结尾 |
Name | 标签的名字,<p>...</p>的名字是‘p’,格式:<tag>.name |
Attributes | 标签的属性,字典形式组织,格式:<tag>.attrs |
NavigableString | 标签内非属性性字符串,<>...</>中字符串,格式:<tag>.string |
Comment | 标签内字符串的注释部分,一种特殊的Comment类型 |
Beautiful Soup库的函数:(表中的<tag>指标签)
函数 | 说明 | 备注 |
---|---|---|
soup.<tag> | 获取已被解析过的网页中的标签 | t=soup.<tag> |
[soup.<tag>].name | 获取<tag>标签的名称 | |
[soup.<tag>].parent.name | 获取<tag>标签的上一层标签的名称 | 以此可向上类推 |
[soup.<tag>].attrs | 获取<tag>标签的属性 | |
[soup.<tag>].string | 获取<tag>标签的非属性字符串/注释 |
代码实例(+说明)
1 r = requests.get('http://python123.io/ws/demo.html')
2 print(r.text)
3 demo = r.text #demo == url网页代码
4 soup = BeautifulSoup(demo,'html.parser')
5 print(soup.prettify())
6 r = requests.get('http://python123.io/ws/demo.html')
7 demo = r.text
8 soup = BeautifulSoup(demo,"html.parser")
9 print(soup.title) #查找 title 标签
10 print(soup.a.string) #打印a标签的字符串内容
11 print(soup.a.name) #打印a标签的名字
12 print(soup.a.parent.name) #打印a标签的父标签(上一层标签)
13 print(soup.a.parent.parent.name) #打印a标签的父标签的上一层标签
14 tag = soup.a #soup:查找标签a
15 print(soup.a.attrs) #打印标签的属性
16 print(tag.attrs['class']) #打印标签属性中的‘class’的内容
17 print(tag.attrs['href']) #打印标签属性中的‘href’的内容
18 print(tag) #打印a标签的内容
HTML遍历:
下行遍历:
属 性 | 说 明 |
---|---|
.contents | 子节点的列表,将<tag>所有儿子节点存入列表 |
.children | 子节点的迭代类型,与.contents类似,用于循环遍历儿子节点 |
.descendants | 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历 |
1 r = requests.get('http://python123.io/ws/demo.html') 2 demo = r.text #demo == url网页代码 3 soup = BeautifulSoup(demo,'html.parser') 4 print(soup.head) #获取head标签 5 print(soup.head.contents) #返回形式是列表,list形式检索head, 6 print(soup.body.contents) #获得body标签的儿子节点(返回形式是list) 7 print(len(soup.body.contents)) #利用len测试body标签的节点总数 8 print(soup.body.contents[1]) #list形式返回,用list方式查看其中的一个节点 9 #遍历儿子节点 10 for child in soup.body.children: 11 print(child) 12 #遍历子孙节点 13 for child in soup.body.descendants: 14 print(child) |
上行遍历:
属性 | 说明 |
---|---|
.parent | 节点的父亲标签 |
.parents | 节点先辈标签的迭代类型,用于循环遍历先辈节点 |
1 r = requests.get('http://python123.io/ws/demo.html') 2 demo = r.text #demo == url网页代码 3 soup = BeautifulSoup(demo,'html.parser') 4 print(soup.title.parent) #title标签节点的父辈 >>> <head>标签 5 #上行遍历 6 for parent in soup.a.parents: #从a标签向上遍历父辈 7 if parent is None: 8 print(parent) #先辈如果没有则执行 9 else: 10 print(parent.name) #打印先辈的标签名称 |
平行遍历:
属性 | 说明 |
---|---|
.next_sibling | 返回按照HTML文本顺序的下一个平行节点标签 |
.previous_sibling | 返回按照HTML文本顺序的上一个平行节点标签 |
.next_siblings | 迭代类型,返回HTML文本顺序后续所有平行节点标签 |
.previous_siblings | 迭代类型,返回HTML文本顺序后续所有平行节点标签 |
1 r = requests.get('http://python123.io/ws/demo.html') 2 demo = r.text #demo == url网页代码 3 soup = BeautifulSoup(demo,'html.parser') 4 print(soup.a.next_sibling) #a 的下一个平行节点是字符串 >>> 'and' 5 print(soup.a.next_sibling.next_sibling) #a 的下一个平行标签的在下一个平行标签 6 print(soup.a.previous_sibling) #a 的上一个平行标签 7 print(soup.a.previous_sibling.previous_sibling) #>>> None (空信息) 8 #遍历后续平行节点: 9 for sibling in soup.a.next_siblings: 10 print(sibling) 11 #遍历前序平行节点: 12 for sibling in soup.a.previous_siblings: 13 print(sibling) |
基于bs4库的HTML格式输出:
prettify()函数:打印标签(HTML格式打印)
1 r = requests.get('http://python123.io/ws/demo.html')
2 demo = r.text
3 soup = BeautifulSoup(demo,'html.parser')
4 print(soup.a.prettify()) #输出a标签(HTML格式)
5 '''
6 <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">
7 Basic Python
8 </a>
9 '''
10 soup = BeautifulSoup('<p>中文</p>','html.parser')
11 print(soup.p.string) #以[获取<tag>标签的非属性字符串/注释]输出标签
12 '''
13 中文
14 '''
15 print(soup.p.prettify()) #以HTML格式输出标签
16 '''
17 <p>
18 中文
19 </p>
20 '''
信息组织和提取:{0.3.py}
信息的标记:
标记后的信息可形成信息组织结构,增加了信息维度
标记后的信息可用于通信
存储或展示
标记的结构与信息一样具有重要价值
标记后的信息更利于程序的理解和运用
HTML的信息标记:
HTML(Hyper Text Markup Language):超文本标记语言;是WWW(World Wide Web)的信息组织方式将声音、图像、视频利用超文本的方式嵌入到文本中;
HTML通过预定义的<>...</>标签形式组织不同类型的信息
信息标记的三种形式:( XML \ JSON \ YAML )
XML (eXtensible Markup Language):扩展标记语言(基于HTML)
JSON(JavsScript Object Notation):有类型的键、值对(key:value)表达方式
YAML(YAML Ain’t Markup Language):递归的定义;无类型的键值对 Key : Value
三种信息标记方式的比较:(实例)
XML实例 | |
---|---|
<person> <firstName>Tian</firstName> <lastName>Song</lastName> <address> <streetAddr>中关村南大街5号</streetAddr> <city>北京市</city> <zipcode>100081</zipcode> </address> <prof>Computer System</prof><prof>Security</prof> </person> | 过实例可以看出,其中有效的信息文本比例并不高,大多数信息被标签占用 |
JSON实例 | ||
---|---|---|
{ “firstName” : “Tian” , “lastName” : “Song” , “addresss” : { “streetAddr” : “中关村南大街5号”, “city” : “北京市”, “zipcode” : ”100081” } “prof” : [ “Computer System” , “Security” ] } | JSON通过键值对的方式,定义相关的键<tag>,但无论如何都要用冒号和花括号来区分结构体和键值对 | |
YAML实例 (YAML简洁、明了) | ||
firstName : Tiam lastName : Song address : streetAddr : 中关村南大街5号 city : 北京市 zipcode : 100081 prof : -Computer System -security |
信息标记 | 比较 | 应用 |
---|---|---|
XML | 最早的通用信息标记语言,可扩展性好,但繁琐 | Internet上的信息交互与传递 |
JSON | 信息有类型,适合程序处理(js),较XML简洁 | 移动应用云端和节点的信息交互,无注释 |
YAML | 信息无类型,文本信息比例最高,可读性好 | 各类系统的配置文件,有注释易读 |
信息提取的一般方法:
方法一:完整解析信息的标记形式,再提取关键信息。
XML JSON YAML_需要标记解析器,例如:bs4库的标签树遍历
优点:信息解析准确
缺点:提取过程繁琐,速度慢
方法二:无视标记形式,直接搜索关键信息。
搜索 对信息的文本查找函数即可
优点:提取过程简洁,速度较快
缺点:提取结果准确性与信息内容相关(缺乏)
融合方法:结合形式解析与搜索方法,提取关键信息
XML JSON YAML + 搜索 >>> 需要标记解析器及文本查找函数
实例:
1 import requests
2 from bs4 import BeautifulSoup
3 '''#提取HTML中所有的URL链接
4 1、搜索到所有的<a>标签(a标签的内容即url)
5 2、解析<a>标签格式,提取href后的链接内容
6 '''
7
8
9 r = requests.get('http://python123.io/ws/demo.html')
10 demo = r.text
11 soup = BeautifulSoup(demo,'html.parser')
12 for link in soup.find_all('a'): #查找demo中的<a>标签
13 print(link.get('href')) #在<a>标签中查找“href”函数
14
15
16 '''
17 结果:
18 http://www.icourse163.org/course/BIT-268001
19 http://www.icourse163.org/course/BIT-1001870001
20 '''
Beautiful Soup库的方法:
bs4.element.Tag:标签类型;
方法 | <>.find_all (name,attrs,recursive,string,**kwargs) | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
说明 | 可以在soup的变量中去查找里边的信息 | ||||||||||||||||
参数 | 返回一个列表类型,存储查找的结果 参数 说明 提升 name 对标签的名称的检索字符串 如果对多个内容检索,则可以用list方式 attrs 对标签属性值的检索字符串 可标注属性索引 recursive 是否对子孙全部检索(默认True) False则只对儿子层检索 string <>...</>中字符串区域的检索字符串 string=’....’ | 参数 | 说明 | 提升 | name | 对标签的名称的检索字符串 | 如果对多个内容检索,则可以用list方式 | attrs | 对标签属性值的检索字符串 | 可标注属性索引 | recursive | 是否对子孙全部检索(默认True) | False则只对儿子层检索 | string | <>...</>中字符串区域的检索字符串 | string=’....’ | |
参数 | 说明 | 提升 | |||||||||||||||
name | 对标签的名称的检索字符串 | 如果对多个内容检索,则可以用list方式 | |||||||||||||||
attrs | 对标签属性值的检索字符串 | 可标注属性索引 | |||||||||||||||
recursive | 是否对子孙全部检索(默认True) | False则只对儿子层检索 | |||||||||||||||
string | <>...</>中字符串区域的检索字符串 | string=’....’ | |||||||||||||||
简短检索 | <tag>(...) 等价于 <tag>.find_all(..) soup (...) 等价于 soup.find_all(..) | ||||||||||||||||
扩展方法 | 方法 说明 <>.find() 搜索且只返回一个结果,字符串(string)类型, <>.find_parents() 在先辈节点中搜索,返回list类型, <>.find_parent() 在先辈节点中返回一个节点,string类型, <>.find_next_siblings() 在后续平行节点中搜索,返回list类型, <>.find_next_sibling() 在后续平行节点中返回一个结果,string类型 <>.find_previous_siblings() 在前序平行节点中搜索,返回list类型, <>.find_previous_sibling() 在前序平行节点中返回一个结果,string类型 | 方法 | 说明 | <>.find() | 搜索且只返回一个结果,字符串(string)类型, | <>.find_parents() | 在先辈节点中搜索,返回list类型, | <>.find_parent() | 在先辈节点中返回一个节点,string类型, | <>.find_next_siblings() | 在后续平行节点中搜索,返回list类型, | <>.find_next_sibling() | 在后续平行节点中返回一个结果,string类型 | <>.find_previous_siblings() | 在前序平行节点中搜索,返回list类型, | <>.find_previous_sibling() | 在前序平行节点中返回一个结果,string类型 |
方法 | 说明 | ||||||||||||||||
<>.find() | 搜索且只返回一个结果,字符串(string)类型, | ||||||||||||||||
<>.find_parents() | 在先辈节点中搜索,返回list类型, | ||||||||||||||||
<>.find_parent() | 在先辈节点中返回一个节点,string类型, | ||||||||||||||||
<>.find_next_siblings() | 在后续平行节点中搜索,返回list类型, | ||||||||||||||||
<>.find_next_sibling() | 在后续平行节点中返回一个结果,string类型 | ||||||||||||||||
<>.find_previous_siblings() | 在前序平行节点中搜索,返回list类型, | ||||||||||||||||
<>.find_previous_sibling() | 在前序平行节点中返回一个结果,string类型 |
name实例:
1 r = requests.get('http://python123.io/ws/demo.html')
2 demo = r.text
3 soup = BeautifulSoup(demo,"html.parser")
4 print(soup.find_all('a')) #list形式返回soup(文本)中的标签字符串(检索a标签)
5 print(soup.find_all(['a','b']))
6 for tag in soup.find_all(True): #遍历soup中的所有标签
7 print(tag.name) #打印(获得)所有标签的name(名称)!!
8 '''
9 [<a class="py1" href="http://www.icourse163.org/course/BIT-268001"
10 id="link1">Basic Python</a>, <a class="py2" href="http://www.icourse163.org
11 /course/BIT-1001870001" id="link2">Advanced Python</a>]
12 [<b>The demo python introduces several python courses.</b>, <a class="py1"
13 href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic
14 Python</a>, <a class="py2" href="http://www.icourse163.org/course/
15 BIT-1001870001" id="link2">Advanced Python</a>]
16 html
17 head
18 title
19 body
20 p
21 b
22 p
23 a
24 a
25 '''#结果
attrs实例:
1 r = requests.get('http://python123.io/ws/demo.html')
2 demo = r.text
3 soup = BeautifulSoup(demo,'html.parser')
4 print(soup.find_all('p', 'course')) #返回带有course属性的P标签
5 print(soup.find_all(id='link1')) #返回ID属性==link1的内容
6
7 ‘’’
8
9 [<p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:
10
11 <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a> and <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">Advanced Python</a>.</p>]
12
13 [<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>]
14
15 ‘’’
recursive实例:
1 r = requests.get('http://python123.io/ws/demo.html')
2 demo = r.text
3 soup = BeautifulSoup(demo,'html.parser')
4 print(soup.find_all('a')) #检索a标签
5 print(soup.find_all('a',recursive=False))#返回一个空列表,说明a的下层节点没有a标签(False下只检索一层(儿子层))
string实例:
1 r = requests.get('http://python123.io/ws/demo.html')
2 demo = r.text
3 soup = BeautifulSoup(demo,'html.parser')
4 print(soup) #输出经过解析器解析的完整HTML代码
5 print(soup.find_all(string = 'Basic Python')) #列表返回检索到的字符串信息
中国大学排名定向爬虫案例{0.4 bs.py}
1 import requests
2 import bs4
3 from bs4 import BeautifulSoup
4 #soup = BeautifulSoup(demo,'html.parser')
5 def getHTML(url):#标准框架
6 try:
7 r = requests.get(url)
8 r.raise_for_status()
9 r.encoding = r.apparent_encoding
10 return (r.text)
11 except:
12 print("链接爬取失败")
13
14 def uitHTML(ulist,demo):
15 soup = BeautifulSoup(demo,'html.parser')
16 for tr in soup.find('tbody').children:
17
18 #.find(..)搜索标签结果返回 .children同时遍历儿子(下层)节点
19 if isinstance(tr,bs4.element.Tag):
20
21 #检测tr的类型,如果不是标签类型,则过滤(避免遍历到字符串内容)isinstance判断变量类型
22 tds = tr('td') #等价于tr.find(...)>>>查找 td 标签 (返回的对象是list)
23 #print(tds) #检验使用:查看tds内容
24 #tds此时得到了url的HTML中所有tr中td标签的HTML格式(list格式返回)
25 ulist.append([tds[0].string, tds[1].string, tds[3].string])
26 #将tds中需要的数据(只提取.string>>字符串 区域)存入ulist列表中
27
28 def uitUlist(ulist,num):
29 print("{:^16}\t{:^16}\t{:^16}".format("排名","学校名称","总分"))
30 for i in range(num):
31 u = ulist[i]
32 print("{:^16}\t{:^16}\t{:^16}".format(u[0],u[1],u[2]))
33
34 def main():
35 ulist = []
36 url = "http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html"
37 demo = getHTML(url)
38 uitHTML(ulist,demo)
39 uitUlist(ulist,20)
40
41 main()
正则表达式:re库:(https://www.cnblogs.com/wangyuyang1016/p/10034868.html)
正则表达式的常用操作符
. | 表示任何单个字符 | |
---|---|---|
[ ] | 字符集,对单个字符给出取值范围 | [a,b,c]表示a,b,c ; [a-z]表示a到z的单个字符 |
[^] | 非字符集,对单个字符给出排除范围 | [^a,b,c]表示非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] |
re库主要功能函数
re.search() | 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象 |
---|---|
re.match() | 在一个字符串的开始位置起匹配正则表达式,返回match对象 |
re.findall() | 搜索字符串,以列表类型返回全部能匹配的子串 |
re.split() | 将一个字符串按照正则表达式匹配结果进行分割,返回list类型 |
re.finditer() | 搜索字符串,返回一个匹配结果的迭代(iterable)类型,每个iterable元素是match对象 |
re.sub() | 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串 |
功能函数详讲:
语法 | re.search ( pattern , string , flags=0 ) | ||||||
---|---|---|---|---|---|---|---|
参数 说明 | pattern 正则表达式的字符串或原生字符串表示 string 待匹配的字符串(string) flags 正则表达式使用时的控制标记 | pattern | 正则表达式的字符串或原生字符串表示 | string | 待匹配的字符串(string) | flags | 正则表达式使用时的控制标记 |
pattern | 正则表达式的字符串或原生字符串表示 | ||||||
string | 待匹配的字符串(string) | ||||||
flags | 正则表达式使用时的控制标记 | ||||||
flags控制标记 | re.I 忽略正则表达式的大小写 re.M (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 re.S 正则中的 . 操作符能够匹配所有字符 | re.I | 忽略正则表达式的大小写 | re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | re.S | 正则中的 . 操作符能够匹配所有字符 |
re.I | 忽略正则表达式的大小写 | ||||||
re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | ||||||
re.S | 正则中的 . 操作符能够匹配所有字符 |
语法 | re.match ( pattern , string , flags=0 ) | ||||||
---|---|---|---|---|---|---|---|
参数 说明 | pattern 正则表达式的字符串或原生字符串表示 string 待匹配的字符串(string) flags 正则表达式使用时的控制标记 | pattern | 正则表达式的字符串或原生字符串表示 | string | 待匹配的字符串(string) | flags | 正则表达式使用时的控制标记 |
pattern | 正则表达式的字符串或原生字符串表示 | ||||||
string | 待匹配的字符串(string) | ||||||
flags | 正则表达式使用时的控制标记 | ||||||
flags控制标记 | re.I 忽略正则表达式的大小写 re.M (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 re.S 正则中的 . 操作符能够匹配所有字符 | re.I | 忽略正则表达式的大小写 | re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | re.S | 正则中的 . 操作符能够匹配所有字符 |
re.I | 忽略正则表达式的大小写 | ||||||
re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | ||||||
re.S | 正则中的 . 操作符能够匹配所有字符 |
语法 | re.findall ( pattern , string , flags=0 ) | ||||||
---|---|---|---|---|---|---|---|
参数 说明 | pattern 正则表达式的字符串或原生字符串表示 string 待匹配的字符串(string) flags 正则表达式使用时的控制标记 | pattern | 正则表达式的字符串或原生字符串表示 | string | 待匹配的字符串(string) | flags | 正则表达式使用时的控制标记 |
pattern | 正则表达式的字符串或原生字符串表示 | ||||||
string | 待匹配的字符串(string) | ||||||
flags | 正则表达式使用时的控制标记 | ||||||
flags控制标记 | re.I 忽略正则表达式的大小写 re.M (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 re.S 正则中的 . 操作符能够匹配所有字符 | re.I | 忽略正则表达式的大小写 | re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | re.S | 正则中的 . 操作符能够匹配所有字符 |
re.I | 忽略正则表达式的大小写 | ||||||
re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | ||||||
re.S | 正则中的 . 操作符能够匹配所有字符 |
语法 | re.split ( pattern , string , maxsplit=0 , flags=0 ) | ||||||||
---|---|---|---|---|---|---|---|---|---|
参数 说明 | pattern 正则表达式的字符串或原生字符串表示 string 待匹配的字符串(string) maxsplit 最大分割数,剩余部分作为最后一个元素输出 flags 正则表达式使用时的控制标记 | pattern | 正则表达式的字符串或原生字符串表示 | string | 待匹配的字符串(string) | maxsplit | 最大分割数,剩余部分作为最后一个元素输出 | flags | 正则表达式使用时的控制标记 |
pattern | 正则表达式的字符串或原生字符串表示 | ||||||||
string | 待匹配的字符串(string) | ||||||||
maxsplit | 最大分割数,剩余部分作为最后一个元素输出 | ||||||||
flags | 正则表达式使用时的控制标记 | ||||||||
flags控制标记 | re.I 忽略正则表达式的大小写 re.M (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 re.S 正则中的 . 操作符能够匹配所有字符 | re.I | 忽略正则表达式的大小写 | re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | re.S | 正则中的 . 操作符能够匹配所有字符 | ||
re.I | 忽略正则表达式的大小写 | ||||||||
re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | ||||||||
re.S | 正则中的 . 操作符能够匹配所有字符 |
语法 | re.finditer ( pattern , string , flags=0 ) | ||||||
---|---|---|---|---|---|---|---|
参数 说明 | pattern 正则表达式的字符串或原生字符串表示 string 待匹配的字符串(string) flags 正则表达式使用时的控制标记 | pattern | 正则表达式的字符串或原生字符串表示 | string | 待匹配的字符串(string) | flags | 正则表达式使用时的控制标记 |
pattern | 正则表达式的字符串或原生字符串表示 | ||||||
string | 待匹配的字符串(string) | ||||||
flags | 正则表达式使用时的控制标记 | ||||||
flags控制标记 | re.I 忽略正则表达式的大小写 re.M (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 re.S 正则中的 . 操作符能够匹配所有字符 | re.I | 忽略正则表达式的大小写 | re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | re.S | 正则中的 . 操作符能够匹配所有字符 |
re.I | 忽略正则表达式的大小写 | ||||||
re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | ||||||
re.S | 正则中的 . 操作符能够匹配所有字符 |
语法 | re.sub ( pattern , repl , string , count=0 , flags=0 ) | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
参数 说明 | pattern 正则表达式的字符串或原生字符串表示 repl 替换匹配字符串的字符串 string 待匹配的字符串(string) count 替换匹配的最大次 flags 正则表达式使用时的控制标记 | pattern | 正则表达式的字符串或原生字符串表示 | repl | 替换匹配字符串的字符串 | string | 待匹配的字符串(string) | count | 替换匹配的最大次 | flags | 正则表达式使用时的控制标记 |
pattern | 正则表达式的字符串或原生字符串表示 | ||||||||||
repl | 替换匹配字符串的字符串 | ||||||||||
string | 待匹配的字符串(string) | ||||||||||
count | 替换匹配的最大次 | ||||||||||
flags | 正则表达式使用时的控制标记 | ||||||||||
flags控制标记 | re.I 忽略正则表达式的大小写 re.M (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 re.S 正则中的 . 操作符能够匹配所有字符 | re.I | 忽略正则表达式的大小写 | re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | re.S | 正则中的 . 操作符能够匹配所有字符 | ||||
re.I | 忽略正则表达式的大小写 | ||||||||||
re.M | (多行匹配)的 ^ 操作符能够将给定的字符串的每行当作匹配开始 | ||||||||||
re.S | 正则中的 . 操作符能够匹配所有字符 |
re.compile():面向对象用法(操作):
语法 | regex = re.compile( pattern , flags = 0) | ||||
---|---|---|---|---|---|
说明 | ·将正则表达式的字符串形式编译成正则表达式对象(object) pattern 正则表达式的字符串或原生字符串表示(正则表示方法) flags 正则表达式使用时的控制标记 match = regex.search(‘string’) #compile()后的re函数功能使用和原来一样 | pattern | 正则表达式的字符串或原生字符串表示(正则表示方法) | flags | 正则表达式使用时的控制标记 |
pattern | 正则表达式的字符串或原生字符串表示(正则表示方法) | ||||
flags | 正则表达式使用时的控制标记 |
Match对象的属性
属性 | 说明 |
---|---|
.string | 待匹配的文本 |
.re | 匹配时使用的pattern对象(正则表达式) |
.pos | 正则表达式搜索文本的开始位置 |
.endpos | 正则表达式搜索文本的结束位置 |
Match对象的方法
方法 | 说明 |
---|---|
.group(0) | 获得匹配后的字符串 |
.start() | 匹配字符串在原始字符串的开始位置 |
.end() | 匹配字符串在原始字符串的结束位置 |
.span() | 返回(.start() , .end()) |
实例:
1 import re
2 match = re.search(r'[1-9]\d{5}','BIT 100081')
3 #Match对象的属性
4 print(match.string) #返回匹配时的待匹配字符串
5 print(match.re) #返回匹配时的re表达式
6 print(match.pos) #返回匹配的搜索文本开始的位置
7 print(match.endpos) #返回匹配的搜索文本结束的位置
8 #Match对象的方法
9 print(match.group(0)) #返回匹配后的字符串(第一次匹配结果)
10 print(match.start()) #匹配原始字符串开始的位置
11 print(match.end()) #匹配原始字符串结束的位置
12 print(match.span()) #元组形式返回 .start()和.end()
re库的贪婪匹配和最小匹配
1 match = re.search(r'PY.*N','PYANBNCNDN') #贪婪匹配(re库默认贪婪匹配,即输出最长的子串匹配)
2 print(match.group(0))
3 match = re.search(r'PY.*?N','PYANBNCNDN') #最小匹配
4 print(match.group(0))
最小匹配操作符
操作符 | 说明 |
---|---|
*? | 前一个字符0次或无限次扩展,最小匹配 |
+? | 前一个字符1次或无限次扩展,最小匹配 |
?? | 前一个字符0次或1次扩展,最小匹配 |
{m,n}? | 扩展前一个字符m至n次(含n),最小匹配 |