Bs4 与xpath对比

bs4, xpath都是比较有优秀的解析框架,但使用上是有些区别的,这里对它们的基本用法做下对比:

实例化:

bs4:

from bs4 import BeautifulSoup

# bs4 是不能直接加载文件,而是文件句柄
fp = open('./poet.html','r', encoding='utf-8') # 获取文件句柄
soup = BeautifulSoup(fp,'lxml') # 本地文件
# soup = BeautifulSoup(page_text, 'lxml') page_text为从网络抓取的源码文件

fp.close()

xpath:

from lxml import etree
tree = etree.parse('./poet.html') # 本地文件
# tree = etree.HTML(page_text) # 网络源码

标签定位

bs4: soup.tagName

# 上面已经实例化
print(soup.head)

输出结果是html标签:bs标签定位返回的一定是定位到的标签 

<head>
<meta charset="utf-8"/>
<title>测试bs4</title>
</head>

xpath

xpath中的斜杠的一些说明:

最左侧的/: xpath表达式一定要从要根标签(/html)开始进行定位
非最左侧的/:表示一个层级
最左侧的//:从任意位置进行标签定位 (常用)
非最左侧的//:表示多个层级

print(tree.xpath('//head'))

输出:返回的是一个一个列表,列表中是匹配到的所有的tag对象。

[<Element head at 0x17399c8e0c8>]

 

属性定位:

bs4:  soup.find('tagName', attrName='Value') 返回的是匹配到的第一个标签,结果是列表,列表中只有一个元素

tag = soup.find('div', class_='tang')
print(tag)

输出:

<div class="tang">
<ul>
<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
<li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>
<li><a alt="qi" href="http://www.126.com">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>
<li><a class="du" href="http://www.sina.com">杜甫</a></li>
<li><a class="du" href="http://www.dudu.com">杜牧</a></li>
<li><b>杜小月</b></li>
<li><i>度蜜月</i></li>
<li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>
</ul>
</div>

xpath: //tagName[@attrName="value"]  返回的也是一个列表,列表中只有一个元素,是对象而非html标签

tag = tree.xpath('//div[@class="tang"]')
print(tag)

输出:

[<Element div at 0x1739a358fc8>]

soup.find_all: 返回所有匹配的标签 与xpath中的//效果一样,只是返回是对象,而不是html标签

tags = soup.find_all('li')
print(tags)

输出


[<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>, <li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>, <li><a alt="qi" href="http://www.126.com">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>, <li><a class="du" href="http://www.sina.com">杜甫</a></li>, <li><a class="du" href="http://www.dudu.com">杜牧</a></li>, <li><b>杜小月</b></li>, <li><i>度蜜月</i></li>, <li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>]

从这点看,bs中有匹配单个对象的find方法,匹配多个对象的find_all, 而xpath则只有//匹配多个对象。

在bs4中由于find_all返回的是一个有多个元素的列表,可以在soup.find_all('li')[index] 来实索引定位,在xpath中则是:

tree.xpath('//li[index]') 的方式,需要注意的是 index是从1开始的,而非0

 

选择器定位:

bs4 中 select  

tags = soup.select('.tang > ul')
print(tags)

输出:

[<ul>
<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
<li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>
<li><a alt="qi" href="http://www.126.com">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>
<li><a class="du" href="http://www.sina.com">杜甫</a></li>
<li><a class="du" href="http://www.dudu.com">杜牧</a></li>
<li><b>杜小月</b></li>
<li><i>度蜜月</i></li>
<li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>
</ul>]

这里的.tang为类名,> 表示后代标签 一个层级,如果 多个层级用 空格 表示,比如,tang这个div下面第二级有多个li,可以这样获取

tags = soup.select('.tang li')
print(tags)

输出结果:

[<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>, <li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>, <li><a alt="qi" href="http://www.126.com">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>, <li><a class="du" href="http://www.sina.com">杜甫</a></li>, <li><a class="du" href="http://www.dudu.com">杜牧</a></li>, <li><b>杜小月</b></li>, <li><i>度蜜月</i></li>, <li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>]

xpath中没有选择器定位,但是有模糊匹配,下面看看:

tags = tree.xpath('//div[contains(@class,"ng")]')
print(tags)

输出结果:

[<Element div at 0x1739a092208>, <Element div at 0x17399fd7808>]

可以看到获取到了类中包含“ng"这两个字母的div

另一个则是:starts-with

tags = tree.xpath('//div[starts-with(@class,"t")]')
print(tags)

我们看输出结果:也顺利取到了div

[<Element div at 0x17399fcb348>]

 

取文本和取属性:

bs4;

string

text = soup.select('.tang li')[0].string
print(text)

 输出结果:可以看到取到了第一个Li标签的文字内容

清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村

我们取div的文字试试: 

text = soup.select('.tang')[0].string
print(text)

结果为: None

所以:在bs4中string只能取到标签中直系的文本内容,因为div标签直系没有文本,而是孙标签li中才有内容。

text

text = soup.select('.tang')[0].text
print(text)

输出:标签中所有文本都被取出来了,且只包含文字。



清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村
秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山
岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君
杜甫
杜牧
杜小月
度蜜月
凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘

 

我们再看看xpath

xpath中也有两种取文本的方法:

text = tree.xpath('//div[@class="tang"]/text()')
print(text)

输出:可以看到div中的直系的文本内容,包括换行都被取出来了,且是源码格式,未经处理。

['\n\t\t', '\n\t']

那如果要取所有的文本内容呢?

text = tree.xpath('//div[@class="tang"]//text()')
print(text)

输出:

['\n\t\t', '\n\t\t\t', '清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村', '\n\t\t\t', '秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山', '\n\t\t\t', '岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君', '\n\t\t\t', '杜甫', '\n\t\t\t', '杜牧', '\n\t\t\t', '杜小月', '\n\t\t\t', '度蜜月', '\n\t\t\t', '凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘', '\n\t\t', '\n\t']

可以看到,这里有一点非常不好的是,取出来的文本内容中有大理的换行,tab等。

这里可以参考我的另一篇文章:Xpath 去掉文本中的空格换行符 https://www.jingang.ga/article/120/

 

取属性:

xpath中定位到标签对象是可以直接取标签的属性的,比如取a标签的href属性:

text = tree.xpath('//div[@class="tang"]//a/@href')
print(text)

输出结果:

['http://www.baidu.com', 'http://www.163.com', 'http://www.126.com', 'http://www.sina.com', 'http://www.dudu.com', 'http://www.haha.com']

 

以上内容就是对bs4和xpath标签定位的简单对比。暂时写这么多,后续若再有再更新。

上一篇:Xpath 去掉文本中的空格换行符

下一篇:Requests 使用之代理