声明:所有案例均仅供个人学习使用
为什么要学习XPATH和LXML类库
lxml是⼀款⾼性能的Python HTML/XML解析器,我们可以利⽤Xpath来快速的 定位特定元素以及获取节点信息
什么是XPATH
Xpath是⼀⻔在HTML/XML⽂档中查找信息的语⾔,可⽤来在HTML/XML⽂档 中对元素和属性进⾏遍历
文档:www.w3school.com.cn/xpath/index…
认识XML
| 数据格式 | 描述 | 设计目标 |
|---|---|---|
| XML | 可扩展标记语言 | 被设计为传输和存储数据,其焦点是数据的内容 |
| HTML | 超文本标记语言 | 显示数据以及如何更好显示 |
XML的节点关系
节点的概念:每个XML的标签我们都称之为节点
<book>
<title>hello world<title>
<author>yantong<author>
<year>2024<year>
<book>
节点选择语法
XPath使⽤路径表达式来选取XML⽂档中的接待或者节点集。这些路径表达式 和我们在常规的电脑⽂件系统中看到的表达式⾮常相似。
| 表达式 | 描述 |
|---|---|
| / | 从根节点选取 |
| // | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 |
| . | 选取当前节点 |
| .. | 选取当前接到的父节点 |
| @ | 选取 |
查找某个特定的节点或者包含某个指定的值的节点
| 路径表达式 | 结果 |
|---|---|
| /bookstore/book[1] | 选取属于bookstore子元素的第一个book元素 |
| /bookstore/book[last()] | 选取属于bookstore子元素的最后一个book元素 |
| /bookstore/book[last()-1] | 选取属于 bookstore ⼦元素的倒数第二个book元素 |
| /bookstore/book[position()<3] | 选取最前⾯的两个属于 bookstore 元素的⼦元素的 book 元素 |
| //title[@lang] | 选取所有拥有名为 lang 的属性的title 元素 |
| //title[@lang='eng'] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
| /bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book元素,且其中的 price 元素的值须⼤于 35.00。 |
选择未知节点
XPath 通配符可⽤来选取未知的 XML 元素。
| 通配符 | 描述 |
|---|---|
| * | 匹配任何元素节点 |
| @* | 匹配任何属性节点 |
| node() | 匹配任何类型的节点 |
列出了⼀些路径表达式,以及这些表达式的结果
| 路径表达式 | 结果 |
|---|---|
| /bookstore/* | 选取bookstore元素的所有子元素 |
| //* | 选取文档中的所有元素 |
| html/node()/meta/@* | 选择html下面任意节点下的meta节点的所有属性 |
| //title[@*] | 选取所有带有属性的title元素 |
xpath的更多语法:learn.microsoft.com/zh-cn/previ…
lxml库
- lxml 是 ⼀个HTML/XML的解析器,主要的功能是如何解析和提取HTML/XML 数据
- ⽤etree.HTML,将字符串转化为Element对象
- lxml python 官⽅⽂档:lxml.de/index.html
- 可使⽤ pip 安装:
pip install lxml(或通过wheel⽅式安装) - lxml 可以⾃动修正 html 代码
# -*- coding: UTF-8 -*-
# @Project :网络爬虫
# @File :lxml练习.py
# @IDE :PyCharm
# @Author :艳烔
# @Date :2024/10/25 17:06
import requests
from lxml import etree
class LxmlPractice():
def __init__(self, url):
self.url = url
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"
}
# 获取页面源码
def get_html(self):
try:
response = requests.get(self.url, headers=self.headers)
response.raise_for_status() # 检查请求是否成功
return response.text
except requests.RequestException as e:
print(f"请求失败: {e}")
return None
# 解析页面源码
def parse_html(self, html):
if html is not None:
etree_html = etree.HTML(html)
return etree_html
else:
return None
def get_data(self, parse_html):
data = []
# 获取电影标题 上映时间
for i in range(1,11):
data_dict = {}
xpath_expression_title = f'/html/body/div/div[2]/div[1]/div[1]/div[{i}]/div/div/div[2]/a/h2'
xpath_expression_time = f'/html/body/div/div[2]/div[1]/div[1]/div[{i}]/div/div/div[2]/div[3]/span'
h2_elements = parse_html.xpath(xpath_expression_title)
time_elements = parse_html.xpath(xpath_expression_time)
try:
h2_content =h2_elements[0].text.strip()
time_content = time_elements[0].text.strip()
except:
h2_content = ''
time_content = ''
# print(time_content)
# print(h2_content)
data_dict['title'] = h2_content
data_dict['time'] = time_content.replace(' 上映','')
data.append(data_dict)
return data
def run(self):
html = self.get_html()
if html:
parse_html = self.parse_html(html)
data = self.get_data(parse_html)
print(data)
else:
print("未能获取页面源码")
if __name__ == '__main__':
url = 'https://ssr1.scrape.center/page/1' # 注意:这个URL可能需要替换为实际存在的页面
lxmlPractice = LxmlPractice(url)
lxmlPractice.run()
Beautliful Soup
Beautilful Soup的简介
Beautiful Soup是python的⼀个库,最主要的功能是从⽹⻚抓取数据,官⽅解释如下:
- Beautiful Soup提供⼀些简单的、python式的函数⽤来处理导航、搜索、 修改分析树等功能。它是⼀个⼯具箱,通过解析⽂档为⽤户提供需要抓取 的数据,因为简单,所以不需要多少代码就可以写出⼀个完整的应⽤程 序。
- Beautiful Soup⾃动将输⼊⽂档转换为Unicode编码,输出⽂档转换为 utf-8编码。你不需要考虑编码⽅式,除⾮⽂档没有指定⼀个编码⽅式,这 时,Beautiful Soup就不能⾃动识别编码⽅式了。然后,你仅仅需要说明 ⼀下原始编码⽅式就可以了。
- Beautiful Soup已成为和lxml⼀样出⾊的python解释器,为⽤户灵活地提 供不同的解析策略或强劲的速度。
GitHub地址:beautifulsoup.readthedocs.io/zh_CN/v4.4.…
和lxml⼀样,BeautifulSoup也是⼀个HTML/XML的解析器,主要功能也是如何解析和提取HTML/XML数据。
lxml只会局部遍历,⽽BeautifulSoup是基于HTML DOM的,会载⼊整个⽂ 档,解析整个DOM树,因此时间和内存开销都会很⼤,所以性能要低于lxml BeautifulSoup⽤来解析HTML⽐较简单,⽀持CSS选择器,Python标准库中 的HTML解析器,也⽀持lxml的XML解析器。
BeautifulSoup安装 pip install bs4
解析工具对比
| 抓取工具 | 速度 | 使用难度 |
|---|---|---|
| 正则 | 最快 | 困难 |
| lxml | 快 | 简单 |
| Beautiful Soup | 慢 | 最简单 |
# -*- coding: UTF-8 -*-
# @Project :网络爬虫
# @File :BeautifulSoup的基本使用_01.py
# @IDE :PyCharm
# @Author :艳烔
# @Date :2024/10/26 9:03
from bs4 import BeautifulSoup
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
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>
<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对象
soup = BeautifulSoup(html_doc, 'html.parser')
# 格式化输出 soup 对象的内容
print(soup.prettify())
解析器
搜索文档树 Beautiful Soup定义了很多搜索⽅法,这⾥着重介绍2个: find() 和 find_all() .其 它⽅法的参数和⽤法类似,
# -*- coding: UTF-8 -*-
# @Project :网络爬虫
# @File :BeautifulSoup搜索文档树_02.py
# @IDE :PyCharm
# @Author :艳烔
# @Date :2024/10/26 9:11
from bs4 import BeautifulSoup
html_doc = '''
<table class="tablelist" cellspacing="0" cellpadding="0">
<tbody>
<tr class="h">
<td class="1" width="374">职位名称</td>
<td>职位类别</td>
<td>⼈数</td>
<td>地点</td>
<td>发布时间</td>
</tr>
<tr class="even">
<td class="l"><a href="https://www.baidu.com">区块链⾼级研发⼯
<td class="l">技术类</td>
<td class="l">1</td>
<td class="l">深圳</td>
<td class="l">2018-11-25</td>
</tr>
<tr>
<td><a href="https://www.qq.com">⾦融云⾼级后台开发</a></td>
<td>技术类</td>
<td>2</td>
<td>深圳</td>
<td>2018-11-24</td>
</tr>
<tr>
<td><a href="https://www.juran.com">⾼级研发⼯程师</a></td>
<td>技术类</td>
<td>2</td>
<td>深圳</td>
<td>2018-11-24</td>
</tr>
<tr>
<td><a href="https://www.python.com">⾼级图像算法⼯程师</a></t
<td>技术类</td>
<td>2</td>
<td>深圳</td>
<td>2018-11-24</td>
</tr>
<tr>
<td><a href="https://www.lg.com" id="test" class="test">⾼级
<td>技术类</td>
<td>2</td>
<td>深圳</td>
<td>2018-11-24</td>
</tr>
</tbody>
</table>
'''
soup = BeautifulSoup(html_doc, 'lxml')
# 格式化输出
# print(soup.prettify())
# 1. 获取所有tr标签
tr_find = soup.find_all('tr')
print(tr_find)
for tr in tr_find:
print(tr)
print("="*30)
# 2. 获取第二个tr标签
tr_find_2 = soup.find_all('tr',limit=2)[1]
print(tr_find_2)
# 3. 获取所有class等于even的tr标签
tr_class_even = soup.find_all('tr', class_='even')
print(tr_class_even)
# 4. 将所有id等于test,class也等于test的a标签提取出来
trs = soup.find_all('a', attrs={'id': "test", 'class': "test"})
for tr in trs:
print(tr)
print("=" * 30)
# 5. 将所有的a标签href属性
hrefs = soup.find_all('a')
for a in hrefs:
# print(a)
# 通过下标的方式获取
# print(a['href'])
print(a.get('href'))
# 通过attrs属性的方式
# print(a.attrs['href'])
print("=" * 30)
# 6. 获取所有的职位信息(纯文本)
trs = soup.find_all('tr')[1:]
for tr in trs:
# print(tr)
# tds = tr.find_all('td')
# print(tds)
# title = tds[0]
# print(title.string)
# 获取tr标签的所有文本
# infos = list(tr.strings)
# print(infos[1])
# 去除空格去纯文本
infos = list(tr.stripped_strings)
print(infos)
pass
CSS常用选择器介绍
# 通过标签名查找
print(soup.select('a'))
print('+'*30)
# 通过类名查找
print(soup.select('.even'))
print('+'*30)
# 通过id查找
print(soup.select("#test"))
print('+'*30)
# 组合查找
print(soup.select("tr #link"))
print("="*30)
# 直接子标签查找,则使用 > 分隔
print(soup.select("tr > td"))
print('+'*30)
# 通过属性查找
print(soup.select('a[href="https://www.baidu.com"]'))
# 1.获取所有tr标签
print(soup.select('tr'))
print("-"*30)
# 2.获取第2个tr标签
print(soup.select('tr')[1])
print("-"*30)
# 3.获取所有class等于even的tr标签
print(soup.select("tr.even"))
print("-"*30)
# 4.获取所有的a标签的href属性
alist = soup.select("a")
for a in alist:
print(a.get('href'))
print("-"*30)
# 5.获取所有的职位信息(纯⽂本)
trs = soup.select('tr')
for tr in trs:
infos = list(tr.stripped_strings)
print(infos)
案例一 抓取图片
# -*- coding: UTF-8 -*-
# @Project :网络爬虫
# @File :爬取 爱二次元 网 图片.py
# @IDE :PyCharm
# @Author :艳烔
# @Date :2024/10/26 10:35
from bs4 import BeautifulSoup
import requests
import re
from retrying import retry
from time import sleep
class AiErCiYuan():
def __init__(self, url):
self.url = url
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
}
self.originUrl = 'https://a2cy.com'
@retry(stop_max_attempt_number=3)
def get_html(self):
"""
获取网页源码
:return: 返回网页源码
"""
response = requests.get(self.url, headers=self.headers)
print(response.status_code)
assert response.status_code == 200
response.encoding = 'UTF-8'
html = response.text
return html
def save_img(self, url, title):
"""
根据 图片 URL 保存到本地
:param url: 图片 URL
:return:
"""
response = requests.get(url, title, headers=self.headers)
sleep(0.05)
with open(f'E:/Study/code/Python/图片爬取/爱二次元/{title}.jpg', 'wb') as f:
content = response.content
try:
f.write(content)
print(title + " 保存成功 url :" + url)
except:
print(title + "\033[31m{}\033[0m".format("保存失败!!!") + " url:" + url)
def get_img_info(self):
"""
获取图片的链接和标题
:return:
"""
html_info = self.get_html()
soup = BeautifulSoup(html_info, 'lxml')
divs = soup.select("div.showImg")[6:]
for div in divs:
img = div.select("img")[0]
# 获取图片名称
sub_res = re.sub(r'【.*?】', '', img.get('alt'))
pattern = re.compile('[\u4e00-\u9fa5a-zA-Z]+')
img_title = ''.join(pattern.findall(sub_res))
# print(img_title)
# 获取图片链接
img_url = self.originUrl + img.get('data-loadsrc')
# 保存图片到本地
self.save_img(img_url, img_title)
def run(self):
try:
print("爬虫程序启动... 爬取网站:" + self.url)
self.get_img_info()
except Exception as e:
print(f"出错了:{e}")
finally:
print("爬虫程序关闭!")
if __name__ == '__main__':
# https://a2cy.com/acg/cos/index_2.html
url = 'https://a2cy.com/acg/cos/'
for i in range(1, 112):
if i != 1:
url += 'index_{}.html'.format(i)
aierciyuan = AiErCiYuan(url)
aierciyuan.run()
url = 'https://a2cy.com/acg/cos/'