网页解析,反正。。。。不是很多,就放一起了,常用的也就仨,漂亮的汤🥣(Beautiful
Soup)、lxml、re正则匹配(反正都是做解析用的,就把re放进来了)
PS: 建议稍微学习学习前端知识,HTML、CSS即可,对做爬虫网页解析大有帮助
1. lxml(xpath)
1.1 介绍
高效且灵活的 Python 库,用于处理和解析 HTML 和 XML 文档。
- 高性能:
lxml 提供了非常快的解析速度,尤其适合处理大型 XML/HTML 文件。 - 完整的 DOM 和 XPath 支持:
支持完整的文档对象模型 (DOM) API,可以方便地构建、修改和查询文档。
支持 XPath 查询,可以通过简单的表达式来查找和选择指定的节点。 - HTML 解析:
lxml 能够智能处理和解析不太符合标准的 HTML 文档,这使它在 Web 抓取时非常有用。 - 简洁的 API:
提供易于使用的 API,适合新手快速上手,同时也能满足高级用户的需求。 - 支持 XSLT 和 XML Schema:
lxml 能够进行转换和验证操作,使得处理 XML 文档更为灵活。
ChatGPT上CV来的,CV大法好,叭叭叭说了这么多优点,当然我们主要用来抓取网页和解析用
1.2 官方文档和导入
https://www.w3cschool.cn/lxml/.html
这里给的是w3c上的中文文档,我觉得排版挺清晰的,方便观看,就是感觉网页有点慢,可能是我电脑卡
# 模块安装
pip install lxml
# 导包
import lxml
# 爬虫常用的是etree模块进行HTML解析
from lxml import etree
1.3 Xpath表达式
这也是lxml比较好的一点,从网页copy下来的xpath可以直接使用,类似于css选择器语法,但bs4就有自己的表达式,需要去手动改写
表达式 | 作用 | 例子🌰 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1.4 lxml解析流程
lxml解析本地文件和服而且响应文件,并且获取想要的数据
# 导入etree模块
from lxml import etree
# 解析本地文件
html_tree = etree.parse("xx.html")
# 解析服务器响应文件
html_tree = etree.HTML(response.read().decode('utf-8'))
# 获取想要数据
html_etree.xpath("xpath路径")
来一个大的实战性例子🌰
1. 爬取站长素材,并且根据指定页数批量获取多页高清图片,保存到本地
此案例使用了urllib库,正好复习一下之前的urllib,可能有点写复杂了,这种简单实例用函数其实也可以实现
import os.path
import urllib.request
from lxml import etree
def get_page():
"""手动输入需要爬取网页图片的开始结束页码函数"""
while True:
try:
start_page = int(input("请输入起始页数:"))
end_page = int(input("请输入结束页码:"))
if start_page <= end_page:
return start_page, end_page
else:
print('输入页码有误!!无结果')
except ValueError:
print("输入无效!!")
class GetWomenPicture:
"""图片获取以及下载类"""
url = 'https://sc.chinaz.com/tupian/ribenmeinv.html'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Cookie': "cz_statistics_visitor=f4a94798-a16d-0814-b3de-45735ada46ab; Hm_lvt_9812c79f0a6ed79d80874364fad1b8f2=1747732748; Hm_lvt_398913ed58c9e7dfe9695953fb7b6799=1750418659,1750419273,1750419343,1750419624; HMACCOUNT=14F454179BE4E8D6; _clck=1u417iu%7C2%7Cfwx%7C0%7C1940; Hm_lvt_ca96c3507ee04e182fb6d097cb2a1a4c=1750432138; HMACCOUNT=14F454179BE4E8D6; Hm_lpvt_ca96c3507ee04e182fb6d097cb2a1a4c=1750432159; _clsk=1jfr2nl%7C1750432159578%7C4%7C1%7Cy.clarity.ms%2Fcollect; ucvalidate=b6ae96da-e023-f4ca-61ee-e0858b097747; CzScCookie=4e6f035d-ad7d-a899-ab00-d713fd807c89; Hm_lpvt_398913ed58c9e7dfe9695953fb7b6799=1750432307"
}
def __init__(self):
self.html = None
if not os.path.exists('./page'):
os.makedirs('./page')
def get_first_page(self):
requests = urllib.request.Request(url=self.url, headers=self.headers)
response = urllib.request.urlopen(requests)
self.html = response.read().decode('utf-8')
def get_all_women_picture(self):
self.url = 'https://sc.chinaz.com/tupian/ribenmeinv_{0}.html'.format(str(i))
requests = urllib.request.Request(url=self.url, headers=self.headers)
response = urllib.request.urlopen(requests)
self.html = response.read().decode('utf-8')
def download_women_picture(self):
tree = etree.HTML(self.html)
name_list = tree.xpath('//div[@class="item"]/img/@alt')
src_list = tree.xpath('//div[@class="item"]/img/@data-original')
for i in range(len(name_list)):
name = name_list[i]
src = src_list[i]
name = "".join(c for c in name if c.isalnum() or c in (' ', '-', '_')).strip()
urllib.request.urlretrieve(url=src, filename="./page/{0}.jpg".format(name))
class GetPics:
"""实现批量下载类"""
def __init__(self, num):
self.num = num
def download_pics(self):
print("正在爬取第{0}页图片...".format(self.num))
if self.num == 1:
get_women_pic.get_first_page()
get_women_pic.download_women_picture()
else:
get_women_pic.get_all_women_picture()
get_women_pic.download_women_picture()
if __name__ == '__main__':
start_page, end_page = get_page()
get_women_pic = GetWomenPicture()
for i in range(start_page, end_page + 1):
get_pic = GetPics(i)
get_pic.download_pics()
2. 漂亮的汤
2.1 介绍
Beautiful Soup,简称bs4,是一个HTML解析器,主要用于解析和提取数据用的
优点: 接口人性化,使用方便
缺点: 效率低,比lxml低
2.2 官方文档和导入
https://beautifulsoup.readthedocs.io/zh-cn/latest/
太好了,是中文文档,我们有救了!!!🤪,而且还有八嘎版和棒子版,可以说很全面了📚
# pip一下beautiful soup库
pip install bs4
# 使用前先导包
from bs4 import BeautifulSoup
# 创建对象
# 服务器响应文件生成对象
soup = BeautifulSoup(response.read().decode(), "lxml")
# 本地文件生成对象
soup = BeautifulSoup(open("xx.html"), "lxml")
官方给的安装方式是pip install beautifulsoup4,但。。。都能用,能用就行😌,注意⚠️:bs4默认打开文件编码格式是gbk
2.3 节点定位
定位方法 | 表达式 | 调用例子 | 解释 |
---|---|---|---|
|
|
|
找到第一个符合条件的数据 |
|
|
返回标签属性和属性值 | |
|
返回第一个符合条件的数据 |
|
根据tittle的值,找到对应的标签对象 |
|
根据class的值来找到对应的标签对象,class需要加下划线,不然和类定义重名 | ||
返回一个列表 |
|
查到所有的xx标签 | |
|
返回所有的a和span | ||
|
查询前两个a标签 | ||
返回一个列表并且是多个数据 根据选择器找到节点对象 |
|
soup.select("a") | |
|
soup.select(".a") | ||
|
soup.select("li[class]") | ||
|
soup.select("li[class='hengheng']") | ||
|
soup.select("#a") | ||
|
空格 | div p | |
|
> | div>p | |
|
, | div, p |
2.4 节点信息
2.4.1 获取节点内容
适用于标签中嵌套标签的结构
- obj.string
- obj.get_text() 推荐使用
如果标签对象中只有内容,那么string和get_text()都能获取到内容
如果标签对象中还有标签,那么string获取不到内容
2.4.2 节点属性
- tag.name 获取标签名
- tag.attrs 将属性值作为一个字典返回
2.4.3 获取节点属性
- obj.attrs.get(‘tittle’) 常用
- obj.get(“tittle”)
- obj[“tittle”]
再来一个小例子🌰
1. 使用bs4定位方式,爬取麦当劳商品图片,并保存到本地
import urllib.request
from bs4 import BeautifulSoup as bs
from lxml import etree
def people_message():
url = 'https://www.mcdonalds.com.cn/index/Food/menu/burger'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Cookie': 'ARRAffinity=1ce7c1f395b5a48d30b8c50f0ca2b9c61b26204706d05327db11e7f501495c8a; ARRAffinitySameSite=1ce7c1f395b5a48d30b8c50f0ca2b9c61b26204706d05327db11e7f501495c8a; _gid=GA1.3.399133088.1750492704; sajssdk_2015_cross_new_user=1; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22197917efc90995-0db57a24b00b2f8-26011e51-1327104-197917efc91daa%22%2C%22%24device_id%22%3A%22197917efc90995-0db57a24b00b2f8-26011e51-1327104-197917efc91daa%22%2C%22props%22%3A%7B%22%24latest_referrer%22%3A%22https%3A%2F%2Fwww.baidu.com%2Flink%22%2C%22%24latest_referrer_host%22%3A%22www.baidu.com%22%2C%22%24latest_traffic_source_type%22%3A%22%E8%87%AA%E7%84%B6%E6%90%9C%E7%B4%A2%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC%22%7D%7D; _gat_UA-49420844-1=1; _gat_realTime=1; _ga_H0FLFLQXE1=GS2.1.s1750492702$o1$g1$t1750492790$j51$l0$h0; _ga=GA1.1.225713622.1750492703; _ga_ZNNHN4PDZF=GS2.3.s1750492791$o1$g0$t1750492791$j60$l0$h0',
'referer': 'https://www.mcdonalds.com.cn/index/McD/about/company'
}
return url, headers
class GetData:
def __init__(self, url, headers):
self.url = url
self.headers = headers
self.html = None
def load_web(self):
request = urllib.request.Request(url=self.url, headers=self.headers)
response = urllib.request.urlopen(request)
self.html = response.read().decode("utf-8")
def handle_web(self):
soup = bs(self.html, 'lxml')
# //div[@class="row"]/div/a/span
name_list = soup.select("div[class='row'] > div > a > span")
# tree = etree.HTML(self.html)
# name_list = tree.xpath("//div[@class='row']/div/a/span")
for i in range(len(name_list)):
print(name_list[i].get_text())
if __name__ == '__main__':
url, headers = people_message()
get_data = GetData(url, headers)
get_data.load_web()
get_data.handle_web()
bs4先总结到这儿,爬虫常用的知识点就这么多,其他的。。。如果用到了再看文档或甩给GPT解决吧
3. re
re 是 Python 的一个内置模块,用于处理正则表达式。正则表达式是一种文本模式,用于描述字符串的结构。借助 re 模块,您可以执行复杂的字符串搜索、匹配和替换等操作。
3.1 re&正则表达式官方文档
https://www.w3cschool.cn/python3/python3-reg-expressions.html
在爬虫中正则表达式经常使用,而且不仅爬虫,在其他方向re正则也是相当重要,所以学好re,也是很必须的。文档是w3c的,感觉清晰明了
3.2 正则表达式模式
- 模式字符串使用特殊的语法来表示一个正则表达式:
- 字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。
- 多数字母和数字前加一个反斜杠时会拥有不同的含义。
- 标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。
- 反斜杠本身需要使用反斜杠转义。
- 由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r’/t’,等价于’//t’)匹配相应的特殊字符。
- 下表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。
表达式 | 解释 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3.3 正则表达式修饰符
表达式 | 解释 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
3.4 re方法
3.4.1 re.match(pattern, string, flags=0)
尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match() 就返回 none
- pattern: 匹配的正则表达式
- string: 要匹配的字符串。
- flags: 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
import re
print(re.match('www', 'www.w3cschool.cn').span()) # 在起始位置匹配
print(re.match('cn', 'www.w3cschool.cn')) # 不在起始位置匹配
3.4.2 re.match(pattern, string, flags=0)
扫描整个字符串并返回第一个成功的匹配
- pattern: 匹配的正则表达式
- string: 要匹配的字符串。
- flags: 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
import re
print(re.search('www', 'www.w3cschool.cn').span()) # 在起始位置匹配
print(re.search('cn', 'www.w3cschool.cn').span()) # 不在起始位置匹配
3.4.3 re.sub(pattern, repl, string, max=0)
用于替换字符串中的匹配项,返回的字符串是在字符串中用 re 最左边不重复的匹配来替换。如果模式没有发现,字符将被没有改变地返回
import re
phone = "2004-959-559 # 这是一个电话号码"
# 删除注释
num = re.sub(r'#.*$', "", phone)
print ("电话号码 : ", num)
# 移除非数字的内容
num = re.sub(r'\D', "", phone)
print ("电话号码 : ", num)
网页解析总结到此结束,应该。。。差不多。。。暂时。。。够用了,如果后续还有问题解决不了再说吧👋