Python笔记:网络爬虫之XPath、Beautiful Soup、PyQuery的使用

网络爬虫解析库的引入

  1. 之前基于正则,比较繁琐,出错率较高
  2. 爬取信息大多基于html结构的web页面, 网页节点较多,各种层级关系

常见的Python解析库

  1. XPath: 基于XML文档,包含html查找功能 官方文档
  2. Beautiful Soup 依赖于lxml的解析库 官方文档
  3. PyQuery Python仿照jQuery严格实现 官方文档

通用的html模板文件

index.html

<!DOCTYPE html>
<html>
<head>
    <title>我的水果摊</title>
</head>
<body>
    <h3 id="title">我的水果</h3>
    <ul>
        <li class="item-0"><a class="onea" href="http://www.test1.com">橘子</a></li>
        <li class="item-1 shop"><a href="http://www.test2.com">橙子</a></li>
        <li class="item-2"><a href="http://www.test3.com">苹果</a></li>
        <li class="item-3"><a href="http://www.test4.com">樱桃</a></li>
        <li class="item-4 shop"><a href="http://www.test5.com">梨子</a></li>
    </ul>
</body>
</html>

关于 XPath 的使用

1)安装相关库

$ pip3 install lxml

2)进行解析处理

# 导入模块
from lxml import etree

# 读取html文件信息
f = open("./index.html",'r',encoding="utf-8")
content = f.read()
f.close()

# 解析HTML文档,返回根节点对象
html = etree.HTML(content)
#print(html)  # <Element html at 0x103534c88>

# 获取网页中所有标签并遍历输出标签名
result = html.xpath("//*")
for t in result:
    print(t.tag, end=" ")
#[html head title body h3 ul li a li a ...]
print()

# 获取节点
result = html.xpath("//li") # 获取所有li节点
result = html.xpath("//li/a") # 获取所有li节点下的所有直接a子节点
result = html.xpath("//ul//a") # 效果同上(ul下所有子孙节点)
result = html.xpath("//a/..") #获取所有a节点的父节点
print(result)

# 获取属性和文本内容
result = html.xpath("//li/a/@href") #获取所有li下所有直接子a节点的href属性值
result = html.xpath("//li/a/text()") #获取所有li下所有直接子a节点内的文本内容
print(result) #['橘子', '橙子', '苹果', '樱桃', '梨子']

result = html.xpath("//li/a[@class]/text()") #获取所有li下所有直接含有class属性子a节点内的文本内容
print(result) #['橘子', '苹果', '樱桃']

#获取所有li下所有直接含有class属性值为aa的子a节点内的文本内容
result = html.xpath("//li/a[@class='aa']/text()") 
print(result) #['苹果', '樱桃']

#获取class属性值中含有shop的li节点下所有直接a子节点内的文本内容
result = html.xpath("//li[contains(@class,'shop')]/a/text()") 
print(result) #['苹果', '樱桃']

# 按序选择
result = html.xpath("//li[1]/a/text()") # 获取每组li中的第一个li节点里面的a的文本
result = html.xpath("//li[last()]/a/text()") # 获取每组li中最后一个li节点里面的a的文本
result = html.xpath("//li[position()<3]/a/text()") # 获取每组li中前两个li节点里面的a的文本
result = html.xpath("//li[last()-2]/a/text()") # 获取每组li中倒数第三个li节点里面的a的文本
print(result) 

print("--" * 10)

# 节点轴选择
result = html.xpath("//li[1]/ancestor::*") # 获取li的所有祖先节点
result = html.xpath("//li[1]/ancestor::ul") # 获取li的所有祖先中的ul节点
result = html.xpath("//li[1]/a/attribute::*") # 获取li中a节点的所有属性值
result = html.xpath("//li/child::a[@href='http://www.sohu.com']") #获取li子节点中属性href值的a节点
result = html.xpath("//body/descendant::a") # 获取body中的所有子孙节点a
print(result) 

result = html.xpath("//li[3]") #获取li中的第三个节点    
result = html.xpath("//li[3]/following::li") #获取第三个li节点之后所有li节点
result = html.xpath("//li[3]/following-sibling::*") #获取第三个li节点之后所有同级li节点
for v in result:
    print(v.find("a").text)

# 获取id属性为title的节点中的文本内容
print(html.xpath("//h3[@id='title']/text()")) #['我的水果']

# 获取li中所有超级链接a的信息
result = html.xpath("//li/a")
for t in result:
    # 通过xapth()二次解析结果
    #print(t.xpath("text()")[0], ':', t.xpath("@href")[0])
    # 效果同上,使用节点对象属性方法解析
    print(t.text, ':', t.get("href"))

'''
#结果:
橘子 : http://www.test1.com
橙子 : http://www.test2.com
苹果 : http://www.test3.com
樱桃 : http://www.test4.com
梨子 : http://www.test5.com
'''

'''
重点补充:)

HTML元素的属性:
    tag:元素标签名
    text:标签中间的文本
HTML元素的方法:
    find()    查找一个匹配的元素
    findall() 查找所有匹配的元素    
    get(key, default=None) 获取指定属性值
    items()获取元素属性,作为序列返回
    keys()获取属性名称列表
    value是()将元素属性值作为字符串序列
'''

关于 Beautiful Soup 的使用

1)安装相关库

$ pip3 install lxml
$ pip3 install beautifulsoup4

2)主要的解析器的优缺点:

  • Python标准库 | BeautifulSoup(markup, "html.parser") | Python的内置标准库,执行速度适中,文档容错能力强 | Python 2.7.3 or 3.2.2前的版本中文档容错能力差
  • lxml HTML 解析器 | BeautifulSoup(markup, "lxml") | 速度快 文档容错能力强 | 需要安装C语言库
  • lxml XML 解析器 | BeautifulSoup(markup, ["lxml-xml"]) BeautifulSoup(markup, "xml") | 速度快 唯一支持XML的解析器 | 需要安装C语言库
  • html5lib | BeautifulSoup(markup, "html5lib") | 最好的容错性,以浏览器的方式解析文档,生成HTML5格式的文档 | 速度慢、不依赖外部扩展

lxml解析器有解析html和xml的功能,而且速度快,容错能力强,比较推荐这个。

3)分析使用

# 导入模块
from bs4 import BeautifulSoup
import re

# 读取html文件信息
f = open("./index.html",'r',encoding="utf-8")
content = f.read()
f.close()

# 创建解析器
soup = BeautifulSoup(content,"lxml")

# 输出网页内容:此内容已被缩进格式化(自动更正格式),在上一步实例化时就已完成
# print(soup.prettify())

# 输出网页中title标签中的内容
# print(soup.title.string)

# 选择元素测试
'''
print(soup.title) # <title>我的水果摊</title>
print(type(soup.title)) # <class 'bs4.element.Tag'>
print(soup.head) # 获取整个head元素,及内部元素
print(soup.li) # 获取第一个li元素(后面其他li不会获取)<li class="item-0"><a href="http://www.test1.com">橘子</a></li>
'''

# 提取信息测试
'''
print(soup.a) #获取第一个a元素标签:<a href="http://www.test1.com">橘子</a>
print(soup.a.name)    #获取标签名: a
print(soup.a.attrs)    #获取所有属性:{'class': ['onea'], 'href': 'http://www.test1.com'}
print(soup.a.attrs['href']) #获取其中一个属性:http://www.test1.com
print(soup.a.string) # 获取元素标签中间的文本内容:橘子
'''

# 嵌套选择测试
'''
print(soup.li.a) #获取网页中第一个li中的第一个a元素节点 输出 <a class="onea" href="http://www.test1.com">橘子</a>
print(type(soup.body.h3))  ##获取body中的第一个h3元素的类型:<class 'bs4.element.Tag'>
print(soup.body.h3.string)  #获取body中的第一个h3中的文本内容: 我的水果
'''

# 关联选择测试
'''
# 子或子孙节点,以下获取的节点列表都可以使用for...in遍历
print(soup.ul.contents) #获取ul下面的所有直接子节点,返回列表
print(soup.ul.children) #获取ul下面的所有直接子节点,返回一个:<list_iterator object at 0x121786a20>

print(soup.ul.descendants) # 获取ul下的所有子孙节点。
for v in soup.ul.descendants:
    print("item:", v)

# 父祖节点
print(soup.a.parent.name) #通过parent属性获取a的父节点 li
print(list(soup.a.parents)) # 获取所有祖先节点

#兄弟节点
print(soup.li.next_siblings)    #获取指定li节点的所有后面的兄弟节点
print(soup.li.previous_siblings)#获取指定li节点的所有前面的兄弟节点
for v in soup.li.next_siblings:
    print(v)

print(soup.a.string) #获取a节点中的文本
print(soup.a.attrs['href']) # 或a节点的href属性值
'''

# 方法选择器测试 find_all
'''
# 通过name指定li值,获取所有li元素节点,返回列表
lis = soup.find_all(name="li")
# 通过attrs指定属性来获取所有元素节点
lis = soup.find_all(attrs={"class":"onea"})
lis = soup.find_all(class_="onea") #同上(class属性中包含就可以了)
lis = soup.find_all(class_="shop") #class属性值中包含shop的所有节点
lis = soup.find_all(id="title") #<h3 id="title">我的水果</h3>
# 通过文本内容获取
lis = soup.find_all(text='橘子') # 橘子
lis = soup.find_all(text=re.compile('我')) # 我的水果摊 我的水果
for i in lis:
    print(i)

'''

# 方法选择器测试 find
'''
# 获取一个li元素节点
lis = soup.find(name="li")
# 通过attrs指定属性来获取一个元素节点
lis = soup.find(attrs={"class":"onea"})
for i in lis:
    print(i)

'''

# CSS选择器测试

'''
print(soup.select("ul li a")) # 获取ul里面li下面的a元素节点
print(soup.select("#title")) # 获取id属性值为title的元素节点
print(soup.select("li.shop a")) # 获取class属性为shop的li元素里面所有的a元素节点

# 套用选择解析器
lis = soup.select("ul li")
for li in lis:
    a = li.select("a")[0] #获取每个li里面的a元素节点
    print(a)
    print(a['href']) # 获取属性href的值
    # print(a.attrs['href']) # 同上 获取属性值
    print(a.get_text()) 
    print(a.string) # 同上 获取元素节点的文本内容

'''

关于 PyQuery 的使用

1)安装相关库

$ pip3 install pyquery

2)测试

from pyquery import PyQuery as pq

# doc = pq(url="http://www.baidu.com", encoding="utf-8")
# print(doc('title'))

'''
# 推荐使用requests爬取信息
from pyquery import PyQuery as pq
import requests
res = requests.get("http://www.baidu.com")
res.encoding = "utf-8" # 原编码为ISO-8859-1
# print(res.text)
doc = pq(res.text)
print(doc("title"))
'''

doc = pq(filename='index.html', encoding="utf-8")
print(doc('title')) # 

print(doc('title')) # 通过html标签名获取元素节点
print(doc('#title'))  # 获取id属性值为title的元素节点
print(doc('.onea'))  # 获取class属性值为onea的元素节点
print(doc('title, h3')) # 选择符组的使用

print(doc("ul li.shop a")) #关联选择符的使用

print(doc("a")) # 获取所有a
print(doc("a:first")) # 获取第一个a
print(doc("a:last")) # 获取最后一个a
print(doc("a:lt(2)")) # 获取前连个a
print(doc("a:eq(2)")) # 获取索引位置2的a(第三个)

print(doc('a[href="http://www.test1.com"]')) # 获取指定属性值的节点

print("=" * 10)

# 节点的二次筛选:
lis = doc("ul li") #获取ul中所有的li
print(type(lis)) # <class 'pyquery.pyquery.PyQuery'>
print(lis.find("a.onea")) # 在结果的基础上再次查找
print(lis.children("a.onea")) # 在结果的基础上再次查找

print(doc("a.onea").parent()) # 获取指定元素的直接父节点
#print(doc("a.onea").parents()) # 获取指定元素的所有父节点
print(doc("a.onea").parent().siblings()) # 获取兄弟节点

print("=" * 10)

# 遍历:
alist = doc("a")
for a in alist.items():
    print(a.attr.href)
    #print(a.attr('href')) #同上
    print(a.text())  #获取内容
    print(a.html()) 

发布了370 篇原创文章 · 获赞 169 · 访问量 66万+

猜你喜欢

转载自blog.csdn.net/Tyro_java/article/details/102993454