简介这份PDF面向具备一定Python基础、希望系统掌握网络爬虫设计与实现的开发者与在校学生以股票信息抓取为完整案例讲解从页面采集、解析到数据存储与可视化的全流程。内容围绕Scrapy框架定向抓取、Requests多线程处理URL、BeautifulSoup解析网页展开并覆盖股票网页采集、网页解析、数据挖掘、信息维护与信息浏览五个模块同时给出软硬件环境要求与可行性分析适合作为课程设计或项目实践的参考。资源包共1个PDF文件约1.2MB为期刊论文排版结构完整、便于打印研读。目前已有486人学习下载可帮助读者理清爬虫系统的模块划分与框架选型思路理解增量采集与结构化存储的实现要点并借鉴其技术方案组织自己的爬虫项目。1. 从一份“股票信息采集系统”说起Python 爬虫项目到底能跑出什么很多人第一次接触 Python 爬虫都是从requests.get()加BeautifulSoup抓一个静态页面开始的跑通那一刻挺爽但真要把爬虫做成一个能交付的系统问题就来了数据怎么增量抓、抓回来怎么清洗入库、前端怎么展示、管理员怎么维护。我手上这份“基于 Python 的网络爬虫设计与实现”资源恰好就是一个把爬虫从脚本做成完整系统的样本——它定向抓取股票网页信息用 Scrapy 做采集、Requests 做请求连接、BeautifulSoup 做页面解析数据落到 PostgreSQL再通过 Flask 后端和 Vue 前端做可视化呈现覆盖用户注册登录、大盘走势、个股价格走势、成交量分析、智能推荐以及管理员侧的数据维护。适合两类人想照着跑通一个爬虫全链路的初学者和想看看“爬虫 Web 可视化”怎么拼起来的二次开发者。2. 系统架构拆解五个模块如何串成一条数据链路这个项目最值得先看的是它的模块划分而不是某一段爬虫代码。原文把系统拆成股票网页采集、股票网页解析、数据挖掘、股票信息维护、股票信息浏览五个模块这五个模块其实对应一条完整的数据流水线采集负责“拿回来”解析负责“拆干净”挖掘负责“算出价值”维护负责“管得住”浏览负责“看得见”。理解这条链路后面读代码和改功能才不会迷路。2.1 采集层Scrapy 定向爬取与增量思路采集层用的是 Scrapy 框架做定向抓取。定向的意思是只盯着股票信息相关的 URL不做全网漫游这样既快又不至于把目标站点拖垮。原文提到“快速并且增量地采集股票网页”增量是这里的关键词——股票数据每天都在变全量重爬既浪费带宽又容易触发限制常见做法是用一个“已抓取 URL / 已抓取日期”的记录表做去重只抓新增或更新的页面。下面是一个贴近该项目场景的 Scrapy Spider 骨架我按原文的“定向 URL 存入数据库”思路补全了关键部分import scrapy from datetime import datetime class StockSpider(scrapy.Spider): name stock allowed_domains [example-stock.com] # 定向域名防止爬出边界 start_urls [http://example-stock.com/list?page1] def parse(self, response): # 1. 从列表页提取每只股票的详情链接 for link in response.css(a.stock-detail::attr(href)).getall(): yield response.follow(link, callbackself.parse_detail) # 2. 翻页找到下一页按钮递归跟进 next_page response.css(a.next::attr(href)).get() if next_page: yield response.follow(next_page, callbackself.parse) def parse_detail(self, response): # 3. 详情页字段抽取字段名对齐 gp_info 表 yield { shares_f12: response.css(.code::text).get(), # 股票代码 shares_f14: response.css(.name::text).get(), # 股票名称 shares_f2: response.css(.price::text).get(), # 最新价格 shares_f3: response.css(.change-pct::text).get(),# 涨跌幅度 r_time: datetime.now(), }这段代码的逻辑很直白parse处理列表页一边跟进详情页一边处理翻页parse_detail只负责单页字段抽取。参数上allowed_domains是安全阀防止爬虫顺着外链跑偏start_urls是入口实际项目里通常从配置或数据库读取方便换目标字段名直接对齐数据库表gp_info的列名是为了后面 Pipeline 入库时不用再做一层映射。增量控制一般不在 Spider 里做而是在 Pipeline 或入库前去查“这条记录今天是否已存在”存在就跳过。2.2 解析与请求层Requests 多线程 BeautifulSoup 的分工原文有一处容易让人困惑既说用 Scrapy又说用 Requests 多线程处理 URL。我的理解是Scrapy 负责主采集流程Requests 多线程用于补充场景比如批量校验链接有效性、抓取一些 Scrapy 不方便处理的接口或者对已入库 URL 做二次请求。BeautifulSoup 则负责把 HTML 解析成结构化数据和 Scrapy 的 CSS/XPath 选择器是两条并行的解析路线。import requests from bs4 import BeautifulSoup from concurrent.futures import ThreadPoolExecutor HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 } def fetch_one(url): try: resp requests.get(url, headersHEADERS, timeout5) resp.encoding resp.apparent_encoding # 防止中文乱码 soup BeautifulSoup(resp.text, html.parser) return { title: soup.select_one(h1).get_text(stripTrue) if soup.select_one(h1) else , price: soup.select_one(.price).get_text(stripTrue) if soup.select_one(.price) else , } except Exception as e: return {url: url, error: str(e)} def batch_fetch(urls, workers8): with ThreadPoolExecutor(max_workersworkers) as pool: return list(pool.map(fetch_one, urls))这里几个参数值得说清楚headers里的 User-Agent 是原文强调的“隐蔽身份与伪装”不加很容易被直接拒绝timeout5是必须的多线程下没有超时会导致线程池被慢请求拖死apparent_encoding用来应对目标页编码不统一max_workers8是经验值线程开太多反而会因为目标站点限流而大面积失败。fetch_one里用try/except兜住单条异常保证一条失败不影响整批这是多线程爬虫的血泪经验——不兜异常一个坏链接就能让整批任务崩掉。2.3 数据层三张表撑起采集、详情与用户体系数据库设计是这个项目里比较扎实的部分原文给了三张核心表gp_user用户信息、gp_info股票基础数据、gp_details股票详细数据。gp_info存的是某只股票的当前快照字段包括股票代码shares_f12、名称shares_f14、最新价shares_f2、涨跌幅shares_f3、成交量shares_f5、市盈率shares_f9等gp_details存的是时间序列字段有shares_time、当前价、最高价、最低价、均价、成交量、成交金额用来画走势图gp_user管账号、角色、状态。表名作用关键字段使用场景gp_user用户与权限username、password、Role、State登录注册、角色区分gp_info股票当前快照shares_f12、shares_f14、shares_f2、shares_f3列表展示、智能推荐gp_details股票时间序列shares_time、shares_price、shares_num价格/成交量走势图建表时我一般会把shares_f12股票代码加唯一索引配合r_time做增量判断gp_details上按(shares_f12, shares_time)建联合索引走势图按时间范围查询时才不会全表扫。原文没写索引但这是这个场景下合格开发几乎必做的动作。2.4 展示层Flask Vue 的 B/S 结构系统是 B/S 结构后端 Flask、前端 VueNginx 做 Web 服务器。Flask 在这里的角色是提供 REST 接口登录校验、股票列表、走势数据、管理员增删改查。Vue 负责把接口数据渲染成折线图、堆积图。原文提到“数据处理时间控制在 3 s 内页面加载与响应不超过 2 s”这个指标落到实现上就是接口要做分页、走势查询要带时间范围参数、图表数据尽量在后端聚合好再返回而不是把原始明细全丢给前端算。from flask import Flask, jsonify, request import psycopg2 app Flask(__name__) app.route(/api/stock/trend) def stock_trend(): code request.args.get(code) start request.args.get(start) end request.args.get(end) conn psycopg2.connect(databasestock, userapp, password***, host127.0.0.1) cur conn.cursor() cur.execute( SELECT shares_time, shares_price, shares_num FROM gp_details WHERE shares_f12 %s AND shares_time BETWEEN %s AND %s ORDER BY shares_time, (code, start, end) ) rows cur.fetchall() cur.close(); conn.close() return jsonify([{time: r[0], price: r[1], num: r[2]} for r in rows])接口参数code、start、end都是必传缺一个就返回空前端图表自然空着——这种“宁可空也不全量返回”的约定是保证响应时间的关键。SQL 用参数化查询既防注入也避免拼接字符串出错。3. 把项目跑起来环境、依赖与启动顺序这一章按“先环境、再依赖、后启动”的顺序走照着做基本能跑通。原文给的环境是 Windows 8 及以上或 CentOS 7.4 及以上、Nginx 1.6、Python 2.7、PostgreSQL 9.3。这里要提醒一句原文代码在 Python 3.6 下实现Python 2.7 已经不适合新项目实际搭建建议直接用 Python 3.8 以上Scrapy、Requests、BeautifulSoup 在 3.x 下都更稳。3.1 环境准备与依赖安装先确认 Python 和数据库版本再装依赖。依赖清单按原文用到的库整理如下# 确认版本 python --version # 建议 3.8 psql --version # PostgreSQL 9.3 # 创建虚拟环境并安装依赖 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install scrapy requests beautifulsoup4 flask psycopg2-binarypsycopg2-binary是 PostgreSQL 的 Python 驱动装 binary 版省去本地编译依赖Scrapy 自带 Twisted 等依赖pip 会自动拉取。如果安装 Scrapy 时卡在编译多半是缺少 C 编译工具链Linux 下装build-essential、Windows 下装对应版本的构建工具即可。3.2 数据库初始化与配置建库建表字段按原文三张表来。下面给出gp_info的建表语句其余两张表同理CREATE DATABASE stock; \c stock CREATE TABLE gp_info ( shares_id SERIAL PRIMARY KEY, shares_f12 VARCHAR(525) NOT NULL, -- 股票代码 shares_f14 VARCHAR(525), -- 股票名称 shares_f2 VARCHAR(525), -- 最新价格 shares_f3 VARCHAR(525), -- 涨跌幅度 shares_f5 VARCHAR(525), -- 成交量 shares_f9 VARCHAR(525), -- 市盈率 state VARCHAR(1) DEFAULT 1, r_time TIMESTAMP DEFAULT now() ); CREATE UNIQUE INDEX idx_gp_info_code ON gp_info(shares_f12);state字段用来做软删除管理员“删除错误数据”时改状态而不是物理删方便回溯r_time默认当前时间增量判断直接用它。数据库连接信息建议放配置文件或环境变量不要硬编码在代码里。3.3 启动顺序爬虫 → 后端 → 前端启动顺序有讲究先让数据进来再让接口有东西可查最后前端才有图可画。第一步跑爬虫把数据写进gp_info和gp_details第二步启动 Flask 后端确认/api/stock/trend能返回数据第三步用 Nginx 托管 Vue 打包后的静态文件或者开发阶段直接npm run serve。# 1. 运行爬虫Scrapy 项目根目录下 scrapy crawl stock # 2. 启动 Flask 后端 export FLASK_APPapp.py flask run --host 0.0.0.0 --port 5000 # 3. 前端开发模式 npm install npm run serveNginx 生产部署时把 Vue 打包产物放到静态目录/api反向代理到 Flask 的 5000 端口这样前后端同域省掉跨域配置的麻烦。验证是否跑通就看三件事数据库里有数据、接口返回 JSON、页面图表有曲线。4. 避坑与排查五个真实会卡住你的地方4.1 爬虫跑起来但数据库是空的现象是 Scrapy 日志显示抓到了 item但表里没数据。原因通常是 Pipeline 没启用或者ITEM_PIPELINES配置了但入库代码里连接参数写错。解决方法是先在settings.py里确认 Pipeline 已注册再在 Pipeline 的process_item里加日志打印确认 item 真的走到了入库那一步连接失败时 psycopg2 会抛异常把异常打出来基本就能定位。4.2 中文股票名称变成乱码现象是shares_f14存进去是问号或乱码。原因是目标页编码不是 UTF-8Requests 默认按响应头猜编码猜错就乱。解决是在请求后显式设置resp.encoding resp.apparent_encodingScrapy 里则在 Spider 或中间件里统一处理响应编码。数据库建库时字符集也要用 UTF-8两头都对齐才不会乱。4.3 多线程爬取被目标站点限流或封禁现象是跑一会儿大量请求超时或返回 403。原因是并发太高、请求间隔太短、User-Agent 单一。解决是把max_workers降到 4 到 8请求间加随机延时准备多个 User-Agent 轮换必要时用代理池——但代理这块要合规使用只针对允许抓取的公开数据。原文强调“隐蔽身份与伪装”本质就是让请求看起来像正常浏览器而不是暴力刷。4.4 走势图接口慢页面转圈现象是打开走势分析页要等好几秒。原因是gp_details数据量大查询没带时间范围或没走索引。解决是接口强制要求start、end参数SQL 按(shares_f12, shares_time)联合索引查前端默认只查最近 30 天。原文的“3 秒内”指标靠的就是这种“限制范围 索引 后端聚合”的组合拳。4.5 登录后刷新页面又回到登录页现象是登录成功但一刷新就掉登录态。原因是前端 token 或 session 没持久化或者后端跨域配置导致 cookie 没带上。解决是前端把登录凭证存 localStorage 并在请求头带上后端 Flask 配置好 CORS 的supports_credentialsNginx 反代时保证前后端同域。这类问题排查顺序永远是先看浏览器 Network 里请求带没带凭证再看后端有没有收到。5. 二次开发怎么改从换数据源到加一个新指标拿到这个项目想改成自己的最省力的路径不是重写而是沿着“采集 → 解析 → 入库 → 接口 → 页面”这条链路逐段替换。第一步换数据源改 Spider 的start_urls和allowed_domains把parse_detail里的 CSS 选择器换成新站点的结构字段名保持和gp_info一致这样入库代码一行不用动。第二步加指标比如想加“换手率”在gp_info加一列shares_f8Spider 里多抽一个字段Pipeline 的 INSERT 语句补一列接口和前端表格各加一处展示改动是线性的不会牵一发动全身。验证改动是否成功我习惯用一个小脚本直接查库对账而不是只看页面import psycopg2 conn psycopg2.connect(databasestock, userapp, password***, host127.0.0.1) cur conn.cursor() cur.execute(SELECT shares_f12, shares_f14, shares_f2, r_time FROM gp_info ORDER BY r_time DESC LIMIT 5) for row in cur.fetchall(): print(row) cur.close(); conn.close()跑完看输出代码、名称、价格、时间是否齐全时间是否是刚才抓的。如果时间对但价格空说明选择器写错如果时间不对说明增量逻辑没生效。这个对账脚本比翻日志快得多。再往深一层如果想加“智能推荐”原文只提了功能没给算法常见做法是基于gp_details算近 N 日涨跌幅、成交量变化率按规则打分排序先跑通规则版再考虑模型。改造时有个原则我踩过坑不要一上来就动数据库表结构先在代码层用视图或临时字段验证逻辑确认没问题再落表否则改表容易把已有数据搞乱。从那以后我每次改爬虫项目都强制先跑一遍“抓一条 → 查一次库 → 看一眼接口”的最小闭环再动大结构。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?