1. 为什么需要学习HTML和CSS选择器?
当你第一次接触Python爬虫时,可能会觉得直接获取网页数据很简单。但实际操作中,你会发现网页就像是一个精心设计的迷宫,而HTML和CSS选择器就是你的地图和指南针。我刚开始做爬虫时,经常遇到明明在浏览器里能看到的内容,用代码却怎么也抓不到的情况,后来才发现问题出在没有正确理解网页结构上。
HTML(超文本标记语言)是构成网页的骨架。每个网页都是由各种HTML标签组成的,比如<div>、<p>、<a>等。这些标签像积木一样搭建起了整个网页的结构。举个例子,当你在浏览器中看到一个新闻标题,背后可能是这样的HTML代码:
html复制<h1 class="news-title">Python爬虫教程更新</h1>
CSS选择器则是定位这些元素的利器。它们就像精确的GPS坐标,能帮你在复杂的HTML结构中快速找到目标数据。常见的选择器包括:
- 类选择器:
.news-title - ID选择器:
#main-content - 标签选择器:
h1 - 属性选择器:
[href="https://example.com"]
提示:在Chrome浏览器中按F12打开开发者工具,点击左上角的箭头图标,然后点击页面上的元素,就能看到对应的HTML代码和CSS选择器路径。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. HTML基础:爬虫工程师必须掌握的标签
2.1 文档结构标签
每个HTML文档都有一些基本的结构标签,理解这些对爬虫至关重要:
html复制<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>网页标题</title>
</head>
<body>
<!-- 这里是网页的可见内容 -->
</body>
</html>
<!DOCTYPE html>:声明文档类型<html>:根元素,lang属性指定语言<head>:包含元信息,如字符集、标题等<body>:包含所有可见内容
2.2 常用内容标签
爬虫最常打交道的几种标签:
-
文本类标签:
<h1>到<h6>:标题<p>:段落<span>:行内文本容器
-
链接和媒体:
<a href="...">:超链接<img src="...">:图片<video>/<audio>:视频/音频
-
结构化容器:
<div>:块级容器<ul>/<ol>:无序/有序列表<table>:表格数据
2.3 特殊属性解析
HTML元素的属性常常包含重要数据:
html复制<a href="/news/123" class="news-link" data-id="12345">最新消息</a>
href:链接目标(可能是相对路径)class/id:CSS选择器的主要定位依据data-*:自定义数据属性(现代网站常用)
注意:很多网站会动态生成
class名(如class="jsx-123abc"),这种时候不要依赖具体的class值,而是寻找更稳定的父级结构。
3. CSS选择器实战指南
3.1 基础选择器类型
| 选择器类型 | 示例 | 说明 |
|---|---|---|
| 元素选择器 | div |
选择所有<div>元素 |
| 类选择器 | .content |
选择class="content"的元素 |
| ID选择器 | #header |
选择id="header"的元素 |
| 属性选择器 | [href] |
选择有href属性的元素 |
| 后代选择器 | div p |
选择<div>内的所有<p> |
| 子元素选择器 | ul > li |
只选择直接子元素<li> |
3.2 组合选择器技巧
在实际爬虫开发中,经常需要组合使用选择器:
css复制/* 选择class为news-list的div下的所有li,且这些li包含data-id属性 */
div.news-list > li[data-id]
/* 选择href属性以".pdf"结尾的链接 */
a[href$=".pdf"]
/* 选择class包含"active"的按钮 */
button[class*="active"]
3.3 浏览器工具实战
Chrome开发者工具是练习CSS选择器的绝佳场所:
- 按F12打开开发者工具
- 切换到"Elements"面板
- 按Ctrl+F打开搜索框
- 输入CSS选择器表达式
- 匹配的元素会高亮显示
个人经验:在开发者工具中测试选择器时,如果匹配结果过多,可以逐步增加限定条件。比如先写
div,然后div.container,再div.container > ul,这样逐步精确。
4. XPath:另一种强大的定位方式
4.1 XPath基础语法
XPath是另一种定位XML/HTML节点的语言,相比CSS选择器,它的表达能力更强:
| 表达式 | 说明 |
|---|---|
/html/body/div |
绝对路径 |
//div |
文档中所有div |
//div[@class='content'] |
带特定class的div |
//a[contains(@href,'news')] |
href包含"news"的链接 |
//h1/text() |
获取h1的文本内容 |
//div[@id='main']//p[1] |
id为main的div下的第一个p |
4.2 XPath与CSS选择器对比
| 特性 | CSS选择器 | XPath |
|---|---|---|
| 语法简洁度 | ⭐⭐⭐⭐ | ⭐⭐ |
| 功能强大度 | ⭐⭐ | ⭐⭐⭐⭐ |
| 文本匹配 | 有限支持 | 完全支持 |
| 轴选择 | 不支持 | 支持(parent, child等) |
| 浏览器支持 | 原生支持 | 需要额外支持 |
4.3 实际应用场景
XPath特别适合处理以下情况:
-
需要根据文本内容定位:
xpath复制//button[text()='登录'] -
复杂的层级关系:
xpath复制//div[contains(@class,'product')]/ancestor::section -
需要获取特定属性:
xpath复制//meta[@property='og:title']/@content
踩坑提醒:有些网站会故意在DOM中插入随机变化的div层级来干扰爬虫。这时候用XPath的
//相对路径比CSS选择器更健壮。
5. 实战:用Python解析HTML
5.1 BeautifulSoup基础
BeautifulSoup是Python最常用的HTML解析库:
python复制from bs4 import BeautifulSoup
import requests
html = requests.get('https://example.com').text
soup = BeautifulSoup(html, 'html.parser')
# CSS选择器
titles = soup.select('h1.news-title')
# 等效的XPath方式(需要lxml支持)
# titles = soup.xpath('//h1[@class="news-title"]')
5.2 lxml库的高效解析
对于大型文档,lxml比html.parser更快:
python复制from lxml import etree
html = requests.get('https://example.com').content
tree = etree.HTML(html)
# XPath查询
titles = tree.xpath('//h1[@class="news-title"]/text()')
5.3 性能对比与选择建议
| 解析器 | 速度 | 容错性 | 内存占用 |
|---|---|---|---|
| html.parser | 慢 | 高 | 低 |
| lxml | 快 | 中 | 中 |
| html5lib | 最慢 | 最高 | 高 |
选择建议:
- 简单任务:BeautifulSoup + html.parser
- 大型文档:直接使用lxml
- 极不规范HTML:html5lib
6. 常见问题与解决方案
6.1 元素明明存在却找不到?
可能原因:
- 内容由JavaScript动态加载(需要用Selenium等工具)
- 元素在iframe中(需要先切换到iframe)
- 有不可见字符或动态class(用contains代替精确匹配)
6.2 如何应对网站改版?
防御性编程技巧:
- 同时保留CSS和XPath两种定位方式
- 优先选择语义化的class/id(如
product-name而不是js-123) - 对关键元素添加多个定位策略的fallback
6.3 提高选择器性能的技巧
-
尽量缩小搜索范围:
python复制# 不好 soup.select('.title') # 更好 soup.select('div.main-content .title') -
避免过度使用通配符:
python复制# 性能差 soup.select('div *') # 更好 soup.select('div > p') -
缓存解析结果,避免重复解析
我在实际项目中发现,90%的爬虫问题都出在元素定位上。掌握好HTML结构和选择器技巧,就能像外科手术一样精确提取所需数据。刚开始可能会觉得各种选择器语法很复杂,但坚持练习2-3周后,你会发现看网页就像看结构清晰的数据库一样简单。
