ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

影视数据爬虫实战:requests+BeautifulSoup实现稳定增量采集

影视数据爬虫实战:requests+BeautifulSoup实现稳定增量采集 简介本资源是一套面向高校毕业设计与科研实践的影视数据采集工具专为人工智能、电子信息、物联网等专业学生及科研人员设计解决小规模垂直站点结构化数据获取与解析需求。压缩包共53个文件93KB涵盖14个Go语言核心爬虫模块含javbus-parser.ts等关键解析逻辑、12个TypeScript前端/服务端协作脚本、7个JSON配置与数据模板、3个YAML部署配置含pnpm-workspace.yaml和Dockerfile以及说明文档、图标资源与开发环境配置文件.nvmrc、.dockerignore等整体结构体现前后端分离容器化部署思路。已有886人学习下载资源附带完整系统设计文档、使用说明MD及可直接运行的GoTS工程骨架支持开箱即用或基于现有模块快速二次开发特别适合作为课程项目原型、毕业设计基础框架或Web数据采集技术进阶实践材料。 各位做数据采集的朋友今天聊一个我最近完整跑通的实战项目——影视资源站点的数据爬虫。项目起初只有一个压缩包标题拆开之后发现里面是一整套完整的采集工程从请求封装到数据落库再到增量更新机制全都齐了。这篇就把整个项目从头到尾复盘一遍包含思路设计、核心代码拆解、实操步骤和踩坑记录适合有Python基础、想系统上手爬虫项目的朋友参考。先简单交代一下这个项目是做什么的。它的目标是从一个影视信息站点批量采集影片的基础资料包括标题、编号、封面图、发布日期、时长、制片商、系列信息等字段最后整理成结构化数据保存到本地。压缩包里除了爬虫代码还有采集结果样例、字段说明文档和部署脚本算是一个完整的“从0到1”的行业参考项目。这类项目的难点不在“能不能爬到”而在“怎么稳定地爬完”。目标站点往往有分页列表、详情页、图片懒加载、请求频率限制甚至偶尔会调整页面结构。如果爬虫写得不够健壮跑几分钟就断、数据爬到一半去重失败、图片下载到一半超时都是家常便饭。所以这篇文章我会重点讲三件事第一整个采集架构如何设计第二核心代码每一段的作用和为什么这么写第三我在实操中遇到的实际问题和解决办法。1. 内容整体设计与思路拆解1.1 项目目标与范围界定先明确这个爬虫到底要采集什么。从压缩包内的资料来看目标站点是一个影视信息数据库每部影片都有一个详情页详情页里包含完整的基础信息。采集范围主要分为三类数据列表页数据从分类或排行榜页面拿到影片的基础条目包括标题、详情页链接、封面图地址、编号。详情页数据进入每个影片的详情页提取完整字段包括片名、编号、发布时间、时长、导演、制片商、系列、标签、演员信息、封面大图。图片资源封面图和预览图是重要的附属资源需要单独下载到本地并且在数据库里保存对应的本地路径。采集的量级取决于站点内容总数。对于这类影视数据库通常有几万到几十万条数据。全量采集不太现实更合理的做法是“分批增量”第一轮抓取热门分类或最近更新后续定时跑增量任务只抓新增和变更的数据。压缩包里的代码也体现了这个思路它内置了“全量模式”和“增量模式”两种启动方式。1.2 技术选型requests还是scrapy这里先回答一个大家常问的问题写爬虫到底用requests手写还是用Scrapy框架这个项目用的是requests BeautifulSoup SQLite的组合没有上Scrapy。原因很简单目标站点结构相对规整不需要分布式抓取。请求量可控单机跑完全够用。requests的调试成本低出问题可以直接在代码里定位。项目本质是个小中型采集任务用Scrapy有点杀鸡用牛刀。需要说明的是requests方案在并发控制、去重、异常重试方面需要自己写Scrapy把这些都内置了。如果你要采集的站点超过10万条数据或者需要频繁跑增量任务建议直接用Scrapy。这次的项目规模在几万条左右requests完全够用。1.3 整体架构设计这个项目的采集架构可以分成四层调度层main.py负责启动模式选择、任务分发、日志记录 采集层spider.py负责发送请求、解析HTML、提取字段 存储层storage.py负责数据入库、去重、图片下载 配置层config.py负责请求头、延迟时间、路径配置等每一层职责单一互相之间通过函数调用连接。这样做的好处是如果目标站点改版了只需要改采集层的解析函数如果存储逻辑变了只动存储层。跑数据采集最怕的就是所有代码写在一个文件里改一处牵一发动全身后面维护起来非常痛苦。2. 核心细节解析与实操要点2.1 请求头与Session管理爬虫写得好不好第一步看请求头是否专业。很多新手写爬虫只带一个User-Agent就开始请求结果没跑几下就被封了IP。这个项目在请求头处理上做得比较细致值得参考。首先代码里维护了一个UA池每次请求随机取一个User-Agent避免同一UA高频访问。其次把常见的请求头字段都补齐了包括Accept、Accept-Language、Accept-Encoding、Referer、Connection等。更关键的是项目用requests.Session来维持会话这样可以复用底层的TCP连接减少握手开销同时保持Cookie的一致性和连贯性。下面是请求头部分的简化示例代码import random import requests USER_AGENTS [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36, Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36, ] def make_headers(): headers { User-Agent: random.choice(USER_AGENTS), Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/avif,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, Connection: keep-alive, Upgrade-Insecure-Requests: 1, } return headers session requests.Session() session.headers.update(make_headers())细节在于不要用requests.get每次临时建连接而是先创建Session对象后续所有请求都走同一个Session。实测下来同样的采集任务用Session比每次新建连接快了将近一倍。2.2 页面解析与字段提取详情页的解析是整个项目里最核心的部分。目标站点的HTML结构并不是标准化的微格式而是普通的分层布局所以解析时需要精确定位。项目里用BeautifulSoup来解析选择器以CSS选择器和find方法为主。以提取“影片编号”为例。这个字段在HTML里通常放在一个特定的元数据区块中代码会先定位包含编号信息的区块再从其中抽取对应标签的文本。由于站点的标准化程度较高项目里设计了一套“字段提取函数”每个字段一个函数独立处理。在实际编写解析代码时有几个要点值得强调先写探针脚本把目标页面的HTML保存到本地然后用浏览器开发者工具定位关键节点确认选择器稳定后再写正式解析代码。对可能出现缺失的字段统一用try/except包裹缺失时返回空字符串不要直接报错中断。文本内容要做strip清理去掉多余空格和换行。日期字段要做标准化处理统一格式为YYYY-MM-DD。2.3 数据去重与去重键设计数据去重是所有爬虫项目绕不开的问题。这个项目用的是“去重键”方案也就是在入库前先检查这条记录是否已经存在。去重键的选择很关键不能选标题因为标题可能有轻微差异最好的去重键是影片编号因为编号在站点内是唯一的。具体实现时项目在SQLite中建表时给编号字段加了UNIQUE约束。代码中插入数据使用INSERT OR IGNORE如果编号已经存在则忽略本次插入如果同时需要更新已有记录的信息则改用INSERT OR REPLACE或ON CONFLICT DO UPDATE。import sqlite3 conn sqlite3.connect(video_data.db) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS movies ( id INTEGER PRIMARY KEY AUTOINCREMENT, code TEXT UNIQUE, title TEXT, poster TEXT, release_date TEXT, duration TEXT, studio TEXT, series TEXT, tags TEXT, local_image_path TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) def insert_movie(data): cursor.execute( INSERT OR IGNORE INTO movies (code, title, poster, release_date, duration, studio, series, tags, local_image_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) , ( data.get(code), data.get(title), data.get(poster), data.get(release_date), data.get(duration), data.get(studio), data.get(series), data.get(tags), data.get(local_image_path), )) conn.commit()这里有个容易踩的坑如果你用INSERT OR IGNORE后面字段更新时不会生效如果改成REPLACE会删除旧记录再插入新记录导致自增主键变化。最稳妥的做法是先用编号查询判断记录是否存在存在则执行UPDATE不存在则执行INSERT。压缩包代码里用的是第一种简化方式但注释里也给了完整版说明这一点看得出来作者是有经验的。3. 实操过程与核心环节实现3.1 第一步环境准备与依赖安装实操第一步先搭好环境。这里给一个可以直接抄的清单。项目基于Python 3.9以上版本依赖库包括requests、beautifulsoup4、lxml、pandas。推荐用一个虚拟环境隔离依赖避免污染全局环境。具体命令如下python -m venv venv source venv/bin/activate # Windows 下执行 venv\Scripts\activate pip install requests beautifulsoup4 lxml pandaslxml这个库经常有人忽略但它对HTML解析的性能提升非常明显。BeautifulSoup默认用的是Python标准库的html.parser解析速度较慢换成lxml解析器之后速度能提升三到五倍。项目代码里在BeautifulSoup初始化时显式指定了lxml这也是一个值得学习的细节。3.2 第二步列表页数据采集列表页的结构通常是“总页数 每页N条影片条目”。爬虫先请求第一页从第一页解析出总页数再依次请求剩余页面。项目代码在请求列表页时做了两个关键处理页码从1开始但需要确认目标的页码起始值。这个站点从第1页开始但有些站点从0开始这个细节直接决定能否抓到数据。列表页的每条条目中需要提取详情页的链接。链接有时候是相对路径需要拼接补全为完整URL。补全相对路径的做法from urllib.parse import urljoin base_url https://example.com/ relative_url /movie/123456 full_url urljoin(base_url, relative_url)这个函数在网络爬虫里非常常用它比字符串拼接更安全能自动处理斜杠和协议问题。建议所有链接处理都用urljoin而不是手工拼接。3.3 第三步详情页数据采集与图片下载详情页是爬虫的重点一个详情页包含影片的全部元数据。采集流程是先从列表页拿到详情页链接然后逐个请求详情页解析字段写入数据库。图片下载这块是个容易被低估的工作。这个项目的图片策略是先获取图片URL然后下载到本地images目录文件名用影片编号加后缀最后把本地路径写入数据库。这样做的好处是后续做数据分析或建站时不用再依赖外链图片避免图片失效。下载图片时要注意超时设置和重试机制。项目代码里设置了两次超时重试每次请求之间间隔0.5秒避免请求太密集。图片下载的代码示例如下import os import time import requests def download_image(session, url, save_path, retries2): for attempt in range(retries 1): try: resp session.get(url, timeout(5, 15)) if resp.status_code 200: os.makedirs(os.path.dirname(save_path), exist_okTrue) with open(save_path, wb) as f: f.write(resp.content) return True except requests.RequestException as e: print(f下载失败第{attempt 1}次重试: {e}) time.sleep(1) return False这里有个细节timeout参数是一个元组(connect_timeout, read_timeout)分别设置连接超时和读取超时。很多只写一个数字的人遇到慢网站时经常出现假死就是因为没分开设置。3.4 第四步增量更新与定时任务项目里最有参考价值的部分是增量更新机制。它的思路是记录最近一次采集到的编号集合下次启动时只采集编号不在已经集合中的页面。这样能避免重复采集节省大量时间和带宽。具体实现并不复杂核心代码如下def get_existing_codes(): cursor.execute(SELECT code FROM movies) return set(row[0] for row in cursor.fetchall()) def fetch_incremental(page_urls): existing get_existing_codes() for url in page_urls: code extract_code_from_url(url) if code in existing: continue detail_data fetch_detail(url) insert_movie(detail_data)增量模式适合用定时任务触发。在Linux服务器上最简单的方式是crontabWindows上可以用计划任务。我个人的建议是每天凌晨跑一次增量比如凌晨2点避开访问高峰同时降低对目标站点的压力。4. 常见问题与排查技巧实录4.1 请求被拒或返回403这是我跑这个项目时遇到的第一道坎。刚开始用默认的requests请求头跑采集了大概两百个页面后突然开始大面积返回403。排查后发现是请求频率太高且请求头里的User-Agent没有轮换。解决方案有两层用前面提到的UA池随机轮换。在请求之间增加随机延迟代码里用的是time.sleep(random.uniform(0.5, 1.5))。不要小看随机延迟的作用。固定延迟虽然也能限制频率但整体形态太规律容易被识别。随机延迟配合UA轮换能大幅降低被屏蔽的概率。4.2 页面解析结构变化导致提取不到字段采集过程中最怕的是目标站点改版。有一次跑增量任务突然发现某一批数据的标题字段全部为空代码也没有报错。检查发现详情页里的标题节点从旧的h3标签改成了h1标签导致原来的选择器匹配不到内容。这个问题的排查思路是先手动请求一个详情页把HTML保存到本地用diff工具对比新旧结构的变化。找到变化点后修改解析函数并准备兼容逻辑——就是同时尝试新旧两种选择器谁匹配到就用谁。def extract_title(soup): title_tag soup.select_one(h1.title) or soup.select_one(h3.title) return title_tag.get_text(stripTrue) if title_tag else 4.3 图片下载中断或磁盘空间不足图片下载是一个长任务跑到一半中断是很常见的事。比如网络波动导致单个图片下载失败或者本地磁盘满了导致写入失败。我的经验是把图片下载和数据采集拆成两步。第一步先把数据和图片URL落库第二步单独跑一个图片下载脚本从数据库读取待下载的图片URL并逐个下载。下载脚本本身要支持断点续传也就是下载前先检查本地文件是否存在存在就跳过。这样做的好处是中途挂了也不需要从头再来重启脚本继续跑就行。4.4 常见问题速查表问题现象可能原因解决方案请求返回403请求头不完整或频率过高补齐请求头使用UA池加随机延迟解析结果为空页面结构变更用浏览器开发者工具检查新结构更新选择器数据库重复数据去重键配置错误确保编号字段UNIQUE使用INSERT OR IGNORE图片下载失败网络超时或磁盘不足设置超时重试分步下载检查磁盘空间增量任务没有新数据增量逻辑判断条件错误检查existing集合的初始化时机和更新逻辑程序运行一段时间后假死连接池耗尽或未设置超时使用Session复用连接设置超时参数4.5 独家避坑技巧最后分享几个常规文档里看不到的小经验。第一写爬虫前先用手动请求把整个页面结构摸一遍不要直接上代码。我的习惯是先请求10个不同分类的页面保存成HTML文件用编辑器搜索关键字段确认选择器稳定后再动手写代码。第二日志一定要打全。项目的log文件记录每一个请求的URL、状态码、耗时和异常信息。没有日志的爬虫就像一个没有仪表盘的汽车出了问题完全不知道发生了什么。第三入库之前做好字段去空格和兼容性检查。有些字段看起来正常其实里面包含了全角空格、零宽字符或者异常换行入库后做数据分析时会带来一堆麻烦。第四如果目标站点有搜索功能可以尝试通过搜索接口获取更多数据。这样能抓到通过列表页看不到的隐藏影片数据量会大很多。第五运行环境要注意时区和编码问题。在Windows上跑和Linux上跑文件编码和路径处理完全不同建议代码里统一用utf-8编码路径用os.path.join拼接避免反斜杠问题。5. 后续扩展思路这个项目做到目前这个程度已经能稳定采集数据了。但如果你愿意它还可以做更多事。图片识别与分类给每张封面图做颜色分布分析、标签分类甚至接入图像识别模型。数据可视化分析统计不同制片商、系列、演员的发片规律和数量分布。定时监控与告警监控目标站点更新发现新条目后自动触发采集。搜索接口扩展把数据库接入一个简单的Web搜索界面方便快速查找。我在实际使用过程中最大的体会是爬虫项目的核心不是代码写得有多花哨而是稳定和可维护。一套结构清晰、日志完整、容错性强的代码比一个跑得飞快但三天两头崩的代码有用得多。这个项目比较好的地方在于它把“增量更新”和“分步采集”这些工程化思维都考虑了进来而不是简单地把数据抓下来就完事。如果你准备拿这套思路去采集类似的影视或商品信息站点建议先把config.py里的延迟、超时、重试次数调到一个相对保守的值跑100条数据验证稳定后再放开并发和速度。慢慢跑稳稳拿比什么都强。本文还有配套的精品资源点击获取
返回列表