爬虫基础03 数据解析

        数据解析即从获取的html页面内容中获取指定标签属性或标签文本的过程。本文首先介绍了对数据持久化储存的操作,然后再进行对数据解析操作的介绍

解析方式描述
正则式存在难以构造、可读性差的问题,速度最快
BeautifulSoup容易构造和理解,文档容错能力较强
XPath通用性较强,效率与速率适中

1、csv -逗号分割值文件

1.1 读操作

导入csv相关包 : from csv import reader,DictReader

  • reader
# 1、reader   每一行数据对应一个列表
# a、打开需要虚得csv文件
f = open('filies/电影.csv', encoding='utf-8')
# b、创建文件对象的reader获取文件内容,返回值就是文件内容对应的生成器,
# 生成器中的元素就是文件每一个行内容对应的列表
r1 = reader(f)
print(next(r1))
print(list(r1))
# 读完数据后才能关闭文件
f.close()
  • DictReader
# 2、DictReader     每一行数据对应一个字典,键是第一行内容
# a、打开需要读的csv文件
f = open('filies/电影.csv', encoding='utf-8')

# b、创建DictReader对象获取文件内容,返回一个生成器,生成器中的元素是从第二行开始的每一行内容对应的字典
r2 = DictReader(f)
print(next(r2))
print(list(r2))
f.close()

1.2 写操作

from csv import writer,DictWriter

  • writer
# 1、writer
# a、以写的方式打开文件
f = open('filies/students.csv', 'w', encoding='utf-8', newline='')

# b、创建writer对象
w1 = writer(f)
# c、写入数据
# 一次写一行,一行内容对应一个列表
w1.writerow(['姓名', '年龄', '电话', '分数'])
# 同时写入多行内容,需要一个大的列表,列表中的元素是每一行内容对应的小列表
w1.writerows([
    ['小明', 18, '110', 98],
    ['小li', 17, '510', 95],
    ['小ha', 20, '150', 88],
])
f.close()
  • DictWriter
# DictWriter
f = open('filies/students2.csv', 'w', encoding='utf-8', newline='')
# 创建DictWriter对象
w2 = DictWriter(f, ['姓名', '年龄', '电话', '分数'])
# 写入数据
# 1、将字段写入到第一行中
w2.writeheader()
# 2、一次写入一行内容,一行内容对应一个字典
w2.writerow({'姓名': '小明', '年龄': 18, '电话': 183, '分数': 99})
# 同时写入多行内容
w2.writerows([
    {'姓名': '小明', '年龄': 18, '电话': 183, '分数': 99},
    {'姓名': '小hu', '年龄': 17, '电话': 103, '分数': 99},
    {'姓名': '小dd', '年龄': 22, '电话': 153, '分数': 99}
])
f.close()

 2. 正则式

2.1 分组命名

import re

str = """
<div class="info">
    <h2>title1</h2>
    <p>hello</p>
</div>
<div class="info">
    <h2>title2</h2>
    <p>world</p>
</div>
"""

# regex = '<div class="info">.*?<h2>(.*?)</h2>.*?<p>(.*?)</p>.*?</div>'
# result = re.findall(regex,str,re.S)
# print(result)

regex = '<div class="info">.*?<h2>(?P<title>.*?)</h2>.*?<p>(?P<content>.*?)</p>.*?</div>'
result2 =re.finditer(regex,str,re.S)
for obj in result2:
    print(obj.group("title")) # 通过名字获取值

    info_dict = obj.groupdict() # 根据?P<key> 将分组生成字典
    print(info_dict.values(),type(info_dict.values())) # dict_values(['title1', 'hello'])  <class 'dict_values'>

若后期对获取的分组进行加工,使用finditer()更合适

2.2 获取指定内容

# 提取<div id="first"></div>中所用<h2>和<p>元素之间的内容
import re

str = """
    <div id="first">
        <div class="info">
            <h2>title_first_1</h2>
            <p>content_first_1</p>
        </div>
        <div class="info">
             <h2>title_first_2</h2>
            <p>content_first_2</p>
        </div>
    </div>
    <div id="second">
        <div class="info">
            <h2>title_second_1</h2>
            <p>content_second_1</p>
        </div>
        <div class="info">
            <h2>title_second_2</h2>
            <p>content_second_2</p>
        </div>
    </div>
"""

# 先找到重复部分
info = re.search('<div id="first">(.*)</div>.*?<div id="second">', str, re.S)

# 再对内容进行提取
result = re.findall('<div class="info">.*?<h2>(.*?)</h2>.*?<p>(.*?)</p>.*?</div>',info.group(),re.S)
print(result)

         采用正则式进行匹配时,先分析页面规律(找到重复部分),然后采用re模块进行数据提取

2.3 爬取豆瓣top250

import requests
import re
import csv

url = "https://movie.douban.com/top250"

header = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
}

params = {
    "start": "0",
    "filter": ""
}

resp = requests.get(url, headers=header, params=params)
resp.encoding = "utf-8"
page_text = resp.text
regex = '<li>.*?<div class="hd">.*?<span class="title">(?P<name>.*?)</span>.*?<br>.*?(?P<year>.*?)&nbsp;' \
        '.*?<span property="v:best" content="10.0"></span>.*?<span>(?P<person_num>.*?)人评价</span>'

# csv文件写入(newline=''处理windows下多出空行问题)
fp = open("../files/info.csv", mode="w", encoding="utf-8", newline='')
csvwriter = csv.writer(fp)

movie_iter = re.finditer(regex, page_text, re.S)
for movie in movie_iter:
    # print(movie.group(1))
    # print(movie.group(2).strip())
    # print(movie.group(3))
    dicts = movie.groupdict()
    dicts['year'] = dicts['year'].strip()
    # print(dicts.values())
    csvwriter.writerow(dicts.values())
fp.close()

3、beautifulsoup

3.1 概述

BeautifulSoup是一个从html字符串提取数据的工具。BeautifulSoup特点包括以下几个方面:

  • API简单,功能强大

  • 自动实现编码转换(自动将输入文档转为Unicode类型,将输出文档转为utf-8编码)

  • 支持多种解析器(通常使用lxml解析器,若遇到一些无法使用lxml解析器解析的网站,使用html5lib解析器)

解析器使用方法优势劣势
Python标准库soup = BeautifulSoup(page_text,"html.parser")pthon内置标准库;执行速度适中容错能力较差
lxml HTML解析器soup = BeautifulSoup(page_text,"lxml")速度快;文档容错能力强需要安装C语言库
lxml XML解析器soup = BeautifulSoup(page_text,"xml")速度快;唯一支持XML的解析器需要安装C语言库
html5libsoup = BeautifulSoup(page_text,"html5lib")容错性好;像浏览器一样解析html;不依赖外部扩展库;速度慢

 3.2 beautifulsoup的安装

pip install bs4 -i https://pypi.doubanio.com/simple
    
#使用lxml解析器进行解析,需要安装lxml三方库
pip install lxml -i https://pypi.doubanio.com/simple

'''
'''
	若lxml库安装失败,提示需要C语言环境,只需要更新pip版本后再次安装即可
	1、在pycharm终端将路径切换到Scripts目录
	2、执行easy_install -U pip命令
	3、重新安装lxml三方库:pip install lxml -i https://pypi.doubanio.com/simple
'''
'''

采用BeautifulSoup进行解析的流程如下图所示:

<img src="python爬虫.assets/image-20220326224749490.png" alt="image-20220326224749490" style="zoom:67%;" />

**BeautifulSoup初始化**

```python
from bs4 import BeautifulSoup
BeautifulSoup(markup,features)  # markup:解析对象(html字符串或文件); features:解析器类型

# 字符串初始化(html_text通常为requests模块爬取的页面内容)
soup = BeautifulSoup(html_text,"lxml")

# 文件初始化
with open("index.html", encoding="utf-8") as fp:
    soup = BeautifulSoup(fp, "lxml")

3.3 选择器

选择器用来查找、定位元素,并获取数据。BeautifulSoup选择器分为节点选择器、方法选择器和CSS选择器。

节点选择器是获取数据的基本方法,方法选择器和css选择器是查找、定位元素的常用方法。

3.3.1 节点选择器

通过元素节点间的关系进行元素选择与信息的提取。节点选择器利用Tag对象选择节点元素,对应html中的标签。

3.3.2 获取节点元素

节点选择器通过【soup.tag】获取节点元素

from bs4 import BeautifulSoup

html_str = '''
'''
<html lang="en">
<head>
	<title>BeautifulSoup Test</title>
</head>
<body>
    <p class="intro"><em>This is em in p element</em></p>
    <p class="mainInfo">There are three cats, their names are
        <a href="http://baidu.com/little_white" class="cat" id="little_white">小白</a>,
        <a href="http://baidu.com/little_red" class="cat" id="little_red">小红</a> and
        <a href="http://baidu.com/little_blue" class="cat" id="little_blue">小蓝</a>;
        story over!
    </p>
</body>
</html>

soup = BeautifulSoup(html_str,'lxml')

# 获取title节点元素
print(soup.title)  # <title>BeautifulSoup Test</title>

# 获取节点元素类型(节点选择器返回类型为Tag)
print(type(soup.title))  # <class 'bs4.element.Tag'>

# 当html中存在多个相同节点时,仅返回第一个满足条件的节点
print(soup.a) # <a class="cat" href="http://baidu.com/little_white" id="little_white">小白</a>

# 获取嵌套子节点(每次返回都是Tag对象,可以级联选择)
print(soup.head.title) # <title>BeautifulSoup Test</title>


**关联节点选择**

| 操作                       | 返回类型                       | 描述                       |
| -------------------------- | ------------------------------ | -------------------------- |
| soup.tag.contents          | <class 'list'>                 | 返回元素直接子节点         |
| soup.tag.children          | <class 'list_iterator'>        | 返回元素直接子节点         |
| soup.tag.descendants       | <class 'generator'>            | 返回元素子孙节点           |
| soup.tag.parent            | <class 'bs4.element.Tag'>      | 返回元素父节点             |
| soup.tag.parents           | <class 'generator'>            | 返回元素祖先节点           |
| soup.tag.next_sibling      | 根据情况返回标签、文本、None等     | 返回元素后面第一个兄弟节点   |
| soup.tag.next_siblings     | <class 'generator'>            | 返回元素后面所有兄弟节点    |
| soup.tag.previous_sibling  | 根据情况返回标签、文本、None等	 | 返回元素前面第一个兄弟节点   |
| soup.tag.previous_siblings | <class 'generator'>            | 返回元素前面所有兄弟节点    |

```python
from bs4 import BeautifulSoup

html_str = '''
'''
<html lang="en">
<head><title>BeautifulSoup Test</title></head>
<body>
    <p class="mainInfo">There are three cats, their names are
        <a href="http://baidu.com/little_white" class="cat" id="little_white">小白</a>,
        <a href="http://baidu.com/little_red" class="cat" id="little_red">小红</a> and
        <a href="http://baidu.com/little_blue" class="cat" id="little_blue">小蓝</a>;
        story over!
    </p>
</body>
</html>

soup = BeautifulSoup(html_str,'lxml')

# 以列表形式返回直接子节点(['There are three cats, their names are\n        ', <a id="little_white">小白</a>, ',\n        ', <a id="little_red">小红</a>, ' and\n        ', <a id="little_blue">小蓝</a>, ';\n        story over!\n    '])
print(soup.p.contents) 

# 以迭代器形式返回直接子节点
print(soup.p.children)  # <list_iterator object at 0x0000027591E18130>
for obj in soup.p.children:
    print(obj)
for i,obj in enumerate(soup.p.children):
    print(i,obj)

# 以生成器形式返回子孙节点
print(soup.p.descendants)
for i,obj in enumerate(soup.p.descendants):
    print(i,obj)

# 返回第一个a元素的父节点
print(soup.a.parent)
print(type(soup.a.parent))  # <class 'bs4.element.Tag'>

# 返回第一个a元素的祖先节点
for i,obj in enumerate(soup.a.parents):
    print(i,obj)
print(type(soup.a.parents)) # <class 'generator'>

# 返回第一个a元素前面的第一个兄弟节点
print(soup.a.previous_sibling) # There are three cats, their names are

# 返回第一个a元素的所有后续兄弟节点
for i,obj in enumerate(soup.a.next_siblings):
    print(i,obj)

 3.3.3 css选择器

BeautifulSoup使用select()方法结合CSS选择器语法实现元素定位。

soup.select(css选择)
3.3.3.1 标签选择器
<p>hello world<p>

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_str,'lxml')
soup.select("p")
 3.3.3.2 id选择器
<p id="info">hello world<p>

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_str,'lxml')
soup.select("#info")
3.3.3.3 class选择器
<p class="font20">hello world<p>

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_str,'lxml')
soup.select(".font20")
3.3.3.4 子元素选择器
<div>
    <span>div span</span>
	<p>
    	<span>div p span</span>
    <p>
</div>
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_str,'lxml')
soup.select("div span")
soup.select("div > span")
3.3.3.5 属性选择器
<p class="ele">info</p>

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_str,'lxml')
soup.select("p[class='ele']")
3.3.3.6 css选择器实例
html_str = """
<div class="wrap">
    <div class="heading">
        <h2>title information</h2>
    </div>
    <div class="content">
        <ul class="list" id="list-main">
            <li class="item">li1-1</li>
            <li class="item">li1-2</li>
            <li class="item">li1-3</li>
        </ul>
        <ul class="list list-small" id="list-follow">
            <li class="item">li2-1</li>
            <li class="item">li2-2</li>
            <li class="item">li2-3</li>
        </ul>
    </div>
</div>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_str,'lxml')

# 1、获取所有li元素
print(soup.select("li"))

# 2、获取第二个ul的li子节点
print(soup.select("#list-follow li"))

# 3、获取class='heading'的div
print(soup.select(".heading"))

# 4、获取<li class="item">li2-2</li> 可通过索引和选择器获取子元素
print(soup.select("#list-follow li")[1])
print(soup.select("#list-follow li:nth-of-type(2)"))
3.3.3.7 select返回类型
# 注意:安装的时候安装beautifulsoup4, 使用的使用的是bs4; 使用的时候需要额外安装第三方库lxml
# beautifulsoup4是基于css选择器的网页数据解析器(可以解析html也可以解析xml)
# 通过css选择器获取内容的步骤:
# 1、soup.select() 返回的结果是bs4.element.ResultSet
# 2、若想取到某一个元素的话,需要通过索引或切片进行选择(bs4.element.Tag)
# 3、只有bs4.element.Tag类型的对象才能获取文本或属性值
from bs4 import BeautifulSoup
import requests

# 1. 准备网页源代码(来源于requests或者selenium)
header = {
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
}
response = requests.get('<https://movie.douban.com/top250>', headers=header)
result = response.text

# 2. 创建BeautifulSoup对象:BeautifulSoup(网页源代码, 'lxml')
soup = BeautifulSoup(result, 'lxml')

# 3. 获取标签(需要获取网页内容,先获取提供网页内容的标签)
"""
BeautifulSoup对象.select(css选择器)      -    获取整个网页中指定选择选中的所有标签,返回值是列表,列表中的元素是标签对象  
BeautifulSoup对象.select_one(css选择器)  -    获取整个网页中指定选择选中的第一个标签,返回值是标签对象
标签对象.select(css选择器)               -    获取指定标签中中指定选择选中的所有标签,返回值是列表,列表中的元素是标签对象  
标签对象.select_one(css选择器)           -   获取指定标签中指定选择选中的第一个标签,返回值是标签对象
"""
# result = soup.select('div.nav-items>ul>li>a')
result1 = soup.select('div.nav-items a')
print(result1)

print('--------------------------------华丽的分割线-------------------------------------')
result2 = soup.select_one('div.nav-items a')
print(result2)

print('--------------------------------华丽的分割线-------------------------------------')
result3 = soup.select('div.nav-items a')[1]
print(result3)

result4 = soup.select_one('div.nav-items>ul>li:nth-child(2)>a')
print(result4)

print('--------------------------------华丽的分割线-------------------------------------')
score_tags = soup.select('.rating_num')
print(score_tags)

print('--------------------------------华丽的分割线-------------------------------------')

# 4. 获取标签内容和标签属性
# 1)获取标签内容: 标签对象.text
score1 = score_tags[0]          # <span class="rating_num" property="v:average">9.7</span>
print(score1.text)          # 9.7

# 2)获取标签属性值: 标签对象.attrs[属性名]
img = soup.select_one('.pic img')       # <img alt="肖申克的救赎" class="" src="<https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg>" width="100"/>
print(img.attrs['alt'])
print(img.attrs['src'])
print(img.attrs['width'])

result = [x.text for x in result1]
print(result)

         通过select()返回的结果为ResultSet,需要通过索引才可以获取Tag对象;只有Tage对象才可提取内容

3.3.3.8 css选择器获取属性
<p class="ele">info</p>

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_str,'lxml')
print(soup.select("p")[0].attrs)
3.3.3.9 css选择器获取文本
html_str = """
<main>
	<div>
    	<p> main div p </p>
    </div>
	<p>main p</p>
</main>
"""

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_str,'lxml')
print(soup.select("p")[0].text)

3.4 函数选择器

方法描述
soup.find(name, attrs, recursive, text, **kwargs)获取第一个满足条件的元素
soup.find_all(name, attrs, recursive, text, limit, **kwargs)获取所有满足条件的元素

3.4.1 find()

搜索并返回第一个满足条件的元素,返回形式为Tag对象。

  • name属性(查找所有名字为name的节点<tag对象>)
from bs4 import BeautifulSoup

html_str = '''
<html lang="en">
<head><title>BeautifulSoup Test</title></head>
<body>
    <p class="mainInfo">There are three cats, their names are
        <a href="http://baidu.com/little_white" class="cat" id="little_white">小白</a>,
        <a href="http://baidu.com/little_red" class="cat" id="little_red">小红</a> and
        <a href="http://baidu.com/little_blue" class="cat" id="little_blue">小蓝</a>;
        story over!
    </p>
</body>
</html>
'''
soup = BeautifulSoup(html_str,'lxml')

print(soup.find("title")) # <title>BeautifulSoup Test</title>
print(type(soup.find("title")))  # <class 'bs4.element.Tag'>

# 当文本中存在多个元素时,返回第一个满足查询的元素
print(soup.find("a")) # <a class="cat" href="http://baidu.com/little_white" id="little_white">小白</a>
  • attrs属性(通过属性进行查询,属性以字典的形式提供)
from bs4 import BeautifulSoup

html_str = '''
<html lang="en">
<head><title>BeautifulSoup Test</title></head>
<body>
    <p class="mainInfo">There are three cats, their names are
        <a href="http://baidu.com/little_white" class="cat" id="little_white">小白</a>,
        <a href="http://baidu.com/little_red" class="cat" id="little_red">小红</a> and
        <a href="http://baidu.com/little_blue" class="cat" id="little_blue">小蓝</a>;
        story over!
    </p>
</body>
</html>
'''

# {'href': 'http://baidu.com/little_white', 'class': ['cat'], 'id': 'little_white'}
print(soup.find("a", attrs={"id":"little_blue","class":"cat"})) 
  • kwargs属性(通过属性进行查询,属性以属性=属性值的方式提供)
from bs4 import BeautifulSoup

html_str = '''
<html lang="en">
<head><title>BeautifulSoup Test</title></head>
<body>
    <p class="mainInfo">There are three cats, their names are
        <a href="http://baidu.com/little_white" class="cat" id="little_white">小白</a>,
        <a href="http://baidu.com/little_red" class="cat" id="little_red">小红</a> and
        <a href="http://baidu.com/little_blue" class="cat" id="little_blue">小蓝</a>;
        story over!
    </p>
</body>
</html>
'''
soup = BeautifulSoup(html_str, 'lxml')
print(soup.find("a", id="little_blue")) # <a class="cat" href="http://baidu.com/little_blue" id="little_blue">小蓝</a>
print(soup.find("a", id= "little_blue",class_="cat")) # 多属性书写方法

        采用dwargs方式时,当属性与python关键字冲突时,属性采用追加下划线的方式。如class -> class_

  • text属性(通过文本查询)
from bs4 import BeautifulSoup

html_str = '''
<body>
    <span>小白</span>
    <p>
        <a href="1.html">小白</a>
        <a href="2.html">小红</a> 
    </p>
</body>
'''
soup = BeautifulSoup(html_str, 'lxml')

print(soup.find(text="小白"))  # 小白
print(soup.find("a", text="小白"))  # <a href="1.html">小白</a>
print(soup.find(True, text="小白"))  # <span>小白</span>(只返回第一个满足条件的对象)

        name属性为True表示在所有元素中进行查询

  • recursive属性(设置是否搜索子孙节点,默认为True)
print(soup.find("a", recursive=False))

3.4.2 find_all() 

用于搜索当前节点下所有符合条件的节点,若未指定当前节点,就进行全文搜索

  • name属性(查找所有名字为name的节点<tag对象>)
from bs4 import BeautifulSoup

html_str = '''
<html lang="en">
<head><title>BeautifulSoup Test</title></head>
<body>
    <p class="mainInfo">There are three cats, their names are
        <a href="http://baidu.com/little_white" class="cat" id="little_white">小白</a>,
        <a href="http://baidu.com/little_red" class="cat" id="little_red">小红</a> and
        <a href="http://baidu.com/little_blue" class="cat" id="little_blue">小蓝</a>;
        story over!
    </p>
</body>
</html>
'''
soup = BeautifulSoup(html_str,'lxml')

# 1、name为字符串时是通过标签名查找
print(soup.find_all("a"))
print(type(soup.find_all("a"))) # <class 'bs4.element.ResultSet'>

# 2、name为列表时,表示与列表任意一项匹配,并以列表形式返回
print(soup.find_all(["a","head"]))
print(type(soup.find_all(["a","head"]))) # <class 'bs4.element.ResultSet'>

# 3、name为True时表示查询所有
print(soup.find_all(True))
print(type(soup.find_all(True))) # <class 'bs4.element.ResultSet'>

# 4、通过索引获取标签
a_list = soup.find_all("a")[1]
print(a_list)

# 5、通过切片获取标签
a_list2 = soup.find_all(("a"))[:2]
print(a_list2)
  • attrs属性(通过属性进行查询,属性以字典的形式提供)
from bs4 import BeautifulSoup

html_str = '''
<html lang="en">
<head><title>BeautifulSoup Test</title></head>
<body>
    <p class="mainInfo">There are three cats, their names are
        <a href="http://baidu.com/little_white" class="cat" id="little_white">小白</a>,
        <a href="http://baidu.com/little_red" class="cat" id="little_red">小红</a> and
        <a href="http://baidu.com/little_blue" class="cat" id="little_blue">小蓝</a>;
        story over!
    </p>
</body>
</html>
'''
soup = BeautifulSoup(html_str, 'lxml')
print(soup.find_all("a", attrs={"class": "cat"}))
  • kwargs属性(通过属性进行查询,属性以属性=属性值的方式提供)
from bs4 import BeautifulSoup

html_str = '''
<html lang="en">
<head><title>BeautifulSoup Test</title></head>
<body>
    <p class="mainInfo">There are three cats, their names are
        <a href="http://baidu.com/little_white" class="cat" id="little_white">小白</a>,
        <a href="http://baidu.com/little_red" class="cat" id="little_red">小红</a> and
        <a href="http://baidu.com/little_blue" class="cat" id="little_blue">小蓝</a>;
        story over!
    </p>
</body>
</html>
'''
soup = BeautifulSoup(html_str, 'lxml')

print(soup.find_all("a", id="little_white"))
print(soup.find_all("a", class_="cat"))

print(soup.find_all("a", id=True)) # 获取所有具有id属性的a标签
  • text属性(通过文本查询)
from bs4 import BeautifulSoup

html_str = '''
<html lang="en">
<head><title>BeautifulSoup Test</title></head>
<body>
    <span>小白</span>
    <p class="mainInfo">There are three cats, their names are
        <a href="http://baidu.com/little_white" class="cat" id="little_white">小白</a>,
        <a href="http://baidu.com/little_red" class="cat" id="little_red">小红</a> and
        <a href="http://baidu.com/little_blue" class="cat" id="little_blue">小蓝</a>;
        story over!
    </p>
</body>
</html>
'''
soup = BeautifulSoup(html_str, 'lxml')

print(soup.find_all(text="小白")) # ['小白', '小白']
print(soup.find_all(True, text="小白"))
print(soup.find_all("a", text="小白"))
  • limit属性(限制返回条数)
print(soup.find_all("a", limit=1))
  • recursive属性(设置是否搜索子孙节点,默认为True)
from bs4 import BeautifulSoup

html_str = '''
<body>
    <div>
        <a href="sina.com">百度</a>
        <p>
            <a href="sina.com">新浪</a>
            <a href="aliyun.com">阿里</a> 
        </p>
    </div>
</body>
'''
soup = BeautifulSoup(html_str, 'lxml')

print(soup.find("div").find_all("a")) # [<a href="baidu.com">百度</a>, <a href="sina.com">新浪</a>, <a href="aliyun.com">阿里</a>]
print(soup.find("div").find_all("a",recursive=False)) # [<a href="baidu.com">百度</a>]

3.5 提取信息

首先通过选择器获取Tag对象,然后采用表格中的属性提取相关信息

操作描述
soup.tag.name获取元素名称
soup.tag.attrs获取元素属性
soup.tag.string获取元素文本
from bs4 import BeautifulSoup

html_str = '''
<body>
    <span>小白</span>
    <p>
        <a href="1.html" class="cat">Mary</a>
        <a href="2.html" id="tar">Lucy</a> 
    </p>
</body>
'''
soup = BeautifulSoup(html_str, 'lxml')

# 返回标签名字
print(soup.p.a.name)   # a  通过节点选择器获取标签名
print(soup.select("p a")[0].name)  # a  通过CSS选择器获取标签名
print(soup.find("a").name)  # a  通关函数选择器获取标签名

# 返回标签属性
print(soup.p.a.attrs)  # {'href': '1.html', 'class': ['cat']}  通过节点选择器获取标签属性
print(soup.select("p a")[0].attrs)  # {'href': '1.html', 'class': ['cat']}  通过CSS选择器获取标签属性
print(soup.find("a").attrs)  # {'href': '1.html', 'class': ['cat']}  通过函数选择器获取标签属性

# 返回标签文本
print(soup.p.a.string)  # Mary  通过节点选择器获取标签属性
print(soup.select("p a")[0].string)  # Mary}  通过CSS选择器获取标签属性
print(soup.find("a").string)  # Mary  通过函数选择器获取标签属性

3.6 获取属性值

属性描述
[attribute]img['src']
attrs[attribute]img.attrs['src']
get(attribute)img.get("src")
from bs4 import BeautifulSoup

html_str = '''
<body>
    <span>小白</span>
    <p>
        <a href="1.html" class="cat">Mary</a>
        <a href="2.html" id="tar">Lucy</a> 
    </p>
</body>
'''

soup = BeautifulSoup(html_str,"lxml")
a_list = soup.find_all("a")

# 方法1:通过属性获取
for obj in a_list:
    print(obj['href'])

# 方法2:通过attrs[]方法获取
for obj in a_list:
    print(obj.attrs['href'])

# 方法3:通过get()方法获取
for obj in a_list:
    print(obj.get('href'))

3.7 获取文本

属性/方法描述
string获取目标路径下第一个非标签字符串,返回字符串
text获取目标路径下的子孙非标签字符串,返回字符串
strings获取目标路径下所有的子孙非标签字符串,返回生成器
stripped_strings获取目标路径下所有的子孙非标签字符串,会自动去掉空白字符串,返回生成器
from bs4 import BeautifulSoup

html_str = '''
<body>
    <span>百度</span>
    <p>hello
        <a href="sina.com" class="cat">新浪</a>
        <a href="aliyun.com" id="tar">阿里</a> 
    </p>
</body>
'''
soup = BeautifulSoup(html_str, 'lxml')

# 当元素内包含多个子节点时,string无法判别返回哪个节点的文本,结果为None
print(soup.find("span").string) # 百度
print(soup.find("p").string) # None

# text返回所有子孙节点的文本
print(soup.find("span").text) # 百度
'''
hello
        新浪
阿里
'''
print(soup.find("p").text)

# strings和stripped_strings都返回子孙节点的文本,stripped_strings会自动去除空白字符
for info in soup.find("p").strings:
    print(info)

for info1 in soup.find("p").stripped_strings:
    print(info1)

4. xpath

        XPath 是一门在 XML 文档中查找信息的语言。XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。

4.1 安装

pip install bs4 -i https://pypi.doubanio.com/simple

4.2 节点选取

表达式描述用法说明
nodename选取此节点的所有子节点div选取div的所有子标签
/从根节点选取//head/title选择head下的title标签
//从全局节点中寻找节点,忽略位置//div选取html页面所有div标签
.选取当前节点./span选择当前节点下所有span标签
..选取当前节点的父节点../span父节点下所有span标签
@选取属性//div[@id]选择所有带id属性的div标签
'''
<main id="wrap">
    <title>手机促销</title>
	<div>
	    <title id='first'>华为nova7</title>
		<strong class="low_price">2598</strong>
    </div>
	<div>
	    <title>OPPO Find X5</title>
		<strong>6299</strong>
    </div>
    <div>
	    <title>Redmi 8100</title>
		<strong class="low_price">2550</strong>
    </div>
    <p>gehehw</p>
</main>
'''

//div # 查找文档中的全部div标签

//div/title # 查找文档中的div下的所有title标签

//*[@id] # 查找所有具有id属性的标签

//div/strong[@class="low_price"]  # 查找div下所有class="low_price"的strong标签

//main/title  # 获取main标签下的title子标签 (<title>手机促销</title>)

4.3 xpath路径

XPath路径分为绝对路径和相对路径

  • 绝对路径:绝对路径从 HTML 根节点开始算,当页面较为复杂时,书写起来比较繁琐;

  • 相对路径:相对路径从任意节点开始,通常会选取一个可以唯一定位到的元素开始写,可以增加查找的准确性。通常以"//"开头

# 绝对路径(按层级找到元素)
/html/body/div[2]/div/div/div/div/form/span/input 

# 相对路径(选在id='nav-small'的div下所有p元素)
//div[@id='nav-small']/p

4.4 谓语

谓语用来查找某个特定的节点或者包含某个指定的值的节点,谓语被嵌在方括号中。

表达式用法描述
tag[index]//div/a[1]选择div下第一个a标签,需要从1开始
tag[last()]//div/a[last()]选择div下最有一个a标签
tag[position()<3]//div/a[position()<3]选择div下前两个a标签
[tag>3]//div[p>10]/p选择div下所有p元素,并且p元素取值大于10

谓语索引从1开始

'''
<main id="wrap">
    <title>手机促销</title>
	<div>
	    <title id='first'>华为nova7</title>
		<strong class="low_price">2598</strong>
    </div>
	<div>
	    <title>OPPO Find X5</title>
		<strong>6299</strong>
    </div>
    <div>
	    <title>Redmi 8100</title>
		<strong class="low_price">2550</strong>
    </div>
    <p>gehehw</p>
</main>
'''

# 获取所有title元素
titles = html.xpath("//main//title")
titles2 = html.xpath("//title")
titles3 = html.xpath("//*[@id='wrap']//title")

# 获取<strong class="low_price">2550</strong>
//main/div[3]/strong

# 获取<strong>6299</strong>
# //main/div[strong>5000]取出的是div元素,[strong>5000]作为div的限定条件
//main/div[strong>5000]/strong

4.4.1 谓语中的序号

'''
<main>
    <p>游泳</p>
    <p>爬山</p>
    <p>跑步</p>
<main>
'''

//p  # 获取所有p元素
//p[2] # 获取第二个p元素 (<p>爬山</p>)


'''
<main>
    <div>
        <p>拳击</p>
    </div>
    <div>
        <p>游泳</p>
    </div>
<main>
'''
//p  # 获取所有p元素
//p[1] # 获取<p>拳击</p> <p>游泳</p>


str = '''
<main>
    <div>
        <p>拳击</p>
        <p>游泳</p>
    </div>
<main>
'''
//p  # 获取所有p元素
//p[1] # 获取<p>拳击</p>

        XPath谓语是按层级关系返回,在实际开发中谨慎使用

4.5 通配符

通配符描述示例说明
*匹配任意元素节点//div[@id="tar"]/*选择id="tar"的div标签下所有元素节点
@*匹配任意属性节点//a[@*]选择所有拥有属性的a标签
//p/*	# 选取p元素的所有子元素

//*	 # 选取文档中的所有元素

//a[@*]	# 选取所有带有属性的a元素

//div[@id="tar"]/* # 选择id="tar"的div标签下的所有节点

 4.6 多路径选择

通过在路径表达式中使用“|”运算符,您可以选取若干个路径

//div/p | //div/a   # 选取div元素的所有p和a元素
​
//p | //div  # 选取文档中的所有p和div元素
​
//p[@id]/a | //div  # 选取所有具有id属性p元素下的a元素,以及所有的div元素

4.7 属性值/文本

表达式描述示例说明
text()获取文本//meta | //p获取所有的meta标签和p标签
/@获取属性值//a/@href获取a标签的href属性值
html_str = '''
<tr class="hobits">
    <td id="hobit1">游泳</td>
    <td id="hobit2">爬山</td>
    <td id="hobit3">跑步</td>
    <td id="hobit4">击剑</td>
    <td id="hobit5">射击</td>
</tr>
'''
# 获取爬山
//td[@id="hobit2"]/text()

# 获取hobit3
//td[3]/@id

4.8 内容解析

XPath不能直接解析字符串,要先将html文本转为html对象,然后再解析。

html字符串(requests获取的结果) -> html -> XPath解析

html字符串由requests库通过请求获取,html对象通过lxml库中的etree实现,内容提取由XPath实现

4.8.1 字符串创建html对象(常用)

html_str = '''
<tr class="hobits">
    <td id="hobit1">游泳</td>
    <td id="hobit2">爬山</td>
    <td id="hobit3">跑步</td>
    <td id="hobit4">击剑</td>
    <td id="hobit5">射击</td>
</tr>
'''

from lxml import etree

# etree会将文本转为html结构,并补全必要的内容
html = etree.HTML(html_str)
print(html) # <Element html at 0x27724813980>

# html对象本身无法以文本形式打印,可通过下列方式获取文本内容
info = etree.tostring(html,encoding="utf-8").decode("utf-8")
print(info)

4.8.2 文件创建html对象

# 采用该方法要求本地html文件完全遵循xml语法(例如标签必须封闭等)
from lxml import etree

# 根据实际情况更改文件路径 
html = etree.parse("index.html")
print(html)

result = etree.tostring(html,encoding="utf-8").decode("utf-8")
print(result)

        将网页下载到本地,然后通过本地加载的方式进行解析,通常会报错     

4.8.3 通过指定解析器创建html对象

from lxml import etree

# 创建解析器
parser = etree.HTMLParser(encoding="utf-8")

# 为parse指定解析器
html = etree.parse("../files/index.html",parser=parser)
result = etree.tostring(html,encoding="utf-8").decode("utf-8")
print(result)

        通过指定解析器的方法可以修正本地html文件结构,确保解析正确。

4.8.4 通过xpath获取百度信息

import requests
from lxml import etree

url = "https://www.baidu.com"

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
}

resp = requests.get(url, headers=headers)
page_text = resp.text

# 方法1:通过拼接方式获取
# html = etree.HTML(page_text)
#
# info_list = html.xpath('//div[@id="s-top-left"]/a/text()')
# href_list = html.xpath('//div[@id="s-top-left"]/a/@href')
#
# reslut = []
# for info, href in zip(info_list, href_list):
#     eg = {
#         "info": info,
#         "href": href
#     }
#     reslut.append(eg)
# print(reslut)

# 方法2:逐层获取
result_list = []
html = etree.HTML(page_text)
elements =  html.xpath('//div[@id="s-top-left"]/a')

for aobj in elements:
    info = aobj.xpath("./text()")[0]
    href = aobj.xpath("./@href")[0]
    eg = {
        "info": info,
        "href": href
    }
    result_list.append(eg)
print(result_list)

xpath()函数返回的结果为列表,可以通过索引或切片的方式获取列表中的部分内容。

通常情况下使用XPath语法获取整体内容,然后通过索引或切片方式过滤需求内容,对于需求内容过滤要谨慎使用XPath谓语语法(谓语语法是按层次获取)

5. 多页内容抓取

# 爬取多记录时,过滤掉规则不同的页面
for url in page:
    try:
    	#...
    except:
        continue

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值