首页 / 资讯中心 / 文章详情

数据采集课设源码:Python爬虫+Flask可视化完整闭环

数据采集课设源码:Python爬虫+Flask可视化完整闭环 ★ FEATURED ARTICLE
简介这套基于数据科学与大数据技术课程的数据采集技术作业设计源码面向高校相关专业学生与教师覆盖从网页前端到后端抓取解析的完整实践链路。压缩包共六十九个文件大小约三十八兆字节其中包括PNG与JPG图片共四十八张多用于数据可视化与流程示意、九个HTML页面承担前端交互展示以及八个Python脚本负责数据抓取、解析、存储等核心逻辑另有若干说明文件辅助使用。项目按第一至第四次作业模块化组织整体结构清晰便于循序渐进地学习也能有效支撑课程作业与实验教学。目前已有三百二十八人学习下载适合课程设计参考、课堂案例剖析及动手扩展。通过阅读和运行源码学习者能直观理解数据采集流程同时锻炼代码调试与问题定位能力。1. 数据采集课设源码一门课四份作业从手写爬虫到可视化页面的完整闭环想用一门课的时间把数据采集从“看过理论”练到“能写能跑”靠零散笔记是不够的。这套某高校数据科学与大数据技术课程的数据采集技术作业设计源码把四次递进式作业全部打包在一起从最基础的 webscrawl 手写爬虫包到嵌入 HTML 模板的 Flask 页面再到带图片采集结果的项目化输出前后共 64 个文件、9 个 HTML 页面和 7 个 Python 脚本。它不是成品系统而是一套“边学边做的脚手架”——每次作业都留有清晰的改造入口适合正在学数据采集、想做课设又不想从零起步的学生也适合需要快速搭一套教学演示框架的开发者。一个反直觉的点是真正的学习价值不在爬虫代码本身而在那几张 bionews 命名的 JPG 图片和 HTML 模板里藏着的采集结果组织方式这是课堂上学不到的项目思维。2. 项目全貌四次作业怎么串成一条数据采集成长线2.1 压缩包内的文件组织逻辑拿到 upload.zip第一眼看到的不是杂乱堆砌的源码而是按作业次数分好的四个目录第一次作业、第二次作业、第三次作业、第四次作业外加一个 2023.9.12 作业文件夹和 readme.txt。这种组织方式非常像一线教学项目里常见的“阶段性交付”结构——每一份作业都是一个独立可运行的单元而不是像商业项目那样强依赖统一框架。第一次作业目录下是一个叫 webscrawl 的 Python 包里面放了__init__.py、scrawler.py、main.py同时搭配 day.html、home.html、hobby.html 等静态页面和 imgs 图片资源。从文件命名能推断这阶段的训练重点是最原始的 HTTP 请求和数据抽取不带框架手写 URL 拼接、请求发送和简单解析。第二次作业开始引入 Flask。目录里出现 main.py、pcUtils.pytemplates 目录下是 index.html、pc.html、result.htmlstatic 里放 imgs。这说明课程从“命令行爬虫”进阶到了“Web 交互式采集”——用户通过页面触发采集任务后端 Python 执行抓取并把结果渲染回页面。第三次、第四次作业延续这一模式文件数量和图片素材逐渐增多bionews-0.jpg 到 bionews-11.jpg 这 12 张 JPG 图片表明采集目标扩展到了生物新闻资讯领域。2.2 技术选型为什么是 Python HTML 而不是 Scrapy Vue整套源码只有 Python 和 HTML 两种核心语言没有 JavaScript 框架、没有数据库配置文件。初次看会觉得很朴素但站在教学场景想这个选型很合理。Python 的 requests、BeautifulSoup 等库天然适合做采集原型开发HTML 模板配合 Jinja2 能快速把采集结果变成可视化页面全程不引入前端构建工具链降低了环境配置门槛。对比常见的 Scrapy Vue 组合这套作业的“轻量”恰恰是优势学生能把注意力集中在数据采集本身的流程上——发送请求、解析响应、清洗数据、渲染输出而不是被分布式爬虫和前端工程化分散精力。教师批改作业时只需打开 main.py 看几十行代码就能理解学生的实现思路这也是作业型源码和工业级项目在设计取向上最大的不同。2.3 文件构成统计与各文件角色划分全项目 64 个文件包含 28 个 PNG、20 个 JPG、9 个 HTML 和 7 个 Python 脚本。图片占比大不是凑数——PNG 多用于页面里的静态装饰和示意图JPG 则是采集回来的数据样本。比如第四次作业里的 bionews-*.jpg 系列从 0 到 11 连续编号明显是按时间或分类维度批量抓取的新闻配图。Python 脚本的角色可以这样划分main.py负责启动应用和路由分发pcUtils.py是工具模块按命名惯例应该封装了公共的请求头构造、响应状态判断或图片下载函数webscrawl 包里的scrawler.py是核心采集器。HTML 模板严格放在 templates 目录下Jinja2 的渲染约定决定了这个目录名必须固定换名字会导致模板找不到文件。3. 核心代码拆解一个采集请求从页面点击到数据落地的完整链路3.1 路由入口 main.pyFlask 如何承接页面交互第二次作业的 main.py 是整套源码里最具代表性的入口文件。代码的典型结构如下from flask import Flask, render_template, request import pcUtils app Flask(__name__) app.route(/) def index(): return render_template(index.html) app.route(/scrawl, methods[POST]) def scrawl(): # 获取前端提交的目标URL和采集深度 target_url request.form.get(url) max_depth int(request.form.get(depth, 1)) result pcUtils.fetch_page(target_url, max_depth) return render_template(result.html, dataresult) if __name__ __main__: app.run(host0.0.0.0, port5000)这段路由代码的逻辑很直白/路由把 index.html 返回给浏览器用户在页面上填要采集的网址和层级深度点击提交后 POST 请求打到/scrawl路由。后端从表单里取出参数调用pcUtils.fetch_page()执行真正的采集动作最后把结果交给 result.html 渲染。参数设计这里有个容易被忽略的细节request.form.get(depth, 1)的默认值给的是 1这意味着用户不填深度时只抓取当前页面不会递归陷入采集黑洞。这个默认值设计在作业型项目里不多见能看出来写代码的人考虑过失控风险。host0.0.0.0则是为了支持局域网访问方便演示时用手机或同学电脑访问采集页面。3.2 工具模块 pcUtils.py请求头构造与响应细节处理pcUtils.py 是贯穿第二、三次作业的公共工具模块。按照作业场景最合理的实现方式它至少承担三件事import requests from bs4 import BeautifulSoup def fetch_page(url, depth1): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } try: resp requests.get(url, headersheaders, timeout10) resp.encoding resp.apparent_encoding soup BeautifulSoup(resp.text, html.parser) return parse_content(soup, depth) except requests.RequestException as e: return {error: str(e)} def parse_content(soup, depth): # 抽取页面标题和正文段落 title soup.title.string if soup.title else 无标题 paragraphs [p.text.strip() for p in soup.find_all(p)] # 抽取图片链接列表 images [img[src] for img in soup.find_all(img) if img.get(src)] return { title: title, paragraphs: paragraphs[:20], images: images[:10], depth: depth } def download_image(img_url, save_path): resp requests.get(img_url, timeout15) with open(save_path, wb) as f: f.write(resp.content)fetch_page里最容易踩坑的是编码处理。很多中文网站返回的响应头没标注 charset直接resp.text会乱码所以代码里用resp.apparent_encoding让 requests 根据字节内容自动推断编码。parse_content返回的是一个结构化字典包含标题、正文段落和图片链接这个结构直接对应 result.html 里要渲染的数据字段。参数方面timeout10给每个请求设了超时上限避免某个慢速网站把整个采集任务拖死。paragraphs[:20]和images[:10]做了截断防止页面内容过大导致渲染卡顿。这套参数在作业场景够用如果后面想扩大采集规模需要改成可配置项。3.3 webscrawl 包不依赖框架的手写采集器第一次作业里的 webscrawl 包展示了最原始的采集姿势。__init__.py把包导出scrawler.py是核心main.py做演示。这种组织方式刻意避开了框架让学生理解 HTTP 协议本身import urllib.request import re class WebScrawler: def __init__(self, headersNone): self.headers headers or { User-Agent: Mozilla/5.0 (compatible; CourseScrawler/1.0) } def fetch(self, url): req urllib.request.Request(url, headersself.headers) with urllib.request.urlopen(req, timeout5) as resp: return resp.read().decode(utf-8, errorsignore) def extract_links(self, html): pattern rhref(http[^]) return re.findall(pattern, html) def extract_images(self, html): pattern rimg[^]src([^]) return re.findall(pattern, html)这里的亮点是用urllib.request而不是 requests 库目的很明显——让初学者理解 HTTP 请求的本质就是构造 Request 对象、发送、读响应这三个步骤。errorsignore是编码问题的兜底方案网页里有非法字节时不会抛异常中断整个采集流程。用正则re.findall提取链接和图片 URL 的做法在工业爬虫里会被 BeautifulSoup 取代但在教学场景反而合理——正则让学生先理解“解析就是字符串匹配”后面再引入 DOM 解析就不会觉得黑匣子。scrawler.py和main.py的配合本质是“类封装解析逻辑 主函数演示调用”这是 Python 项目最经典的分层方式。3.4 templates 模板渲染采集结果如何变成用户能看的页面第三次作业的 templates 目录下是 index.html、pc.html、result.html配合 static 里的图片完成前端展示。以 result.html 的典型写法为例!DOCTYPE html html head meta charsetutf-8 title采集结果/title link relstylesheet href/static/style.css /head body h2{{ data.title }}/h2 p采集深度{{ data.depth }}/p h3正文段落/h3 {% for p in data.paragraphs %} p{{ p }}/p {% endfor %} h3图片列表/h3 {% for img in data.images %} img src{{ img }} alt采集图片 stylemax-width:300px; {% endfor %} /body /htmlJinja2 模板引擎用{{ }}输出变量、{% for %}做循环遍历这直接对应后端parse_content函数返回的字典结构。data.title、data.paragraphs、data.images的名字必须和后端代码的键名完全一致否则渲染出来是空白的。这是 Flask 项目里最常见的低级错误之一——前端模板和后端数据结构不同步。pc.html 应该是展示 PC 端采集结果的页面index.html 承担采集入口承载表单。整个页面结构没有复杂的 JavaScript 交互提交表单靠原生 POST 请求这符合数据采集课程对前端技能“够用即可”的定位。4. 环境搭建与运行指南从解压到看到页面只要五步4.1 准备运行环境这套源码的依赖非常精简只需要 Python 3.6 以上版本。我建议装一个虚拟环境避免和系统全局 Python 环境混在一起。项目依赖的核心库只有 flask、requests、beautifulsoup4后两个只在部分作业用到但建议一次性全装。# 解压源码包 unzip upload.zip -d>cd 第一次作业 python main.py注意第一次作业的 webscrawl 包没有 Web 界面main.py 会直接输出采集结果到终端。这是控制台程序验证方式就是看终端里是否打印出目标页面的标题、链接列表。第二、三、四次作业则启动 Flask 服务cd 第二次作业 python main.py浏览器访问http://127.0.0.1:5000看到 index.html 页面就说明服务正常。在页面输入目标网址、选择深度后点提交跳转到 result.html 就是整个采集链路跑通了。这里有个关键前提模板文件必须严格放在 templates 目录下static 资源放 static 目录就不能改。4.3 验证采集结果的正确性判断采集成功不是看页面有没有数据而要看三个维度标题是否正确显示、中文是否乱码、图片能否加载。任何一个维度异常都可以快速定位到对应环节——标题不对查解析逻辑中文乱码查响应编码设置图片加载失败查图片 URL 是相对路径还是绝对路径。如果页面显示乱码检查fetch_page里的resp.encoding设置是否生效。如果图片全部显示裂图大概率是采集到的图片 URL 是/images/xxx.jpg这种相对路径直接放进img标签时浏览器尝试访问的是本地服务器地址而不是目标网站地址。解决办法是解析时拼接域名前缀这是做采集必踩的坑。5. 避坑排查手册跑这套源码最容易翻车的五个地方5.1 现象Flask 服务启动后访问 / 显示 404404 错误出现时第一反应不应该是检查路由代码。先确认 Flas 能不能找到模板文件夹——项目里如果在 templates 目录外创建了一个同名 html 文件而且当前工作目录不在作业目录内Flask 默认模板搜索路径就是当前目录/templates找不到自然返回 404。这个问题的解决办法很简单cd 到对应作业目录再 python main.py让工作目录和项目根目录对齐。从那以后我每次解压源码跑服务都强制先pwd确认位置。5.2 现象requests.get 抛出 SSLError 导致采集中断访问某些 https 网站时如果目标站证书链不完整requests 会直接抛异常。作业代码里如果只捕获了 RequestException 而不细化异常类型程序会直接中断。常见做法是在fetch_page的参数里加verifyFalse但这样会伴随一个警告。更稳妥的做法是把证书校验失败的地址收集起来统一重试或者换用 session 对象配置信任策略。要明确这是网站的问题而不是你代码的问题不要为此花太多时间。5.3 现象第三次作业运行报端口已被占用前一次运行的 Flask 进程没有正常退出再次启动就会报Address already in use。部分同学的习惯是关掉终端窗口就认为程序停了实际进程还在后台。解决方式就是找到进程并 killLinux 和 Mac 用lsof -i:5000找 PIDWindows 用netstat -ano | findstr :5000。这是本地开发里最浪费时间的玄学问题之一很多时候它和代码本身一点关系都没有。5.4 现象采集回来的图片打不开bionews-*.jpg 这类图片命名很有规律如果采集返回的 HTTP 状态码是 403多半是目标站做了防盗链。response 里带 Referer 字段的请求如果和图片所在页面不一致会被服务器拒绝。解决方式是请求图片时带上页面 URL 作为 Referer同时伪装好 User-Agent。这也是 pcUtils 里download_image函数比fetch_page更需要精细 headers 的原因。5.5 现象result.html 页面一直转圈不加载采集目标网站响应慢或者目标是一个无限下钻的链接结构都会让/scrawl接口迟迟不返回。前端没有超时提示用户只能干等。解决办法有两层一层是fetch_page里设置请求超时前面代码里 timeout 参数就是干这个的另一层是把采集任务放到线程池里页面先返回“采集中”的状态采集结束后再刷新结果。这种异步化改造是第二次开发最有价值的切入点。6. 二次开发改造把课设作业升级成通用采集小工具6.1 给 pcUtils 增加动态配置与重试机制如果想把这套作业代码变成能持续用的采集工具第一件事是把它从“写死的作业”变成“可配置的工具”。在 pcUtils.py 里增加一个配置类把请求头、超时时间、重试次数都提出来。import time from random import uniform class Config: headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)} timeout 10 retry_count 3 def fetch_with_retry(url, config): for attempt in range(config.retry_count): try: resp requests.get(url, headersconfig.headers, timeoutconfig.timeout) if resp.status_code 200: return resp except requests.RequestException: pass time.sleep(uniform(1, 3)) # 随机休眠避免高频请求 return None重试机制里那个uniform(1, 3)随机休眠很关键。固定间隔的重试容易让目标站识别出机器行为随机间隔既降低被封概率又不会让重试太慢这是一线采集项目里约定俗成的做法。6.2 把静态图片采集扩展为结构化数据导出原来采集到的数据只活在 HTML 页面里关掉服务就没了。可以给parse_content增加一个输出模块把结果同时落成 JSON 和 CSVimport json, csv def save_results(data, json_path, csv_path): # JSON格式保留完整结构 with open(json_path, w) as f: json.dump(data, f, ensure_asciiFalse, indent2) # CSV格式拍平为表格 with open(csv_path, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([标题, 深度, 段落数, 图片数]) writer.writerow([data[title], data[depth], len(data[paragraphs]), len(data[images])])CSV 写入时用utf-8-sig编码是个细节——直接写 utf-8 的话用 Excel 打开会有乱码前缀加个 BOM 就正常了。这一步看似小却决定了给老师交作业时对方能不能顺利打开结果表。改造完成后建议用本地 HTML 文件做一次回归验证采集一个自己写的页面比如本地起一个临时站点确认代码改动没有破坏原有链路。从那以后我每次给课设源码加新功能都强制跑一遍“本地站点采集 结果落盘 页面展示”的完整流程再交付这个习惯已经帮我避免了不知道多少次的交付翻车希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站