python爬虫学习系列之正则、bs4、xpath、jsonpath知识要点与对比
正则
正则规则:
单字符:
.:除换行符外的所有字符
[]:[aoe]a,o,e之中的一个;[a-w]a到w之间任意一个字符
\d:数字[0-9]
\D:非数字
\w:数字,字母,下划线,中文
\W:非\w
\s:所有的空白字符
\S:所有的非空白
数量修饰:
*:任意多次,大于等于0次
+:至少一次,>=1
?:可有可无,0次或者1次
{m}:固定m次
{m,}:至少m次
{m,ne}:至少m次,最大n次
边界
\b
\B
$:以某某开头
^:以某某结尾
分组:
():(ab){4}视为一个整体,
():子模式 \组模式 \1,\2
贪婪模式:
.*?
.+?
match :只从最开始找
search:从任意位置找
findall:找所有
re.sub(正则表达式,替换的内容,查找的字符串)
import re
string='<p><div><span>猪八戒</span></div></p>'
pattern=re.compile(r'<(\w+)><(\w+)>\w+</\2></\1>')
ret=pattern.search(string)
print(ret)
#输出:<re.Match object; span=(3, 30), match='<div><span>猪八戒</span></div>'>
string='<p><div><div>猪八戒<div></div></p>'
pattern=re.compile(r'<div>.*</div>')
# pattern=re.compile(r'<div>.*?</div>')
ret=pattern.search(string)
print(ret)
#输出:<re.Match object; span=(3, 27), match='<div><div>猪八戒<div></div>'>
# re.I:忽略大小写
# re.M:多行匹配
# re.S:单行匹配
string='''hate is a beautiful feel
love you
love
love '''
pattern=re.compile(r'^love',re.M)
ret=pattern.findall(string)
print(ret)
#输出:['love', 'love', 'love']
string1='''<div>沁园春-雪
北国风光
千里冰封
万里雪飘
望长城内外
惟余莽莽
大河上下
顿失滔滔
山舞银蛇
原驰蜡象
欲与天公试比高
</div>'''
pattern=re.compile(r'<div>(.*?)</div>',re.S)
ret=pattern.findall(string1)
print(ret)
#输出:['沁园春-雪\n北国风光\n千里冰封\n万里雪飘\n望长城内外\n惟余莽莽\n大河上下\n顿失滔滔\n山舞银蛇\n原驰蜡象\n欲与天公试比高\n']
# match :只从最开始找
# search:从任意位置找
# findall:找所有
# re.sub(正则表达式,替换的内容,查找的字符串)
string ='i love you,you love me'
pattern=re.compile(r'love')
ret=re.sub(pattern,'hate',string)
print(ret)
#输出:i hate you,you hate me
ret=re.sub(r'love','hate',string)
print(ret)
#输出:i hate you,you hate me
ret=pattern.sub('hate',string)
print(ret)
#输出:i hate you,you hate me
print(string)
#输出:i love you,you love me
# 正则替换
def fn(a):
ret=int(a.group())
return str(ret-10)
string='我喜欢身高为175的女孩'
pattern=re.compile(r'\d+')
ret=pattern.sub(fn,string)
print(ret)
#输出:我喜欢身高为165的女孩
bs4
BeautifulSoup 可以将html文件转换为指定的对象,然后通过对象的方法或属性查找指定的内容
需要安装pip install bs4
bs4 在使用的时候需要一个第三方库,也需要安装
pip install lxml
from bs4 import BeautifulSoup
#转化网络文件
soup=BeautifulSoup('字符串类型或字节类型','lxml')
#转化本地文件
soup=BeautifulSoup(open('soup_text.html',encoding='utf8'),'lxml')
#根据标签名查找,只能找到第一个符合要求的标签
print(soup.a)
#找到标签的属性
print(soup.a['href'])
print(soup.a['title'])
print(soup.a['target'])
#返回a标签的所有属性,返回一个字典:{'href': 'http://www.zyglz.com', 'title': '资源管理站', 'target': '__self'}
print(soup.a.attrs)
#取某一个属性
print(soup.a.attrs['href'])
#获取标签里面的内容
print(soup.div.string)#只获取标签里面的文本文字,如果内容里面有其他标签则,为none
print(soup.div.text)
print(soup.div.get_text())
#find 找到第一个符合要求的标签
print(soup.find('a'))
print(soup.find('a',title="清"))
print(soup.find('a',alt="唐"))
print(soup.find('a',class_="明"))#注意class为关键字,需要用class_
print(soup.find('a',id="du"))
print(soup.find('div',class_='tang').find('a',class_='明'))
print(soup.find(name='a')
print(soup.find(attrs={'class':'title'})#返回class属性为title的第一个标签
print(soup.find(text='title')
print(soup.find(
name='a',
attrs={'class':'title'},
)
# findall
print(soup.find_all('a'))
print('*'*50)
print(soup.find('div',class_='tang').find_all('a'))
print('*'*50)
print(soup.find_all(['b','span']))#查找多个
print('*'*50)
print(soup.find_all('a',limit=2))#只要前两个
print(soup.find_all('a',attrs={'class':'tang'}))
#select根据选择器选择制定的内容,返回的是一个列表,
#常用的选择器,标签选择器,类选择器,id选择器,并集选择器,层级选择器,伪类选择器,属性选择器,组合选择器
#a
#.dudu
#a,.dudu,#lala
#div a .dudu #lala .xx
#div > p > a 只能是次级
#input[name="username"]
print(soup.select('.tang>ul>li>a')[1])
print(soup.select('#du')[0].text)
print(soup.select('#du')[0].get_text())
print(soup.select('#du')[0]['href'])
print(soup.select('#du')[0]get('href'))
#select_one
print(select_one('.tan',limit=1)
xpath
xml是用来储存和传输数据的、与html的区别:
1、html 是用来显示数据,xml是用来传输数据;
2、html的标签是固定的,xml的标签是自定义的
xpath是一门在xml文档中查找信息的语言,他是一种路径表达式;需要安装第三方插件,pip install lxml
常用的路径表达式:
1、//:不考虑位置的查找
2、./:从当前节点开始往下查找
3、..:当前节点的父节点
4、@:选取属性
使用:安装谷歌xpath插件:xpath helper 2.0.2
导入库:from lxml import etree
使用两种方法:将html文件变成一个对象,然后调用对象的方法去查找指定的节点
1、本地文件:tree=etree.parse(文件名)
2、网络文件:tree=etree.HTML(网页字符串)
ret=tree.xpath(路径表达式) ret是一个列表
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
# 1、/bookstore/book: 选取根节点的bookstore所有的子元素book(直接子节点)
# 2、//book: 选取所有的book
# 3、bookstore//book: 选取bookstore元素的后代所有book元素
# 4、//@lang: 属性为lang的所有节点
# 5、/bookstore/book[1]: 选取根节点的bookstore第一个book子元素(直接子节点)
# 6、/bookstore/book[last()]: 选取根节点的bookstore最后一个book子元素(直接子节点)
# 7、/bookstore/book[position()<3]: 选取根节点的bookstore前两个book子元素(直接子节点)
# 8、//title[@lang] :选取所有带有lang属性的title的元素
# 9、//title[@lang='eng'] :选取所有lang属性为eng的title的元素
# 10、/bookstore/book[price>35]: 选取根节点的bookstore的所有book子元素(且其中的price子元素的值大于35)
# *:通配符,任何元素节点
# /bookstore/*:bookstore元素下的所有节点
# //*:文档中所有节点
# //title[@*]:选取所有带有属性的title节点
# //input[@id="kw"]
# //*[@class="btn self-btn bg s_btn"]
# //div[@id="head"]/div/a[1]
# 逻辑运算://input[@class="s_ipt" and @name="wd"]
# 模糊匹配:
# 1、contais://input[contains(@class,"s_i")]:所有的input,有class属性,属性中带有s_i的节点
# '//li[contains(text(),"爱")]/text()'
# 2、starts-with://input[starts-with(@class,"s")]:所有的input,有class属性,属性以s开头的节点
# 取文本:
# //div[@id="u_sp"]/a[5]/text():取a链接的文本
# //div[@id="u_sp"]/text():取div下的文本
# //div[@id="u_sp"]//text():取div下所有子节点的文本
#
# //直接将内容拼接,替换换行和制表符
# ret=tree.xpath('//div[@class="song"]')
# string=ret[0].xpath('string(.)')
# pr(string.replace('\n','').replace('\t',''))#
#
# 获取属性:
# //div[@id="u_sp"]/a[5]/@href:获取a链接的href属性值
jsonpath
jsonpath:用来解析json数据使用
python处理json格式用到的函数:
json.dumps():将字典或者列表转换为json格式的字符串
json.loads():将json格式的字符串转换为python对象
json.dump():将字典或列表转换为json格式字符串,并且写入到文件中
json.load():从文件中读取json格式字符串,转换为python对象
前端处理:
JSON.parse('json格式字符串')
eval('('+json格式字符串+')')
安装:pip install jsonpath
jsonpath:是xpath在json的应用。
JSONPath 是参照,xpath表达式来解析xml文档的方式,json数据结构通常是匿名的并且不一定需要有根元素。JSONPaht 用一个抽象的名字$来表示最外层对象。
JOSNPath 表达式可以使用“.”符号如下:
$.store.book[0].title
或者使用[] 符号
$['store']['book'][0]['title']
jsonpath 与 xpath的对比
XPath | JSONPath | Description |
---|---|---|
/ | $ | 表示根元素 |
. | @ | 当前元素 |
/ | . or [] | 子元素 |
.. | n/a | 父元素 |
// | .. | 递归下降,JSONPath是从E4X借鉴的。 |
* | * | 通配符,表示所有的元素 |
@ | n/a | 属性访问字符 |
[] | [] | 子元素操作符 |
| | [,] | 连接操作符在XPath 结果合并其它结点集合。JSONP允许name或者数组索引。 |
n/a | [start🔚step] | 数组分割操作从ES4借鉴。 |
[] | ?() | 应用过滤表示式 |
n/a | () | 脚本表达式,使用在脚本引擎下面。 |
() | n/a | Xpath分组 |
[]在xpath表达式总是从前面的路径来操作数组,索引是从1开始。
使用JOSNPath的[]操作符操作一个对象或者数组,索引是从0开始。
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{ "category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
import json
import jsonpath
#将json字符串转换为python对象
obj=json.load(open('bookstore.json','r',encoding='utf8'))
#查找book下所有的author
#[]里面写0的话,代表查询第一本书的anthor
ret=jsonpath.jsonpath(obj,'$.store.book[*].author')
#查找所有的author
ret=jsonpath.jsonpath(obj,'$..author')
#store的所有元素。所有的bookst和bicycle
ret=jsonpath.jsonpath(obj,'$.store.*')
#查询store下面所有的price
ret=jsonpath.jsonpath(obj,'$.store..price')
#查找第三个book节点,返回一个列表
ret=jsonpath.jsonpath(obj,'$..book[2]')
#查询最后一本book
ret=jsonpath.jsonpath(obj,'$..book[(@.length-1)]')
#查找前两本book
# ret=jsonpath.jsonpath(obj,'$..book[0,1]')
ret=jsonpath.jsonpath(obj,'$..book[:2]')
#过滤出所有的包含isbn的书。
ret=jsonpath.jsonpath(obj,'$..book[?(@.isbn)]')
#过滤出所有价格低于10的书。
ret=jsonpath.jsonpath(obj,'$..book[?(@.price<10)]')
#所有元素。
ret=jsonpath.jsonpath(obj,'$..*')