python爬虫学习系列之正则、bs4、xpath、jsonpath

python爬虫学习系列之正则、bs4、xpath、jsonpath知识要点与对比

mark

正则

正则规则:
	单字符:
       .:除换行符外的所有字符
		[]:[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,'$..*')