python解析库
一、写在前面
对于网页来说,对应的id、class、name等等,我们不能只能通过re来实现网页的检索,这比较繁琐,所以python引入了解析库,当然,re也属于python解析库,但其操作较为繁琐。所以我们引入了Xpath、Beautiful soup、pyquery等,它们可以通过css,元素的属性,节点之间的层次关系来更快,高效的获得我们所需要的的元素和其属性。
二、内容
(一)Xpath概述与使用
Xpath全称XML Path Language,即XML路径语言,在做爬虫的时候,我们完全能用XPath做相应的信息提取。
XPath常用规则
表达试 | 描述 |
nodename | 选取此节点的所有子节点 |
\ | 从当前节点选取直接子节点 |
\\ | 从当前节点选取子孙节点 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
直接对文本进行解析:
from lxml import etree
html = etree.parse(’./test.html’, etree.HTMLParser())
result = etree.tostring(html)
print(result.decode(‘utf-8’))
实例
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//title[@lang = 'eng']/text()')
所有的节点
result = html.xpath('//*') 返回element类型
子节点
result = html.xpath('//li/a') 返回li节点的所有a节点
父节点
result = html.xpath('//a/parent::*/@class')
属性匹配
result = html.xpath('//li[@class="item-0"]')
文本获取
result = html.xpath('//li[@class='item-0']/text()')
属性获取
result = html.xpath('//li/a/@href')
多属性匹配
result = html.xpath('//li[contains(@class, 'li') and @name = 'item']/a/text()')
逻辑运算符
运算符 | 描述 |
or | 或 |
and | 与 |
| | 计算两个点集 |
按序选择
result = html.xpath("li[last()]/a/text()")
result = html.xpath("li[first()]/a/text()")
result = html.xpath("li[position() < 3]/a/text()")
result = html.xpath("li[1]/a/text()")
注意:xpath中没有索引0这个,第一个就是1
节点轴选择
Xpath提供很多节点轴的选择方法,包括获取子元素,兄弟元素,父元素,祖先元素等等
返回祖先节点
result = html.xpath("//ancestor::(此处可以加限制条件 如:div,li等)")
返回所有属性值
result = html.xpath("//li/attribute::(可以加限制条件 如:name,href等)")
子孙节点
result = html.xpath("//li/descendant::(可以加限制条件 如:name,href等)")
(二)Beautiful Soup
简介
Beautiful Soup提供一些简单的、python式的函数来处理导航、搜索、修改分析树等,他是一个工具箱,通过解析文档为用户提供需要爬取的数据,因为简单,所以可以使用很少的代码完成一个完整的应用程序。
Beautiful Soup自动将输入的文档转换为Unicode编码,输出文档为utf—8编码。不需要考虑编码问题。除非文档制定一个编码方式。
Beautiful Soup已经成为和lxml,html6lib一样出色的python解释器,为用户灵活的提供不同的解析策略或强劲的速度。
解析器
解析器 | 使用方法 | 劣势 | 优势 |
python标准库 | Beautiful.soup(markup,“html.parser”) | 速度适中,文档容错性强 | 低版本容错性差 |
lxml html解析器 | Beautiful.soup(markup,‘lxml’) | 速度快,容错能力强 | 需要安装c语言库 |
lxml XML解析器 | Beautiful.soup(markup,“xml”) | 速度快,唯一支持xml的解析器 | 需要安装c语言库 |
html5lib | Beautiul,soup(markup,“html5lib”) | 最好的容错性,以浏览器的方式解析文档,生成html5格式的文档 | 速度慢,不依赖外部扩展 |
通过对比,lxml解析器有解析html和xml的功能,而且速度快,容错能力强,所以推荐使用lxml。
使用lxml时,需要先初始化Beautiful soup
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup.title.string)
prettify()方法,可以把要解析的字符串以标准缩进格式输出,里面包含body和html节点,对于不标准的html字符串,BeautifulSoup可以自动更改格式,不是由prettify()方法实现的。
在这里,soup.title可以选中html中的title元素,再使用string强调就可以选中title中的文本。
选中元素
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup.title) # 获得title信息
print(soup.head)
print(soup.p)
提取信息
*获取名称
print(soup.title.name)
*获取属性
print(soup.p.attrs['name']) #获得name属性
print(soup.p.attrs) # 获得全部属性 字典形式
嵌套选择
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup.head.title) # 嵌套选择,head里面包含title元素
关联选择
直接子节点
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup.p.contents) # 选择p的直接子节点
for i, child in enumerate(soup.p.children):
print(i,child)
children和contents都是返回元素的直接子节点,但特殊的是childre返回的是一个生成器类型,需要用for循环遍历得到相应内容。
子孙节点
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
for i, child in enumenrate(soup.p.descendants):
print(i,child)
和children一样,descendants也是返回一个生成器类型,但是返回的是该元素的子孙节点。
父节点
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup.p.parent)
parent返回一个该元素的父亲,众所周知,人只有一个父亲,所以返回的是一个Tag类型。
祖先节点
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
for i, parent in enumenrate(soup.p.parents):
print(i,parent)
parents返回该元素的祖先节点,一个人的祖先有很多,所以返回的是一个生成器类型,我们可以用for来遍历该元素祖先。
兄弟节点
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print('next_sibing:',soup.a.next_sibling)
print("prev_sibing:",soup.a.previous_sibling)
print("next_sibings:",list(enumerate(soup.s.next_sibings)))
print("pre_sibings:",list(enumerate(soup.a.previous_sibings)))
提取信息
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup.a.next_sibing.string)
print(list(soup.a.parents)[0].attrs['class']) # 获取属性值
方法选择器
find_all(name,attrs,recursive,text,**kwargs)
返回列表格式 可以使用for循环获取其中每一个元素
name
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup,find_all(name='ul'))
attrs
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup.find_all(attrs={'id' : 'list-1'}))
print(soup.find_all(attrs={'name' : "elements"}))
print(soup.find_all(class_ = 'element'))
text
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup.find_all(text=re.compile(‘link’))) # 返回text中含有link的一个列表
find()
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup.find(name = 'ul'))
与find_all一样,但是find()返回的是单个元素。
CSS选择器
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
print(soup.select('ul li'))
print(soup.select('#list-2 .elenmet'))
嵌套选择
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
for ul in soup.select('ul'):
print(ul.select("li"))
获取属性
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
for ul in soup.select("ul"):
print(ul['id'])
print(ul.attes['id'])
获取文本
from bs4 import BeautifulSoup
soup = BeautifulSoup('html',lxml)
for ul in soup.select("ul"):
print(ul.get_text())
print(ul.string)
实验说明,get_text()与string二者效果完全一致
ps: 再使用lxml时,必须先使用html.parser。
soup = BeatifulSoup(html,"lxml")
(二) 使用pyquery
初始化
与BeatifulSoup一样,初始化pyquery时,我们也需要传入HTML文本来初始化一个pyquery对象,但是他懂得初始化方法有很多种,如:直接传入字符串,传入url,传入文件名等,下面我们来介绍他的使用。
字符串初始化
html = ''' ''' # 此处省略html内容
from pyquery import PyQuery as pq
doc = pq(html)
print(doc('li'))
url初始化
from pyquery import PyQuery as pq
doc = pq(url="http://www.baidu.com" ,encoding='utf-8')
print(doc('title'))
值得一提的是,在获得百度的title时出现乱码现象。这里我们需要指定编码方式为utf-8.
文件初始化
from pyquery import PyQuery as pq
import requests
doc = pq(requests.get('http://www.baidu.com').text)
print(doc('title'))
基本css选择器
from pyquery import PyQuery as pq
doc = pq(url="http://www.baidu.com" ,encoding='utf-8')
print(doc('#elemnent .list li') # 选取所有满足条件的li元素
查找节点
字节点
from pyquery import PyQery as pq
doc = pq(html)
items = doc.find("li")
lis = items.children()
在寻找子节点的时候,我们使用了find()方法,为得到节点的所有的子节点,我们需要使用children()方法。
父节点
from pyquery import PyQery as pq
doc = pq(html)
items = doc('.list')
container = items.parent()
print(container)
可以的到该元素的父节点,类型为PyQuery类型,这里的父节点为直接父节点,为得到该元素的祖先节点,我们可以使用parents。
兄弟节点
from pyquery import PyQery as pq
doc = pq(html)
li = doc('.list .item-0.active')
print(li.siblings())
为了获得li的兄弟节点,我们需要使用siblings得到它的所有兄弟节点,为获得特定的节点,我们可以使用li.siblings(),在()中填写css选择器,用于限制特定的兄弟节点。
获取信息
比较重要的信息包括:(1)获取属性;(2)获得文本
获取属性
from pyquery import PyQery as pq
doc = pq(html)
a = doc('item-0 a')
print(a.attr('href'))
获取文本
from pyquery import PyQery as pq
doc = pq(html)
a = doc('.item-0 a')
print(a.text())
节点操作
addClass 和 removeClass
from pyquery import PyQery as pq
doc = pq(html)
li = doc('.item-0 .active')
li.removeClass('active')
li.addClass('active')
用addClass 和 removeClass可以对节点进行操作,实现对html的动态修改。
attr, text, html
from pyquery import PyQery as pq
doc = pq(html)
li = doc('.item-0 .active')
li.attr('name','link')
li.text('I love you')
li.html('<span>changed item(/span)')
remove
from pyquery import PyQery as pq
doc = pq(html)
wrap = doc('.wrap')
wrap.find('p').remove()
print(wrap.text())
伪类选择题
from pyquery import PyQery as pq
doc = pq(html)
li = doc('li::first-child')
li = doc('li::last-child')
li = doc('li::nth-child(2)')
li = doc('li:gt(2)')
li = doc('li::nth-child(2n)')
li = doc('li:contains(second)')