网络爬虫(Web Crawler / Spider)是一个按照一定规则,自动地抓取万维网信息的程序或脚本,Python 凭借其简洁的语法和强大的库生态系统,成为了编写网络爬虫的首选语言。

本指南将分为以下几个部分:
- 爬虫基本原理
- 核心库介绍
- 实战案例:从简单到复杂
- 爬虫进阶
- 爬虫的“道德”与法律
- 总结与学习路径
爬虫基本原理
一个典型的爬虫工作流程如下:
- 发起请求:爬虫模拟浏览器向目标网站的某个 URL(网址)发送一个 HTTP 请求。
- 获取响应:服务器收到请求后,会返回一个 HTTP 响应,这个响应中包含了网页的源代码(HTML)、状态码、响应头等信息。
- :爬虫从响应内容(通常是 HTML)中提取出需要的数据,这就像用一把“剪刀”从一本杂志上剪下你需要的文章和图片。
- 存储数据:将提取出的数据保存到本地文件(如 CSV, TXT)或数据库中,以便后续分析使用。
- 发现新链接:从当前解析的页面中提取出新的 URL,将这些 URL 放入一个待抓取的队列中,然后循环执行步骤 1,直到满足停止条件(如抓取足够多的页面或队列已空)。
模拟 vs 真实浏览器:
- 无头浏览器:爬虫直接向服务器请求 HTML 文件,速度快,但无法执行 JavaScript。
- 真实浏览器:爬虫通过 Selenium 等工具控制一个真实的浏览器(如 Chrome),可以执行 JavaScript,速度慢,但能获取到动态加载的内容。
核心库介绍
Python 爬虫生态非常成熟,下面是几个最核心的库:

| 库名称 | 主要功能 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
requests |
发送 HTTP 请求 | 基础中的基础,几乎所有爬虫都会用到 | API 极其简洁,易用 | 无法执行 JavaScript |
BeautifulSoup |
解析 HTML/XML | 从 requests 获取的页面源码中提取数据 |
语法简单,容错性强,支持多种解析器 | 依赖 requests 或其他请求库 |
lxml |
解析 HTML/XML | 性能要求高的场景 | 速度快,功能强大 | API 相对复杂,容错性不如 BS |
Selenium |
自动化浏览器 | 抓取由 JavaScript 动态渲染的页面 | 能模拟真实用户操作,JS 渲染无压力 | 速度慢,依赖浏览器驱动 |
Scrapy |
框架 | 构建大型、复杂、高性能的爬虫项目 | 功能全面(异步下载、管道、调度器等) | 学习曲线较陡,不适合小型项目 |
aiohttp |
异步 HTTP 客户端 | 高并发、高性能爬虫 | 基于异步 asyncio,效率极高 |
需要异步编程思维 |
实战案例:从简单到复杂
我们将通过三个逐步深入的案例来掌握爬虫的编写。
静态网页数据抓取(requests + BeautifulSoup)
目标:抓取豆瓣电影 Top 250 的电影名称、评分和一句话评价。
目标网址:https://movie.douban.com/top250
步骤:

-
安装库:
pip install requests beautifulsoup4 lxml
-
编写代码:
import requests from bs4 import BeautifulSoup import csv # 1. 发起请求 url = 'https://movie.douban.com/top250' # 设置请求头,模拟浏览器访问 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } try: response = requests.get(url, headers=headers, timeout=10) # 确保请求成功 response.raise_for_status() # 2. 获取响应内容 (HTML) html = response.text except requests.RequestException as e: print(f"请求失败: {e}") exit() # 3. 解析内容 soup = BeautifulSoup(html, 'lxml') # 使用 lxml 解析器,需要安装 lxml # 找到所有包含电影信息的 <ol class="grid_view"> 下的 <li> movie_list = soup.find_all('li', class_='item') # 创建一个列表来存储所有电影数据 all_movies_data = [] for movie in movie_list: # 提取电影名称 title = movie.find('span', class_='title').text # 提取评分 score = movie.find('span', class_='rating_num').text # 提取一句话评价 quote = movie.find('span', class_='inq') quote_text = quote.text if quote else '无' # 处理可能为空的情况 all_movies_data.append([title, score, quote_text]) print(f"正在抓取: {title}, 评分: {score}") # 4. 存储数据 (保存到 CSV 文件) with open('douban_top250.csv', 'w', newline='', encoding='utf-8-sig') as f: writer = csv.writer(f) writer.writerow(['电影名称', '评分', '一句话评价']) # 写入表头 writer.writerows(all_movies_data) # 写入所有数据 print("\n数据已成功保存到 douban_top250.csv")
代码解析:
headers:用于伪装成浏览器,防止被网站识别为爬虫。requests.get():发送 GET 请求。response.raise_for_status():如果请求返回的状态码不是 200(成功),则会抛出异常。BeautifulSoup(html, 'lxml'):创建一个 BeautifulSoup 对象,将 HTML 文档解析成树形结构。soup.find_all():查找所有符合条件的标签,返回一个列表。find()和text:查找单个标签并获取其内部的文本内容。csv.writer:将数据写入 CSV 文件,utf-8-sig编码可以避免 Excel 打开中文乱码。
处理分页与动态内容(Selenium)
目标:抓取知乎某个话题下所有回答的作者和赞同数,知乎的内容是动态加载的,requests 无法直接获取。
目标网址:知乎话题页(https://www.zhihu.com/topic/19552769/hot)
步骤:
-
安装库:
pip install selenium
-
下载浏览器驱动:
- 访问 ChromeDriver for Testing 下载与你 Chrome 浏览器版本匹配的
chromedriver。 - 将下载的
chromedriver.exe(Windows) 或chromedriver(Mac/Linux) 放到你的 Python 脚本同目录下,或者将其路径添加到系统环境变量中。
- 访问 ChromeDriver for Testing 下载与你 Chrome 浏览器版本匹配的
-
编写代码:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.service import Service from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time # 1. 配置 Selenium WebDriver # 如果你把 chromedriver 放在了脚本同目录下,可以这样写 service = Service(executable_path='chromedriver.exe') driver = webdriver.Chrome(service=service) url = 'https://www.zhihu.com/topic/19552769/hot' driver.get(url) # 2. 处理动态加载 # 模拟点击“加载更多”按钮,直到没有新内容 while True: try: # 等待“加载更多”按钮出现并点击 load_more_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, '//button[@class="Button.Button--primary.Button--plain"]')) ) load_more_button.click() print("点击了“加载更多”...") time.sleep(2) # 等待新内容加载 except Exception as e: print("没有更多内容了或出现其他错误:", e) break # 3. 解析内容 # 等待所有回答加载完成 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '.ContentItem.AnswerItem')) ) answers = driver.find_elements(By.CSS_SELECTOR, '.ContentItem.AnswerItem') for answer in answers: try: # 作者 author = answer.find_element(By.CSS_SELECTOR, '.AuthorInfo-name').text # 赞同数 votes = answer.find_element(By.CSS_SELECTOR, '.VoteButton--up').text print(f"作者: {author}, 赞同: {votes}") except Exception as e: print(f"解析单个回答时出错: {e}") continue # 4. 关闭浏览器 driver.quit()
代码解析:
webdriver.Chrome():启动一个 Chrome 浏览器实例。WebDriverWait:显式等待,比time.sleep()更智能,它会等待某个条件(如元素可点击、元素出现)满足后再执行下一步。EC.element_to_be_clickable:等待元素可被点击。driver.find_elements():查找所有匹配的元素,返回一个列表。driver.quit():关闭浏览器并释放资源。
使用框架(Scrapy)
目标:使用 Scrapy 框架重构案例一,实现更高效、更结构化的爬虫。
步骤:
-
安装 Scrapy:
pip install scrapy
-
创建项目:
scrapy startproject douban_movie cd douban_movie scrapy genspider top250 movie.douban.com
这会创建一个项目结构,并自动生成一个名为
top250的爬虫文件。 -
项目结构:
douban_movie/ ├── scrapy.cfg # 部署配置文件 └── douban_movie/ # 项目 Python 模块 ├── __init__.py ├── items.py # 定义要抓取的数据结构 ├── middlewares.py # 中间件 ├── pipelines.py # 数据处理管道 ├── settings.py # 爬虫设置 └── spiders/ # 爬虫目录 ├── __init__.py └── top250.py # 我们将在这里编写爬虫逻辑 -
修改
items.py:定义我们要抓取的数据字段。# douban_movie/items.py import scrapy class DoubanMovieItem(scrapy.Item): title = scrapy.Field() score = scrapy.Field() quote = scrapy.Field() -
修改
spiders/top250.py:编写爬虫核心逻辑。# douban_movie/spiders/top250.py import scrapy from ..items import DoubanMovieItem class Top250Spider(scrapy.Spider): name = 'top250' allowed_domains = ['movie.douban.com'] start_urls = ['https://movie.douban.com/top250'] def parse(self, response): # 找到所有电影条目 movie_list = response.css('ol.grid_view li.item') for movie in movie_list: # 创建一个 Item 对象 item = DoubanMovieItem() # 使用 CSS 选择器提取数据 item['title'] = movie.css('span.title::text').get() item['score'] = movie.css('span.rating_num::text').get() item['quote'] = movie.css('span.inq::text').get() # 将 item 交给引擎,进入处理管道 yield item # 处理分页 next_page = response.css('a.next::attr(href)').get() if next_page is not None: # 构造下一页的完整 URL 并继续爬取 yield response.follow(next_page, self.parse) -
修改
pipelines.py:配置数据存储。# douban_movie/pipelines.py import csv class DoubanMoviePipeline: def open_spider(self, spider): # 爬虫启动时打开文件 self.f = open('douban_top250_scrapy.csv', 'w', newline='', encoding='utf-8-sig') self.writer = csv.DictWriter(self.f, fieldnames=['title', 'score', 'quote']) self.writer.writeheader() def close_spider(self, spider): # 爬虫结束时关闭文件 self.f.close() def process_item(self, item, spider): # 处理每个 item,写入文件 self.writer.writerow(item) return item -
修改
settings.py:开启管道。# douban_movie/settings.py # ... (其他设置) ITEM_PIPELINES = { 'douban_movie.pipelines.DoubanMoviePipeline': 300, } -
运行爬虫:
scrapy crawl top250
Scrapy 优势:
- 异步下载:Scrapy 使用 Twisted 库实现异步 I/O,可以同时发起多个请求,速度极快。
- 管道:数据流经多个处理管道,可以方便地进行清洗、去重、存储等操作。
- 调度器:自动管理请求队列和去重。
- 可扩展性:支持中间件、扩展等,功能强大。
爬虫进阶
-
反爬虫对策:
- User-Agent 池:随机更换
User-Agent。 - IP 代理:使用代理 IP 池来避免因请求频率过高而被封禁 IP。
- 验证码:使用 OCR 库(如
pytesseract)或第三方打码平台。 - 动态渲染:使用
Selenium或Pyppeteer(无头 Chrome)。 - 登录:模拟登录,维持
Session。
- User-Agent 池:随机更换
-
数据存储:
- 关系型数据库:MySQL, PostgreSQL,适合结构化数据。
- 非关系型数据库:MongoDB, Redis,适合半结构化和海量数据。
- 云存储:AWS S3, Google Cloud Storage。
-
部署与调度:
- Scrapy-Redis:基于 Scrapy 和 Redis 的分布式爬虫框架。
- Celery:一个强大的分布式任务队列,可以用来定时调度爬虫任务。
爬虫的“道德”与法律
爬虫技术是中性的,但使用它必须遵守法律法规和网站的 robots.txt 协议。
robots.txt:这是一个放在网站根目录下的文本文件,规定了哪些页面可以爬取,哪些不可以,在爬取一个网站前,务必先查看其robots.txt(https://www.douban.com/robots.txt)。- 尊重网站资源:
- 设置合理的请求间隔:使用
time.sleep()或scrapy的DOWNLOAD_DELAY设置,避免对服务器造成过大压力。 - 不要频繁请求:避免短时间内大量请求同一页面。
- 设置合理的请求间隔:使用
- 数据使用:抓取的数据仅用于个人学习或研究,不得用于商业用途或侵犯他人隐私。
- 版权:尊重网站的版权声明。
总结与学习路径
-
入门阶段:
- 核心库:
requests+BeautifulSoup。 - 目标:能够抓取静态网页,提取数据并保存到本地文件。
- 实践:从豆瓣、知乎等简单网站开始。
- 核心库:
-
进阶阶段:
- 核心库/框架:
Selenium+Scrapy。 - 目标:能够处理动态加载的页面,并使用框架构建稳定、高效的爬虫。
- 实践:尝试爬取更复杂的电商、社交媒体网站,学习使用数据库存储数据。
- 核心库/框架:
-
高级阶段:
- 技术点:分布式爬虫、反反爬策略、数据清洗与分析、部署。
- 目标:构建企业级、高可用的数据采集系统。
- 实践:学习
Scrapy-Redis,研究 IP 代理池,学习使用Docker进行部署。
网络爬虫是一个实践性极强的领域,最好的学习方式就是动手去做,从简单的项目开始,逐步挑战更复杂的网站,你很快就能成为一名合格的爬虫工程师,祝你学习愉快!
