Skip to content

第1章 爬虫基础与法律伦理

1.1 什么是网络爬虫:定义、用途与典型场景

网络爬虫的基本概念

网络爬虫(Web Crawler 或 Web Spider)是一种自动从互联网上抓取网页内容的程序。它就像一只勤劳的小蜘蛛,在万维网这张巨大的网上爬行,按照预设的规则访问网页、提取数据并存储起来。

简单来说,爬虫就是模拟人类浏览网页的行为,但速度更快、效率更高、可以 24 小时不间断工作。当你在百度搜索某个关键词时,背后就有成千上万的爬虫在为搜索引擎收集和整理网页信息。

爬虫的主要用途

爬虫技术在现代互联网应用中无处不在,以下是一些典型的应用场景:

功能名称应用场景具体说明
搜索引擎索引Google、百度等爬取全网网页建立搜索索引
数据分析市场调研、竞品分析抓取电商平台价格、评论等数据
内容聚合新闻聚合、招聘信息整合从多个网站收集同类信息
学术研究社会学、经济学研究收集公开的统计数据和文本
监控告警价格监控、舆情监控定期检查特定网页的变化

简单爬虫示例

让我们来看一个最基础的爬虫示例,它只是简单地获取一个网页的内容:

python
# 导入 requests 库,用于发送 HTTP 请求
import requests

# 定义要爬取的目标 URL
url = "https://httpbin.org/html"

try:
    # 发送 GET 请求获取网页内容
    response = requests.get(url)
    
    # 检查请求是否成功(状态码 200 表示成功)
    if response.status_code == 200:
        # 打印网页的 HTML 内容
        print("成功获取网页内容:")
        print(response.text[:200] + "...")  # 只打印前200个字符
    else:
        # 如果状态码不是 200,打印错误信息
        print(f"请求失败,状态码:{response.status_code}")
        
except requests.exceptions.RequestException as e:
    # 捕获所有 requests 相关的异常
    print(f"请求过程中发生错误:{e}")

这个简单的例子展示了爬虫的基本工作流程:发送请求 → 获取响应 → 处理数据。虽然功能简单,但它包含了爬虫的核心要素。

网络爬虫是数据获取的重要工具,既能为正当的商业和研究目的服务,也可能被滥用于不当的数据采集。理解爬虫的基本概念是学习更高级爬虫技术的第一步,也是建立正确使用观念的基础。

1.2 HTTP 协议基础:请求方法、状态码、Headers

HTTP 协议概述

HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最为广泛的一种网络协议。所有的爬虫本质上都是在模拟 HTTP 请求,因此理解 HTTP 协议是编写有效爬虫的前提。

当你在浏览器地址栏输入网址并按下回车时,浏览器就会向服务器发送一个 HTTP 请求,服务器处理后返回 HTTP 响应,浏览器再将响应内容渲染成网页展示给你。

HTTP 请求方法

HTTP 定义了多种请求方法,爬虫中最常用的是 GET 和 POST:

请求方法用途说明爬虫应用场景
GET从服务器获取资源获取网页内容、API 数据查询
POST向服务器提交数据表单提交、登录认证、搜索请求
PUT更新服务器上的资源较少在爬虫中使用
DELETE删除服务器上的资源较少在爬虫中使用

HTTP 状态码

HTTP 状态码是服务器对请求的响应结果,对于爬虫来说非常重要,因为它告诉我们请求是否成功:

状态码范围含义常见状态码
1xx信息性状态码100 Continue
2xx成功200 OK, 201 Created
3xx重定向301 Moved Permanently, 302 Found
4xx客户端错误400 Bad Request, 403 Forbidden, 404 Not Found
5xx服务器错误500 Internal Server Error, 502 Bad Gateway

HTTP Headers

HTTP Headers(头部信息)包含了请求和响应的元数据,对于爬虫来说特别重要:

  • User-Agent:标识客户端类型(浏览器、爬虫等)
  • Referer:表示请求来源页面
  • Cookie:用于维持会话状态
  • Content-Type:指定请求或响应的内容类型

HTTP 请求示例

下面演示如何使用 Python 的 requests 库发送不同类型的 HTTP 请求:

python
# 导入 requests 库
import requests

# GET 请求示例
def get_request_example():
    """演示 GET 请求的使用"""
    url = "https://httpbin.org/get"
    
    # 设置请求头,模拟浏览器访问
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
    
    try:
        # 发送 GET 请求
        response = requests.get(url, headers=headers)
        print(f"GET 请求状态码: {response.status_code}")
        print(f"响应内容: {response.json()}")  # httpbin 返回 JSON 格式
        
    except requests.exceptions.RequestException as e:
        print(f"GET 请求错误: {e}")

# POST 请求示例
def post_request_example():
    """演示 POST 请求的使用"""
    url = "https://httpbin.org/post"
    
    # 要提交的数据
    data = {
        'username': 'testuser',
        'password': 'testpass'
    }
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    try:
        # 发送 POST 请求
        response = requests.post(url, data=data, headers=headers)
        print(f"POST 请求状态码: {response.status_code}")
        print(f"响应内容: {response.json()}")
        
    except requests.exceptions.RequestException as e:
        print(f"POST 请求错误: {e}")

# 执行示例
if __name__ == "__main__":
    get_request_example()
    print("-" * 50)
    post_request_example()

掌握 HTTP 协议基础是编写高质量爬虫的关键。通过理解请求方法、状态码和头部信息,我们可以更好地模拟真实用户行为,提高爬虫的成功率和稳定性。

1.3 robots.txt 协议与爬虫道德规范

robots.txt 协议详解

robots.txt 是网站根目录下的一个文本文件,用于告诉网络爬虫哪些页面可以抓取,哪些页面不应该抓取。这是网站管理员与爬虫开发者之间的一种约定,体现了互联网的礼仪和尊重。

例如,访问 https://www.example.com/robots.txt 可能会看到这样的内容:

User-agent: *
Disallow: /admin/
Disallow: /private/
Allow: /

Sitemap: https://www.example.com/sitemap.xml

这段配置的含义是:

  • User-agent: * 表示规则适用于所有爬虫
  • Disallow: /admin/ 禁止爬取 /admin/ 目录
  • Disallow: /private/ 禁止爬取 /private/ 目录
  • Allow: / 允许爬取其他所有内容
  • Sitemap 指向网站地图,帮助爬虫发现重要内容

常见的 robots.txt 规则

规则类型示例说明
禁止所有爬虫User-agent: *``Disallow: /完全禁止爬取
允许所有爬虫User-agent: *``Disallow:允许爬取所有内容
针对特定爬虫User-agent: Googlebot``Disallow: /temp/只对 Googlebot 限制
通配符支持Disallow: /*.pdf$禁止爬取所有 PDF 文件

爬虫道德规范

除了遵守 robots.txt 协议,负责任的爬虫开发者还应该遵循以下道德规范:

  1. 控制请求频率:不要对服务器造成过大压力,合理设置请求间隔
  2. 识别自己身份:在 User-Agent 中明确标识爬虫身份和联系方式
  3. 尊重版权:只抓取允许公开访问的内容,不用于侵犯版权的目的
  4. 处理敏感信息:避免抓取包含个人隐私或敏感商业信息的页面
  5. 及时停止:当网站明确表示不欢迎爬虫时,应立即停止抓取

解析 robots.txt 的示例

Python 提供了 urllib.robotparser 模块来解析和检查 robots.txt 规则:

python
# 导入 robotparser 模块
from urllib import robotparser
import time

def check_robots_txt(url, user_agent='*'):
    """
    检查指定 URL 是否允许被爬虫访问
    
    参数:
    url: 要检查的完整 URL
    user_agent: 爬虫的 User-Agent 标识,默认为 '*'(所有爬虫)
    
    返回:
    bool: True 表示允许访问,False 表示禁止访问
    """
    
    # 创建 RobotFileParser 对象
    rp = robotparser.RobotFileParser()
    
    # 设置 robots.txt 文件的 URL
    # 从目标 URL 中提取域名,构造 robots.txt 地址
    from urllib.parse import urlparse
    parsed_url = urlparse(url)
    robots_url = f"{parsed_url.scheme}://{parsed_url.netloc}/robots.txt"
    
    try:
        # 读取并解析 robots.txt 文件
        rp.set_url(robots_url)
        rp.read()
        
        # 检查指定 User-Agent 是否可以访问该 URL
        can_fetch = rp.can_fetch(user_agent, url)
        print(f"robots.txt 检查结果: {'允许' if can_fetch else '禁止'}")
        print(f"robots.txt 地址: {robots_url}")
        
        return can_fetch
        
    except Exception as e:
        print(f"解析 robots.txt 时出错: {e}")
        print("无法确定是否允许访问,建议谨慎处理")
        return False

def respectful_crawler(url):
    """
    一个遵守 robots.txt 协议的礼貌爬虫示例
    """
    
    # 首先检查 robots.txt
    if not check_robots_txt(url):
        print("根据 robots.txt 协议,此页面不允许爬取")
        return None
    
    # 设置合适的 User-Agent,表明身份
    headers = {
        'User-Agent': 'MyRespectfulCrawler/1.0 (+http://mywebsite.com/bot-info)'
    }
    
    try:
        import requests
        # 添加适当的延迟,避免对服务器造成压力
        time.sleep(1)
        
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            print(f"成功获取页面: {url}")
            return response.text
        else:
            print(f"请求失败,状态码: {response.status_code}")
            return None
            
    except requests.exceptions.RequestException as e:
        print(f"请求过程中发生错误: {e}")
        return None

# 使用示例
if __name__ == "__main__":
    test_url = "https://httpbin.org/html"
    content = respectful_crawler(test_url)

遵守 robots.txt 协议和爬虫道德规范不仅是对网站管理员的尊重,也是确保爬虫长期稳定运行的重要保障。一个负责任的爬虫开发者应该始终将道德规范放在技术实现之前。

1.4 法律风险提示:数据版权、反爬条款与合规建议

主要法律风险

网络爬虫虽然技术上可行,但在实际应用中可能面临多种法律风险:

1. 数据版权问题

  • 受版权保护的内容:文章、图片、视频等内容通常受版权法保护
  • 数据库权利:某些国家对数据库结构和内容提供特殊保护
  • 合理使用原则:即使是公开内容,大规模复制也可能超出合理使用范围

2. 服务条款违反

  • 网站 Terms of Service (ToS):大多数网站的服务条款明确禁止自动化抓取
  • 合同违约:访问网站即视为同意其服务条款,违反可能构成合同违约
  • 技术措施规避:绕过反爬虫技术可能违反相关法律(如美国 DMCA)

3. 计算机犯罪相关法律

  • 未经授权访问:某些司法管辖区将违反 robots.txt 视为未经授权访问
  • 系统资源滥用:高频请求可能被视为拒绝服务攻击
  • 个人信息保护:抓取个人数据可能违反 GDPR、CCPA 等隐私法规

合规建议

为了降低法律风险,爬虫开发者应该遵循以下合规建议:

风险类型合规建议实施方法
版权风险只抓取事实性数据避免复制原创内容,只提取基本事实信息
服务条款仔细阅读 ToS在爬取前检查网站的服务条款
隐私保护避免个人敏感信息不抓取姓名、电话、地址等个人信息
技术合规遵守 robots.txt始终检查并遵守 robots.txt 规则
商业用途获取明确授权用于商业目的时,最好获得网站所有者许可

风险评估清单

在开始任何爬虫项目之前,建议进行以下风险评估:

  1. 目标网站性质:政府网站、新闻媒体、电商平台的风险等级不同
  2. 数据类型:公开数据 vs 私有数据,事实数据 vs 创作内容
  3. 使用目的:个人学习 vs 商业应用,研究分析 vs 直接竞争
  4. 抓取规模:少量页面 vs 大规模全站抓取
  5. 更新频率:一次性抓取 vs 持续监控

合规爬虫示例

下面是一个强调合规性的爬虫框架示例:

python
# 合规爬虫框架示例
import requests
import time
import logging
from urllib import robotparser
from urllib.parse import urlparse

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class CompliantCrawler:
    """
    一个强调合规性的爬虫类
    """
    
    def __init__(self, crawler_name, contact_email, delay=2):
        """
        初始化爬虫
        
        参数:
        crawler_name: 爬虫名称,用于 User-Agent 识别
        contact_email: 联系邮箱,便于网站管理员联系
        delay: 请求间隔(秒),避免对服务器造成压力
        """
        self.crawler_name = crawler_name
        self.contact_email = contact_email
        self.delay = delay
        self.session = requests.Session()
        
        # 设置 User-Agent,包含联系方式
        self.session.headers.update({
            'User-Agent': f'{crawler_name}/1.0 (+mailto:{contact_email})'
        })
    
    def _check_robots_txt(self, url):
        """检查 robots.txt 规则"""
        try:
            rp = robotparser.RobotFileParser()
            parsed_url = urlparse(url)
            robots_url = f"{parsed_url.scheme}://{parsed_url.netloc}/robots.txt"
            
            rp.set_url(robots_url)
            rp.read()
            
            # 检查是否允许当前 User-Agent 访问
            user_agent = self.session.headers['User-Agent']
            can_fetch = rp.can_fetch(user_agent.split('/')[0], url)
            
            if not can_fetch:
                logger.warning(f"robots.txt 禁止访问: {url}")
                return False
                
            return True
            
        except Exception as e:
            logger.warning(f"无法解析 robots.txt: {e}")
            # 无法确定时,采取保守策略
            return False
    
    def _respect_rate_limit(self):
        """遵守速率限制"""
        time.sleep(self.delay)
    
    def fetch_page(self, url, respect_robots=True):
        """
        安全地获取网页内容
        
        参数:
        url: 目标 URL
        respect_robots: 是否遵守 robots.txt(默认 True)
        
        返回:
        dict: 包含状态和内容的字典
        """
        
        # 检查 robots.txt(如果启用)
        if respect_robots and not self._check_robots_txt(url):
            return {
                'success': False,
                'error': 'Blocked by robots.txt',
                'content': None
            }
        
        # 遵守速率限制
        self._respect_rate_limit()
        
        try:
            logger.info(f"正在抓取: {url}")
            response = self.session.get(url, timeout=10)
            
            if response.status_code == 200:
                logger.info(f"成功获取: {url}")
                return {
                    'success': True,
                    'error': None,
                    'content': response.text,
                    'status_code': response.status_code
                }
            else:
                logger.error(f"请求失败 ({response.status_code}): {url}")
                return {
                    'success': False,
                    'error': f'HTTP {response.status_code}',
                    'content': None,
                    'status_code': response.status_code
                }
                
        except requests.exceptions.Timeout:
            logger.error(f"请求超时: {url}")
            return {
                'success': False,
                'error': 'Timeout',
                'content': None
            }
        except requests.exceptions.RequestException as e:
            logger.error(f"请求异常: {url}, 错误: {e}")
            return {
                'success': False,
                'error': str(e),
                'content': None
            }

# 使用示例
if __name__ == "__main__":
    # 创建合规爬虫实例
    crawler = CompliantCrawler(
        crawler_name="EducationalCrawler",
        contact_email="research@example.edu",
        delay=3  # 3秒间隔
    )
    
    # 尝试抓取页面
    result = crawler.fetch_page("https://httpbin.org/html")
    
    if result['success']:
        print("抓取成功!")
        print(f"内容长度: {len(result['content'])} 字符")
    else:
        print(f"抓取失败: {result['error']}")

法律合规是爬虫开发中不可忽视的重要环节。虽然技术上可以绕过各种限制,但负责任的开发者应该始终将法律和道德考量放在首位。在不确定的情况下,最好的做法是联系网站所有者获取明确授权,或者寻找官方提供的 API 接口。