解析工具用来分析爬取到的内容,常用的工具有:
re 库是 python 用来操作正则表达式的一个内置模块,直接引用就可以使用了。
注,很多时候返回的是一个 Match 对象,可以使用 group 方法获取对象中的匹配结果
re 模块常用的方法有:
re.findall(regexp, string)->Listlst = re.findall('m', 'mai le fo len, mai ni mei!')
print(lst) # ['m', 'm', 'm']
lst = re.findall(r"\d+", '5点之前,你要给我5000万')
print(lst) # ['5', '5000']
ret = re.search(r"\d", '5点之前,你要给我5000万').group()
print(ret) # 5
re.compile(regexp)->Reobj = re.complie(r'\d')
ret = obj.search('5点之前,你要给我5000万')
# complie 方法的参数可以对正则表达式进行扩展,例如
obj = re.complie(r'11.*?11', re.S) # 参数2 re.S 可以让正则表达式中 . 可以匹配换行符
当匹配结果有很多,而我们只想要匹配结果其中的一部分时,可以使用分组
import re
s = """
<div class='class1'><span id='1'>孙悟空</span></div>
<div class='class2'><span id='2'>猪八戒</span></div>
<div class='class3'><span id='3'>唐僧</span></div>
<div class='class4'><span id='4'>沙和尚</span></div>
<div class='class5'><span id='5'>白龙马</span></div>
"""
obj = re.compile(r"<div class='.*?'><span id='\d+'>(?P<gp1>.*?)</span></div>", re.S)
res = obj.finditer(s)
for it in res:
print(it.group('gp1'))
此例中,(?P ) 内为分组,分组名称为 <> 内的 gp1 ,分组内容就是括号中的 .*? 所匹配的内容。使用 group 方法调用分组信息,如果此方法没有参数则是返回全部匹配信息,有参数则返回参数指定的分组信息。
BS4 是一个第三方库,全称 beautiful soup 版本4。它可以根据 HTML 的标签来进行分析并获取需要的数据
使用 pip install bs4 进行安装,使用 from bs4 import BeautifulSoup 引用 BS4
使用 bs4 解析的步骤如下:
获取解析内容就是正常的获取页面源代码
import requests
url ='https://movie.douban.com/top250'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
}
resp = requests.get(url, headers=headers)
from bs4 import BeautifulSoup
page = BeautifulSoup(resp.text, "html.parser")
参数2是指定了传到 bs4 对象里的数据格式。
使用 find(标签名, 属性=值) 和 find_all(标签名, 属性=值) 这两个方法来获取需要的对象
# 查找并获取第一个 ol 标签
ol = page.find("ol", class_='grid_view') # class 是关键字,所以使用 class_
如果要避免使用关键字或常用字产生冲突,可以这样使用:
ol = page.find("ol", attrs={"class": "grid_view"})
这两者是等同的。获取所有数据:
lis = ol.find_all("li") # 获取所有 li 标签,生成一个列表
根据获取的标签,取得标签的文本内容:
for li in lis:
title = li.find("span", attrs={"class": "title"})
print(title.text) # .text 为被标签的文本内容
使用另一个实例演示如何获取标签属性
import requests, time
from bs4 import BeautifulSoup
url = 'https://www.umei.cc/bizhitupian/weimeibizhi/'
resp = requests.get(url)
resp.encoding = 'utf-8'
# 获取主页面代码
main_page = BeautifulSoup(resp.text, "html.parser")
# 查找子页面超链接
a_list = main_page.find("div", attrs={"class": "pic-box"}).find("ul", attrs={"class", "pic-list after"}).find_all("a")
for a in a_list:
herf = 'https://www.umei.cc/' + a.get('href').strip('/') # 通过 get 方法获取标签属性的值
# 获取子页面源代码
resp_child = requests.get(herf)
resp_child.encoding = 'utf-8'
# 获取子页面需要的数据
child_page = BeautifulSoup(resp_child.text, 'html.parser')
img = child_page.find("section", attrs={"class": "img-content"}).find('img') # 获取 img 标签
src = img.get('src') # 获取 img 标签的 src 属性值
img_name = src.split('/')[-1] # 以 / 进行切割,取最后一部分,作为图片名称
img_resp = requests.get(src) # 请求图片
with open("./download/" + img_name, mode='wb') as f:
f.write(img_resp.content) # 图片内容写入文件
print('Over!!', img_name)
time.sleep(1) # 设置间隔时间,防止请求速度太快
即,使用 BeautifulSoup.get(attrs_name)->String 来获取标签内的属性值
xpath 解析是一种新的解析方式,比 re 简单,比 bs 高效,所以会经常用到。xpath 是在 xml 文档中搜索内容的一种语言,类似正则表达式。
xpath 解析使用第三方库 lxml
使用 pip install lxml 安装 lxml库,使用 from lxml import etree 来引用
xpath 可以根据输入的数据类型不同指定处理格式,例如:
from lxml import etree
xml = """...""" # 定义一段 xml 文本
tree_xml = etree.XML(xml) # 生成基于 xml 的对象
tree_file = etree.parse('b.html') # 基于文件生成对象
这里先定义一段 xml 文本
xml = """
<book>
<id>1</id>
<name>野花遍地香</name>
<price>1.23</price>
<nick>臭豆腐</nick>
<author>
<nick id="10086">周大强</nick>
<nick id="10010">周芷若</nick>
<nick class="joy">周杰伦</nick>
<nick class="jolin">蔡依林</nick>
<div>
<nick>热热热热</nick>
</div>
<div>
<nick>特别热</nick>
<div>
<nick>好热好热</nick>
</div>
</div>
<span>
<nick>span的热</nick>
</span>
</author>
<partner>
<nick id="ppc">胖胖陈</nick>
<nick id="ppbc">胖胖不陈</nick>
</partner>
</book>
"""
tree = etree.XML(xml) # 生成基于 xml 的对象
# result = tree.xpath('/book') # / 表示层级关系,第一个 / 是根节点
# 获取 name 的文本内容,使用 text()
# result = tree.xpath('/book/name/text()') # ['野花遍地香']
# 获取的节点不是唯一,输出的是同胞节点列表
# result = tree.xpath('/book/author/nick/text()') # ['周大强', '周芷若', '周杰伦', '蔡依林']
# 跨层级查找同名节点,使用 // 即跨层级查找所有后代节点,查找节点 nick
# result = tree.xpath('/book/author//nick/text()') # ['周大强', '周芷若', '周杰伦', '蔡依林', '热热热热', '特别热', '好热好热', 'span的热']
# 跨节点(任意节点)查找节点,使用通配符 *
result = tree.xpath('/book/author/*/nick/text()') # ['热热热热', '特别热', 'span的热']
print(result)
除了按照层级和节点来获取需要的数据,还可以根据情况,进行具体的筛选定位。假设筛选对象为 b.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="feiji">飞机</a></li>
<li><a href="dapao">大炮</a></li>
<li><a href="huoche">火车</a></li>
</ol>
<div class="job">李嘉诚</div>
<div class="common">胡辣汤</div>
</body>
</html>
from lxml import etree
tree = etree.parse("b.html") # 以文件创建 xpath 对象
# 获取节点文本列表
# result = tree.xpath('/html/body/ul/li/a/text()') # ['百度', '谷歌', '搜狗']
# 获取第一个 li 节点里的 a 标签的文本内容,使用 [1] ,注意从1开始而不是0
# result = tree.xpath('/html/body/ul/li[1]/a/text()') # ['百度']
# 使用属性筛选定位标签,使用 [@属性名=属性值]
# result = tree.xpath('/html/body/ol/li/a[@href="dapao"]/text()') # ['大炮']
# print(result)
# 分次查找
ol_li_list = tree.xpath('/html/body/ol/li')
for li in ol_li_list:
# 从每一个 li 中提取文本信息
result = li.xpath('./a/text()') # 在li中继续寻找, ./ 表示当前节点 (相对路径)
result2 = li.xpath('./a/@href') # 获取 a 标签的 href 属性的值
print(result)
print(result2)
# 快速获取属性值列表
print(tree.xpath('/html/body/ul/li/a/@href'))
通常爬网页上的数据时,可以将相同类的数据做为一个块(div 的概念),然后使用循环获取每个块的需要的数据。
import requests
from lxml import etree
# 获取页面代码
url = 'https://beijing.zbj.com/search/f/?kw=saas&r=1'
resp = requests.get(url)
# 解析
html = etree.HTML(resp.text)
# 获取每一个块内容
divs = html.xpath('//div[@id="__nuxt"]//div[@class="search-content"]//div[@class="search-result-list-service"]/div')
# 从块内中获取需要的数据
for div in divs:
com_name = div.xpath('./a[@class="name-address"]//div[@class="shop-detail"]/div/text()')[0]
price = div.xpath('.//div[@class="price"]/span/text()')[0].strip('¥')
title = div.xpath('.//div[@class="bot-content"]/a/text()')
location = div.xpath('.//div[@class="price"]/div/text()')[0].strip()
print(com_name, price, title, location)