Python解析庫
目錄
一、lxml庫
二、BeautifulSoup庫
三、PyQuery庫
一、lxml庫
教程:https://www.w3school.com.cn/xpath/index.asp
1.安裝lxml庫
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ lxml
2.解析規(guī)則
解析規(guī)則:XPath
| 表達式 | 描述 |
|---|---|
| nodename | 選取此節(jié)點的所有子節(jié)點 |
| / | 從當前節(jié)點選取直接子節(jié)點 |
| // | 從當前節(jié)點選取所有子孫節(jié)點 |
| . | 選取當前節(jié)點 |
| .. | 選取當前節(jié)點的父節(jié)點 |
| @ | 選取屬性 |
| * | 通配符,選擇所有元素節(jié)點與元素名 |
| @* | 選取所有屬性 |
| [@attrib] | 選取具有給定屬性的所有元素 |
| [@attrib='value'] | 選取給定屬性具有給定值的所有元素 |
| [tag] | 選取所有具有指定元素的直接子節(jié)點 |
| [tag='text'] | 選取所有具有指定元素并且文本內容是text節(jié)點 |
3.XPath運算符
| 運算符 | 描述 | 實例 | 返回值 |
|---|---|---|---|
| or | 或 | age=19 or age=20 | 如果age等于19或者等于20則返回true反正返回false |
| and | 與 | age>19 and age<21 | 如果age等于20則返回true,否則返回false |
| mod | 取余 | 5 mod 2 | 1 |
| | | 取兩個節(jié)點的集合 | //book | //cd | 返回所有擁有book和cd元素的節(jié)點集合 |
| + | 加 | 6+4 | 10 |
| - | 減 | 6-4 | 2 |
| * | 乘 | 6*4 | 24 |
| div | 除法 | 8 div 4 | 2 |
| = | 等于 | age=19 | True或False |
| != | 不等于 | age!=19 | True或False |
| < | 小于 | age<19 | True或False |
| <= | 小于或等于 | age<=19 | True或False |
| > | 大于 | age>19 | True或False |
| >= | 大于或等于 | age>=19 | True或False |
4.構建etree實例
(1)從文本構建
示例:
from lxml import etree
def load_text():
text = """
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">forth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
"""
html = etree.HTML(text)
result = etree.tostring(html)
print(result.decode("utf-8"))

經過處理后的html代碼會被自動修復,如添加一些缺失的標簽。
(2)從文件構建
示例:
from lxml import etree
def load_text():
html = etree.parse("./test.html", etree.HTMLParser())
print(type(html))
result = etree.tostring(html)
print(result.decode("utf-8"))
注意:etree.tostring()方法返回的數據為bytes類型,需要調用decode方法將其轉換成sgr類型。
5.節(jié)點選取
(1)選中所有節(jié)點
- 選中所有節(jié)點
//*
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://*")
print(result)
- 選中所有的某一類節(jié)點
//標簽
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://li")
print(result)
(2)選取子孫節(jié)點
①選取直接子節(jié)點
/標簽
/child::標簽
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://li/a")
print(result)
②選取后代節(jié)點
//標簽
/descendant::標簽
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://li//a")
print(result)
(3)選取祖先節(jié)點
①選取父節(jié)點
- 方法1
/..
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://a/..")
print(result)
- 方法2
/parent::
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://a/parent::*")
print(result)
②選取祖先節(jié)點
/ancestor::*
/ancestor::標簽
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://li[1]/ancestor::*")
print(result)
(4)選取兄弟節(jié)點
# 獲取該節(jié)點后的所有兄弟節(jié)點以及這些兄弟節(jié)點的所有子孫節(jié)點
result = html.xpath("http://li[1]/following::*")
print(result)
# 獲取該節(jié)點后的所有兄弟節(jié)點以及這些兄弟節(jié)點的所有子孫節(jié)點中的第一個節(jié)點
result = html.xpath("http://li[1]/following::*[1]")
print(result)
# 獲取該節(jié)點后的所有兄弟節(jié)點
result = html.xpath("http://li[1]/following-sibling::*")
print(result)
(5)屬性匹配
- 選取具有某屬性的節(jié)點
[@屬性]
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://li[@class]")
print(result)
選取擁有class屬性的li標簽。
- 選取具有某屬性且其值符合要求的節(jié)點
[@屬性=值]
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://li[@class='item-0']")
print(result)
選取擁有屬性class='item-0'的li標簽。
(6)屬性多值匹配
[contains(@屬性, 值)]
text = """
<li class="li li-first"><a href="link1.html">first item</a></li>
"""
html = etree.HTML(text)
result = html.xpath("http://li[@class='li']/a/text()")
print(result)
result = html.xpath("http://li[contains(@class, 'li')]/a/text()")
print(result)

(7)多屬性匹配
[contains(@屬性, 值) and @屬性=值]
[contains(@屬性, 值) or @屬性=值]
text = """
<li class="li li-first" name='item'><a href="link1.html">first item</a></li>
"""
html = etree.HTML(text)
result = html.xpath("http://li[contains(@class, 'li') and @name='item']/a/text()")
print(result)
(8)按順序位置選取節(jié)點
# 選取第一個節(jié)點
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://li[1]/a/text()")
print(result)
# 選取最后一個節(jié)點
result = html.xpath("http://li[last()]/a/text()")
print(result)
# 選取前兩個節(jié)點
result = html.xpath("http://li[position() < 3]/a/text()")
print(result)
# 選取倒數第三個節(jié)點
result = html.xpath("http://li[last() - 2]/a/text()")
print(result)

6.屬性獲取
(1)獲取節(jié)點屬性
/@*
/@屬性
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://a[@href='link4.html']/../@class")
print(result)
獲取擁有屬性href='link4.html的a標簽的父親節(jié)點的class屬性的值。
(2)獲取節(jié)點內容
/text()
html = etree.parse("./test.html", etree.HTMLParser())
result = html.xpath("http://li[@class='item-0']/text()")
print(result)
獲取擁有屬性class='item-0'的li標簽的文本內容。
二、BeautifulSoup庫
文檔:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/
1.安裝BeautifulSoup庫
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ beautifulsoup4
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ bs4
2.解析
(1)文檔解析語法
soup = BeautifulSoup(待解析文檔, 解析器)
(2)解析器
- 常用解析器
| 解析器 | 使用方法 | 性能 |
|---|---|---|
| Python標準庫 | BeautifulSoup(html_doc,"html.parser") | 執(zhí)行速度適中 文檔容錯能力強 |
| lxml HTML 解析器 | BeautifulSoup(html_doc,"lxml") | 執(zhí)行速度快 容錯能力強 要求安裝lxml擴展 |
| lxml XML 解析器 | BeautifulSoup(html_doc,"xml") BeautifulSoup(markup,["lxml", "xml"]) |
支持解析xml 執(zhí)行速度快 要求安裝外部擴展 |
| html5lib解析器 | BeautifulSoup(html_doc,"html5lib") | 以瀏覽器的方式解析文檔,生成HTML5格式文檔 執(zhí)行速度慢 最好的容錯性 要求安裝html5lib擴展 |
- 解析器安裝
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ lxml
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ html5lib
3.修復文檔
soup = BeautifulSoup(html, "lxml")
print(soup.prettify())
4.節(jié)點選取
# 整個HTML文檔
soup
# title節(jié)點的HTML文本
soup.title
# title節(jié)點的文本內容
soup.title.string
print(type(soup))
print(type(soup.title))
print(type(soup.title.string))

(1)按標簽名
# 獲取節(jié)點名稱
print(soup.title.name)
# 獲取屬性值(字典類型)
print(soup.p.attrs)
print(soup.p.attrs['name'])
# 獲取文本內容(str類型)
print(soup.p.string)
# 嵌套選擇
print(soup.head.title)
# 關聯選擇
# contents屬性:選取直接子節(jié)點
print(soup.p.contents)
(2)按節(jié)點關系
# 獲取直接子節(jié)點(返回列表)
print(soup.p.contents)
# 獲取直接子節(jié)點(返回生成器)
for i, child in enumerate(soup.p.children):
print(i, child)
# 獲取所有子孫節(jié)點(返回生成器)
for i, child in enumerate(soup.p.descendants):
print(i, child)
獲取直接父節(jié)點:parent
獲取祖先節(jié)點:parents
獲取兄弟節(jié)點:next_sibling(下一個兄弟節(jié)點) | previous_sibling(上一個兄弟節(jié)點)
next_siblings(后面的所有兄弟節(jié)點) | previous_siblings(前面的所有兄弟及誒單)
(3)find方法
- find_all()方法:返回符合條件的所有節(jié)點元素
# 通過name查找
print(soup.find_all(name="ul"))
print(type(soup.find_all(name="ul")[0]))
print(soup.find_all(name="ul")[0].find_all(name='li'))
# 通過屬性查找
print(soup.find_all(attrs={'id': 'list'}))
print(soup.find_all(attrs={'id': 'list', 'name': 'elements'}))
print(soup.find_all(id='list2'))
print(soup.find_all(class_='list'))
# 通過文本查找節(jié)點
import re
print(soup.find_all(text=re.compile('^1+')))
print(type(soup.find_all(text=re.compile('^1+'))[0]))
- find():返回單個元素(即匹配的第一個元素)
find_parent() | find_parents()
find_next_sibling() | find_next_siblings()
find_previous_sibling() | find_previous_siblings()
find_next() | find_all_next():返回節(jié)點后 第一個|所有 符合條件的節(jié)點
find_previous() | find_all_previous():返回節(jié)點前 第一個|所有 符合條件的節(jié)點
(4)CSS選擇器
soup.select("CSS選擇器")
三、PyQuery庫
1.安裝PyQuery庫
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ pyquery