解析库会按照层次来提取,所以效率会大大的提升
PyQuery:强大好用
1. 字符串初始化
html = 一段网页源代码
from pyquery import PyQuery as pq
doc = pq(html)
print(doc('li'))
选择所有的li节点
2. URL初始化:
from pyquery import PyQuery as pq
doc = pq(url='http://cuiqingcai.com')
print(doc('title'))
传递的参数不仅可以是字符串,还可以是网页的URL,他会先请求这个URL,然后使用得到的HTML初始化,它相当于下面代码:
from pyquery import PyQuery as pq
import requests
doc = pq(requests.get('http://cuiqingcai.com').text)
print(doc('title'))
3. 文件初始化:
传入文件名,直接初始化里面的HTML
from pyquery import PyQuery as pq
doc = pq(filename='demo.html')
print(doc('li'))
4. CSS选择器:
from pyquery import PyQuery as pq
doc = pq(html)
print(doc('#container .list li'))
选取ID为container的节点内部的class为list的节点内部的所有li标签节点
5. 查找节点:
首先有:
from pyquery import PyQuery as pq
doc = pq(html)
items = doc('.list')
返回的结果基本上都是PyQuery类型!!!!
- 所有子孙节点find:
lis = items.find('li')
结果类型不是list,而是pyquery - 直接子节点children:
lis = items.children()
- 符合条件的直接子节点:
lis = items.children('.active')
可传入css选择器 - 直接父节点parent:
container = items.parent()
- 祖先父节点parents:
parent = items.parents('.wrap')
- 兄弟节点siblings:
print(li.siblings())
6. 遍历:
根据上面的查找节点方法,我们可以发现,PyQuery选择出来的节点可能是单个也可能是多个,类型也都是PyQuery类型而不是beautifulsoup那样的列表,那么我们需要遍历这个特殊类型:
from pyquery import PyQuery as pq
doc = pq(html)
lis = doc('li').items()
print(type(lis))
for li in lis:
print(li, type(li))
items()函数会得到一个生成器,遍历一下就可以得到li标签节点了。
7. 获取信息
我们现在已经找到了特定节点,现在我们就需要获取节点的特定信息:
a = doc('.item-0.active a')
- 获取属性:
print(a.attr('href'))
或者print(a.attr.href)
,当选中多个节点的时候,attr只会返回第一个节点的属性,所以需要用到遍历 - 获取纯文字文本:
print(a.text())
会忽略掉节点内包含的所有HTML,只返回纯的文字内容 - 获取HTML文本:
print(li.html())
返回节点内的所有HTML文本 - 当选中多个节点的时候,html()返回的是第一个节点的内部HTML文本,但是text()返回了所有节点的内部纯文本,中间用一个空格分割,实际上总体是一个字符串。
8. 节点操作
PyQuery有方法可以动态的修改节点,这样会对提取信息带来极大的遍历。
li = doc('.item-0.active')
首先选取了单个节点
- 增加移除class:
li.removeClass('active')
和li.addClass('active')
- 修改属性:
li.attr('name', 'link')
将name属性改为link - 修改纯文本内容:
li.text('changed item')
- 修改HTML内容:
li.html('<span>changed item</span>')
移除remove(): 这个会为筛选信息提供极大的帮助!!!!
例子:
html = '''
<div class="wrap">
Hello, World
<p>This is a paragraph.</p>
</div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
wrap = doc('.wrap')
print(wrap.text())
我们这样使用的text()方法会返回Hello, World This is a paragraph.
这个结果包含了内部的p节点内容,因为text()会把所有的纯文本全部提取,这个时候我们可以:
wrap.find('p').remove()
直接去掉里面的p标签节点
9. 伪类选择器:
伪类选择器可以选择第一个节点,最后一个节点,奇偶数节点,包含某一文本的节点等等
- 第一个节点:
li = doc('li:first-child')
- 最后一个节点:
li = doc('li:last-child')
- 第二个li节点:
li = doc('li:nth-child(2)')
- 偶数位置的节点:
li = doc('li:nth-child(2n)')
- 包含特殊文本的节点:
li = doc('li:contains(second)')
BeautifulSoup
BeautifulSoup 是 Python 的一个 HTML 或 XML 的解析库,我们可以用它来方便地从网页中提取数据。 BeautifulSoup 在解析的时候实际上是依赖于解析器的,它除了支持 Python 标准库中的 HTML 解析器,还支持一些第三方的解析器比如 LXML,推荐使用LXML解析库,因为它能解析HTML和XML:
BeautifulSoup(markup, "lxml") # 解析HTML
BeautifulSoup(markup, "xml") # 解析XML
例子:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml') # 会自动更正HTML里面未闭合的错误
print(soup.prettify()) # 将要解析的字符串以标准的缩进格式输出
print(soup.title.string) # 选中节点然后输出文本
# 这样选择title只会选择第一个匹配到的节点
1. 方法选择器!!!推荐使用
最常用的查询方法莫过于 find_all() 和 find() 了
find_all():
find_all(name , attrs , recursive , text , **kwargs)
- 根据名字查询:
print(soup.find_all(name='ul'))
可以继续遍历:
for ul in soup.find_all(name='ul'):
print(ul.find_all(name='li'))
for li in ul.find_all(name='li'):
print(li.string)
- 根据属性查询:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(attrs={'id': 'list-1'}))
print(soup.find_all(attrs={'name': 'elements'}))
对于特殊的id和class,我们有更方便的方法查询:
print(soup.find_all(id='list-1'))
print(soup.find_all(class_='element'))
这里注意class在python里面是一个关键字,所以需要在后面加一个下划线
- 根据文本关键字查询
匹配的可以是字符串也可以是正则表达式对象:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(text=re.compile('link')))
结果会返回所有匹配正则表达式的节点文本组成的列表
find():
该方法返回单个元素,也就是第一个匹配的元素
还有一些其他方法:
find_parents() 返回所有祖先节点
find_parent() 返回直接父节点
find_next_siblings() 返回后面所有兄弟节点
find_next_sibling() 返回后面第一个兄弟节点
find_previous_siblings() 返回前面所有兄弟节点
find_previous_sibling() 返回前面第一个兄弟节点
find_all_next() 返回节点后所有符合条件的节点
find_next() 返回第一个符合条件的节点
find_all_previous() 返回节点后所有符合条件的节点
find_previous() 返回第一个符合条件的节点
!!!!上面是推荐使用方法,下面是介绍一下其他方法!!!!!!!
2.节点选择器 X不推荐使用X:
获取节点名称:
print(soup.title.name)
获取节点属性:
print(soup.p.attrs) # 返回字典结构,所有属性
print(soup.p.attrs['name'])
print(soup.p['name']) # 简易写法
print(soup.p['class']) # 如果想获取Class,会返回一个列表,因为一个节点有可能有多个class
获取节点内容:
print(soup.p.string)
嵌套选择:
print(soup.head.title.string)
可以在一个节点的基础上再选择节点
关联选择:
子节点:
使用contents
属性可以得到一个列表形式的直接子节点,如果里面既包含文本,也包含节点,返回结果都将以列表的形式统一返回,
使用children
属性也可以得到结果
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.p.children)
for i, child in enumerate(soup.p.children):
print(i, child)
返回的结果一样,只不过该属性返回的是生成器类型,而contents返回的是列表类型
子孙节点:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.p.descendants)
for i, child in enumerate(soup.p.descendants):
print(i, child)
父节点:
使用parent
属性可以获取节点的直接父节点:
print(soup.a.parent)
祖父节点:
使用parents
属性可以获取所有的祖先节点:
list(enumerate(soup.a.parents))
返回结果是一个生成器类型,上面代码用列表输出了它的索引和内容
兄弟节点:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print('Next Sibling', soup.a.next_sibling)
print('Prev Sibling', soup.a.previous_sibling)
print('Next Siblings', list(enumerate(soup.a.next_siblings))) # 返回生成器
print('Prev Siblings', list(enumerate(soup.a.previous_siblings))) # 返回生成器
3. CSS选择器
使用 CSS 选择器,只需要调用 select() 方法,传入相应的 CSS 选择器即可,这里不多做介绍,因为如果要使用CSS选择器,推荐使用PyQuery!!!
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select('#list-2 .element'))
print(type(soup.select('ul')[0]))
XPath(LXML):
首先先安装LXML库
常用规则:
表达式 | 用法 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
例子://title[@lang=’eng’]
表示所有标签为title,并且lang属性为eng的节点
例子:
from lxml import etree
html = etree.HTML(text) # 要么将文本用HTML类初始化
html = etree.parse('./test.html', etree.HTMLParser()) # 要么直接对文本文件进行解析
result = etree.tostring(html)
print(result.decode('utf-8'))
result = html.xpath('//*') # 返回了所有节点列表
result = html.xpath('//li/a') # 选择 li 节点下的所有直接 a 子节点
result = html.xpath('//a[@href="link4.html"]/../@class') # 首先选中href为特定东西的a节点,然后选取它的父节点,在得到它的class属性。
result = html.xpath('//li[@class="item-0"]/text()') # text()方法可以获取节点的文本
result = html.xpath('//li/a/@href') # 获取li节点下所有a节点的href属性
属性多值匹配:
result = html.xpath('//li[contains(@class, "li")]/a/text()')
通过contains()方法,第一个参数为属性名称,第二个为属性值,只要属性包含这个传入的属性值就可以匹配
**多属性匹配:**from bs4 import BeautifulSoup
soup = BeautifulSoup(html, ‘lxml’)
print(soup.select(’.panel .panel-heading’))
print(soup.select(‘ul li’))
print(soup.select(’#list-2 .element’))
print(type(soup.select(‘ul’)[0]))
result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
使用运算符来连接
按序选择节点:
result = html.xpath('//li[1]/a/text()') # 选择第一个li节点,这里是从1开始计数!!!
result = html.xpath('//li[last()]/a/text()') # 选取最后一个节点
result = html.xpath('//li[position()<3]/a/text()') # 选择位置小于三的节点,也就是1和2
result = html.xpath('//li[last()-2]/a/text()') # 选取倒数第三个节点
使用节点轴选择节点:
result = html.xpath('//li[1]/ancestor::*')
# 使用了ancestor轴,获取所有的祖先节点,后面需要跟着两个冒号,后面才是节点选择器,使用了*代表匹配所有节点
result = html.xpath('//li[1]/ancestor::div')
# 加上限定条件,只返回div这个祖先节点
result = html.xpath('//li[1]/attribute::*')
# 使用attribute轴,获取所有属性,两个冒号后面是*即获取所有属性,所以最后返回第一个li的所有属性
result = html.xpath('//li[1]/child::a[@href="link1.html"]')
# 使用child轴,获取所有子节点
result = html.xpath('//li[1]/descendant::span')
# 使用descendant轴,获取所有的子孙节点
result = html.xpath('//li[1]/following::*[2]')
# 使用了following轴,可以获取所有的后续节点,虽然使用了*匹配所有,但是后面紧跟
result = html.xpath('//li[1]/following-sibling::*')
# 使用了following-sibling轴,可以获取当前节点之后的所有同级节点,加上*匹配,即所有后续同级节点