
爬虫代码里有一个地方特别容易写着写着失控,就是解析 HTML。
刚开始写爬虫的时候,很多人看到网页源码,第一反应就是正则。
一个 re.findall(),感觉天下无敌。
后来遇到动态页面、标签嵌套、属性变化,再换 BeautifulSoup。
再后来项目复杂一点,又开始用 XPath、CSS Selector。
其实这几个工具没有谁一定替代谁,我平时写爬虫时,更关注一个问题:
这个页面的数据结构稳定不稳定?我要抓的是一个字符串,还是一个节点?
选择器选错了,后面维护起来会非常痛苦。
比如一个新闻列表页面:
<div class="article">
<h2>Python性能优化实战</h2>
<span class="author">东哥</span>
<p>今天排查一个线上脚本问题...</p>
</div>
目标是拿标题、作者、摘要。
不同选择器写出来完全是两种思路。
正则表达式,适合简单、固定格式的数据提取。
很多新人喜欢正则,因为它直接。
看到:
<span class="price">99.8</span>
直接:
import re
html = """
<span class="price">99.8</span>
"""
price = re.findall(r'<span class="price">(.*?)</span>', html)
print(price)
输出:
['99.8']
很爽。
但是爬虫项目里,HTML 很少这么听话。
比如价格节点改成:
<span class="price">
99.8
</span>
中间多了换行和空格,正则可能直接失效。
再比如:
<div class="goods">
<span class="price">99.8</span>
<span class="old-price">199</span>
</div>
你想拿第一个价格,还得继续调整表达式。
所以我的习惯是:
正则只负责处理文本,不负责解析 HTML 结构。
比如抓取页面里的手机号、订单号、特殊编码,这种场景非常合适。
例如处理爬下来的文章内容:
import re
content = """
用户联系电话:
13800138000
"""
phones = re.findall(r'1[3-9]\d{9}', content)
print(phones)
这种事情交给正则,比引入 HTML 解析库舒服很多。
BeautifulSoup(BS4)更像一个 HTML 检查员。
它不会让你关心标签层级,只需要告诉它:
我要找什么。
安装:
pip install beautifulsoup4
简单示例:
from bs4 import BeautifulSoup
html = """
<div class="article">
<h2>Python爬虫选择器</h2>
<span class="author">东哥</span>
</div>
"""
soup = BeautifulSoup(html, "html.parser")
title = soup.find("h2").text
author = soup.find("span", class_="author").text
print(title)
print(author)
结果:
Python爬虫选择器
东哥
BS4 最大的优点是代码可读性高。
实际项目里面,我经常用它处理结构不是特别复杂的页面。
比如:
博客文章列表
简单资讯网站
表格数据
商品信息页
但是 BS4 有一个问题。
速度不是最快。
因为它需要把整个 HTML 转成对象树。
页面小没关系。
如果你一天跑几十万个页面,这里就需要考虑性能。
XPath 是我在复杂页面里最常用的。
原因很简单:
它定位精准。
尤其是遇到这种结构:
<div id="content">
<ul>
<li>
<a href="/news/1">文章1</a>
</li>
<li>
<a href="/news/2">文章2</a>
</li>
</ul>
</div>
我要拿所有文章链接。
XPath:
from lxml import etree
html = """
<div id="content">
<ul>
<li><a href="/news/1">文章1</a></li>
<li><a href="/news/2">文章2</a></li>
</ul>
</div>
"""
tree = etree.HTML(html)
links = tree.xpath('//div[@id="content"]//a/@href')
print(links)
输出:
['/news/1', '/news/2']
XPath 的优势:
可以按照层级找。
比如:
//div[@class="box"]/ul/li[1]/a/text()
意思是:
找到 box 下面 ul 的第一个 li 里的 a 标签文本。
页面结构复杂的时候,这种定位方式非常省时间。
我排查爬虫问题时,经常看到有人写:
//div/div/div/span
这种 XPath。
能跑,但是非常脆。
网页加一层 div,直接废。
实际项目里,我更喜欢带属性:
//div[@class='product']//span[@class='price']/text()
不要过度依赖层级。
CSS Selector 对前端开发人员比较友好。
因为和 CSS 写法接近。
比如:
<div class="product">
<span class="name">机械键盘</span>
</div>
选择:
from bs4 import BeautifulSoup
html = """
<div class="product">
<span class="name">机械键盘</span>
</div>
"""
soup = BeautifulSoup(html, "html.parser")
name = soup.select_one(".product .name").text
print(name)
输出:
机械键盘
常见写法:
类:
.product
ID:
标签:
div span
属性:
input[type='text']
CSS Selector 最大的问题是:
复杂定位能力没有 XPath 强。
比如:
找第三个节点:
XPath:
//ul/li[3]
CSS:
ul li:nth-child(3)
也可以,但是复杂条件组合起来,XPath 更舒服。
我自己实际选择,大概是这样。
页面结构简单:
BS4。
比如:
soup.find_all("a")
够用了。
页面结构复杂:
XPath。
尤其是 Scrapy 项目。
比如:
response.xpath(
"//div[@class='item']/a/@href"
).getall()
这种写起来很快。
数据只是文本:
正则。
比如:
re.findall(
r'\d{4}-\d{2}-\d{2}',
text
)
处理日期、编号非常合适。
前端结构比较规范:
CSS Selector。
比如:
soup.select(".news-item .title")
维护成本低。
还有一个实际踩坑点。
不要把选择器写死。
很多爬虫挂掉,不是因为代码错误,而是页面稍微调整了一下。
比如:
以前:
title = tree.xpath(
"//h1[@class='title']/text()"
)
后来网站改成:
<h1 class="article-title">
全部失效。
更稳一点:
title = tree.xpath(
"//h1[contains(@class,'title')]/text()"
)
至少兼容部分变化。
最后总结一下:
正则不是不能用,只是不要拿它硬解析 HTML。
BS4 上手最快,适合大多数简单页面。
XPath 定位能力强,复杂页面效率最高。
CSS Selector 写法舒服,适合熟悉前端的人。
真正做爬虫项目,不是谁代码短谁厉害,而是谁写的解析规则能扛住页面变化。
我一般看到一个网页,不会马上敲代码。
先打开开发者工具,看 DOM 结构。
确定数据在哪一层。
再决定用哪个选择器。
这个顺序反过来,后面基本就是不停修补。
以上就是“Python网络爬虫四大选择器(正则表达式、BS4、Xpath、CSS)总结!”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。
扫码二维码 获取免费视频学习资料

- 本文固定链接: http://www.phpxs.com/post/14657/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料