还记得我刚学Python爬虫那会儿,天真地以为几行requests加BeautifulSoup就能走天下。结果迎面撞上反爬机制、频繁被封IP、页面结构一变代码就崩……踩过无数坑后我才明白,写出健壮的爬虫不仅是技术活,更是对耐心和细心的考验。今天我想分享这些用教训换来的经验,希望能帮你少走些弯路。

我们来深入探讨一下用 Python 写爬虫时常见的误区及其解决方案。这对于初学者甚至一些有经验的开发者都很有帮助,可以避免很多“坑”。
常见误区与解决方案
误区一:忽视法律法规与道德规范(Robots.txt)
• 表现:不顾及目标网站的 robots.txt 协议,粗暴抓取所有数据,甚至抓取个人隐私等敏感信息,可能引发法律风险。
• 解决方案:
0. 遵守 Robots 协议:使用 urllib.robotparser 模块来解析和遵守目标网站的 robots.txt。

from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
rp.set_url('https://www.example.com/robots.txt')
rp.read()
can_scrape = rp.can_fetch('YourBotName', 'https://www.example.com/some/page.html')
if can_scrape:
    # 进行爬取
else:
    # 跳过爬取


1. 尊重版权和隐私:只爬取公开且允许爬取的数据,不爬取未授权的内容(如付费内容、用户个人信息等)。
2. 设置友好速率:在请求之间添加延迟(如 time.sleep()),避免对目标网站服务器造成过大压力。rate 参数在 robots.txt 中可能有建议。
误区二:缺乏必要的请求头模拟
• 表现:使用默认的请求头(尤其是 User-Agent 是明显的 python-requests/2.xx.x),极易被网站识别为爬虫并封禁 IP。
• 解决方案:
0. 模拟真实浏览器:总是设置常见的请求头,特别是 User-Agent。可以准备一个列表随机选择,增加隐蔽性。

import requests
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',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.5',
    'Connection': 'keep-alive',
}
response = requests.get('https://httpbin.org/headers', headers=headers)


1. 处理 Cookie:对于需要登录的页面,使用 requests.Session() 来自动管理 Cookies,模拟登录状态。
误区三:错误处理与异常机制不足
• 表现:代码没有 try-except 块,遇到网络波动、HTTP 错误(404, 500)、解析错误时程序直接崩溃,非常脆弱。
• 解决方案:
0. 包装请求代码:对网络请求进行异常捕获。
1. 检查响应状态码:总是检查 response.status_code 是否为 200。

import requests
from requests.exceptions import RequestException
import time
​
def get_page(url):
    try:
        response = requests.get(url, headers=headers, timeout=10)
        if response.status_code == 200:
            return response.text
        else:
            print(f'获取页面失败,状态码: {response.status_code}')
            return None
    except RequestException as e:
        print(f'请求发生错误: {e}')
        return None
​
html = get_page('https://example.com')
if not html:
    # 处理错误情况,如重试、记录日志等
    pass


2. 实现重试机制:对于可能 transient(临时性)的错误(如网络超时),可以使用 tenacity 等库实现自动重试。
误区四:过度依赖页面结构(脆弱的解析器)
• 表现:解析代码(XPath, CSS Selector)直接写死,一旦网站前端改版,爬虫立即失效。
• 解决方案:
0. 选择稳定的标识符:尽量选择 id, class 等不太容易变化的属性或结构进行解析。避免使用绝对路径。
1. 代码解耦:将选择器字符串统一放在代码开头或配置文件中,方便网站改版后集中修改。

# config.py
SELECTORS = {
    'title': 'div.product-name h1::text',
    'price': 'span.price::text',
    # ...
}
​
# main.py
from config import SELECTORS
title = selector.css(SELECTORS['title']).get()


2. 添加断言:在解析关键数据后,检查数据是否存在或符合预期格式,及早发现解析失败。
误区五:处理 JavaScript 渲染页面方法不当
• 表现:直接用 requests 请求动态网页(如 SPA),获取到的 HTML 是空的或不完整,因为数据由 JS 加载。
• 解决方案:
0. 分析 API:打开浏览器开发者工具(F12)-> Network -> XHR,寻找真正提供数据的 Ajax API 接口。直接用 requests 调用这个接口往往更高效。
1. 使用渲染工具:如果数据没有独立接口,必须渲染 JS,则使用:
■ Selenium:模拟真实浏览器操作,功能强大但速度慢,资源消耗大。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
​
options = Options()
options.headless = True # 无头模式,不显示浏览器窗口
driver = webdriver.Chrome(options=options)
driver.get("https://example.com")
html = driver.page_source
# ... 解析 html
driver.quit()


■ Splash:一个带有 HTTP API 的轻量级 JavaScript 渲染服务,常与 Scrapy 配合使用。
■ Playwright / Puppeteer:现代浏览器自动化库,比 Selenium 更强大和快速。
误区六:同步阻塞式请求,效率低下
• 表现:使用简单的 for 循环 + time.sleep() 进行请求,每个请求都等待上一个完成,速度极慢。
• 解决方案:
0. 多线程/多进程:使用 concurrent.futures 线程池。

from concurrent.futures import ThreadPoolExecutor, as_completed
import requests
​
urls = [...] # 一个很大的URL列表
​
def download_url(url):
    return requests.get(url).text
​
with ThreadPoolExecutor(max_workers=10) as executor: # 控制并发数
    future_to_url = {executor.submit(download_url, url): url for url in urls}
    for future in as_completed(future_to_url):
        html = future.result()
        # 处理html


1. 异步IO(推荐) :使用 aiohttp + asyncio,性能极高,是专业爬虫的首选。

import aiohttp
import asyncio
​
async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()
​
async def main():
    urls = [...]
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        htmls = await asyncio.gather(*tasks)
        for html in htmls:
            # 处理html
            pass
​
asyncio.run(main())


注意:高并发请求一定要谨慎,控制速率,否则相当于对网站发起 DOS 攻击。
误区七:数据存储与管理混乱
• 表现:将所有数据一股脑打印到控制台或写入一个巨大的文件,不利于后续处理和分析。
• 解决方案:
◦ 结构化存储:根据数据量和用途选择合适的存储方式。
■ JSON/CSV 文件:适合中小规模数据。
■ 数据库(SQLite, MySQL, PostgreSQL, MongoDB) :适合大规模、结构化数据,方便查询和管理。使用 ORM(如 SQLAlchemy)或数据库驱动(如 pymysql, psycopg2)。
误区八:单打独斗,忽视成熟框架
• 表现:所有功能都从 requests 和 BeautifulSoup 开始自己写,重复造轮子,项目难以维护和扩展。
• 解决方案:
◦ 使用 Scrapy 框架:对于大型、复杂的爬虫项目,Scrapy 是行业标准。它提供了:
■ 高性能的异步处理。
■ 内置的中间件、管道(Pipeline)、调度器系统,功能强大且易于扩展。
■ 丰富的生态系统(如 Scrapy-Redis 用于分布式爬虫)。
◦ 即使是小项目,也可以借鉴它的设计思想。
误区九:IP 被封禁应对策略单一
• 表现:一个 IP 地址疯狂请求,很快被网站封禁,爬虫无法继续工作。
• 解决方案:
0. 代理IP池:使用第三方代理IP服务(如芝麻代理、快代理等)或自建代理IP池,轮流使用不同IP发送请求。

import requests
proxies = {
    'http': 'http://10.10.1.10:3128',
    'https': 'http://10.10.1.10:1080',
}
requests.get('http://example.org', proxies=proxies, timeout=10)


1. 降低请求频率:合理设置下载延迟 (DOWNLOAD_DELAY in Scrapy)。
2. 识别验证码:接入打码平台(如超级鹰)进行验证码识别(通常作为最后手段)。
总结:最佳实践 checklist
0. [法律] 检查 robots.txt,尊重网站规则。
1. [请求] 设置合理的请求头(UA, Referer, Cookie等)。
2. [健壮性] 添加完善的异常处理(网络、解析)和重试机制。
3. [解析] 编写健壮的解析代码,选择相对稳定的选择器,并与核心逻辑解耦。
4. [动态内容] 优先寻找隐藏的 API 接口,必要时使用 Selenium/Splash。
5. [效率] 对于大量请求,使用异步(aiohttp)或并发(线程池)来提高效率。
6. [反爬] 使用代理IP池、控制访问速率、处理验证码来应对反爬虫。
7. [存储] 设计良好的数据存储方案(文件/数据库)。
8. [工程化] 大型项目优先考虑使用 Scrapy 框架。
9. [日志] 为爬虫添加详细的日志记录,方便排查问题。
遵循这些原则和解决方案,你就能写出更加健壮、高效且合规的 Python 爬虫。
回头看这段爬虫学习之旅,从最初的横冲直撞到如今能成熟考虑法律伦理、性能优化和系统设计,每个坑都让我受益匪浅。爬虫世界没有一劳永逸的银弹,唯有保持敬畏、持续学习才是正道。希望我的这些经验能成为大家爬虫路上的垫脚石,帮助大家写出更优雅强大的程序。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐