Python爬虫数据解析之bs4解析和xpath解析

Posted 程序喵正在路上

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python爬虫数据解析之bs4解析和xpath解析相关的知识,希望对你有一定的参考价值。

🔥一个人走得远了,就会忘记自己为了什么而出发,希望你可以不忘初心,不要随波逐流,一直走下去🎶
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
🦄 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Python爬虫
🌠 首发时间:2022年8月9日
✅ 如果觉得博主的文章还不错的话,希望小伙伴们三连支持一下哦

阅读指南

bs4解析

1. html语法规则

bs4 解析比较简单,但是呢,我们需要了解一些基础的前端知识,然后再使用 bs4 去提取,逻辑就会清晰很多,编写难度也会大大降低

HTMLHyper Text Markup Language)超文本标记语言,是我们编写网页的最基本同时也是最核心的一种语言,其语法规则就是用不同的标签对网页上的内容进行标记,从而使网页显示出不同的展示效果

<h1>数据解析</h1>

上述代码的含义是在页面中显示 “数据解析” 4 个字,但是 “数据解析” 这 4 个字被 “<h1>” 和 “</h1>” 标记了,这个时候,浏览器在展示的时候就会让里面的内容,也就是 ”数据解析“ 这 4 个字变大加粗,俗称标题,所以 HTML 的语法就是用类似这样的标签对页面内容进行标记,不同的标签表示出来的效果也是不一样的

h1:一级标题
h2:二级标题
p:段落
font:字体
body:主体

这里只是给小白们简单科普一下,HTML 的标签远不止这些

好的,标签我们明白了,接下来就是属性了

<h1>数据解析</h1>
<h1 align='right'>数据解析</h1>

我们还可以在标签中添加 ” align='right' " 这样的东西,这又是什么呢?

首先,这两个标签都是 h1 标签,都是一级标题,但是下面这个会显示在右边,也就是说,通过这种形式对 h1 标签添加了说明,这种语法在 HTML 中被称为标签的属性,并且属性可以有很多个

总结:HTML 语法:

<标签 属性="" 属性="">
	被标记的内容
</标签>

有了这些知识,我们再去看 bs4 就会得心应手了,因为 bs4 就是通过标签和属性去定位页面上的内容的

2. bs4模块安装

bs4 是一个第三方库,首先我们得进行安装

Python 中推荐使用 pip 进行安装,很简单,在 Pycharm 下方找到 Terminal,输入以下命令:

pip install bs4

如果安装过慢,可以考虑更换为国内源:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple bs4

3. 搞搞农产品价格

bs4 在使用的时候需要参照一些 HTML 的基本语法来进行使用,通过案例来学会使用 bs4 更加快捷

我们来尝试抓取北京岳各庄批发市场的农产品价格 ➔ 目标网站

代码实现:

import csv
import requests
from bs4 import BeautifulSoup

# 获取页面源代码
url = "http://www.ygzapm.com/web/dailyPrice"
resp = requests.get(url)
# print(resp.text)  # 测试用

f = open("农产品信息.csv", mode="w", encoding='utf-8', newline='')
csvWriter = csv.writer(f)

# 解析数据
# 1. 把页面源代码交给 BeautifulSoup 进行处理,生成 bs 对象
page = BeautifulSoup(resp.text, "html.parser")  # 指定html解析器

# 2. 从bs对象中查找数据,两个函数
# find(标签,  属性=值)       找一个
# find_all(标签, 属性=值)        找全部

# table = page.find("table", class_="table")    # 加下划线区分关键字,与下面的表达式一样
table = page.find("table", attrs="class": "table")
# print(table)  # 测试用

trs = table.find_all("tr")[1:]
for tr in trs:  # 遍历表格的每一行
    tds = tr.find_all("td")
    type = tds[0].text  # 拿到被标签标记的内容
    name = tds[1].text
    max_price = tds[2].text
    min_price = tds[3].text
    avg_price = tds[4].text
    unit = tds[5].text
    market = tds[6].text
    date = tds[7].text
    # print(type, name, max_price, min_price, avg_price, unit, market, date)  # 测试用
    csvWriter.writerow([type, name, max_price, min_price, avg_price, unit, market, date])

resp.close()
f.close()
print("over!!!")

效果:

你也可以对代码进行改进,抓取多页数据,但个人建议不要将全部数据都抓取下来

4. 抓取彼岸图网壁纸

彼岸图网

你以为我们要抓取的是这些图片吗?

其实不是,我们要抓取的是点击图片进入的页面的高清图片,也就是如下

具体步骤一共有三步:

  1. 获取主页面源代码,然后提取子页面的链接地址
  2. 获取子页面源代码,查找图片的下载地址
  3. 下载图片

废话不多说,开搞

import requests
from bs4 import BeautifulSoup
import time

# 1. 获取主页面源代码,然后提取子页面的链接地址
url = "https://pic.netbian.com/"
resp = requests.get(url)
resp.encoding = 'gbk'

main_page = BeautifulSoup(resp.text, "html.parser")
alist = main_page.find("div", class_="slist").find_all("a")
# print(alist) # 测试用

for a in alist:
    href = url + a.get('href')     # 通过get得到属性的值,拼接得到完整子页面链接
    # print(a)  # 测试用

    # 2. 获取子页面源代码,查找图片的下载地址
    sub_page_resp = requests.get(href)
    sub_page_resp.encoding = 'gbk'
    sub_page_text = sub_page_resp.text

    # 从子页面中获取图片的下载地址
    sub_page = BeautifulSoup(sub_page_text, "html.parser")
    div = sub_page.find("div", class_="photo-pic")
    img = div.find("img")
    img_href = url + img.get("src").strip("/")       # 拼接图片的下载地址
    # print(img_href)   # 测试用

    # 3. 下载图片
    img_resp = requests.get(img_href)
    img_name = img_href.split("/")[-1]      # 图片名称
    img_package = "D:\\\\pythonProject\\\\images\\\\"     # 先自己创建一个文件夹以便存放图片,再运行程序
    with open(img_package + img_name, mode='wb') as f:
        f.write(img_resp.content)   # 这里拿到的是字节

    img_resp.close()
    sub_page_resp.close()
    print(img_name + " 已下载成功")
    time.sleep(1)       # 让服务器休息一下

resp.close()
print("over!!!")

这里是因为我的编译器设置了背景,图片效果不是特别好,还行

xpath解析

xpath 是一门在 XML 文档中查找信息的语言,xpath 可用来在 XML 文档中对元素和属性进行遍历,而我们熟知的 HTML 恰巧属于 XML 的一个子集,所以完全可以用 xpath 去查找 html 中的内容

首先,我们先了解几个概念

<book>
	<id>1</id>
 	<name>天才基本法</name>
 	<price>8.9</price>
 	<author>
 		<nick>林朝夕</nick>
 		<nick>裴之</nick>
 	</author>
</book> 

在上述 html 中,

  1. book, id, name, price…都被称为节点.
  2. Id, name, price, author 被称为 book 的子节点
  3. book 被称为 id, name, price, author 的父节点
  4. id, name, price,author 被称为同胞节点

好的,有了这些基础知识后,我们就可以开始了解 xpath 的基本语法了

Python 中想要使用 xpath,需要安装 lxml 模块

pip install lxml

用法:

  1. 将要解析的 html 内容构造出 etree 对象.
  2. 使用 etree 对象的 xpath() 方法配合 xpath 表达式来完成对数据的提取
from lxml import etree

xml = """
<book>
    <id>1</id>
    <name>天才基本法</name>
    <price>8.9</price>
    <author>
        <nick id="10086">林朝夕</nick>
        <nick id="10010">裴之</nick>
        
        <div>
            <nick id="jay1">周杰伦1</nick>
        </div>
        
        <span>
            <nick id="jay2">周杰伦2</nick>
        </span>
    </author>
    
     <partner>
         <nick id="ppc">佩奇</nick>
         <nick id="ppbc">乔治</nick>
     </partner>
</book>
"""

tree = etree.XML(xml)
# result = tree.xpath("/book")     # / 表示层级关系,第一个 / 是根节点
# result = tree.xpath("/book/name/text()")        # text() 获取文本
# result  = tree.xpath("/book/author/nick/text()")
# result  = tree.xpath("/book/author//nick/text()")       # 获取author里面所有nick里面的内容
# result  = tree.xpath("/book/author/*/nick/text()")       # * 任意的节点,通配符
result  = tree.xpath("/book//nick/text()")       # 获取所有nick里面的内容

print(result)

准备一个 HTML 文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Title</title>
</head>

<body>
    <ul>
        <li><a href="http://www.baidu.com">百度</a></li>
         <li><a href="http://www.google.com">⾕歌</a></li>
         <li><a href="http://www.sogou.com">搜狗</a></li>
    </ul>
     <ol>
         <li><a href="plane">⻜机</a></li>
         <li><a href="cannon">⼤炮</a></li>
         <li><a href="train">⽕⻋</a></li>
     </ol>
     <div class="jay">周杰伦</div>
     <div class="food">麻辣烫</div>
 </body>
 </html>

xpath 最方便的地方在于它可以直接复制,都不用你写

浏览器右击选择检查,点左上角小箭头点击你要提取的内容,它会自动匹配对应的 HTML 代码,右击 HTML 代码,选择复制,选择 xpath

from lxml import etree

tree = etree.parse("data.html")
# result = tree.xpath('/html/body/ul/li[1]/a/text()')        #xpath的顺序是从1开始数的
# result = tree.xpath("/html/body/ol/li/a[@href='cannon']/text()")        # 属性筛选
# print(result)

# ol_li_list = tree.xpath("/html/body/ol/li")
# for li in ol_li_list:
#     print(li.xpath("./a/text()"))       # 在li中继续查找,相对查找
#     print(li.xpath("./a/@href"))     # 获取属性值
#
# print(tree.xpath("/html/body/ul/li/a/@href"))
print(tree.xpath("/html/body/div[1]/text()"))

🧸 这次的分享就到这里啦,继续加油哦^^
🐱 我是程序喵,陪你一点点进步
🍭 有出错的地方欢迎在评论区指出来,共同进步,谢谢啦

爬虫之BS&Xpath

一 简介

简单来说,Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据。官方解释如下:

'''
Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。
它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。
'''

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.你可能在寻找 Beautiful Soup3 的文档,Beautiful Soup 3 目前已经停止开发,官网推荐在现在的项目中使用Beautiful Soup 4。

安装

pip3 install beautifulsoup4

解析器

Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python默认的解析器,lxml 解析器更加强大,速度更快,推荐安装。

pip3 install lxml

另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:

pip install html5lib

解析器对比:

技术图片

官方文档

简单使用

下面的一段HTML代码将作为例子被多次用到.这是 爱丽丝梦游仙境的 的一段内容(以后内容中简称为 爱丽丝 的文档):

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>
"""

使用BeautifulSoup解析这段代码,能够得到一个 BeautifulSoup 的对象

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')

从文档中找到所有标签的链接:

for` `link ``in` `soup.find_all(``'a'``):``    ``print``(link.get(``'href'``))

从文档中获取所有文字内容:

print(soup.get_text())

二 标签对象

通俗点讲就是 HTML 中的一个个标签,Tag 对象与XML或HTML原生文档中的tag相同:

soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
tag = soup.b
type(tag)
# <class 'bs4.element.Tag'>

Tag的名字

soup对象再以爱丽丝梦游仙境的html_doc为例,操作文档树最简单的方法就是告诉它你想获取的tag的name.如果想获取 标签,只要用 soup.head :

soup.head
# <head><title>The Dormouse's story</title></head>

soup.title
# <title>The Dormouse's story</title>

这是个获取tag的小窍门,可以在文档树的tag中多次调用这个方法.下面的代码可以获取标签中的第一个标签:

soup.body.b
# <b>The Dormouse's story</b>

通过点取属性的方式只能获得当前名字的第一个tag:

soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>

如果想要得到所有的标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到 Searching the tree 中描述的方法,比如: find_all()

soup.find_all('a')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

我们可以利用 soup加标签名轻松地获取这些标签的内容,注意,它查找的是在所有内容中的第一个符合要求的标签。

Tag的name和attributes属性

Tag有很多方法和属性,现在介绍一下tag中最重要的属性: name和attributes

每个tag都有自己的名字,通过 .name 来获取:

tag.name
# u'b'

tag['class']
# u'boldest'

tag.attrs
# {u'class': u'boldest'}

tag的属性可以被添加,删除或修改. 再说一次, tag的属性操作方法与字典一样

tag['class'] = 'verybold'
tag['id'] = 1
tag
# <blockquote class="verybold" id="1">Extremely bold</blockquote>

del tag['class']
del tag['id']
tag
# <blockquote>Extremely bold</blockquote>

tag['class']
# KeyError: 'class'
print(tag.get('class'))
# None

三 遍历文档树

'''
1、用法
2、获取标签的名称
3、获取标签的属性
4、获取标签的内容
5、嵌套选择
6、子节点、子孙节点
7、父节点、祖先节点
8、兄弟节点
'''
#遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">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>
"""

#1、用法
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml')
# soup=BeautifulSoup(open('a.html'),'lxml')

print(soup.p) #存在多个相同的标签则只返回第一个
print(soup.a) #存在多个相同的标签则只返回第一个

#2、获取标签的名称
print(soup.p.name)

#3、获取标签的属性
print(soup.p.attrs)

#4、获取标签的内容
print(soup.p.string) # p下的文本只有一个时,取到,否则为None
print(soup.p.strings) #拿到一个生成器对象, 取到p下所有的文本内容
print(soup.p.text) #取到p下所有的文本内容
for line in soup.stripped_strings: #去掉空白
    print(line)


'''
如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None,如果只有一个子节点那么就输出该子节点的文本,比如下面的这种结构,soup.p.string 返回为None,但soup.p.strings就可以找到所有文本
<p id='list-1'>
    哈哈哈哈
    <a class='sss'>
        <span>
            <h1>aaaa</h1>
        </span>
    </a>
    <b>bbbbb</b>
</p>
'''

#5、嵌套选择
print(soup.head.title.string)
print(soup.body.a.string)


#6、子节点、子孙节点
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)

#7、父节点、祖先节点
print(soup.a.parent) #获取a标签的父节点
print(soup.a.parents) #找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...


#8、兄弟节点
print('=====>')
print(soup.a.next_sibling) #下一个兄弟
print(soup.a.previous_sibling) #上一个兄弟

print(list(soup.a.next_siblings)) #下面的兄弟们=>生成器对象
print(soup.a.previous_siblings) #上面的兄弟们=>生成器对象

四 搜索文档树

BeautifulSoup定义了很多搜索方法,这里着重介绍2个: find() 和 find_all() .其它方法的参数和用法类似

1、五种过滤器

#搜索文档树:BeautifulSoup定义了很多搜索方法,这里着重介绍2个: find() 和 find_all() .其它方法的参数和用法类似
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">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')

#1、五种过滤器: 字符串、正则表达式、列表、True、方法
#1.1、字符串:即标签名
print(soup.find_all('b'))

#1.2、正则表达式
import re
print(soup.find_all(re.compile('^b'))) #找出b开头的标签,结果有body和b标签

#1.3、列表:如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:
print(soup.find_all(['a','b']))

#1.4、True:可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
print(soup.find_all(True))
for tag in soup.find_all(True):
    print(tag.name)

#1.5、方法:如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

print(soup.find_all(has_class_but_no_id))

2、find_all()

#2、find_all( name , attrs , recursive , text , **kwargs )
#2.1、name: 搜索name参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .
print(soup.find_all(name=re.compile('^t')))

#2.2、keyword: key=value的形式,value可以是过滤器:字符串 , 正则表达式 , 列表, True .
print(soup.find_all(id=re.compile('my')))
print(soup.find_all(href=re.compile('lacie'),id=re.compile('d'))) #注意类要用class_
print(soup.find_all(id=True)) #查找有id属性的标签

# 有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','lxml')
# data_soup.find_all(data-foo="value") #报错:SyntaxError: keyword can't be an expression
# 但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:
print(data_soup.find_all(attrs={"data-foo": "value"}))
# [<div data-foo="value">foo!</div>]

#2.3、按照类名查找,注意关键字是class_,class_=value,value可以是五种选择器之一
print(soup.find_all('a',class_='sister')) #查找类为sister的a标签
print(soup.find_all('a',class_='sister ssss')) #查找类为sister和sss的a标签,顺序错误也匹配不成功
print(soup.find_all(class_=re.compile('^sis'))) #查找类为sister的所有标签

#2.4、attrs
print(soup.find_all('p',attrs={'class':'story'}))

#2.5、text: 值可以是:字符,列表,True,正则
print(soup.find_all(text='Elsie'))
print(soup.find_all('a',text='Elsie'))

#2.6、limit参数:如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果
print(soup.find_all('a',limit=2))

#2.7、recursive:调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .
print(soup.html.find_all('a'))
print(soup.html.find_all('a',recursive=False))

'''
像调用 find_all() 一样调用tag
find_all() 几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同,下面两行代码是等价的:
soup.find_all("a")
soup("a")
这两行代码也是等价的:
soup.title.find_all(text=True)
soup.title(text=True)
'''

3、find()

#3、find( name , attrs , recursive , text , **kwargs )
find_all() 方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果.比如文档中只有一个<body>标签,那么使用 find_all() 方法来查找<body>标签就不太合适, 使用 find_all 方法并设置 limit=1 参数不如直接使用 find() 方法.下面两行代码是等价的:

soup.find_all('title', limit=1)
# [<title>The Dormouse's story</title>]
soup.find('title')
# <title>The Dormouse's story</title>

唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果.
find_all() 方法没有找到目标是返回空列表, find() 方法找不到目标时,返回 None .
print(soup.find("nosuchtag"))
# None

soup.head.title 是 tag的名字 方法的简写.这个简写的原理就是多次调用当前tag的 find() 方法:

soup.head.title
# <title>The Dormouse's story</title>
soup.find("head").find("title")
# <title>The Dormouse's story</title>

4、其他方法

#见官网:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#find-parents-find-parent

5、css选择器

我们在写 CSS 时,标签名不加任何修饰,类名前加点,id名前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list

(1)通过标签名查找

print(soup.select("title"))  #[<title>The Dormouse's story</title>]
print(soup.select("b"))      #[<b>The Dormouse's story</b>]

(2)通过类名查找

print(soup.select(".sister")) 

'''
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, 
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

'''

(3)通过 id 名查找

print(soup.select("#link1"))
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

(4)组合查找

组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开

print(soup.select("p #link2"))

#[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

直接子标签查找

print(soup.select("p > #link2"))
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

(5)属性查找

查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。

print(soup.select("a[href='http://example.com/tillie']"))
#[<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

select 方法返回的结果都是列表形式,可以遍历形式输出,然后用 get_text() 方法来获取它的内容:

for title in soup.select('a'):
    print (title.get_text())

'''
Elsie
Lacie
Tillie
'''

五 修改文档树

修改文档树

xpath

xpath简介

XPath在Python的爬虫学习中,起着举足轻重的地位,对比正则表达式 re两者可以完成同样的工作,实现的功能也差不多,但XPath明显比re具有优势,在网页分析上使re退居二线。

XPath介绍

是什么? 全称为XML Path Language 一种小型的查询语言
说道XPath是门语言,不得不说它所具备的优点:

  • 可在XML中查找信息
  • 支持HTML的查找
  • 通过元素和属性进行导航

python开发使用XPath条件: 由于XPath属于lxml库模块,所以首先要安装库lxml。

XPath的简单调用方法:

from lxml import etree

selector=etree.HTML(源码) #将源码转化为能被XPath匹配的格式

selector.xpath(表达式) #返回为一列表

Xpath语法

查询

html_doc = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div class="d1">
    <div class="d2">
            <p class="story">
                <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" id="link3">Tillie</a>
            </p>
    </div>
    <div>
        <p id="p1">ALex is dsb</p>
        <p id="p2">Egon too</p>
    </div>
</div>

<div class="d3">
    <a href="http://www.baidu.com">baidu</a>
    <p>百度</p>
</div>

</body>
</html>
"""

from lxml import etree
selector=etree.HTML(html_doc) # 将源码转化为能被XPath匹配的格式


'''
一、选取节点

nodename     选取nodename节点的所有子节点         xpath(‘//div’)         选取了所有div节点
/            从根节点选取                        xpath(‘/div’)          从根节点上选取div节点
//           选取所有的当前节点,不考虑他们的位置    xpath(‘//div’)         选取所有的div节点
.            选取当前节点                        xpath(‘./div’)         选取当前节点下的div节点
..           选取当前节点的父节点                 xpath(‘..’)            回到上一个节点
@            选取属性                           xpath(’//@calss’)     选取所有的class属性

'''

ret=selector.xpath("//div")
ret=selector.xpath("/div")
ret=selector.xpath("./div")
ret=selector.xpath("//p[@id='p1']")
ret=selector.xpath("//div[@class='d1']/div/p[@class='story']")


'''
二、谓语

表达式                                         结果
xpath(‘/body/div[1]’)                     选取body下的第一个div节点
xpath(‘/body/div[last()]’)                选取body下最后一个div节点
xpath(‘/body/div[last()-1]’)              选取body下倒数第二个div节点
xpath(‘/body/div[positon()<3]’)           选取body下前两个div节点
xpath(‘/body/div[@class]’)                选取body下带有class属性的div节点
xpath(‘/body/div[@class=”main”]’)         选取body下class属性为main的div节点
xpath(‘/body/div[price>35.00]’)           选取body下price元素值大于35的div节点

'''

ret=selector.xpath("//p[@class='story']//a[2]")
ret=selector.xpath("//p[@class='story']//a[last()]")


'''
通配符 Xpath通过通配符来选取未知的XML元素

表达式                 结果
xpath(’/div/*’)     选取div下的所有子节点
xpath(‘/div[@*]’)    选取所有带属性的div节点


'''

ret=selector.xpath("//p[@class='story']/*")
ret=selector.xpath("//p[@class='story']/a[@class]")

'''
四、取多个路径
使用“|”运算符可以选取多个路径

表达式                         结果
xpath(‘//div|//table’)    选取所有的div和table节点


'''

ret=selector.xpath("//p[@class='story']/a[@class]|//div[@class='d3']")
print(ret)

'''


五、Xpath轴
轴可以定义相对于当前节点的节点集

轴名称                      表达式                                  描述
ancestor                xpath(‘./ancestor::*’)              选取当前节点的所有先辈节点(父、祖父)
ancestor-or-self        xpath(‘./ancestor-or-self::*’)      选取当前节点的所有先辈节点以及节点本身
attribute               xpath(‘./attribute::*’)             选取当前节点的所有属性
child                   xpath(‘./child::*’)                 返回当前节点的所有子节点
descendant              xpath(‘./descendant::*’)            返回当前节点的所有后代节点(子节点、孙节点)
following               xpath(‘./following::*’)             选取文档中当前节点结束标签后的所有节点
following-sibing        xpath(‘./following-sibing::*’)      选取当前节点之后的兄弟节点
parent                  xpath(‘./parent::*’)                选取当前节点的父节点
preceding               xpath(‘./preceding::*’)             选取文档中当前节点开始标签前的所有节点

preceding-sibling       xpath(‘./preceding-sibling::*’)     选取当前节点之前的兄弟节点
self                    xpath(‘./self::*’)                  选取当前节点
 

六、功能函数   
使用功能函数能够更好的进行模糊搜索

函数                  用法                                                               解释
starts-with         xpath(‘//div[starts-with(@id,”ma”)]‘)                        选取id值以ma开头的div节点
contains            xpath(‘//div[contains(@id,”ma”)]‘)                           选取id值包含ma的div节点
and                 xpath(‘//div[contains(@id,”ma”) and contains(@id,”in”)]‘)    选取id值包含ma和in的div节点
text()              xpath(‘//div[contains(text(),”ma”)]‘)                        选取节点文本包含ma的div节点

'''

Element对象

from lxml.etree import _Element
for obj in ret:
    print(obj)
    print(type(obj))  # from lxml.etree import _Element

'''
Element对象

class xml.etree.ElementTree.Element(tag, attrib={}, **extra)

  tag:string,元素代表的数据种类。
  text:string,元素的内容。
  tail:string,元素的尾形。
  attrib:dictionary,元素的属性字典。
  
  #针对属性的操作
  clear():清空元素的后代、属性、text和tail也设置为None。
  get(key, default=None):获取key对应的属性值,如该属性不存在则返回default值。
  items():根据属性字典返回一个列表,列表元素为(key, value)。
  keys():返回包含所有元素属性键的列表。
  set(key, value):设置新的属性键与值。

  #针对后代的操作
  append(subelement):添加直系子元素。
  extend(subelements):增加一串元素对象作为子元素。#python2.7新特性
  find(match):寻找第一个匹配子元素,匹配对象可以为tag或path。
  findall(match):寻找所有匹配子元素,匹配对象可以为tag或path。
  findtext(match):寻找第一个匹配子元素,返回其text值。匹配对象可以为tag或path。
  insert(index, element):在指定位置插入子元素。
  iter(tag=None):生成遍历当前元素所有后代或者给定tag的后代的迭代器。#python2.7新特性
  iterfind(match):根据tag或path查找所有的后代。
  itertext():遍历所有后代并返回text值。
  remove(subelement):删除子元素。



'''

以上是关于Python爬虫数据解析之bs4解析和xpath解析的主要内容,如果未能解决你的问题,请参考以下文章

Python 爬虫--数据解析(bs4方法)

小爬虫学习——网页解析工具(bs4xpath)

爬虫之数据解析,网页源码数据分析

全网最全python爬虫系统进阶学习(附原代码)学完可就业

全网最全python爬虫系统进阶学习(附原代码)学完可就业

关于爬虫中常见的两个网页解析工具的分析 —— lxml / xpath 与 bs4 / BeautifulSoup