第10讲 爬虫抓到的数据算谁的?数据权属的技术事实与法律事实

0 阅读6分钟

学习目标

跟随架构师律师马原的脚步读完这一讲,你能:

  1. 区分原始数据、结构化数据和分析结果的法律属性
  2. 理解"实质性投资"在司法实践中的含义
  3. 评估自己的数据产品的"增值度"和法律风险

技术原理

数据在技术层面可以分成三层:

Layer 1: 原始 HTML
         ↓ parse
Layer 2: 结构化数据 {title: "...", author: "...", content: "..."}
         ↓ process
Layer 3: 分析结果 "该作者近30天平均阅读量 1500,环比增长 20%"

Layer 1 - 原始 HTML

这是服务器返回的原始字节流。它可能包含 CSS、JavaScript、图片链接、广告代码。法律上,HTML 本身可能受著作权保护(如果其编排具有独创性),但通常不保护。

Layer 2 - 结构化数据

从 HTML 中提取的字段。比如从测试网站文章页提取:标题、作者、发布时间、阅读量、点赞数、标签。这些数据本身通常不享有著作权——它们是事实信息,不是独创性表达。

Layer 3 - 分析结果

对结构化数据进行统计、建模、分析后的输出。比如"2024年测试网站前端领域十大热门话题"、"某技术栈的采用率趋势报告"。这一层通常享有著作权(作为文字作品),也可能构成商业秘密。

关键问题:你在哪一层使用数据,决定了法律风险。


真实判例

大众点评诉百度案

百度抓了大众点评的用户点评,展示在百度地图里。技术上,百度做的是 Layer 1 → Layer 2 的转换:从 HTML 提取点评内容,展示在自己的产品里。

法院认定:这种行为"实质替代了大众点评网向用户提供信息"。用户不需要去大众点评,在百度地图里就能看到全部点评。

赔偿:300万元经济损失 + 23万元合理费用。

抖音诉刷宝案

刷宝 APP 用爬虫抓取抖音的短视频、用户信息、用户评论,直接搬运到自己的 APP 上。技术上也是 Layer 1 → Layer 2,然后原样展示。

法院认定:构成不正当竞争。赔偿500万元。

法院特别指出:

"抖音平台针对上述非独创性数据的收集、存储、加工和传输进行了实质性的投资,因此抖音平台对非独创性数据的集合应该具备合法权益。"

"实质性的投资"是这里的关键词。法院保护的不是"数据"本身,而是"获取和整理数据的劳动"。

微博诉蚁坊案

蚁坊从微博抓取用户数据,用来做舆情分析服务。技术上,蚁坊做的是 Layer 2 → Layer 3:抓取原始数据后,进行分析加工,输出报告。

法院仍然认定构成不正当竞争。但注意:这个案子的判决逻辑和大众点评案不同。蚁坊不是在"原样展示"数据,而是在"利用数据做竞争业务"。法院认为,微博的数据生态是微博投入大量成本建设的,蚁坊直接抓取这些数据去卖钱,是"搭便车"。


法律分析

中国目前没有专门的数据权属法。民法典第127条留了白:"法律对数据、网络虚拟财产的保护有规定的,依照其规定。"——但至今没有"规定"出来。

司法实践中,数据保护主要通过三条路径:

路径一:反不正当竞争法

保护的是"竞争利益",不是"数据所有权"。要点:

  • 原告对数据有实质性投资
  • 数据能给原告带来竞争优势
  • 被告的行为构成"搭便车"或"实质性替代"

路径二:著作权法

如果数据集合的编排具有独创性(比如数据库的筛选、排序、注释),可能构成汇编作品。但大多数爬虫抓取的原始数据不满足独创性要求。

路径三:商业秘密

如果数据是不公开的、有商业价值、原告采取了保密措施,可能构成商业秘密。但爬虫通常抓的是公开数据,商业秘密保护不适用。

2025年修订的《反不正当竞争法》第13条新增:

"经营者不得以欺诈、胁迫、避开或者破坏技术管理措施等不正当方式,获取、使用其他经营者合法持有的数据,损害其他经营者的合法权益,扰乱市场竞争秩序。"

这个新条款把"不正当获取数据"明确列为不正当竞争行为,不再需要用第2条"一般条款"来兜底。


实战输出:数据产品增值度自评表

"""
数据产品增值度自评表
评估你的数据处理到了哪一层,法律风险在哪个档
"""

class DataProductAssessor:
    def __init__(self, product_description):
        self.desc = product_description
        self.layer = 0
        self.risk = 'low'
    
    def assess_layer(self, features):
        """
        评估数据加工层级
        features: 产品功能特征列表
        """
        layer_1_signs = ['原始展示', '全文复制', '镜像', '聚合']
        layer_2_signs = ['结构化', '分类', '标签', '索引']
        layer_3_signs = ['分析报告', '趋势预测', '模型', '算法推荐']
        
        l1 = sum(1 for f in features if any(s in f for s in layer_1_signs))
        l2 = sum(1 for f in features if any(s in f for s in layer_2_signs))
        l3 = sum(1 for f in features if any(s in f for s in layer_3_signs))
        
        if l1 > l2 and l1 > l3:
            self.layer = 1
            self.risk = 'high'
        elif l2 > l3:
            self.layer = 2
            self.risk = 'medium'
        else:
            self.layer = 3
            self.risk = 'low'
        
        return self.layer
    
    def assess_substitution(self, factors):
        """
        评估是否构成"实质性替代"
        factors: {
            'user_stays_on_platform': False,  # 用户是否留在你的平台
            'shows_full_content': True,       # 是否展示全文
            'no_attribution': True,           # 是否标注来源
            'same_service_type': True,        # 是否提供同类服务
        }
        """
        score = 0
        if not factors.get('user_stays_on_platform', True):
            score += 30
        if factors.get('shows_full_content', False):
            score += 25
        if factors.get('no_attribution', False):
            score += 20
        if factors.get('same_service_type', False):
            score += 25
        
        if score >= 70:
            print("🔴 高概率构成'实质性替代'")
        elif score >= 40:
            print("🟠 可能构成'实质性替代'")
        else:
            print("🟡 替代风险较低")
        
        return score
    
    def report(self):
        print(f"\n=== 数据产品风险评估: {self.desc} ===")
        print(f"加工层级: Layer {self.layer}")
        print(f"基础风险等级: {self.risk}")
        
        if self.layer == 1:
            print("\n⚠ 建议:")
            print("  1. 避免全文展示原始内容")
            print("  2. 增加数据加工和分析环节")
            print("  3. 明确标注数据来源")
            print("  4. 考虑转型为'分析服务'而非'聚合平台'")

# 使用示例
assessor = DataProductAssessor('我的数据聚合APP')
assessor.assess_layer(['聚合展示', '全文复制', '结构化存储'])
assessor.assess_substitution({
    'user_stays_on_platform': True,
    'shows_full_content': True,
    'no_attribution': False,
    'same_service_type': True,
})
assessor.report()