很多人一听"数据解析"四个字,脑瓜子嗡嗡的。什么正则、BS4、XPath、PyQuery,感觉跟进了仓库分拣中心似的,货堆成山,不知道从哪下手。
其实特简单。数据解析就是"在一堆破烂里把你想要的东西挑出来"。 你把这四种挑货方式搞明白了,这辈子都忘不掉。
零、场景设定:笑笑进了分拣仓库
假设你刚用 requests 要饭成功,老板(服务器)给你扔过来一张菜单(HTML):
<html>
<body>
<div class="menu">
<h1>老王炒粉店</h1>
<ul id="today-special">
<li class="food">蛋炒粉 - <span class="price">15元</span></li>
<li class="food">肉炒粉 - <span class="price">20元</span></li>
<li class="food sold-out">惠灵顿牛排 - <span class="price">999元</span></li>
</ul>
</div>
</body>
</html>
你的任务:把菜名和价格挑出来,存到Excel里。
下面四种方法,就是四种"挑货姿势"。
一、re解析:盲人摸象,靠手感
re(正则表达式) 就像你闭着眼睛,在仓库里靠**"手感"**摸东西。
你知道"价格"长啥样:前面是数字,后面跟着"元"。于是你伸手一摸:
import re
html = """<li class="food">蛋炒粉 - <span class="price">15元</span></li>"""
# 摸规律:数字+元
prices = re.findall(r'(\d+)元', html)
print(prices) # ['15']
原理:你不关心HTML结构,直接把文档当成一大坨字符串,用"规则"去匹配。
再猛一点:把整页所有价格全摸出来:
import requests, re
html = requests.get('https://www.laowangchaofen.com').text
# 摸所有 "xx元" 的 pattern
prices = re.findall(r'<span class="price">(\d+)元</span>', html)
print(prices) # ['15', '20', '999']
优点:快,简单粗暴,不挑食(HTML写得再烂也能摸)。
缺点:瞎。如果老板把 class="price" 改成 class="money",你这一把就摸空了。HTML结构一变,正则全废。
人话总结:
re 像盲人摸象,摸对了很爽,摸错了抓一手屎。适合结构固定、永远不变的数据。
二、bs4解析:温柔老板娘帮你分类
BeautifulSoup(bs4) 就像仓库里有个温柔老板娘。你把那堆破烂HTML扔给她,她默默帮你按标签分类、摆好,还容错——你标签写错了她也能猜出你想干啥。
from bs4 import BeautifulSoup
import requests
html = requests.get('https://www.laowangchaofen.com').text
# 老板娘上线,把破烂HTML整理成树形结构
soup = BeautifulSoup(html, 'html.parser')
# 问她:"老板娘,把所有 class 是 food 的 li 给我拿来"
items = soup.find_all('li', class_='food')
for item in items:
name = item.get_text().split('-')[0].strip() # "蛋炒粉"
price = item.find('span', class_='price').text # "15元"
print(f"{name}: {price}")
老板娘的温柔体现在哪?
- HTML标签没闭合?她帮你补上。
- class写成了大写?她不计较。
- 你想用 CSS 选择器?她也会:
soup.select('ul#today-special li.food span.price')
优点:对新手最友好,容错率极高,HTML写得像屎也能解析。
缺点:慢。老板娘一个一个帮你整理,货少没事,货多了(几万条)她手都酸了。
人话总结:
bs4 像温柔老板娘,适合新手,适合破烂HTML,但不适合急单。
三、xpath解析:GPS坐标导航
XPath 就像你拿着GPS坐标进仓库。你不跟老板娘废话,直接报坐标:
"我要
/html/body/div/ul/li[1]/span这个位置的东西!"
from lxml import etree
import requests
html = requests.get('https://www.laowangchaofen.com').text
# 把HTML转成树,拿到地图
tree = etree.HTML(html)
# GPS导航:直接定位到所有价格标签
prices = tree.xpath('//li[@class="food"]/span[@class="price"]/text()')
print(prices) # ['15元', '20元', '999元']
# 导航:定位到所有菜名(li里面的文本,去掉span)
names = tree.xpath('//li[@class="food"]/text()')
print(names) # ['蛋炒粉 - ', '肉炒粉 - ', '惠灵顿牛排 - ']
XPath 语法速记:
//:不管在哪一层,全仓库扫描/:下一级,精确路径[@class="food"]:按属性筛选,"class等于food的"/text():只要文本内容,不要标签[1]:第一个
优点:指哪打哪,精确到毛孔,适合层级复杂的页面。
缺点:语法像天书,写错了直接找不到,调试靠猜。
人话总结:
XPath 像 GPS 导航,坐标报对了秒定位,坐标报错了直接导沟里。适合结构清晰、层级深的页面。
四、pyquery解析:jQuery风格的快递机器人
PyQuery 就像仓库里有个快递分拣机器人,它的操作方式和前端 jQuery 一模一样。你会写网页,就会写抓取。
from pyquery import PyQuery as pq
import requests
html = requests.get('https://www.laowangchaofen.com').text
# 启动机器人
doc = pq(html)
# jQuery风格:$('li.food'),批量抓取
items = doc('li.food')
for item in items.items():
name = item.remove('span').text().strip() # 去掉span,只留菜名
price = item.find('span.price').text() # 抓价格
print(f"{name}: {price}")
它的语法就是前端语法:
doc('li.food')→ 选所有 class=food 的 lidoc('#today-special')→ 选 id=today-special 的元素.text()→ 拿文本.attr('href')→ 拿属性
优点:语法最简洁,前端工程师零门槛,链式操作爽到飞起。
缺点:小众,文档少,依赖 lxml,出了问题 StackOverflow 都没人回答。
人话总结:
pyquery 像 jQuery 机器人,前端转行的人用起来像回家,但出了 bug 没人救你。
五、四种方式对比(终极对照表)
| 方式 | 仓库比喻 | 适合场景 | 缺点 |
|---|---|---|---|
| re | 盲人摸象,靠手感 | 结构死固定、大数据量、简单提取 | HTML一变全崩 |
| bs4 | 温柔老板娘 | 新手入门、HTML很烂、快速原型 | 慢,量大卡死 |
| xpath | GPS坐标导航 | 层级深、结构清晰、精确打击 | 语法难记,调试痛苦 |
| pyquery | jQuery机器人 | 前端出身、写惯了$()、链式操作 | 小众,社区支持弱 |
六、实战建议:什么时候用什么?
新手:先用 bs4,老板娘带你入门,不容易劝退。
要速度:结构简单用 re,结构复杂用 xpath。
前端转爬虫:直接 pyquery,语法不用学。
大规模项目:上 xpath 或 parsel(Scrapy 内置,就是 xpath + css 混合体)。
七、终极口诀(背下来)
re像盲搜,bs4像老板娘,xpath像导航,pyquery像jQuery。
结构固定用正则,结构混乱找bs4,层级复杂开导航,前端出身用美元符。
写在最后
下次再有人问你"爬虫怎么解析数据",你就说:
"把HTML当成仓库,你想盲摸、找老板娘、开GPS、还是叫jQuery机器人,随便你。"
他要是愣住,你就把这四种比喻甩他脸上。
散会。