
前言在网络数据采集领域Scrapy 作为 Python 生态中最成熟、高效的异步爬虫框架凭借高并发、易扩展、模块化的核心优势成为企业级爬虫开发的首选工具。整站分层遍历采集是爬虫开发中最常用的业务场景广泛应用于资讯站点、科普平台、内容门户网站的数据采集工作核心目标是通过层级化的页面解析精准遍历目标栏目下的所有子页面完整提取结构化数据。本文将以整站科普栏目分层遍历采集为核心场景从环境搭建、项目创建、爬虫编写、数据解析到优化扩展进行全流程、深度化的实战讲解。全文采用专家级书面语结合原理剖析、可直接运行的代码案例、命令实操覆盖 Scrapy 爬虫开发的核心知识点帮助开发者快速掌握整站分层采集的核心逻辑与实战技巧。本文涉及的核心依赖库与官方资源如下读者可直接点击超链接获取详细文档Python 官方下载地址Scrapy 官方文档lxml 解析库官方文档Twisted 异步网络框架官方文档PyPI 仓库Scrapy 安装源一、Scrapy 框架核心基础认知1.1 Scrapy 框架定义与核心优势Scrapy 是一个基于 Python 开发的高性能异步爬虫框架采用 Twisted 异步网络引擎处理网络请求无需手动管理多线程 / 协程即可实现高并发数据采集。相较于 requestsbeautifulsoup 组合Scrapy 具备模块化设计、内置数据提取、自动去重、中间件扩展、管道处理等核心能力尤其适合整站大规模、分层级的数据采集任务。针对科普栏目整站采集场景Scrapy 的核心优势体现为支持递归式页面解析完美适配栏目 - 列表 - 详情的三层 / 多层页面结构内置链接提取器可精准筛选目标 URL避免无效请求异步请求处理大幅提升整站采集效率模块化架构可轻松扩展数据清洗、会话保持、定时任务等功能。1.2 Scrapy 框架核心组件整站采集适配版在整站分层遍历采集项目中Scrapy 五大核心组件各司其职协同完成采集任务引擎Engine框架核心负责调度所有组件的数据流控制爬虫执行流程调度器Scheduler接收引擎发送的请求对请求进行排队、去重等待下载器执行下载器Downloader执行网络请求下载网页源码是爬虫与目标服务器的交互核心爬虫Spiders自定义解析逻辑提取页面数据、生成新的请求是分层遍历的核心实现组件管道Item Pipeline接收爬虫提取的数据进行清洗、验证、存储等后续处理。1.3 整站科普栏目分层采集核心逻辑科普类网站的页面结构通常遵循顶级栏目页 → 二级列表页 → 三级详情页的分层架构采集核心逻辑为访问顶级科普栏目首页提取所有二级子栏目链接遍历二级栏目链接提取列表页中的文章详情页链接遍历详情页链接提取标题、正文、发布时间、作者等结构化数据递归执行上述逻辑完成整站科普栏目的全量采集。二、开发环境搭建与项目初始化2.1 环境依赖要求本项目适配的环境版本要求如下兼容 Windows、MacOS、Linux 全平台表格依赖名称最低版本要求作用Python3.8项目运行基础环境Scrapy2.9.0核心爬虫框架lxml4.9.0网页解析依赖库Twisted22.10.0异步网络请求引擎2.2 环境安装步骤打开系统命令行工具执行以下命令完成环境安装全程无需额外配置bash运行# 升级 pip 工具推荐 python -m pip install --upgrade pip # 安装 Scrapy 核心框架自动关联依赖库 pip install scrapy2.9.0安装完成后执行验证命令确认环境搭建成功bash运行# 查看 Scrapy 版本 scrapy version若输出 Scrapy 版本信息代表环境安装完成。2.3 Scrapy 项目创建整站采集项目需遵循 Scrapy 官方规范创建命令行执行以下操作bash运行# 创建项目项目名science_crawler可自定义 scrapy startproject science_crawler # 进入项目根目录 cd science_crawler项目创建完成后默认目录结构如下核心文件已标注作用plaintextscience_crawler/ ├── science_crawler/ # 项目核心配置目录 │ ├── __init__.py │ ├── items.py # 定义数据结构字段 │ ├── middlewares.py # 中间件配置文件 │ ├── pipelines.py # 数据处理管道 │ ├── settings.py # 全局配置文件 │ └── spiders/ # 爬虫文件存放目录 │ └── __init__.py └── scrapy.cfg # 项目部署配置文件2.4 数据结构定义items.py在整站科普栏目采集中需要提前定义需要提取的结构化数据字段。打开items.py文件编写代码如下python运行# Define here the models for your scraped items import scrapy class ScienceCrawlerItem(scrapy.Item): 科普栏目数据结构定义 字段栏目名称、文章标题、发布时间、文章作者、文章正文、详情页链接 # 所属科普栏目名称 column_name scrapy.Field() # 文章标题 article_title scrapy.Field() # 发布时间 publish_time scrapy.Field() # 文章作者 article_author scrapy.Field() # 文章正文内容 article_content scrapy.Field() # 文章详情页URL article_url scrapy.Field()代码原理scrapy.Item是 Scrapy 提供的数据容器类用于标准化存储采集到的数据。通过定义scrapy.Field()声明字段确保爬虫提取的数据格式统一便于后续管道处理与数据存储。该设计实现了数据结构与解析逻辑的解耦符合模块化开发规范。三、全局配置优化settings.py全局配置是整站采集的核心合理配置可提升爬虫稳定性、避免被目标服务器封禁。打开settings.py文件替换为以下配置代码python运行# 项目名称 BOT_NAME science_crawler # 爬虫文件路径 SPIDER_MODULES [science_crawler.spiders] NEWSPIDER_MODULE science_crawler.spiders # 整站采集核心配置 # 1. 禁用 robots.txt 协议科普站点通常允许爬虫采集根据目标站点调整 ROBOTSTXT_OBEY False # 2. 设置请求并发数整站采集建议 16-32避免过高导致服务器封禁 CONCURRENT_REQUESTS 32 # 3. 设置下载延迟单位秒避免请求过于频繁建议 0.5-1 秒 DOWNLOAD_DELAY 0.5 # 启用随机延迟进一步模拟人工访问 RANDOMIZE_DOWNLOAD_DELAY True # 4. 请求头配置模拟浏览器访问必须配置 DEFAULT_REQUEST_HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9, } # 5. 启用数据管道优先级数字越小执行越早 ITEM_PIPELINES { science_crawler.pipelines.ScienceCrawlerPipeline: 300, } # 6. 启用自动去重避免重复采集页面 DUPEFILTER_CLASS scrapy.dupefilters.RFPDupeFilter # 7. 超时时间配置整站采集避免长时间等待 DOWNLOAD_TIMEOUT 15代码原理ROBOTSTXT_OBEY控制是否遵守目标站点的 robots 协议科普类非商业站点通常无严格限制禁用后可正常采集CONCURRENT_REQUESTS设置异步并发请求数量数值越大采集效率越高需根据服务器承受能力调整DOWNLOAD_DELAY下载延迟用于控制请求频率是反爬应对的核心配置DEFAULT_REQUEST_HEADERS请求头模拟浏览器User-Agent是服务器识别客户端的核心参数缺失会导致请求被拦截ITEM_PIPELINES启用数据处理管道确保爬虫提取的数据能进入清洗、存储流程。四、整站分层遍历爬虫核心开发4.1 爬虫文件创建在项目的spiders目录下创建爬虫文件命令行执行bash运行# 创建爬虫名称为 science_spider允许的域名示例kepu.com起始URL栏目首页 scrapy genspider science_spider kepu.com创建完成后打开spiders/science_spider.py文件编写分层遍历核心逻辑。4.2 分层遍历爬虫核心代码以下代码适配栏目页 → 列表页 → 详情页三层结构支持递归遍历整站科普栏目可直接替换使用python运行import scrapy from science_crawler.items import ScienceCrawlerItem class ScienceSpider(scrapy.Spider): # 爬虫名称启动爬虫时使用 name science_spider # 允许爬取的域名防止爬虫爬取外部站点 allowed_domains [kepu.com] # 起始URL科普栏目顶级首页替换为目标站点实际栏目地址 start_urls [https://www.kepu.com/science/] def parse(self, response): 第一层解析解析顶级栏目页提取所有二级子栏目链接 :param response: 网页响应对象 # 提取二级栏目链接XPath 匹配根据目标站点HTML结构调整 # 示例匹配栏目列表中所有 a 标签的 href 属性 column_links response.xpath(//div[classcolumn-list]/a/href).extract() # 遍历所有二级栏目链接发起请求回调函数解析列表页 for link in column_links: # 拼接完整URL处理相对路径 full_link response.urljoin(link) # 发起请求meta 参数传递栏目名称用于后续数据关联 yield scrapy.Request( urlfull_link, callbackself.parse_list, meta{column_name: self.get_column_name(full_link)} ) def parse_list(self, response): 第二层解析解析二级栏目列表页提取文章详情页链接 :param response: 网页响应对象 # 获取上一层传递的栏目名称 column_name response.meta.get(column_name, 未知栏目) # 提取文章详情页链接XPath 匹配列表页文章链接 article_links response.xpath(//ul[classarticle-list]/li/a/href).extract() # 遍历详情页链接发起请求回调函数解析详情页 for link in article_links: full_link response.urljoin(link) yield scrapy.Request( urlfull_link, callbackself.parse_detail, meta{column_name: column_name} ) # 提取列表页分页链接递归遍历所有列表页核心实现整站全量采集 next_page response.xpath(//a[classnext-page]/href).extract_first() if next_page: full_next_page response.urljoin(next_page) yield scrapy.Request( urlfull_next_page, callbackself.parse_list, meta{column_name: column_name} ) def parse_detail(self, response): 第三层解析解析文章详情页提取结构化数据 :param response: 网页响应对象 # 初始化数据容器 item ScienceCrawlerItem() # 获取栏目名称 item[column_name] response.meta.get(column_name, 未知栏目) # 文章详情页链接 item[article_url] response.url # 提取文章标题XPath 匹配根据实际结构调整 item[article_title] response.xpath(//h1[classarticle-title]/text()).extract_first() # 提取发布时间 item[publish_time] response.xpath(//span[classpublish-time]/text()).extract_first() # 提取作者 item[article_author] response.xpath(//span[classauthor]/text()).extract_first() # 提取正文合并所有正文段落 content_list response.xpath(//div[classarticle-content]//p/text()).extract() item[article_content] .join(content_list).strip() # 提交数据到管道 yield item def get_column_name(self, url): 自定义工具方法从URL中提取栏目名称 :param url: 栏目链接 :return: 栏目名称 if physics in url: return 物理科普 elif chemistry in url: return 化学科普 elif biology in url: return 生物科普 else: return 综合科普代码原理爬虫核心属性name是爬虫唯一标识allowed_domains限制爬取域名start_urls定义采集入口parse 方法默认回调函数负责解析顶级栏目页提取二级栏目链接是分层遍历的起点递归请求机制通过yield scrapy.Request()发起新请求指定回调函数实现栏目页→列表页→详情页的层级跳转分页遍历在列表页解析中提取下一页链接并递归调用parse_list确保遍历所有列表数据数据传递通过meta参数在不同解析方法间传递数据如栏目名称实现数据关联XPath 解析Scrapy 内置支持 XPath 语法精准提取网页元素是网页数据解析的核心方式。4.3 核心解析语法说明XPath 适配整站采集在分层遍历中XPath 是提取链接与数据的核心工具本文使用的核心语法如下表格XPath 语法作用//标签[属性值]/href提取指定属性的链接地址//标签[属性值]/text()提取标签内的文本内容extract()将匹配结果转换为列表extract_first()提取匹配结果的第一个值避免索引报错response.urljoin(link)将相对路径转换为绝对路径解决链接不完整问题原理说明XPath 是一门在 XML/HTML 文档中查找信息的语言Scrapy 基于 lxml 库实现 XPath 解析解析效率远高于正则表达式。在整站分层采集中XPath 可精准定位不同层级页面的目标元素确保链接提取与数据解析的准确性。五、数据管道基础配置pipelines.py为保证采集数据的有效性基础管道实现数据简单清洗与打印验证打开pipelines.py文件编写代码python运行class ScienceCrawlerPipeline: def process_item(self, item, spider): 数据处理核心方法爬虫提交的每条数据都会经过该方法 :param item: 采集到的数据对象 :param spider: 当前运行的爬虫实例 :return: 处理后的数据 # 基础数据清洗去除空白字符、空值替换 for key in item: if item[key] is None: item[key] 暂无数据 elif isinstance(item[key], str): item[key] item[key].strip() # 打印采集数据测试用正式环境可注释 spider.logger.info(f采集成功【{item[column_name]}】-{item[article_title]}) # 返回处理后的数据可后续扩展存储到数据库/文件 return item代码原理process_item是管道的核心方法Scrapy 引擎会将爬虫提取的Item对象自动传入该方法。本文实现了空值替换、空白字符去除的基础清洗逻辑确保数据格式规范。管道支持多阶段处理可扩展数据存储、去重、过滤等功能。六、爬虫启动与整站采集测试6.1 启动命令在项目根目录下执行以下命令启动爬虫开始整站科普栏目分层采集bash运行# 启动爬虫science_spider 为爬虫名称 scrapy crawl science_spider6.2 日志说明启动后命令行会输出实时日志核心日志含义INFO: Spider opened爬虫启动成功INFO: Crawled 200 (referer: None)页面请求成功INFO: 采集成功【物理科普】-量子力学基础科普数据提取成功INFO: Closing spider (finished)整站采集完成。6.3 采集验证爬虫运行过程中会实时打印采集到的栏目名称与文章标题若无报错且持续输出数据说明整站分层遍历采集逻辑正常运行。七、整站采集核心问题解决方案7.1 相对路径链接处理问题目标站点列表页、栏目页的链接为相对路径如/science/physics/直接请求会报错。解决方案使用response.urljoin(link)拼接完整绝对路径本文爬虫代码已内置该逻辑。原理urljoin方法会自动根据当前页面的 URL将相对路径转换为可直接访问的绝对路径适配所有站点的链接格式。7.2 分页遍历失效问题列表页下一页链接无法提取导致仅能采集第一页数据。解决方案打开目标站点列表页按 F12 查看 HTML 结构修正 XPath 语法确认分页标签的属性如next-page、pagination等精准匹配元素。原理不同站点的分页 HTML 结构不同需根据实际页面调整 XPath 匹配规则递归调用列表页解析方法实现全量采集。7.3 数据为空 / 乱码问题文章标题、正文提取为空或出现乱码。解决方案空数据检查 XPath 语法是否匹配目标元素乱码在settings.py中添加编码配置FEED_EXPORT_ENCODING utf-8。原理XPath 匹配错误会导致数据提取失败网页编码不统一会导致乱码UTF-8 是通用编码格式。7.4 爬虫被服务器封禁问题请求失败、返回 403/429 状态码。解决方案增大DOWNLOAD_DELAY延迟时间降低CONCURRENT_REQUESTS并发数更换User-Agent请求头。原理目标服务器会识别高频请求通过降低请求频率、模拟真实浏览器访问可有效避免封禁。八、项目扩展与进阶优化8.1 多栏目并行采集在start_urls中添加多个栏目首页地址即可实现多栏目并行采集代码示例python运行start_urls [ https://www.kepu.com/science/physics/, https://www.kepu.com/science/chemistry/, https://www.kepu.com/science/biology/ ]原理Scrapy 调度器会自动对多个起始 URL 进行排队、并发处理无需额外代码即可实现多栏目并行采集提升整站采集效率。8.2 数据持久化存储在管道中扩展代码将采集的数据存储到 MySQL、MongoDB 或 Excel 文件中示例存储为 JSON 文件python运行import json class ScienceCrawlerPipeline: def __init__(self): # 打开文件以追加模式写入JSON数据 self.file open(science_data.json, a, encodingutf-8) def process_item(self, item, spider): # 数据清洗 for key in item: if item[key] is None: item[key] 暂无数据 elif isinstance(item[key], str): item[key] item[key].strip() # 转换为JSON字符串并写入文件 line json.dumps(dict(item), ensure_asciiFalse) \n self.file.write(line) return item def close_spider(self, spider): # 爬虫关闭时关闭文件 self.file.close()8.3 异常处理优化在请求方法中添加异常捕获避免因个别页面请求失败导致爬虫中断代码示例python运行def parse_list(self, response): # 捕获页面解析异常 try: column_name response.meta.get(column_name, 未知栏目) article_links response.xpath(//ul[classarticle-list]/li/a/href).extract() for link in article_links: full_link response.urljoin(link) yield scrapy.Request(urlfull_link, callbackself.parse_detail, meta{column_name: column_name}) except Exception as e: self.logger.error(f列表页解析失败{response.url}错误信息{str(e)})原理通过try-except捕获异常记录错误日志保证爬虫在遇到个别异常页面时仍能继续执行后续采集任务提升整站采集的稳定性。九、项目规范与合规说明在进行整站科普栏目采集时需严格遵守以下合规规范避免法律风险遵守站点协议优先查看目标站点的robots.txt文件仅采集允许爬取的内容控制请求频率避免高频请求对目标服务器造成压力合理设置下载延迟数据使用合规采集的科普数据仅用于学习、研究禁止用于商业用途隐私保护不采集用户隐私数据、敏感信息仅采集公开的科普内容。十、总结本文完整实现了基于 Scrapy 框架的整站科普栏目分层遍历采集项目从环境搭建、项目初始化、数据结构定义、全局配置到三层页面解析、递归遍历、数据清洗、问题排查覆盖了整站爬虫开发的全流程。核心知识点总结整站分层采集的核心是递归请求 层级解析适配栏目 - 列表 - 详情的通用网站结构Scrapy 框架的模块化设计让爬虫逻辑、数据处理、配置管理相互解耦易于维护与扩展XPath 是网页数据解析的核心工具精准匹配元素是采集成功的关键合理的全局配置与反爬应对策略是保证整站采集稳定性与效率的核心。