法律和经济科学

法学实证 | 网页结构、网页制作、爬虫原理及实战案例(附代码)

发表时间:2025-06-27 10:13来源:阳李

0.导论

在从事实证研究的过程中,通常需要大量数据作为研究支撑。当数据规模较为庞大时,手动获取显得非常繁琐。即使某个单一网站有需要的所有数据,但数据量可能极其庞大,比如数百万篇司法裁判文书、数十年新闻存档、上万部法律法规、上万篇学术论文等,较高的获取难度成为开展研究的绊脚石。

图片

图片

手动复制粘贴是效率低、耗时长且容易出错的获取方式。爬虫可以自动化、大规模、高效地完成这项工作。实际上,爬虫是一个极为普遍的数据获取方式,例如,谷歌、百度等搜索引擎,就需要爬虫(蜘蛛/机器人)不断抓取整个互联网的页面,随后建立索引库,才能提供搜索服务。

一、网页结构详解和制作

首先来看看上述几个网站的大概结构。

不难发现,网页的内部呈现出较为一致的程式。

当然,如果把网页代码逐级点开,就会发现显得比较混乱和复杂。

大致包括:

1. 文档类型声明(DOCTYPE),也就是<!DOCTYPE html>,作用是告诉浏览器当前文档遵循的是 HTML5 标准,位置则必须是 HTML 文档的第一行。

2.根元素 <html>,包裹整个 HTML 文档。

3. 头部 <head>,包含页面的元数据和资源引用,不直接显示在页面上。关键元素包括字符编码 <meta charset="UTF-8">,视口设置 <meta name="viewport" content="width=device-width, initial-scale=1.0">,标题 <title>页面标题</title>,描述 <meta name="description" content="页面描述">,图标 <link rel="icon" href="favicon.ico">,CSS 样式表 <link rel="stylesheet" href="styles.css">,JavaScript 脚本,其他元数据等等。

4. 主体 <body>,包含所有直接显示在浏览器窗口中的内容。常用结构标签(语义化标签)包括页眉 <header>,主要内容区 <main>,页脚 <footer>等等。

5. 内容元素(位于 <body> 内)。

6. 脚本位置。

为便于理解,可以通过下图来总结展示。

逐渐的,我们可以对网页的基本结构进行总结。

(一)网页的基本结构

1.HTML(骨架):定义网页内容和结构,使用标签组织内容(如<div>, <p>, <h1>)。

示例:

<!DOCTYPE html>

<html>

<head>

    <title>页面标题</title>

</head>

<body>

    <header>网站页眉</header>

    <main>

        <article>主要内容</article>

        <aside>侧边栏</aside>

    </main>

    <footer>页脚信息</footer>

</body>

</html>

2.CSS(外观):控制页面样式和布局,使用选择器定位元素。

示例:

body { font-family: Arial; }

header { background-color: #333; color: white; }

3.JavaScript(行为):实现交互功能,动态修改页面内容。

示例:

javascript

document.querySelector('button').addEventListener('click', function() {

    alert('按钮被点击!');

});

(二)网页制

既然已经熟悉了网页的结构,我们自然可以做一些网页。

先来看一个极简版网页(尽管看起来似乎像一张图片):

它的代码如下:

<!DOCTYPE html>

<html lang="zh-CN">

<head><meta charset="UTF-8"><title>法律实证学习</title><style>body{font-family:sans-serif;max-width:800px;margin:40px auto;padding:20px;background:#f8f9fa}h1{color:#2c3e50;border-bottom:2px solid #3498db;padding-bottom:10px}.resource-list{padding:0;list-style:none}.resource-list li{padding:8px 0;border-bottom:1px solid #eee}.btn{background:#3498db;color:white;border:none;padding:10px 20px;border-radius:4px;cursor:pointer;margin-top:20px}.btn:hover{background:#2980b9}</style></head>

<body>

<h1>法律实证研究</h1>

<ul class="resource-list">

  <li> 法律实证方法导论</li>

  <li> 研究设计与案例解析</li>

  <li> 数据收集与分析技术</li>

  <li> 统计工具在法律中的应用</li>

</ul>

<button class="btn" onclick="alert('开始学习法律实证研究方法!')">开始学习</button>

</body>

</html>


再来看一个复杂版网页

它的代码如下:

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>法律实证研究学习平台</title>

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">

    <style>

        * {

            margin: 0;

            padding: 0;

            box-sizing: border-box;

            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;

        }


        body {

            background: linear-gradient(135deg, #1a2a6c, #2c3e50);

            color: #333;

            min-height: 100vh;

            padding: 20px;

        }


        .container {

            max-width: 1400px;

            margin: 0 auto;

        }


        header {

            text-align: center;

            padding: 30px 0;

            margin-bottom: 30px;

        }


        h1 {

            color: #fff;

            font-size: 3.5rem;

            margin-bottom: 15px;

            text-shadow: 0 2px 10px rgba(0,0,0,0.3);

        }


        .subtitle {

            color: #e0e0e0;

            font-size: 1.3rem;

            max-width: 800px;

            margin: 0 auto;

            line-height: 1.6;

        }


        .content-wrapper {

            display: flex;

            gap: 30px;

            flex-wrap: wrap;

        }


        .panel {

            flex: 1;

            min-width: 400px;

            background: white;

            border-radius: 15px;

            overflow: hidden;

            box-shadow: 0 10px 30px rgba(0,0,0,0.2);

            transition: transform 0.3s ease;

        }


        .panel:hover {

            transform: translateY(-10px);

        }


        .panel-header {

            background: linear-gradient(90deg, #3498db, #2c3e50);

            color: white;

            padding: 25px;

            text-align: center;

        }


        .panel-header h2 {

            font-size: 2.2rem;

            margin-bottom: 10px;

        }


        .panel-header p {

            font-size: 1.1rem;

            opacity: 0.9;

        }


        .panel-content {

            padding: 30px;

        }


        .simple-panel .panel-content {

            background: #f8f9fa;

        }


        .complex-panel .panel-content {

            background: #fff;

        }


        .section-title {

            color: #2c3e50;

            border-bottom: 2px solid #3498db;

            padding-bottom: 10px;

            margin: 20px 0 15px;

            font-size: 1.5rem;

        }


        .features {

            display: flex;

            flex-wrap: wrap;

            gap: 20px;

            margin: 25px 0;

        }


        .feature-card {

            flex: 1;

            min-width: 200px;

            background: white;

            border-radius: 10px;

            padding: 20px;

            box-shadow: 0 4px 8px rgba(0,0,0,0.08);

            border-left: 4px solid #3498db;

            transition: transform 0.2s;

        }


        .feature-card:hover {

            transform: translateY(-5px);

        }


        .feature-card i {

            font-size: 2.5rem;

            color: #3498db;

            margin-bottom: 15px;

        }


        .feature-card h3 {

            color: #2c3e50;

            margin-bottom: 10px;

        }


        .feature-card p {

            color: #555;

            line-height: 1.5;

        }


        .stats {

            display: flex;

            justify-content: space-around;

            margin: 30px 0;

            text-align: center;

        }


        .stat-item {

            padding: 15px;

        }


        .stat-number {

            font-size: 2.5rem;

            font-weight: bold;

            color: #3498db;

            margin-bottom: 5px;

        }


        .stat-label {

            color: #555;

            font-size: 1.1rem;

        }


        .btn {

            display: inline-block;

            background: #3498db;

            color: white;

            padding: 12px 25px;

            border-radius: 50px;

            text-decoration: none;

            font-weight: bold;

            transition: all 0.3s;

            border: none;

            cursor: pointer;

            font-size: 1rem;

            margin-top: 10px;

            box-shadow: 0 4px 6px rgba(0,0,0,0.1);

        }


        .btn:hover {

            background: #2980b9;

            transform: translateY(-3px);

            box-shadow: 0 6px 8px rgba(0,0,0,0.15);

        }


        .btn-outline {

            background: transparent;

            border: 2px solid #3498db;

            color: #3498db;

        }


        .btn-outline:hover {

            background: #3498db;

            color: white;

        }


        .resource-list {

            list-style-type: none;

            margin: 20px 0;

        }


        .resource-list li {

            padding: 15px;

            border-bottom: 1px solid #eee;

            display: flex;

            align-items: center;

        }


        .resource-list li:last-child {

            border-bottom: none;

        }


        .resource-list i {

            color: #3498db;

            margin-right: 15px;

            font-size: 1.2rem;

        }


        .complex-features {

            display: grid;

            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));

            gap: 25px;

            margin: 30px 0;

        }


        .complex-card {

            background: linear-gradient(135deg, #f8f9fa, #e9ecef);

            border-radius: 12px;

            padding: 25px;

            box-shadow: 0 6px 15px rgba(0,0,0,0.08);

            transition: all 0.3s;

            border: 1px solid #dee2e6;

        }


        .complex-card:hover {

            transform: translateY(-8px);

            box-shadow: 0 12px 20px rgba(0,0,0,0.15);

            border-color: #3498db;

        }


        .complex-card h3 {

            color: #2c3e50;

            margin-bottom: 15px;

            display: flex;

            align-items: center;

        }


        .complex-card h3 i {

            margin-right: 10px;

            color: #3498db;

            font-size: 1.5rem;

        }


        .complex-card p {

            color: #495057;

            line-height: 1.7;

            margin-bottom: 15px;

        }


        .progress-container {

            background: #e9ecef;

            border-radius: 10px;

            height: 12px;

            margin: 20px 0;

            overflow: hidden;

        }


        .progress-bar {

            height: 100%;

            background: linear-gradient(90deg, #3498db, #2c3e50);

            border-radius: 10px;

        }


        .chart-container {

            background: white;

            padding: 20px;

            border-radius: 10px;

            margin: 25px 0;

            box-shadow: 0 4px 10px rgba(0,0,0,0.05);

        }


        .chart {

            display: flex;

            align-items: flex-end;

            height: 200px;

            padding: 20px;

            gap: 15px;

        }


        .bar {

            flex: 1;

            background: linear-gradient(to top, #3498db, #2c3e50);

            border-radius: 5px 5px 0 0;

            position: relative;

            min-width: 40px;

        }


        .bar-label {

            position: absolute;

            bottom: -25px;

            width: 100%;

            text-align: center;

            color: #495057;

            font-weight: bold;

        }


        .bar-value {

            position: absolute;

            top: -25px;

            width: 100%;

            text-align: center;

            color: #2c3e50;

            font-weight: bold;

        }


        footer {

            text-align: center;

            color: white;

            padding: 40px 0 20px;

            margin-top: 40px;

        }


        @media (max-width: 900px) {

            .content-wrapper {

                flex-direction: column;

            }


            .panel {

                min-width: 100%;

            }


            h1 {

                font-size: 2.5rem;

            }

        }

    </style>

</head>

<body>

    <div class="container">

        <header>

            <h1><i class="fas fa-balance-scale"></i> 法律实证研究学习平台</h1>

            <p class="subtitle">从基础理论到高级应用,一站式掌握法律实证研究方法与技术,提升法学研究能力</p>

        </header>


        <div class="content-wrapper">

            <!-- 简单版面板 -->

            <div class="panel simple-panel">

                <div class="panel-header">

                    <h2><i class="fas fa-book-open"></i> 基础学习版</h2>

                    <p>适合初学者的法律实证研究入门平台</p>

                </div>

                <div class="panel-content">

                    <h3 class="section-title">核心学习资源</h3>

                    <ul class="resource-list">

                        <li><i class="fas fa-check-circle"></i> 法律实证研究方法导论</li>

                        <li><i class="fas fa-check-circle"></i> 研究设计基础与案例解析</li>

                        <li><i class="fas fa-check-circle"></i> 数据收集入门技巧</li>

                        <li><i class="fas fa-check-circle"></i> 基础统计分析方法</li>

                        <li><i class="fas fa-check-circle"></i> 法律文献检索指南</li>

                    </ul>


                    <h3 class="section-title">学习路径</h3>

                    <div class="progress-container">

                        <div class="progress-bar" style="width: 35%"></div>

                    </div>

                    <p>当前进度:35% - 已完成基础理论部分</p>


                    <div class="stats">

                        <div class="stat-item">

                            <div class="stat-number">12</div>

                            <div class="stat-label">学习课程</div>

                        </div>

                        <div class="stat-item">

                            <div class="stat-number">7</div>

                            <div class="stat-label">实践案例</div>

                        </div>

                        <div class="stat-item">

                            <div class="stat-number">24</div>

                            <div class="stat-label">学习小时</div>

                        </div>

                    </div>


                    <a href="#" class="btn"><i class="fas fa-play-circle"></i> 开始学习</a>

                    <a href="#" class="btn btn-outline"><i class="fas fa-download"></i> 下载资料</a>

                </div>

            </div>


            <!-- 复杂版面板 -->

            <div class="panel complex-panel">

                <div class="panel-header">

                    <h2><i class="fas fa-graduation-cap"></i> 高级研究版</h2>

                    <p>面向专业研究者的法律实证研究综合平台</p>

                </div>

                <div class="panel-content">

                    <h3 class="section-title">高级研究工具</h3>


                    <div class="complex-features">

                        <div class="complex-card">

                            <h3><i class="fas fa-chart-line"></i> 高级统计分析</h3>

                            <p>回归分析、因子分析、结构方程模型等高级统计方法在法律研究中的应用</p>

                            <div class="progress-container">

                                <div class="progress-bar" style="width: 65%"></div>

                            </div>

                            <a href="#" class="btn">查看工具</a>

                        </div>


                        <div class="complex-card">

                            <h3><i class="fas fa-database"></i> 大数据法律研究</h3>

                            <p>利用大数据技术分析裁判文书、法律文献和立法数据</p>

                            <div class="progress-container">

                                <div class="progress-bar" style="width: 45%"></div>

                            </div>

                            <a href="#" class="btn">查看工具</a>

                        </div>


                        <div class="complex-card">

                            <h3><i class="fas fa-network-wired"></i> 法律网络分析</h3>

                            <p>分析法律条文引用网络、司法案例关联网络和法学家合作网络</p>

                            <div class="progress-container">

                                <div class="progress-bar" style="width: 30%"></div>

                            </div>

                            <a href="#" class="btn">查看工具</a>

                        </div>


                        <div class="complex-card">

                            <h3><i class="fas fa-robot"></i> AI法律分析</h3>

                            <p>自然语言处理在法律文本分析、判决预测和合同审查中的应用</p>

                            <div class="progress-container">

                                <div class="progress-bar" style="width: 50%"></div>

                            </div>

                            <a href="#" class="btn">查看工具</a>

                        </div>

                    </div>


                    <h3 class="section-title">研究数据分析</h3>

                    <div class="chart-container">

                        <h4>法律实证研究方法使用频率分析</h4>

                        <div class="chart">

                            <div class="bar" style="height: 80%">

                                <div class="bar-value">42%</div>

                                <div class="bar-label">问卷调查</div>

                            </div>

                            <div class="bar" style="height: 65%">

                                <div class="bar-value">28%</div>

                                <div class="bar-label">案例研究</div>

                            </div>

                            <div class="bar" style="height: 50%">

                                <div class="bar-value">15%</div>

                                <div class="bar-label">实验法</div>

                            </div>

                            <div class="bar" style="height: 95%">

                                <div class="bar-value">68%</div>

                                <div class="bar-label">数据分析</div>

                            </div>

                            <div class="bar" style="height: 40%">

                                <div class="bar-value">10%</div>

                                <div class="bar-label">混合方法</div>

                            </div>

                        </div>

                    </div>


                    <div class="features">

                        <div class="feature-card">

                            <i class="fas fa-book"></i>

                            <h3>法律文献库</h3>

                            <p>超过10万篇法律实证研究文献,涵盖国内外顶级期刊</p>

                        </div>

                        <div class="feature-card">

                            <i class="fas fa-laptop-code"></i>

                            <h3>研究工具集成</h3>

                            <p>SPSS, R, Python等研究工具无缝集成</p>

                        </div>

                        <div class="feature-card">

                            <i class="fas fa-users"></i>

                            <h3>研究社区</h3>

                            <p>与全球法律实证研究者交流合作</p>

                        </div>

                    </div>


                    <a href="#" class="btn"><i class="fas fa-rocket"></i> 启动高级研究平台</a>

                </div>

            </div>

        </div>


        <footer>

            <p>法律实证研究学习平台 © 2023 | 促进法律研究的科学化与实证化</p>

            <p>学术支持:法律实证研究中心</p>

        </footer>

    </div>


    <script>

        // 简单的动画效果

        document.addEventListener('DOMContentLoaded', function() {

            const cards = document.querySelectorAll('.feature-card, .complex-card');


            cards.forEach((card, index) => {

                setTimeout(() => {

                    card.style.opacity = '0';

                    card.style.transform = 'translateY(20px)';

                    card.style.transition = 'opacity 0.5s ease, transform 0.5s ease';


                    setTimeout(() => {

                        card.style.opacity = '1';

                        card.style.transform = 'translateY(0)';

                    }, 100);

                }, index * 100);

            });


            // 进度条动画

            const progressBars = document.querySelectorAll('.progress-bar');

            progressBars.forEach(bar => {

                const width = bar.style.width;

                bar.style.width = '0';

                setTimeout(() => {

                    bar.style.transition = 'width 1.5s ease-in-out';

                    bar.style.width = width;

                }, 500);

            });

        });

    </script>

</body>

</html>


以上述网页结构为基础,就可以开始爬虫原理的介绍了。

二、爬虫原理

爬虫(网络蜘蛛)是自动抓取网页数据的程序,基本流程如下:

1.URL管理:维护待抓取URL队列(通常使用先进先出策略),使用布隆过滤器或哈希表进行URL去重。

2.网页下载:通过HTTP/HTTPS协议获取网页内容,处理请求头(User-Agent, Cookies等),支持重定向和超时处理。

3.内容解析:

HTML解析:使用BeautifulSoup、lxml等库解析DOM树;

动态渲染:通过Selenium/Puppeteer处理JavaScript生成的内容;

数据提取和数据存储。

三、实战:豆瓣电影的网页结构和爬取教程

通过豆瓣电影Top250页面(https://movie.douban.com/top250)详细解析网页结构,并展示如何分析网页元素。

(一)页面整体结构

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <!-- 元数据和资源引入 -->

    <title>豆瓣电影Top250</title>

    <meta charset="utf-8">

    <link rel="stylesheet" href="//.../index.css">

    <script src="//.../jquery.min.js"></script>

</head>

<body>

    <!-- 页面主体 -->

    <div id="wrapper">

        <div id="content">

            <h1>豆瓣电影Top250</h1>


            <!-- 电影列表区域 -->

            <div class="grid_view">

                <ol class="grid_view">

                    <li> <!-- 单个电影项目 --> </li>

                    <li> <!-- 重复25次 --> </li>

                </ol>

            </div>


            <!-- 分页控件 -->

            <div class="paginator">

                <span class="prev">上一页</span>

                <a href="?start=25">2</a>

                <a href="?start=50">3</a>

                ...

            </div>

        </div>


        <!-- 页脚 -->

        <footer>

            <p>© 2005-2023 豆瓣</p>

        </footer>

    </div>

</body>

</html>


(二)关键结构区域分析

1. 单个电影项目结构(核心数据区)

<li>

  <div class="item">

    <!-- 电影海报 -->

    <div class="pic">

      <a href="https://movie.douban.com/subject/1292052/">

        <img src="https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg" alt="肖申克的救赎">

      </a>

    </div>


    <!-- 电影信息 -->

    <div class="info">

      <div class="hd">

        <a href="https://movie.douban.com/subject/1292052/" class="">

          <span class="title">肖申克的救赎</span>

          <span class="title">&nbsp;/&nbsp;The Shawshank Redemption</span>

          <span class="other">&nbsp;/&nbsp;月黑高飞(港)   /   刺激1995(台)</span>

        </a>

      </div>


      <div class="bd">

        <!-- 导演和演员信息 -->

        <p class="">导演: 弗兰克·德拉邦特&nbsp;&nbsp;&nbsp;主演: 蒂姆·罗宾斯 / 摩根·弗里曼...<br>


        <!-- 年份/地区/类型 -->

        1994&nbsp;/&nbsp;美国&nbsp;/&nbsp;犯罪 剧情</p>


        <!-- 评分信息 -->

        <div class="star">

          <span class="rating5-t"></span>

          <span class="rating_num">9.7</span>

          <span>2399589人评价</span>

        </div>


        <!-- 经典台词 -->

        <p class="quote">

          <span class="inq">希望让人自由。</span>

        </p>

      </div>

    </div>

  </div>

</li>


2. 分页控件结构

<div class="paginator">

  <span class="prev disabled">上一页</span>

  <span class="thispage">1</span>

  <a href="?start=25&filter=">2</a>

  <a href="?start=50&filter=">3</a>

  ...

  <a href="?start=225&filter=">10</a>

  <a href="?start=25&filter=" class="next">下一页</a>

</div>

(三)完整爬虫代码设计

import requests

from bs4 import BeautifulSoup


def parse_movie(item):

    """解析单个电影项目"""

    return {

        'title': item.select_one('.title:first-child').text.strip(),

        'rating': item.select_one('.rating_num').text,

        'quote': item.select_one('.inq').text if item.select_one('.inq') else None,

        'detail_url': item.select_one('.pic a')['href'],

        'year': item.select_one('.bd p').text.split('/')[0].strip(),

        'director': item.select_one('.bd p').text.split(':')[1].split('主演')[0].strip()

    }


# 抓取单页数据

url = 'https://movie.douban.com/top250'

response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})

soup = BeautifulSoup(response.text, 'html.parser')


movies = []

for item in soup.select('.grid_view li'):

    movies.append(parse_movie(item))


# 分页处理

next_page = soup.select_one('.next a')

if next_page:

    next_url = url + next_page['href']

三、爬虫的注意事项

爬虫技术本身是中立的。但在进行网络爬虫开发与应用时,必须严格遵守以下法律与技术规范:

首要原则是遵守法律法规和网站的robots协议,该文件明确规定了网站允许爬取的目录和禁止访问的区域,违反此协议可能构成未经授权访问计算机系统;其次需仔细审查网站的服务条款,许多平台明确禁止爬虫行为或要求事先书面授权;在数据采集过程中必须遵守数据保护法规,包括《刑法》《个人信息保护法》等,涉及用户个人信息时需获得明确同意,避免收集身份证号、联系方式等敏感信息,对已公开数据也要评估重新公开的合法性。

技术实施上应遵循善意爬取原则:设置合理的请求间隔(建议≥2秒),避免使用分布式爬虫对服务器造成攻击,伪装正常浏览器的User-Agent而非隐藏爬虫身份,遵守Crawl-delay指令;特别注意知识产权边界,即使数据可爬取,内容仍受著作权保护,大规模复制数据库可能违反《反不正当竞争法》,衍生数据需显著区别于原始数据。