ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python爬虫实战:从零构建合法、稳健的数据采集系统

Python爬虫实战:从零构建合法、稳健的数据采集系统

你是不是也遇到过这样的场景:想分析某个行业的数据,却发现没有现成的API;想追踪竞品价格,只能每天手动复制粘贴;想批量下载一些公开资料,却要一个个点击保存。这些重复、低效的数据收集工作,正是Python爬虫要解决的痛点。

很多人对爬虫的第一印象是“能自动抓取网页数据”,这没错,但理解仅停留于此,往往会踩进大坑。爬虫的真正价值,在于将非结构化的网页信息,转化为结构化、可分析的数据资产。它不只是“抓取”,更核心的是“解析”和“存储”。从技术角度看,一个健壮的爬虫项目,70%的精力可能花在处理反爬机制、数据清洗和异常流程上,而不仅仅是写几行请求代码。

本文不会教你如何“暴力”抓取所有网站,那既不道德也不可持续。相反,我们会聚焦于合法、合规、稳健的Python爬虫实践。你将了解到,如何像一名专业的开发者那样,从零构建一个具备良好工程素养的爬虫:包括环境搭建、请求发送、数据解析、反反爬策略、数据存储,以及最重要的——如何设计一个易于维护和扩展的爬虫架构。

无论你是想入门数据分析、做市场调研,还是希望自动化一些日常工作,掌握爬虫都能让你事半功倍。接下来,我们从最核心的问题开始:在动手写代码之前,你必须知道哪些规则和原理?

1. 爬虫的核心:不只是技术,更是规则与伦理

在写下第一行import requests之前,我们必须先划定边界。爬虫技术本身是中立的,但使用方式决定了它的性质。无视规则的爬虫行为,轻则导致IP被封、数据获取失败,重则可能面临法律风险。

1.1 必须遵守的规则:Robots协议与法律法规

Robots协议(robots.txt)是网站与爬虫之间的“君子协定”。它存放在网站根目录下(如https://example.com/robots.txt),明确告知爬虫哪些目录可以访问,哪些禁止抓取。

例如,查看淘宝的robots协议,你会发现大量目录对通用爬虫是禁止的:

User-agent: * Disallow: /

这并不意味着完全不能抓取,但提示你需要非常谨慎,或寻求官方API。而像维基百科等网站则相对开放。一个负责任的爬虫,应该首先检查并尊重目标网站的robots.txt。

除了技术协议,还需关注:

  • 《数据安全法》与《个人信息保护法》:严禁爬取涉及个人隐私、国家安全、商业秘密的数据。
  • 网站服务条款(Terms of Service):很多网站明确禁止未经授权的自动化数据抓取。
  • 访问频率限制:过于频繁的请求会对目标服务器造成压力,构成拒绝服务攻击(DoS)风险。

核心原则:只爬取公开、非敏感、且对服务器影响小的数据。对于商业网站,优先寻找官方API;对于学术或公益网站,也应控制抓取速度。

1.2 爬虫的基本工作流程

理解流程,才能写出结构清晰的代码。一个典型的爬虫任务遵循以下步骤:

  1. 明确目标:确定要抓取哪些网站、哪些页面、哪些具体数据(如商品标题、价格、评论)。
  2. 分析页面:使用浏览器开发者工具(F12)分析目标页面的网络请求(XHR/Fetch)和HTML结构,确定数据来源(是直接渲染在HTML中,还是通过Ajax接口加载的JSON)。
  3. 发送请求:使用HTTP库(如requests)模拟浏览器,向目标URL发送请求,获取响应内容(HTML、JSON等)。
  4. 解析数据:从响应内容中提取所需信息。常用工具有:
    • 正则表达式:灵活但复杂,适合简单或固定模式。
    • BeautifulSoup:基于HTML/XML解析树,语法简单直观,适合初学者。
    • lxml:解析速度快,功能强大,适合处理大规模数据。
    • 直接处理JSON:如果数据来自API接口,直接用json.loads()解析。
  5. 保存数据:将解析后的结构化数据持久化,如存入CSV文件、JSON文件、MySQL/MongoDB数据库等。
  6. 处理异常与反爬:处理网络超时、页面不存在、IP被封、验证码等异常情况,并采取相应策略(如使用代理IP、添加请求头、降低频率)。

2. 环境准备:搭建你的Python爬虫工作台

工欲善其事,必先利其器。一个稳定的开发环境能避免很多后续麻烦。

2.1 Python与包管理工具安装

Python版本:推荐使用Python 3.8及以上版本,它们有更好的异步支持和库兼容性。避免使用Python 2.x,其已停止维护。

安装步骤(以Windows为例):

  1. 访问 Python官网 下载安装包。
  2. 运行安装程序,务必勾选“Add Python 3.x to PATH”,这样可以在命令行直接使用pythonpip命令。
  3. 安装完成后,打开命令提示符(CMD)或PowerShell,验证安装:
    python --version pip --version
    成功显示版本号即表示安装正确。

包管理工具pip:Python自带的包管理器,用于安装第三方库。建议立即升级到最新版:

python -m pip install --upgrade pip

2.2 虚拟环境管理:隔离项目依赖

不同爬虫项目可能依赖不同版本的库。使用虚拟环境可以为每个项目创建独立的Python运行环境,避免依赖冲突。

使用venv(Python内置)

# 在项目目录下,创建名为‘venv’的虚拟环境 python -m venv venv # 激活虚拟环境 (Windows) venv\Scripts\activate # 激活虚拟环境 (macOS/Linux) source venv/bin/activate # 激活后,命令行提示符前会出现‘(venv)’字样 # 在此环境下安装的包仅对本项目有效 # 退出虚拟环境 deactivate

2.3 核心库安装

激活虚拟环境后,安装爬虫最常用的几个库:

pip install requests beautifulsoup4 lxml pandas pymysql
  • requests:人性化的HTTP库,用于发送网络请求。
  • beautifulsoup4:HTML/XML解析库,配合lxml解析器速度更快。
  • lxml:另一个高效的解析库,也是BeautifulSoup的解析器选项之一。
  • pandas:数据处理和分析神器,可方便地将数据导出为Excel/CSV。
  • pymysql:用于连接MySQL数据库。

IDE选择:推荐使用VS CodePyCharm。VS Code轻量且插件丰富,PyCharm是专业的Python IDE,调试功能强大。确保为IDE配置好已激活的虚拟环境解释器。

3. 第一只爬虫:从静态网页抓取开始

让我们从一个最简单的例子开始:抓取一个静态网页的标题。我们以一个练习网站 http://httpbin.org 为例,它专门用于HTTP请求测试。

3.1 使用Requests获取网页内容

创建一个名为first_spider.py的文件:

import requests # 目标URL url = 'http://httpbin.org/get' # 设置请求头,模拟浏览器访问 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: # 发送GET请求 response = requests.get(url, headers=headers) # 检查请求是否成功 (状态码200表示成功) response.raise_for_status() # 如果状态码不是200,将抛出HTTPError异常 # 打印状态码和响应内容(JSON格式) print(f"状态码: {response.status_code}") print("响应内容:") print(response.json()) # httpbin.org/get 返回的是JSON except requests.exceptions.RequestException as e: print(f"请求出错: {e}") except ValueError as e: print(f"解析JSON出错: {e}")

运行这个脚本,你会看到返回的JSON数据,其中包含了你的请求头、IP等信息。User-Agent是告诉服务器你是什么客户端,不加的话容易被识别为爬虫。

3.2 使用BeautifulSoup解析HTML

静态网页的数据通常嵌套在HTML标签中。我们以抓取“豆瓣电影Top250”的首页电影名称为例(请注意,实际抓取前应查看其robots.txt)。

import requests from bs4 import BeautifulSoup url = 'https://movie.douban.com/top250' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } try: resp = requests.get(url, headers=headers) resp.raise_for_status() # 指定使用‘lxml’解析器,需要先安装 lxml 库 soup = BeautifulSoup(resp.text, 'lxml') # 查找所有class为‘title’的span标签(包含电影名) # 注意:实际豆瓣的HTML结构可能变化,此处仅为示例 title_tags = soup.find_all('span', class_='title') for idx, tag in enumerate(title_tags, 1): # 过滤掉英文/其他语言片名(通常第一个是中文片名) if '/' not in tag.text: # 简单的过滤逻辑 print(f"{idx}. {tag.text}") except Exception as e: print(f"抓取或解析失败: {e}")

关键点解析

  • BeautifulSoup(resp.text, 'lxml'):将HTML文本转化为一个可遍历的树形结构对象。
  • soup.find_all(‘span’, class_=‘title’):查找所有标签名为spanclass属性为title的元素。class_带下划线是为了避免与Python关键字class冲突。
  • 网页结构可能随时变更,因此选择器(如‘span.title’)需要根据实际情况调整。熟练使用浏览器的“检查元素”功能是爬虫工程师的基本功。

4. 应对挑战:反爬虫机制与应对策略

如果所有网站都像上面那样简单,爬虫就毫无难度了。现实是,网站会使用各种反爬虫技术来保护数据和服务器资源。

4.1 常见的反爬虫手段

反爬手段表现目的
User-Agent检测检查请求头中的User-Agent,如果为空或为爬虫库默认值,则拒绝服务。识别并拦截非浏览器流量。
IP频率限制同一IP在短时间内请求次数过多,会被暂时或永久封禁。防止服务器过载和资源滥用。
验证码在多次请求后弹出图片、滑块或点选验证码。区分人类用户和自动化程序。
动态数据加载数据通过JavaScript异步加载(Ajax),初始HTML中不包含有效数据。增加直接解析HTML的难度。
请求参数签名请求必须携带一个由特定算法生成的、会变化的参数(如token,sign)。防止请求被轻易模拟和重放。
Cookie/Session验证需要先登录或完成一系列操作获得有效Cookie,才能访问数据。确保用户状态和权限。

4.2 基础应对策略

策略一:伪装请求头

这是最基本的伪装。除了User-Agent,有时还需要携带Referer(来源页)、Accept-Language等。

headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Referer': 'https://www.google.com/', # 模拟从谷歌跳转而来 'Connection': 'keep-alive', }
策略二:设置请求间隔

在循环抓取时,在请求之间加入随机延时,模拟人类操作。

import time import random for page in range(1, 11): # ... 发送请求,解析数据 ... time.sleep(random.uniform(1, 3)) # 随机等待1-3秒
策略三:处理Cookie

使用requests.Session()对象,它可以自动保存和发送Cookie,模拟一个浏览器会话。

import requests session = requests.Session() # 第一次请求,获取并保存Cookie login_resp = session.post(‘登录URL‘, data={‘username‘: ‘...‘, ‘password‘: ‘...‘}) # 后续请求,session会自动携带Cookie data_resp = session.get(‘数据URL‘)
策略四:使用代理IP

当IP被封锁时,需要通过代理IP池来更换出口IP。可以使用付费代理服务,或寻找免费的HTTP代理(但稳定性差)。

proxies = { ‘http‘: ‘http://12.34.56.78:8080‘, ‘https‘: ‘http://12.34.56.78:8080‘, } try: response = requests.get(url, headers=headers, proxies=proxies, timeout=10) except requests.exceptions.ProxyError: print(‘代理失效,更换下一个代理IP‘)

4.3 进阶挑战:动态页面与Selenium

当数据由JavaScript动态渲染时,requests获取的初始HTML是空的。此时需要能驱动浏览器的工具,如SeleniumPlaywright

使用Selenium示例

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time # 配置Chrome浏览器选项(无头模式,不显示图形界面) options = webdriver.ChromeOptions() options.add_argument(‘--headless‘) # 无头模式 options.add_argument(‘--disable-gpu‘) options.add_argument(‘user-agent=Mozilla/5.0 ...‘) driver = webdriver.Chrome(options=options) # 确保已下载对应版本的ChromeDriver try: driver.get(‘https://example.com/dynamic-page‘) # 显式等待,直到某个元素加载出来 element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, “target-class“)) ) # 此时页面已完全渲染,可以获取HTML page_source = driver.page_source # 接下来可以用BeautifulSoup解析page_source # ... finally: driver.quit() # 务必关闭浏览器,释放资源

注意:Selenium速度慢、资源消耗大,应仅作为获取动态数据的最后手段。优先尝试分析网页的网络请求,看能否直接找到数据接口(XHR/Fetch)。

5. 完整项目实战:爬取公开图书信息并存储

我们将构建一个相对完整的爬虫项目,目标是从一个公开的图书信息网站(例如:http://books.toscrape.com/,一个专为爬虫练习设计的网站)抓取图书列表,并将数据存储到CSV文件和MySQL数据库中。

5.1 项目结构规划

book_scraper/ ├── scraper.py # 主爬虫逻辑 ├── config.py # 配置文件(数据库连接等) ├── utils.py # 工具函数(请求、解析、存储) ├── requirements.txt # 项目依赖 └── data/ # 存放输出的CSV文件

5.2 核心代码实现

1. 配置文件config.py

# 数据库配置 (请根据实际情况修改) DB_CONFIG = { ‘host‘: ‘localhost‘, ‘port‘: 3306, ‘user‘: ‘your_username‘, ‘password‘: ‘your_password‘, ‘database‘: ‘book_data‘, ‘charset‘: ‘utf8mb4‘, } # 爬虫配置 SPIDER_CONFIG = { ‘base_url‘: ‘http://books.toscrape.com/catalogue/page-{}.html‘, ‘start_page‘: 1, ‘end_page‘: 5, # 只爬取5页作为演示 ‘request_headers‘: { ‘User-Agent‘: ‘Mozilla/5.0 ...‘, }, ‘request_timeout‘: 10, ‘delay_range‘: (1, 2), # 请求延迟范围(秒) }

2. 工具函数utils.py

import requests import time import random from bs4 import BeautifulSoup import pymysql from config import DB_CONFIG, SPIDER_CONFIG def get_page_html(url, headers=None): """发送请求,获取页面HTML""" if headers is None: headers = SPIDER_CONFIG[‘request_headers‘] try: resp = requests.get(url, headers=headers, timeout=SPIDER_CONFIG[‘request_timeout‘]) resp.raise_for_status() # 检查编码,避免乱码 resp.encoding = resp.apparent_encoding return resp.text except requests.exceptions.RequestException as e: print(f“请求 {url} 失败: {e}“) return None def parse_book_list(html): """解析图书列表页,提取每本书的详情链接和基本信息""" soup = BeautifulSoup(html, ‘lxml‘) books = [] # 根据books.toscrape.com的实际结构查找 book_elements = soup.select(‘article.product_pod‘) # CSS选择器 for elem in book_elements: book = {} # 提取书名 title_tag = elem.select_one(‘h3 a‘) book[‘title‘] = title_tag[‘title‘] if title_tag else ‘N/A‘ # 提取详情页链接 book[‘detail_url‘] = ‘http://books.toscrape.com/catalogue/‘ + title_tag[‘href‘] if title_tag else ‘N/A‘ # 提取价格 price_tag = elem.select_one(‘p.price_color‘) book[‘price‘] = price_tag.get_text(strip=True) if price_tag else ‘N/A‘ # 提取库存状态 stock_tag = elem.select_one(‘p.instock.availability‘) book[‘stock‘] = stock_tag.get_text(strip=True) if stock_tag else ‘N/A‘ books.append(book) return books def parse_book_detail(html): """解析图书详情页,提取更详细的信息""" soup = BeautifulSoup(html, ‘lxml‘) detail = {} # 提取产品描述 desc_tag = soup.select_one(‘article.product_page > p‘) detail[‘description‘] = desc_tag.get_text(strip=True) if desc_tag else ‘N/A‘ # 提取产品分类 category_tag = soup.select(‘ul.breadcrumb li a‘)[2] # 根据网站结构定位 detail[‘category‘] = category_tag.get_text(strip=True) if category_tag else ‘N/A‘ # 提取评价等级 rating_tag = soup.select_one(‘p.star-rating‘) if rating_tag: rating_class = rating_tag[‘class‘][1] # 例如 ‘Three‘ detail[‘rating‘] = rating_class else: detail[‘rating‘] = ‘N/A‘ return detail def save_to_csv(books, filename=‘data/books.csv‘): """将图书列表保存到CSV文件""" import csv import os os.makedirs(‘data‘, exist_ok=True) if not books: return keys = books[0].keys() with open(filename, ‘w‘, newline=‘‘, encoding=‘utf-8-sig‘) as f: # utf-8-sig支持Excel中文 writer = csv.DictWriter(f, fieldnames=keys) writer.writeheader() writer.writerows(books) print(f“数据已保存到 {filename}“) def init_database(): """初始化数据库连接和表结构""" connection = pymysql.connect(**DB_CONFIG) try: with connection.cursor() as cursor: # 创建表(如果不存在) create_table_sql = “““ CREATE TABLE IF NOT EXISTS books ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(500) NOT NULL, price VARCHAR(50), stock VARCHAR(100), description TEXT, category VARCHAR(200), rating VARCHAR(50), detail_url VARCHAR(500), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; “““ cursor.execute(create_table_sql) connection.commit() print(“数据库表初始化完成。“) finally: connection.close() def save_to_mysql(book_data): """将单条图书数据插入MySQL数据库""" connection = pymysql.connect(**DB_CONFIG) try: with connection.cursor() as cursor: sql = “““ INSERT INTO books (title, price, stock, description, category, rating, detail_url) VALUES (%s, %s, %s, %s, %s, %s, %s) “““ cursor.execute(sql, ( book_data.get(‘title‘), book_data.get(‘price‘), book_data.get(‘stock‘), book_data.get(‘description‘, ‘‘), book_data.get(‘category‘, ‘‘), book_data.get(‘rating‘, ‘‘), book_data.get(‘detail_url‘, ‘‘) )) connection.commit() except pymysql.Error as e: print(f“插入数据库失败: {e}“) connection.rollback() finally: connection.close()

3. 主爬虫逻辑scraper.py

import time import random from utils import get_page_html, parse_book_list, parse_book_detail, save_to_csv, save_to_mysql, init_database from config import SPIDER_CONFIG def main(): print(“开始爬取图书信息...“) all_books = [] base_url = SPIDER_CONFIG[‘base_url‘] delay_min, delay_max = SPIDER_CONFIG[‘delay_range‘] # 初始化数据库表 init_database() for page in range(SPIDER_CONFIG[‘start_page‘], SPIDER_CONFIG[‘end_page‘] + 1): print(f“正在处理第 {page} 页...“) url = base_url.format(page) html = get_page_html(url) if not html: print(f“第 {page} 页获取失败,跳过。“) continue books_on_page = parse_book_list(html) print(f“第 {page} 页找到 {len(books_on_page)} 本书。“) for book in books_on_page: # 获取详情页信息 detail_html = get_page_html(book[‘detail_url‘]) if detail_html: detail_info = parse_book_detail(detail_html) book.update(detail_info) # 合并基本信息与详情信息 else: print(f“获取详情页失败: {book[‘detail_url‘]}“) # 保存到数据库 save_to_mysql(book) # 添加到总列表,用于后续保存CSV all_books.append(book) # 请求间隔,避免过快 time.sleep(random.uniform(delay_min, delay_max)) # 每爬完一页,稍作休息 time.sleep(random.uniform(1, 3)) # 所有数据爬取完成后,保存到CSV if all_books: save_to_csv(all_books) print(f“爬取完成!共获取 {len(all_books)} 条图书信息。“) else: print(“未获取到任何数据。“) if __name__ == ‘__main__‘: main()

4. 依赖文件requirements.txt

requests>=2.28.0 beautifulsoup4>=4.11.0 lxml>=4.9.0 pymysql>=1.0.0 pandas>=1.5.0 # 可选,用于更强大的数据分析

5.3 运行与验证

  1. 安装依赖:在项目根目录下,执行pip install -r requirements.txt
  2. 配置数据库:在config.py中填写你的MySQL数据库信息,并确保数据库book_data已创建。
  3. 运行爬虫:执行python scraper.py
  4. 验证结果
    • 查看data/books.csv文件,应包含爬取的数据。
    • 连接MySQL数据库,查询books表,应能看到插入的记录。

这个项目演示了一个完整爬虫的骨架:配置管理、请求发送、数据解析、异常处理、数据持久化(文件+数据库)以及礼貌的爬取间隔。你可以在此基础上扩展,比如增加代理IP池、分布式任务队列、更复杂的异常重试机制等。

6. 常见问题与排查思路

在实际爬虫开发中,你几乎一定会遇到下面这些问题。

问题现象可能原因排查方式解决方案
返回状态码 403请求被服务器拒绝。常见于未添加合法User-Agent、IP被封、或触发了风控。1. 打印当前请求头。
2. 尝试在浏览器中访问同一URL。
3. 更换IP或使用代理测试。
1. 完善请求头,模拟真实浏览器。
2. 降低请求频率,增加随机延迟。
3. 使用代理IP。
返回状态码 404页面不存在。URL拼写错误或页面已移除。检查URL是否正确,在浏览器中验证。修正URL或处理页面不存在的异常。
返回状态码 500/502/503服务器内部错误或网关错误。可能是目标服务器问题,也可能是你的请求触发了防护。1. 等待一段时间后重试。
2. 简化请求参数。
1. 实现重试机制(如tenacity库)。
2. 检查请求参数是否合理。
数据解析为空1. 网页结构已变更。
2. 数据是动态加载的(JS)。
3. 解析器或选择器写错。
1. 打印resp.text的前1000字符,查看是否包含目标数据。
2. 使用浏览器开发者工具,检查元素是否在初始HTML中。
3. 检查CSS选择器或XPath路径。
1. 更新解析逻辑。
2. 改用Selenium或分析Ajax接口。
3. 使用更稳健的选择器。
乱码响应内容的编码与解析编码不一致。打印resp.encodingresp.apparent_encoding设置resp.encoding = resp.apparent_encoding或指定正确的编码(如‘utf-8‘)。
连接超时网络不稳定或服务器响应慢。检查网络连接,增加timeout参数值。使用try...except捕获超时异常,并加入重试逻辑。
被要求输入验证码触发了网站的反爬验证。观察在浏览器中手动操作是否会触发。1. 大幅降低请求频率。
2. 尝试使用带有验证码识别功能的库(如ddddocr),但复杂度高。
3. 考虑是否应停止爬取。
数据库插入失败1. 连接信息错误。
2. 数据格式与表结构不匹配(如超长、含特殊字符)。
3. 主键或唯一键冲突。
1. 检查数据库连接配置。
2. 打印待插入的数据。
3. 查看数据库错误日志。
1. 确保配置正确,数据库服务开启。
2. 对数据进行清洗和截断。
3. 使用INSERT IGNORE或先查询后插入。

7. 工程化与最佳实践

当爬虫从脚本升级为需要长期稳定运行的系统时,你需要考虑更多工程化问题。

7.1 项目结构规范化

不要把所有代码写在一个文件里。参考前面的示例,按功能模块拆分:

  • spiders/:存放不同网站的爬虫类。
  • items.py:定义统一的数据结构。
  • middlewares.py:处理请求和响应的中间件(如代理、User-Agent轮换)。
  • pipelines.py:数据处理管道(如清洗、去重、存储)。
  • scheduler.py:任务调度器。
  • utils/:公共工具函数。 这种结构清晰,易于维护和扩展,也是Scrapy等框架采用的设计思想。

7.2 使用成熟的爬虫框架:Scrapy

对于中型以上项目,强烈建议使用Scrapy框架。它提供了完整的爬虫开发体系:

  • 异步处理:基于Twisted,速度快。
  • 内置组件:下载器、调度器、中间件、管道一应俱全。
  • 项目模板scrapy startproject myproject一键生成规范结构。
  • 强大扩展:方便地添加代理、处理Cookie、导出数据。

一个简单的Scrapy爬虫示例:

# 安装 pip install scrapy # 创建项目 scrapy startproject book_scraper_scrapy cd book_scraper_scrapy scrapy genspider books books.toscrape.com

然后编辑生成的books.pypipelines.py即可。框架会帮你处理并发、去重、日志等繁杂事务。

7.3 数据存储策略

  • 文件:CSV/JSON适合小批量、临时数据。注意文件读写锁和编码。
  • 数据库
    • MySQL/PostgreSQL:适合关系型数据,便于复杂查询。
    • MongoDB:适合半结构化、模式不固定的数据,写入速度快。
    • SQLite:轻量级,适合单机小型项目,无需安装数据库服务。
  • 去重:使用数据库唯一键,或布隆过滤器(Bloom Filter)在内存中快速判断URL是否已抓取。

7.4 日志与监控

不要只用print。使用Python内置的logging模块记录不同级别(DEBUG, INFO, WARNING, ERROR)的日志,便于问题追踪。

import logging logging.basicConfig(level=logging.INFO, format=‘%(asctime)s - %(name)s - %(levelname)s - %(message)s‘, filename=‘spider.log‘) logger = logging.getLogger(__name__) logger.info(‘开始爬取页面 %s‘, url)

7.5 伦理、法律与风险控制再强调

  • 控制速率:在robots.txt中寻找Crawl-delay提示,若无,建议单域名请求间隔至少1-2秒。
  • 识别敏感数据:绝不爬取个人隐私、账号密码、商业秘密、受版权保护的核心内容。
  • 设置退出机制:当连续遇到大量错误(如403、验证码)时,应自动暂停或停止,并发出警报。
  • 保留证据:对于公开数据的爬取,保留你遵守robots.txt和合理使用原则的证据。

掌握Python爬虫,本质上是掌握了从互联网这片数据海洋中高效、精准、合法获取信息的能力。这项技能的价值不在于能抓取多少数据,而在于你能将这些数据转化为有意义的洞察和解决方案。从今天起,试着用爬虫的思路去观察你常浏览的网站,思考其数据结构和获取方式,这是迈向数据驱动决策的第一步。

返回列表