面试官问我 re/bs4/xpath/pyquery 怎么选,我说:看你是盲人还是老板娘

3 阅读4分钟

很多人一听"数据解析"四个字,脑瓜子嗡嗡的。什么正则、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 的 li
  • doc('#today-special') → 选 id=today-special 的元素
  • .text() → 拿文本
  • .attr('href') → 拿属性

优点:语法最简洁,前端工程师零门槛,链式操作爽到飞起。

缺点:小众,文档少,依赖 lxml,出了问题 StackOverflow 都没人回答。

人话总结

pyquery 像 jQuery 机器人,前端转行的人用起来像回家,但出了 bug 没人救你。


五、四种方式对比(终极对照表)

方式仓库比喻适合场景缺点
re盲人摸象,靠手感结构死固定、大数据量、简单提取HTML一变全崩
bs4温柔老板娘新手入门、HTML很烂、快速原型慢,量大卡死
xpathGPS坐标导航层级深、结构清晰、精确打击语法难记,调试痛苦
pyqueryjQuery机器人前端出身、写惯了$()、链式操作小众,社区支持弱

六、实战建议:什么时候用什么?

新手:先用 bs4,老板娘带你入门,不容易劝退。

要速度:结构简单用 re,结构复杂用 xpath

前端转爬虫:直接 pyquery,语法不用学。

大规模项目:上 xpathparsel(Scrapy 内置,就是 xpath + css 混合体)。


七、终极口诀(背下来)

re像盲搜,bs4像老板娘,xpath像导航,pyquery像jQuery。

结构固定用正则,结构混乱找bs4,层级复杂开导航,前端出身用美元符。


写在最后

下次再有人问你"爬虫怎么解析数据",你就说:

"把HTML当成仓库,你想盲摸、找老板娘、开GPS、还是叫jQuery机器人,随便你。"

他要是愣住,你就把这四种比喻甩他脸上。

散会。