html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"><b>The Dormouse's story</b></p> <p class="story">Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>, <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well.</p> <p class="story">...</p> """
from bs4 import BeautifulSoup soup = BeautifulSoup(html_doc) print(soup.prettify()) # 结构化输出文档 print(soup.title) # 获取title标签 print(soup.title.name) # 获取title标签名称 print(soup.title.parent.name) print(soup.p['class'])
从文档中找到全部<a>标签的连接:css
for link in soup.find_all('a'): print(link.get('href'))
从文档中获取全部文字内容:html
print(soup.get_text())
#一、标签选择器:即直接经过标签名字选择,选择速度快,若是存在多个相同的标签则只返回第一个 #二、获取标签的名称 #三、获取标签的属性 #四、获取标签的内容 #五、嵌套选择 #六、子节点、子孙节点 #七、父节点、祖先节点 #八、兄弟节点
#一、标签选择器:即直接经过标签名字选择,选择速度快,若是存在多个相同的标签则只返回第一个 html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"><b>The Dormouse's story</b></p> <p class="story">Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>, <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well.</p> <p class="story">...</p> """ from bs4 import BeautifulSoup soup=BeautifulSoup(html_doc,'lxml') print(soup.head) print(type(soup.head)) #<class 'bs4.element.Tag'> print(soup.p) #存在多个相同的标签则只返回第一个 print(soup.a) #存在多个相同的标签则只返回第一个 #二、获取标签的名称 print(soup.p.name) #三、获取标签的属性 print(soup.p.attrs) #四、获取表的内容 print(soup.p.string) ''' 对下面的这种结构,soup.p.string 返回为None,由于里面有a <p id='list-1'> 哈哈哈哈 <a class='sss'> <span> <h1>aaaa</h1> </span> </a> <b>bbbbb</b> </p> ''' #五、嵌套选择 print(soup.head.title.string) print(soup.body.a.string) #六、子节点、子孙节点 html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"> <b>The Dormouse's story</b> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1"> <span>Elsie</span> </a>, <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well. </p> <p class="story">...</p> """ from bs4 import BeautifulSoup soup=BeautifulSoup(html_doc,'lxml') print(soup.p.contents) #p下全部子节点 print(soup.p.children) #获得一个迭代器,包含p下全部子节点 for i,child in enumerate(soup.p.children): print(i,child) print(soup.p.descendants) #获取子孙节点,p下全部的标签都会选择出来 for i,child in enumerate(soup.p.descendants): print(i,child) #七、父节点、祖先节点 print(soup.a.parent) #获取a标签的父节点 print(soup.a.parents) #找到a标签全部的祖先节点,父亲的父亲,父亲的父亲的父亲... #八、兄弟节点 print(soup.a.next_siblings) #获得生成器对象 print(soup.a.previous_siblings) #获得生成器对象
#find与findall:用法彻底同样,可根据标签名,属性,内容查找文档,可是find只找第一个元素 html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"> <b>The Dormouse's story</b> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1"> <span>Elsie</span> </a> <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well. </p> <p class="story">...</p> """ from bs4 import BeautifulSoup soup=BeautifulSoup(html_doc,'lxml') #一、按照标签名查找 # print(soup.find_all('a')) # print(soup.find_all('a',id='link3')) # print(soup.find_all('a',id='link3',attrs={'class':"sister"})) # # print(soup.find_all('a')[0].find('span')) #嵌套查找 #二、按照属性查找 # print(soup.p.find_all(attrs={'id':'link1'})) #等同于print(soup.find_all(id='link1')) # print(soup.p.find_all(attrs={'class':'sister'})) # # print(soup.find_all(class_='sister')) #三、按照文本内容查找 print(soup.p.find_all(text="The Dormouse's story")) # 按照完整内容匹配(是==而不是in),获得的结果也是内容 #更多:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#find
##该模块提供了select方法来支持css html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"> <b>The Dormouse's story</b> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1"> <span>Elsie</span> </a> <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; <div class='panel-1'> <ul class='list' id='list-1'> <li class='element'>Foo</li> <li class='element'>Bar</li> <li class='element'>Jay</li> </ul> <ul class='list list-small' id='list-2'> <li class='element'><h1 class='yyyy'>Foo</h1></li> <li class='element xxx'>Bar</li> <li class='element'>Jay</li> </ul> </div> and they lived at the bottom of a well. </p> <p class="story">...</p> """ from bs4 import BeautifulSoup soup=BeautifulSoup(html_doc,'lxml') #一、CSS选择器 print(soup.p.select('.sister')) print(soup.select('.sister span')) print(soup.select('#link1')) print(soup.select('#link1 span')) print(soup.select('#list-2 .element.xxx')) print(soup.select('#list-2')[0].select('.element')) #能够一直select,但其实不必,一条select就能够了 # 二、获取属性 print(soup.select('#list-2 h1')[0].attrs) # 三、获取内容 print(soup.select('#list-2 h1')[0].get_text())
# 总结: #一、推荐使用lxml解析库 #二、讲了三种选择器:标签选择器,find与find_all,css选择器 1、标签选择器筛选功能弱,可是速度快 2、建议使用find,find_all查询匹配单个结果或者多个结果 3、若是对css选择器很是熟悉建议使用select #三、记住经常使用的获取属性attrs和文本值get_text()的方法
若是"/"处在XPath表达式开头则表示文档根元素,(表达式中间做为分隔符用以分割每个步进表达式)如:/messages/message/subject是一种绝对路径表示法,它代表是从文档根开始查找节点。假设当前节点是在第一个message节点【/messages/message[1]】,则路径表达式subject(路径前没有"/")这种表示法称为相对路径,代表从当前节点开始查找。具体请见下面所述的"表达式上下文"。node
上下文其实表示一种环境。以明确当前XPath路径表达式处在什么样的环境下执行。例如一样一个路径表达式处在对根节点操做的环境和处在对某一个特定子节点操做的环境下执行所得到的结果多是彻底不同的。也就是说XPath路径表达式计算结果取决于它所处的上下文。ide
XPath上下文基本有如下几种:函数
<?xml version="1.0" encoding="UTF-8"?> <!-- edited with XMLSpy v2008 rel. 2 sp2 (http://www.altova.com) by Administrator --> <?xml-stylesheet type="text/xsl" href="messages.xsl"?> <messages> <message id="1"> <sender>gukaitong@gmail.com</sender> <to>anonymous@gmail.com <group name="IT"> <address>111@gmail.com</address> <address>222@gmail.com</address> <address>aaa@gmail.com</address> <address>bbb@gmail.com</address> <address>ccc@gmail.com</address> </group> </to> <subject>This is a sample</subject> <datetime date="2008-12-11" time="12:00:00" formatted="12/11/2008 12:00AM">2008-12-11T12:00:00Z</datetime> <body> Are you interested in? <attachments> <attachment id="1"> <message id="0"> <sender>anonymous@gmail.com</sender> <to>gukaitong@gmail.com</to> <body> We strongly recommend the following books <books xmlns:amazon="http://www.amazon.com/books/schema"> <amazon:book> <name>Professional C# 2008 </name> <country>USA</country> <price>37.79</price> <year>2007</year> </amazon:book> <amazon:book> <name>Microsoft Visual C# 2008 Step by Step </name> <country>USA</country> <price>26.39 </price> <year>2008</year> </amazon:book> <amazon:book> <name>C# in Depth</name> <country>USA</country> <price>29.69 </price> <year>2006</year> </amazon:book> <amazon:book> <name>Thinking in Java</name> <country>USA</country> <price>23.69 </price> <year>2004</year> </amazon:book> </books> </body> </message> </attachment> </attachments> </body> </message> <message id="2"> <sender>333@gmail.com</sender> <to>444@gmail.com</to> <subject>No title</subject> <body/> </message> </messages>
当前节点(./):如./sender表示选择当前节点下的sender节点集合(等同于下面所讲的"特定元素",如:sender) 父节点(../):如../sender表示选择当前节点的父节点下的sender节点集合 根元素(/):如/messages表示选择从文档根节点下的messages节点集合. 根节点(/*):这里的*是表明全部节点,可是根元素只有一个,因此这里表示根节点。/*的返回结果和/messages返回的结果同样都是messages节点。 递归降低(//):如当前上下文是messages节点。则//sender将返回如下结果: /messages//sender : <sender>gkt1980@gmail.com</sender> <sender>111@gmail.com</sender> <sender>333@gmail.com</sender> /messages/message[1]//sender: <sender>gkt1980@gmail.com</sender> <sender>111@gmail.com</sender> 咱们能够看出XPath表达式返回的结果是:从当前节点开始递归步进搜索当前节点下的全部子节点找到知足条件的节点集。
运算符/特殊字符spa |
说明rest |
/code |
此路径运算符出如今模式开头时,表示应从根节点选择。orm |
//xml |
从当前节点开始递归降低,此路径运算符出如今模式开头时,表示应从根节点递归降低。 |
. |
当前上下文。 |
.. |
当前上下文节点父级。 |
* |
通配符;选择全部元素节点与元素名无关。(不包括文本,注释,指令等节点,若是也要包含这些节点请用node()函数) |
@ |
属性名的前缀。 |
@* |
选择全部属性,与名称无关。 |
: |
命名空间分隔符;将命名空间前缀与元素名或属性名分隔。 |
( ) |
括号运算符(优先级最高),强制运算优先级。 |
[ ] |
应用筛选模式(即谓词,包括"过滤表达式"和"轴(向前/向后)")。 |
[ ] |
下标运算符;用于在集合中编制索引。 |
| |
两个节点集合的联合,如://messages/message/to | //messages/message/cc |
- |
减法。 |
div, |
浮点除法。 |
and, or |
逻辑运算。 |
mod |
求余。 |
not() |
逻辑非 |
= |
等于 |
!= |
不等于 |
特殊比较运算符 |
< 或者 <<= 或者 <= > 或者 >>= 或者 >=须要转义的时候必须使用转义的形式,如在XSLT中,而在XMLDOM的scripting中不须要转义。 |
四、经常使用表达式
/ |
Document Root文档根. |
/* |
选择文档根下面的全部元素节点,即根节点(XML文档只有一个根节点) |
/node() |
根元素下全部的节点(包括文本节点,注释节点等) |
/text() |
查找文档根节点下的全部文本节点 |
/messages/message |
messages节点下的全部message节点 |
/messages/message[1] |
messages节点下的第一个message节点 |
/messages/message[1]/self::node() |
第一个message节点(self轴表示自身,node()表示选择全部节点) |
/messages/message[1]/node() |
第一个message节点下的全部子节点 |
/messages/message[1]/*[last()] |
第一个message节点的最后一个子节点 |
/messages/message[1]/[last()] |
Error,谓词前必须是节点或节点集 |
/messages/message[1]/node()[last()] |
第一个message节点的最后一个子节点 |
/messages/message[1]/text() |
第一个message节点的全部子节点 |
/messages/message[1]//text() |
第一个message节点下递归降低查找全部的文本节点(无限深度) |
/messages/message[1] /child::node() /messages/message[1] /node() /messages/message[position()=1]/node() //message[@id=1] /node() |
第一个message节点下的全部子节点 |
//message[@id=1] //child::node() |
递归全部子节点(无限深度) |
//message[position()=1]/node() |
选择id=1的message节点以及id=0的message节点 |
/messages/message[1] /parent::* |
Messages节点 |
/messages/message[1]/body/attachments/parent::node() /messages/message[1]/body/attachments/parent::* /messages/message[1]/body/attachments/.. |
attachments节点的父节点。父节点只有一个,因此node()和* 返回结果同样。 (..也表示父节点. 表示自身节点) |
//message[@id=0]/ancestor::* |
Ancestor轴表示全部的祖辈,父,祖父等。 向上递归 |
//message[@id=0]/ancestor-or-self::* |
向上递归,包含自身 |
//message[@id=0]/ancestor::node() |
对比使用*,多一个文档根元素(Document root) |
/messages/message[1]/descendant::node() //messages/message[1]//node() |
递归降低查找message节点的全部节点 |
/messages/message[1]/sender/following::* |
查找第一个message节点的sender节点后的全部同级节点,并对每个同级节点递归向下查找。 |
//message[@id=1]/sender/following-sibling::* |
查找id=1的message节点的sender节点的全部后续的同级节点。 |
//message[@id=1]/datetime/@date |
查找id=1的message节点的datetime节点的date属性 |
//message[@id=1]/datetime[@date] //message/datetime[attribute::date] |
查找id=1的message节点的全部含有date属性的datetime节点 |
//message[datetime] |
查找全部含有datetime节点的message节点 |
//message/datetime/attribute::* //message/datetime/attribute::node() //message/datetime/@* |
返回message节点下datetime节点的全部属性节点 |
//message/datetime[attribute::*] //message/datetime[attribute::node()] //message/datetime[@*] //message/datetime[@node()] |
选择全部含有属性的datetime节点 |
//attribute::* |
选择根节点下的全部属性节点 |
//message[@id=0]/body/preceding::node() |
顺序选择body节点所在节点前的全部同级节点。(查找顺序为:先找到body节点的顶级节点(根节点),获得根节点标签前的全部同级节点,执行完成后继续向下一级,顺序获得该节点标签前的全部同级节点,依次类推。) 注意:查找同级节点是顺序查找,而不是递归查找。 |
//message[@id=0]/body/preceding-sibling::node() |
顺序查找body标签前的全部同级节点。(和上例一个最大的区别是:不从最顶层开始到body节点逐层查找。咱们能够理解成少了一个循环,而只查找当前节点前的同级节点) |
//message[@id=1]//*[namespace::amazon] |
查找id=1的全部message节点下的全部命名空间为amazon的节点。 |
//namespace::* |
文档中的全部的命名空间节点。(包括默认命名空间xmlns:xml) |
//message[@id=0]//books/*[local-name()='book'] |
选择books下的全部的book节点, 注意:因为book节点定义了命名空间<amazone:book>.若写成//message[@id=0]//books/book则查找不出任何节点。 |
//message[@id=0]//books/*[local-name()='book' and namespace-uri()='http://www.amazon.com/books/schema'] |
选择books下的全部的book节点,(节点名和命名空间都匹配) |
//message[@id=0]//books/*[local-name()='book'][year>2006] |
选择year节点值>2006的book节点 |
//message[@id=0]//books/*[local-name()='book'][1]/year>2006 |
指示第一个book节点的year节点值是否大于2006. 返回xs:boolean: true |