爬虫004----网页解析库
网页解析,反正。。。。不是很多,就放一起了,常用的也就仨,漂亮的汤🥣(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 etreedef get_page():"""手动输入需要爬取网页图片的开始结束页码函数"""while True:try:start_page = int(input("请输入起始页数:"))end_page = int(input("请输入结束页码:"))if start_page <= end_page:return start_page, end_pageelse: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 = Noneif 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 = numdef 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 etreedef 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, headersclass GetData:def __init__(self, url, headers):self.url = urlself.headers = headersself.html = Nonedef 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/spanname_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 reprint(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 rephone = "2004-959-559 # 这是一个电话号码"# 删除注释
num = re.sub(r'#.*$', "", phone)
print ("电话号码 : ", num)# 移除非数字的内容
num = re.sub(r'\D', "", phone)
print ("电话号码 : ", num)
网页解析总结到此结束,应该。。。差不多。。。暂时。。。够用了,如果后续还有问题解决不了再说吧👋