HTML解析
通过urllib、requests,都可以拿到HTML内容。
HTML的内容返回给浏览器,浏览器就会解析它,并对它渲染。
HTML 超文本表示语言,设计的初衷就是为了超越普通文本,让文本表现力更强。
XML 扩展标记语言,不是为了代替HTML,而是觉得HTML的设计中包含了过多的格式,承担了一部分数据之外的任务,所以才设计了XML只用来描述数据。
HTML和XML都有结构,使用标记形成树型的嵌套结构。
DOM(Document Object Model)来解析这种嵌套树型结构,浏览器往往都提供了对DOM操作的API,可以用面向对象的方式来操作DOM。
XPath
http://www.w3school.com.cn/xpath/index.asp 中文教程
XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。
工具
XMLQuire win7+需要.NET框架4.0-4.5。测试XML、XPath
<?xml version="1.0" encoding="utf-8"?>
<bookstore>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications with XML.</description>
</book>
<book id="bk102" class="bookinfo even">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,an evil sorceress, and her own childhood to become queen of the world.</description>
</book>
<book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology society in England, the young survivors lay the foundation for a new society.</description>
</book>
<book id="bk104">
<author>Corets, Eva</author>
<title>Oberon's Legacy</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2001-03-10</publish_date>
<description>In post-apocalypse England, the mysterious agent known only as Oberon helps to create a new life for the inhabitants of London. Sequel to Maeve Ascendant.</description>
</book>
</bookstore>
节点
在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。
举例 | 说明 |
---|---|
/ | 根节点 |
<bookstore> |
元素节点 |
<author>Corets, Eva</author> |
元素节点 |
id="bk104" |
属性节点,id是元素节点book的属性 |
44.95 | 文本节点 |
节点之间的嵌套形成父子(parent、children)关系。
具有同一个父节点的不同节点是兄弟(sibling)关系。
节点选择
操作符或表达式 | 含义 |
---|---|
/ | 从根节点开始找 |
// | 从当前节点开始的任意层找 |
. | 当前节点 |
.. | 当前节点的父节点 |
@ | 选择属性 |
节点名 | 选取所有这个节点名的节点 |
* | 匹配任意元素节点 |
@* | 匹配任意属性节点 |
node() | 匹配任意类型的节点 |
text() | 匹配text类型节点 |
谓语(Predicates)
谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。
谓语就是查询的条件。
即在路径选择时,在中括号内指定查询条件。
//book
任意层次下的book节点
//book[3]/*[last()-3]
任意层次下的第三个book下的任意节点的倒数第三个标签
//book[2]/*[last()]
任意层次下第二个book节点下的最后一个节点
//book[2]/*[last()]/following::*
任意层次下第二个book节点下的最后一个节点之后的所有节点
//book[@id='bk102']
任意层次下的id为bk102的book节点
//title/parent::node()
任意层次下的title节点的所有父节点
//title/..
任意层次下的title节点的所有父节点
//title/ancestor::node()
任意层次下的title节点的所有祖先节点
//book/*[starts-with(local-name(),'p')]
任意层次下book节点下名字以p开头的节点
//book[contains(@id,'103')]/price
任意层次下id属性包含103字符串的book节点下的price节点
//*[price>6]
所有price大于6的节点,对标签中的值进行处理
XPath 轴(Axes)
轴的意思是相对于当前节点的节点集
轴名称 | 结果 |
---|---|
ancestor | 选取当前节点的所有先辈(父、祖父等) |
ancestor-or-self | 选取当前节点的所有先辈(父、祖父等)以及当前节点本身 |
attribute | 选取当前节点的所有属性。@id 等价于 attribute::id |
child | 选取当前节点的所有子元素。title 等价于 child:title |
descendant | 选取当前节点的所有后代元素(子、孙等) |
descendant-or-self | 选取当前节点的所有后代元素(子、孙等)以及当前节点本身 |
following | 选取文档中当前节点的结束标签之后的所有节点 |
namespace | 选取当前节点的所有命名空间节点 |
parent | 选取当前节点的父节点 |
preceding | 直到所有这个节点的父辈节点,顺序选择每个父辈节点前的所有同级节点 |
preceding-sibling | 选取当前节点之前的所有同级节点 |
self | 选取当前节点。 . 等价于 self::node() |
例如
//author/parent::node()
author标签的父节点
步Step
步的语法 轴名称::节点测试[谓语]
例子 | 结果 |
---|---|
child::book |
选取所有属于当前节点的子元素的 book 节点 |
attribute::lang |
选取当前节点的 lang 属性 |
child::* |
选取当前节点的所有子元素 |
attribute::* |
选取当前节点的所有属性 |
child::text() |
选取当前节点的所有文本子节点 |
child::node() |
选取当前节点的所有子节点 |
descendant::book |
选取当前节点的所有 book 后代 |
ancestor::book |
选择当前节点的所有 book 先辈 |
ancestor-or-self::book |
选取当前节点的所有 book 先辈以及当前节点(如果此节点是 book 节点) |
child::*/child::price |
选取当前节点的所有 price 孙节点 |
XPATH实例
以斜杠开始的称为绝对路径,表示从根开始。
不以斜杆开始的称为相对路径,一般都是依照当前节点来计算。当前节点在上下文环境中,当前节点很可能已经不是根节点了。
一般为了方便,往往xml如果层次很深,都会使用//来查找节点
title
选取当前节点下所有title子节点/book
从根结点找子节点是book的,找不到book/title
当前节点下所有子节点book下的title节点//title
从根节点向下找任意层中title的节点book//title
当前节点下所有book子节点下任意层次的title节点//@id
任意层下含有id的 属性,取回的是属性//book[@id]
任意层下含有id属性的book节点//*[@id]
任意层下含有id属性的节点//book[@id="bk102"]
任意层下含有id属性且等于'bk102'的book节点/bookstore/book[1]
根节点bookstore下第一个book节点,从1开始/bookstore/book[1]/@id
根节点bookstore下第一个book节点的id属性/bookstore/book[last()-1]
根节点bookstore下倒数第二个book节点,函数last()/bookstore/*
匹配根节点bookstore的所有子节点,不递归/bookstore//*
匹配根节点bookstore的所有子孙节点,递归//*
匹配所有子孙节点//*[@*]
匹配所有有属性的节点//book[@*]
匹配所有有属性的book节点//@*
匹配所有属性//book/title | //price
匹配book下的title节点或者任意层下的price//book[position()=2]
匹配book节点,取第二个//book[position()<last()-1]
匹配book节点,取位置小于倒数第二个//book[price>40]
匹配price节点值大于40的book节点//book[2]/node()
匹配位置为2的book节点下的所有类型的子节点//book[2]//node()
匹配位置为2的book节点下的所有类型的子孙节点//book[1]/text()
匹配第一个book节点下的所有文本子节点//book[1]//text()
匹配第一个book节点下的所有文本节点//*[local-name()='book']
匹配所有节点且不带限定名的节点名称为book的所有节点。local-name函数取不带限定名的名称//book/child::node()
所有book节点的子节点等价于//book/node()//book/child::node()[local-name()='price'and text()<10]
所有book节点的子节点中名字叫做price的且其内容小于10的节点等价于 //book/price[text()<10]//div[@class="avatar"]/following-sibling::div[2]/p/span/text()
属性class="avatar"的div标签下的第二个div标签下的p标签下的span标签下的文本//a[@id='3']/preceding-sibling::a[2]
id=‘3’的a标签的向上两个的兄弟标签"//a[@id='3']/preceding-sibling::*[N]"
id='3'的a标签的前面的第N个标签
//book[price<6]/price
//book/price[text()<6]
//book/child::node()[local-name()='price'and text()<6]
这三种等价
-
//book//*[self::title or self::price]
所有book节点下子孙节点,这些节点是title或者price等价于
//book//title | //book/price
也等价于
//book//*[local-name()='title' or local-name()='price']
多属性
//*[@class]
所有有class属性的节点
//*[@class="bookinfo even"]
所有属性为"bookinfo even"的节点
//*[contains(@class, 'even')]
属性class中包含even字符串的节点
//*[contains(local-name(), 'book')]
标签名包含book的节点
取属性的值
//*[contains(@id,"102")]/@class
取出id包含102的class属性的值
//book[@id="102")]/@style
取出id属性等于102的book标签的style属性的值
lxml
官网
lxml是Python下功能丰富的XML、HTML解析库,性能非常好,是对libxml2 和 libxslt的封装。
由于是基于C封装的库,所以pycharm会没有提示
最新版支持Python 2.6+,python3支持到3.6。
CentOS编译安装需要
# yum install libxml2-devel libxslt-devel
lxml安装
$ pip install lxml
创建简单的html文本
from lxml import etree
# 创建root标签
root = etree.Element('root')
# 创建div标签
div = etree.Element('div')
print(root, div)
# 在root标签下添加子标签div
# 两种方式
# 1. 追加
root.append(div)
# 2. 直接在父标签中创建子标签
etree.SubElement(div, 'p')
# 打印出创建的html文本
print(etree.tostring(root, pretty_print=True))
"""
运行结果
<Element root at 0x2054122a6c8> <Element div at 0x2054122a648>
b'<root>\n <div>\n <p/>\n </div>\n</root>\n'
"""
对html文本文件进行解析
# 把html生成Element html对象,然后解析
root = etree.HTML(b'<root>\n <div>\n <p>hello world!!<p/>\n </div>\n</root>\n')
#可以打印
print(etree.tostring(root))
print(root)
#查询任意层次p节点下的文本
a = root.xpath('//p/text()')
#查询任意节点下的属性
b=root.xpath('//@id')
#有则输出列表
print(a)
#无则输出空列表
print(b)
"""
b'<html><body><root>\n <div>\n <p>hello world!!</p><p/>\n </div>\n</root>\n</body></html>'
<Element html at 0x139410b9708>
['hello world!!']
[]
"""
etree还提供了2个有用的函数
etree.HTML(text)
将bytes文本解析HTML文档,返回根节点
anode.xpath('xpath路径')
对节点使用xpath语法
取xpath出来的值可以直接转换为字符串或者数字
练习:爬取“口碑榜”
from lxml import etree
import requests
url = 'https://movie.douban.com/'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'
with requests.get(url, headers={'user-agent': user_agent}) as res:
print(res.status_code)
#解析
h = etree.HTML(res.content)
#定位
l = h.xpath("//div[@class='billboard-bd']//td[@class='title']/a/text()")
#或者
#l = h.xpath('//td[@class="title"]//text()')
print(l)
"""
200
['呼朋引伴', '加加林', '上帝之手', '浅草小子', '法兰西特派', '不可饶恕', '世界上最糟糕的人', '你永远比那些家伙年轻', '天鹅挽歌', '魔法满屋']
"""
结合selenium,用浏览器获取页面,再交给lxml解析
from lxml import etree
from lxml.etree import _Element
from selenium import webdriver
with webdriver.Firefox() as dev:
dev.maximize_window()
url = "https://movie.douban.com/"
dev.get(url)
time.sleep(5)
print("查找标签")
h: _Element = etree.HTML(dev.page_source.encode('utf8'))
print(h.xpath("//*[@id='billboard']/div/table/tbody/tr/td[@class='title']/a/text()"))
"""
查找标签
['祝你好运,里奥·格兰德', '宿敌', '旋涡', '隐入尘烟', '仙后座', '公爵', '杰瑞和玛姬生活阔绰', '蜂箱', '咒', '人生大事']
"""
标签:XPath,title,python,price,所有,id,book,HTML,节点
From: https://www.cnblogs.com/guangdelw/p/17074223.html