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())
节点操作

addClassremoveClass

from pyquery import PyQery as pq
doc = pq(html)
li = doc('.item-0 .active')
li.removeClass('active')
li.addClass('active')

addClassremoveClass可以对节点进行操作,实现对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)')