目录
#string和strings、stripped_strings属性以及get_text方法:
XPath语法和lxml模块
什么是XPath?
xpath(XML Path Language)是一门在XML和HTML文档中查找信息的语言,可用来在XML和HTML文档中对元素和属性进行遍历。
XPath开发工具
- Chrome插件XPath Helper。
- Firefox插件Try XPath。
XPath语法
选取节点:
XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
表达式 | 描述 | 示例 | 结果 |
---|---|---|---|
nodename | 选取此节点的所有子节点 | bookstore | 选取bookstore下所有的子节点 |
/ | 如果是在最前面,代表从根节点选取。否则选择某节点下的某个节点 | /bookstore | 选取根元素下所有的bookstore节点 |
// | 从全局节点中选择节点,随便在哪个位置 | //book | 从全局节点中找到所有的book节点 |
@ | 选取某个节点的属性 | //book[@price] | 选择所有拥有price属性的book节点 |
. | 当前节点 | ./a | 选取当前节点下的a标签 |
谓语:
谓语用来查找某个特定的节点或者包含某个指定的值的节点,被嵌在方括号中。
在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:
路径表达式 | 描述 |
---|---|
/bookstore/book[1] | 选取bookstore下的第一个子元素 |
/bookstore/book[last()] | 选取bookstore下的倒数第二个book元素。 |
bookstore/book[position()<3] | 选取bookstore下前面两个子元素。 |
//book[@price] | 选取拥有price属性的book元素 |
//book[@price=10] | 选取所有属性price等于10的book元素 |
//@lang | 选取名为 lang 的所有属性。 |
通配符
*表示通配符。
通配符 | 描述 | 示例 | 结果 |
---|---|---|---|
* | 匹配任意节点 | /bookstore/* | 选取bookstore下的所有子元素。 |
@* | 匹配节点中的任何属性 | //book[@*] | 选取所有带有属性的book元素。 |
选取多个路径:
通过在路径表达式中使用“|”运算符,可以选取若干个路径。
示例如下:
//bookstore/book | //book/title
# 选取所有book元素以及book元素下所有的title元素
运算符:
总结xpath语法:
##使用方法:
使用//获取整个页面当中的元素,然后写标签名,然后再写谓词进行提取。比如:
//div[@class='abc']
##需要注意的知识点:
1、/和//的区别:/代表只获取直接子节点。//获取子孙节点。一般//用得比较多。当然也要视情况而定。
2、contains:有时候某个属性中包含了多个值,那么可以使用'contains'函数。示例如下:
//div[contains(@class,'job_detail')]
3、谓词中的下标是从1开始的,不是从0开始。
lxml库
lxml 是 一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。
lxml和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器,我们可以利用之前学习的XPath语法,来快速的定位特定元素以及节点信息。
lxml python 官方文档:http://lxml.de/index.html
需要安装C语言库,可使用 pip 安装:pip install lxml
基本使用:
我们可以利用他来解析HTML代码,并且在解析HTML代码的时候,如果HTML代码不规范,他会自动的进行补全。示例代码如下:
# 使用 lxml 的 etree 库
from lxml import etree
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">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a> # 注意,此处缺少一个 </li> 闭合标签
</ul>
</div>
'''
#利用etree.HTML,将字符串解析为HTML文档
html = etree.HTML(text)
# 按字符串序列化HTML文档
result = etree.tostring(html)
print(result)
输入结果如下:
<html><body>
<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">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
</body></html>
可以看到。lxml会自动修改HTML代码。例子中不仅补全了li标签,还添加了body,html标签。
从文件中读取html代码:
除了直接使用字符串进行解析,lxml还支持从文件中读取内容。我们新建一个hello.html文件:
<!-- hello.html -->
<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"><span class="bold">third item</span></a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
然后利用etree.parse()
方法来读取文件。示例代码如下:
from lxml import etree
# 读取外部文件 hello.html
html = etree.parse('hello.html')
result = etree.tostring(html, pretty_print=True)
print(result)
输入结果和之前是相同的。
在lxml中使用XPath语法:
-
获取所有li标签:
from lxml import etree html = etree.parse('hello.html') print (type(html)) # 显示etree.parse() 返回类型 result = html.xpath('//li') print(result) # 打印<li>标签的元素集合
-
获取所有li元素下的所有class属性的值:
from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li/@class') print(result)
-
获取li标签下href为
www.baidu.com
的a标签:from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li/a[@href="www.baidu.com"]') print(result)
-
获取li标签下所有span标签:
from lxml import etree html = etree.parse('hello.html') #result = html.xpath('//li/span') #注意这么写是不对的: #因为 / 是用来获取子元素的,而 <span> 并不是 <li> 的子元素,所以,要用双斜杠 result = html.xpath('//li//span') print(result)
-
获取li标签下的a标签里的所有class:
from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li/a//@class') print(result)
-
获取最后一个li的a的href属性对应的值:
from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li[last()]/a/@href') # 谓语 [last()] 可以找到最后一个元素 print(result)
-
获取倒数第二个li元素的内容:
from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li[last()-1]/a') # text 方法可以获取元素内容 print(result[0].text)
-
获取倒数第二个li元素的内容的第二种方式:
from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li[last()-1]/a/text()') print(result)
总结:
1、解析html文件:使用'lxml.etree.HTML'进行解析。示例代码如下:
htmlElement = etree.HTML(text)
print(etree.tostring(htmlElement,encoding='utf-8').decode('utf-8'))
2、解析html文件:使用'lxml.etree.parse'进行解析。示例代码如下:
htmlElement = etree.parse("test.html")
print(etree.tostring(htmlElement,encoding='utf-8').decode('utf-8'))
这个函数默认使用的是'XML'解析器,所以如果碰到一些不规范的'HTML'代码的时候就会解析错误,
这时候就要自己创建"HTML"解析器。
parser = etree.HTMLParser(encoding='utf-8')
htmlElement = etree.parse("lagou.html", parser=parser)
print(etree.tostring(htmlElement, encoding='utf-8').decode('utf-8'))
实例
1、用requests和xpath爬取腾讯招聘网信息的demo02.py
BeautifulSoup4库
和 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。
lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM(Document Object Model)的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。
BeautifulSoup 用来解析 HTML 比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持 lxml 的 XML解析器。
Beautiful Soup 3 目前已经停止开发,推荐现在的项目使用Beautiful Soup 4。
安装和文档
- 安装:
pip install bs4
。 - 中文文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
几大解析工具对比
解析工具 | 解析速度 | 使用难度 |
---|---|---|
BeautifulSoup | 最慢 | 最简单 |
lxml | 快 | 简单 |
正则 | 最快 | 最难 |
简单使用
#!/usr/bin/env python3
# --*-- coding:utf-8 --*--
# __Author__ Aaron
from bs4 import BeautifulSoup
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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>
"""
# 创建 Beautiful Soup 对象
# 使用lxml来进行解析
soup = BeautifulSoup(html,"lxml")
print(soup.prettify())
四个常用的对象
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
- Tag
- NavigatableString
- BeautifulSoup
- Comment
1. Tag
Tag 通俗点讲就是 HTML 中的一个个标签。示例代码如下:
from bs4 import BeautifulSoup
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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>
"""
#创建 Beautiful Soup 对象
soup = BeautifulSoup(html,'lxml')
print (soup.title)
# <title>The Dormouse's story</title>
print (soup.head)
# <head><title>The Dormouse's story</title></head>
print (soup.a)
# <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
print (soup.p)
# <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
print (type(soup.p))
# <class 'bs4.element.Tag'
我们可以利用 soup 加标签名轻松地获取这些标签的内容,这些对象的类型是bs4.element.Tag。但是注意,它查找的是在所有内容中的第一个符合要求的标签。如果要查询所有的标签,后面会进行介绍。
对于Tag,它有两个重要的属性,分别是name和attrs。示例代码如下:
print (soup.name)
# [document] #soup 对象本身比较特殊,它的 name 即为 [document]
print (soup.head.name)
# head #对于其他内部标签,输出的值便为标签本身的名称
print (soup.p.attrs)
# {'class': ['title'], 'name': 'dromouse'}
# 在这里,我们把 p 标签的所有属性打印输出了出来,得到的类型是一个字典。
print (soup.p['class']) # soup.p.get('class')
# ['title'] #还可以利用get方法,传入属性的名称,二者是等价的
soup.p['class'] = "newClass"
print (soup.p) # 可以对这些属性和内容等等进行修改
# <p class="newClass" name="dromouse"><b>The Dormouse's story</b></p>
2. NavigableString
如果拿到标签后,还想获取标签中的内容。那么可以通过tag.string
获取标签中的文字。示例代码如下:
print (soup.p.string)
# The Dormouse's story
print (type(soup.p.string))
# <class 'bs4.element.NavigableString'>thon
3. BeautifulSoup
BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.
因为 BeautifulSoup 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 .name 属性是很方便的,所以 BeautifulSoup 对象包含了一个值为 “[document]” 的特殊属性 .name
soup.name
# '[document]'
4. Comment
Tag , NavigableString , BeautifulSoup 几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.容易让人担心的内容是文档的注释部分:
markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
soup = BeautifulSoup(markup)
comment = soup.b.string
type(comment)
# <class 'bs4.element.Comment'>
Comment 对象是一个特殊类型的 NavigableString 对象:
comment
# 'Hey, buddy. Want to buy a used parser'
遍历文档树
1. contents和children
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<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')
head_tag = soup.head
# 返回所有子节点的列表
print(head_tag.contents)
# 返回所有子节点的迭代器
for child in head_tag.children:
print(child)
2. strings 和 stripped_strings
如果tag中包含多个字符串 [2] ,可以使用 .strings 来循环获取:
for string in soup.strings:
print(repr(string))
# u"The Dormouse's story"
# u'\n\n'
# u"The Dormouse's story"
# u'\n\n'
# u'Once upon a time there were three little sisters; and their names were\n'
# u'Elsie'
# u',\n'
# u'Lacie'
# u' and\n'
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'\n\n'
# u'...'
# u'\n'
输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容:
for string in soup.stripped_strings:
print(repr(string))
# u"The Dormouse's story"
# u"The Dormouse's story"
# u'Once upon a time there were three little sisters; and their names were'
# u'Elsie'
# u','
# u'Lacie'
# u'and'
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'...'
搜索文档树
1. find和find_all方法:
搜索文档树,一般用得比较多的就是两个方法,一个是find
,一个是find_all
。find
方法是找到第一个满足条件的标签后就立即返回,只返回一个元素。find_all
方法是把所有满足条件的标签都选到,然后返回回去。使用这两个方法,最常用的用法是出入name
以及attr
参数找出符合要求的标签。
soup.find_all("a",attrs={"id":"link2"})
或者是直接传入属性的的名字作为关键字参数:
soup.find_all("a",id='link2')
2. select方法
使用以上方法可以方便的找出元素。但有时候使用css
选择器的方式可以更加的方便。使用css
选择器的语法,应该使用select
方法。以下列出几种常用的css
选择器方法:
(1)通过标签名查找:
print(soup.select('a'))
(2)通过类名查找:
通过类名,则应该在类的前面加一个.
。比如要查找class=sister
的标签。示例代码如下:
print(soup.select('.sister'))
(3)通过id查找:
通过id查找,应该在id的名字前面加一个#号。示例代码如下:
print(soup.select("#link1"))
(4)组合查找:
组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开:
print(soup.select("p #link1"))
直接子标签查找,则使用 > 分隔:
print(soup.select("head > title"))
(5)通过属性查找:
查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。示例代码如下:
print(soup.select('a[href="http://example.com/elsie"]'))
(6)获取内容
以上的 select 方法返回的结果都是列表形式,可以遍历形式输出,然后用 get_text() 方法来获取它的内容。
soup = BeautifulSoup(html, 'lxml')
print type(soup.select('title'))
print soup.select('title')[0].get_text()
for title in soup.select('title'):
print title.get_text()
总结:
#find_all的使用:
1、在提取标签的时候,第一个参数是标签的名字。然后如果在提取标签的时候想要使用标签属性进行过虑,那么可以在这个方法中通过关键字参数的形式,将属性的名字以及对应的值传进去。或者是使用‘attrs’属性,将所有的属性以及对应的值放在一个字典中传给'attrs'属性。
2、有些时候,在提取标签的时候,不想提取那么多,那么可以使用‘limit’参数。限制提取多少个。
#find与find_all的区别:
1、find:找到第一个满足条件的标签就返回,说白了,就只会返回一个元素
2、find_all:将所有满足条件的标签都返回。说白了,会返回很多标签(以列表的形式)。
#使用find和find_all的过滤条件:
1、关键字参数:将属性的名字作为关键字参数的名字,以及属性的值作为关键字的值进行过虑。
2、attrs参数:将属性条件放到一个字典中,传给attrs参数。
#获取标签的属性:
1、通过下标获取:通过标签下标的方式:
href = a['href']
2、通过attrs属性获取:示例代码:
href = a.attrs['href']
#string和strings、stripped_strings属性以及get_text方法:
1、string:获取某个标签下的非标签字符串。返回来的是个字符串。如果这个标签下有多行字符,那么就不能获取到了。
2、strings:获取某个标签下的子孙非标签字符串。返回来的是一个生成器。
3、stripped_strings:获取某个标签下的子孙非标签字符串,会去掉空白字符。返回来的是一个生成器。
4、get_text:获取某个标签下的子孙非标签字符串。不是以列表的形式返回。是以普通字符串返回。
#css选择器:
1、根据标签的名字选择,示例代码如下:
p{
background-color:pink;
}
2、根据类名选择,那么要在类的前面加一个点。示例代码如下:
.line{
background-color:pink;
}
3、根据id名字选择,那么要在id的前面加一个#号。示例代码如下:
#bos{
background-color:pink;
}
4、查找子孙元素,那么要在子孙元素中间有一个空格。示例代码如下:
#bos p{
background-color:pink;
}
5、查找直接子元素,那么要在父子元素中间有一个>。示例代码如下:
#box > p{
background-color:pink;
}
6、根据属性的名字进行查找。那么应该先写标签名字,然后再在中括号中写属性的值。示例代码如下:
input[name='username']{
background-color:pink;
}
7、在根据类名或者id进行查找的时候,如果还要根据标签名进行过虑,那么可以在类的前面或者id的前面加上标签名字。示例代码如下:
div#line{
background-color:pink;
}
div.line{
background-color:pink;
}
#BeautifulSop中使用css选择器:
在‘BeautifulSop’中,要使用css选择器,那么应该使用‘soup.select()’方法。应该传递一个css选择器的字符串给select方法。
#常见的四种对象:
1、Tag:BeautifulSoup中所有的标签都是Tag类型,并且BeautifulSop的对象其实本质上也是一个Tag类型。所以其实一些方法比如find、find_all并不是BeautifulSoup的,而是Tag的。
2、NavigableString:继承自python中str,用起来就跟使用python的str是一样的。
3、BeautifulSoup:继承处Tag。用来生成BeautifulSoup树的。对于一些查找方法,比如find、select这些,其实还是Tag的。
4、Comment:这个也没什么好说,就是继承自NavigableString。
#contents和children:
返回某个标签下的直接子元素,其中也包括字符串。他们两的区别是:conternts返回来的是一个列表,children:返回的是一个迭代器。
全部代码实例
中国天气网爬虫之所有城市数据爬取(pyecharts)
使用re爬取古诗文的信息