xml parse 基础
常用库
import re
from lxml import etree
from bs4 import BeautifulSoup
from pyquery import PyQuery
import parsel
import requests
content = requests.get("https://www.baidu.com").text
只简单记录各个库解析html的方式,库的其他功能不记录,PyQuery和scrapy Selectors都是基于lxml模块,而lxml和正则表达式都是C语言写的,只有Beautifulsoup是用纯Python编写的,所以在实测中,Beautifulsoup 的解析速度比其他几种慢了5倍以上!
一、 xpath
1. 基本语法
节点类型:元素、属性、文本、命名空间、处理指令、注释以及文档节点(或称为根节点)。
节点关系: 父(parent), 子(Children), 同胞(Sibling), 先辈(Ancestor), 后代(Descendant)
xpath语法:
# 单个路径:
nodename #选取此节点的所有子节点
. #选取当前节点
.. #选取当前节点的父节点
/ #从根节点开始 /nodename 取当前节点的所有nodename子节点
// #从网页中匹配对应节点,不在乎位置 //nodename 取当前节点的所有的nodename后代节点
div/@class #获取属性
/div[@attr] #获取具有某属性的节点
/div[@attr=value] #获取具有指定属性值的节点
div/* #通配符 ,匹配所有节点 //div[@*] # 获取所有有属性的div节点
/div[tag] #获取具有指定子孙节点的直接子节点 //div[a] 具有节点a的div节点
/div[last()-1] #获取匹配到的倒数第二个节点
/div[1] # 获取匹配到的第一个节点
/div[position()<3] # 获取前两个节点
# 选取多个路径:
//div/a | //div/p # 使用或符号'|'来获取多个路径的节点
# 使用轴名索引节点:
轴:ancestor, ancestor-or-self, attribute, child, descendant(后代), descendant-or-self,
following(当前节点结束后的所有节点), namespace(当前节点的所有命名空间节点), parent,
preceding(当前节点开始之前的所有节点), preceding-sibling(当前节点之前的同级节点), self(当前节点),
语法:轴名::节点名/属性
# child::book # 选取当前节点的所有book子节点
# ancestor::book # 选取当前节点的所有book祖先节点
# attribute:: * # 当前节点的所有属性
# child::*/child::price # 当前节点的所有子节点的price子节点(当前节点的孙节点)
...
2. python 调用
from lxml import etree
page = etree.HTML("content")
links = page.xpath("//div/@class ")
link1 = page.xpath("child::div/attribute::class")
二、 css
1. 基本语法
id,类选择器:
#id #id选择器
.class #类选择器
标签选择器:
p #选择所有<p>元素
div,p #选择所有<div>元素和 <p> 元素
div p #选择<div>元素内的所有<p>元素
div>p #选择所有父级是 <div> 元素的 <p> 元素
p.hometown #选择所有 class="hometown" 的 <p> 元素
属性提取器:
div[attribute] #选择所有带有attributet属性的div元素
div[attribute=value] #选择所有attribute=value的div元素
::attr(attribute)/text # 获取属性 a::text a::attr(href) # parsel库支持, bs4不支持
其他:
#下面命令是css选择器支持的部分命令但python的parsel和bs4未必支持
:link a:link 选择所有未访问链接
:visited a:visited 选择所有访问过的链接
:active a:active 选择活动链接
:hover a:hover 选择鼠标在链接上面时
:focus input:focus 选择具有焦点的输入元素
:first-letter p:first-letter 选择每一个<p>元素的第一个字母
:first-line p:first-line 选择每一个<p>元素的第一行
:first-child p:first-child 指定只有当<p>元素是其父级的第一个子级的样式。
:before p:before 在每个<p>元素之前插入内容
:after p:after 在每个<p>元素之后插入内容
:lang(language) p:lang(it) 选择一个lang属性的起始值="it"的所有<p>元素
element1~element2 p~ul 选择p元素之后的每一个ul元素
[attribute^=value] a[src^="https"] 选择每一个src属性的值以"https"开头的元素
[attribute$=value] a[src$=".pdf"] 选择每一个src属性的值以".pdf"结尾的元素
[attribute*=value] a[src*="runoob"] 选择每一个src属性的值包含子字符串"runoob"的元素
:first-of-type p:first-of-type 选择每个p元素是其父级的第一个p元素
:last-of-type p:last-of-type 选择每个p元素是其父级的最后一个p元素
:only-of-type p:only-of-type 选择每个p元素是其父级的唯一p元素
:only-child p:only-child 选择每个p元素是其父级的唯一子元素
:nth-child(n) p:nth-child(2) 选择每个p元素是其父级的第二个子元素
:nth-last-child(n) p:nth-last-child(2) 选择每个p元素的是其父级的第二个子元素,从最后一个子项计数
:nth-of-type(n) p:nth-of-type(2) 选择每个p元素是其父级的第二个p元素
:nth-last-of-type(n) p:nth-last-of-type(2) 选择每个p元素的是其父级的第二个p元素,从最后一个子项计数 3
:last-child p:last-child 选择每个p元素是其父级的最后一个子级。
:root :root 选择文档的根元素
:empty p:empty 选择每个没有任何子级的p元素(包括文本节点)
:target #news:target 选择当前活动的#news元素(包含该锚名称的点击的URL)
:enabled input:enabled 选择每一个已启用的输入元素
:disabled input:disabled 选择每一个禁用的输入元素
:checked input:checked 选择每个选中的输入元素
:not(selector) :not(p) 选择每个并非p元素的元素
::selection ::selection 匹配元素中被用户选中或处于高亮状态的部分
:out-of-range :out-of-range 匹配值在指定区间之外的input元素
:in-range :in-range 匹配值在指定区间之内的input元素
:read-write :read-write 用于匹配可读及可写的元素
:read-only :read-only 用于匹配设置 "readonly"(只读) 属性的元素
:optional :optional 用于匹配可选的输入元素
:required :required 用于匹配设置了 "required" 属性的元素
:valid :valid 用于匹配输入值为合法的元素
:invalid :invalid 用于匹配输入值为非法的元素
python调用
import parsel
selector = parsel.Selector("Content")
span = selector.css(query="div")
三、 bs4
from bs4 import BeautifulSoup
text = "" # html字符串 or open("test.html")
soup = BeautifulSoup(text, "lxml")
# 标签查找
first_div = soup.div # 获取指定标签名的第一个标签
soup.find(id="test")
# soup.find(name, attrs, recursive, text, **kwargs) # 按条件搜索节点,返回找到的第一个节点
# soup.find_all(name, attrs, recursive, text, **kwargs) # 返回找到的全部节点
# name: 节点名
# text:查找对应文本,支持正则 soup.find(text=re.compile("10 minutes to pandas.*"))
# attrs: 通过属性来查找标签 soup.find(attrs={'class':"dpn"})/ find(target='_blank') # class是关键字只能用{}形式,
# 支持回调函数查找: soup.find(lambda x: x.has_attr("title"))
# soup.find_next(), soup.find_parent(), soup.findChild()... # 根据节点之间关系查找
# 属性查找
first_div_attrs = soup.div.attrs # 返回所有属性{class:[a, b], id:[c,d]}
div_class = soup.div['class'] #返回第一个div的class属性[a, b]
title_text = soup.title.text # 获取text字符串
a_string = soup.a.string # 获取a的text(应该和.text功能一样,但有可能获取到Comment(注释)对象,从而导致我们获取到文本中夹杂了不需要到注释), .strings 返回一个数组[text1,text2...]
# if type(soup.a.string) == bs4.element.Comment:... 通过判断获取到文本类型是否为注释,进行对应操作,正常文本类型应为bs4.element.NavigableString(可遍历字符串)
# bs4使用css选择器
soup.select("div, p") # bs4支持部分的css选择器
四、 pyquery
pyquery是基于lxml库开发的: res = doc(“css”) -> PyQuery([lxml.html.HtmlElement,…])
res是pyquery.pyquery.PyQuery对象,支持PyQuery的函数,
但PyQuery对象中的元素res[0]是lxml.html.HtmlElement,支持lxml.etree.HTML的函数
pyquery因此使用items()来将res中的元素变成pyquery.pyquery.PyQuery对象
from pyquery import PyQuery
# 初始化
content = "" # html字符串
doc = PyQuery(content) # 根据字符串初始化, 若content不是一个完整的html文件, 初始化时pyquery会自动补全
# doc = PyQuery(url=url, encoding='utf8') # 根据网址初始化doc
# doc = PyQuery(filename=filepath, encoding='utf8') # 根据文件初始化
# 访问节点
title = doc("title") # 直接通过节点名访问节点(css)
img = doc("img[class='logo]") # 使用css搜索节点
li = doc.find("[target='_blank']")# find(css)
for tag in li.items():
print(tag.text()) # 若无items(), 则tag为lxml对象,使用tag.text
# 访问属性
text = doc.text # 返回网页中的所有文本字符串/注释/style/script代码等内容 soup.text只返回网页中的文本字符串
tag_text = doc("title").text() # 使用text()访问节点的text属性
tag_id = doc.attr("id") # 使用attr()访问属性