ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Python爬虫开发实战:Requests到Scrapy的进阶路线

2026/8/31 4:45:38 拓冰建站 浏览量
Python爬虫开发实战:Requests到Scrapy的进阶路线 简介本资源是面向Python初学者与爬虫入门者的系统性学习套件聚焦Requests与BeautifulSoup两大核心库的实战应用解决从网页请求、HTML解析到数据持久化的完整爬虫开发链路问题。压缩包共208个文件含94个.py源码涵盖基础爬取、反爬绕过、动态页面处理等、50个.xml配置与测试用例、14个.txt说明文档含环境配置、运行指引与注意事项以及.pptx教学课件、.csv示例数据、Dockerfile容器化部署脚本等整体87.17MB结构清晰、模块分明便于按章节循序渐进实践。已有143人下载学习配套代码均来自《Python爬虫开发从入门到实战》教程包含贴吧爬取、动态渲染页面抓取、多线程调度、结果存入CSV/数据库等典型场景且附有chromedriver驱动与scrapy项目模板兼顾requests轻量开发与scrapy工程化拓展需求。 拿到这套《Python爬虫开发从入门到实战》的配套源码时我第一反应是“终于有一套不那么敷衍的学习项目了”。市面上打着爬虫旗号的资料很多但大多是零散的代码片段要么只讲Requests怎么发请求要么只丢一个Scrapy模板让你自己猜。而这套压缩包里的内容从Requests到BeautifulSoup再到Scrapy三块内容刚好覆盖了爬虫学习最核心的一条主线写脚本、解析页面、上框架。我完整跑了一遍代码能直接运行目录结构也很清晰适合两类人一是刚学完Python基础、想找实战项目练手的初学者二是写了不少零散爬虫脚本、想系统梳理Requests、BeautifulSoup、Scrapy技术栈的进阶开发者。这篇博文我结合这套源码里最有价值的部分把爬虫开发中的关键细节、踩坑记录和设计思路重新梳理一遍。1. 项目内容拆解一套爬虫学习的完整路线这个项目最值得肯定的一点是它不是“代码堆砌”而是按照学习路径设计的三阶段结构。打开压缩包你会看到三个主要目录分别对应Requests、BeautifulSoup、Scrapy每个目录下都有配套的示例代码和练习案例。这种组织方式本质上是在帮你建立爬虫开发的整体认知框架而不是只教你记住几个API。1.1 三块核心内容分别解决什么问题先看Requests部分。这个库解决的是爬虫最底层的需求把HTTP请求发出去拿到服务器返回的响应。这套源码里不仅演示了基础的GET和POST请求还重点展示了如何设置请求头、如何处理Cookies、如何管理会话。很多初学者会忽略这些认为只要能用requests.get()拿到页面就够了但实际上没有正确的请求头很多网站根本不会正常返回数据甚至可能直接拒绝连接。BeautifulSoup部分解决的是解析问题。Requests拿到的是HTML源码是一大段夹杂着标签、样式和脚本的文本而BeautifulSoup的核心能力就是从这堆文本里精准提取出你关心的数据。源码里用了大量案例演示find()、find_all()、select()这些方法的使用场景还对比了不同的解析器比如html.parser和lxml在性能上的差异。这些细节在实际项目中直接影响开发效率。Scrapy部分则是把前两个阶段的能力整合成了工程化框架。它不是一个单纯的请求库或解析库而是一个完整的爬虫框架自带调度器、下载器、爬虫中间件、Item Pipeline等组件。源码里的Scrapy示例清晰地展示了如何创建项目、定义Item数据结构、编写Spider逻辑、设置Pipeline进行数据存储。到了这个阶段你写的就不只是一段脚本而是一个可以长期运行、可维护、可扩展的爬虫项目了。1.2 从“能跑”到“能扛”的学习路径设计我特别欣赏这套源码对学习路径的编排顺序。它先让你用Requests写一个几十行的脚本能爬下单个页面的数据然后加上BeautifulSoup把数据结构化地提取出来并保存到文件最后升级到Scrapy通过框架管理请求调度、去重、并发和异常处理。这个顺序背后的逻辑是先理解HTTP请求的本质再学习数据解析的思路最后才引入框架解决工程化问题。如果你一上来就学Scrapy面对项目里自动生成的多个文件、各种配置项和中间件机制很容易产生挫败感。而按这套源码的顺序走一遍每个阶段的代码量都是可接受的你会在跑通一个个小案例的过程中建立信心并且自然地理解“为什么要用框架”——因为当你同时要管理几百个URL、处理请求失败重试、将数据写入数据库时手写循环已经不够用了。从“能跑”到“能扛”这中间缺的不是更多API而是工程思维。这套源码最有价值的地方正是用三个阶段把这种思维转变完整地演示了一遍。2. Requests库实战要点写好爬虫的地基在爬虫开发里Requests的重要程度相当于房子的地基。地基打不好后面解析做得再漂亮也白搭。这套源码里的Requests案例把几个最关键的实战点都覆盖到了我逐一展开说说。2.1 请求头与会话管理别裸奔着访问网站很多新手写爬虫代码是这么写的import requests url https://example.com/data resp requests.get(url) print(resp.text)这段代码在访问一些小网站时也许能跑通但访问稍微正规一点的站点大概率会得到一个403或者跳转到验证页面。原因很简单你的请求没有带上浏览器标识User-Agent服务器一眼就能识别出这不是正常的浏览器访问。源码里的示范是在请求前显式构造headers并传给请求方法import requests 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,en;q0.8 } resp requests.get(https://example.com/data, headersheaders, timeout10)这里值得强调两点。第一User-Agent必须尽量真实最好使用当前主流浏览器的完整UA字符串。但UA也不是万能的有些网站还会校验更多Header字段比如Sec-Fetch-*系列。我的经验是当普通UA无法通过时用浏览器开发者工具复制一份完整的请求头过来一般能解决大部分拦截问题。第二timeout参数一定要设置。如果不设置当某个请求长时间无响应时你的程序会一直挂在那里导致整个爬虫卡死。设置超时后配合异常处理机制可以在请求失败时快速进入重试或跳过逻辑。源代码里还演示了Session的用法这一点容易被初学者忽略。如果一个网站要求你先登录再访问数据或者需要维持某种登录状态你就需要使用requests.Session()来维持会话import requests session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 ... }) # 先登录假设是表单登录 login_data {username: your_account, password: your_password} session.post(https://example.com/login, datalogin_data) # 后续请求自动携带登录Cookie resp session.get(https://example.com/private_data) print(resp.text)使用Session后服务器返回的Set-Cookie会被自动保存并在后续请求中自动携带。这就解决了“登录后抓数据”的核心问题比手动从响应里提取Cookie再拼接到header里要方便得多也可靠得多。2.2 超时、重试与429状态码处理爬虫开发中有一个绕不开的问题请求访问频繁了服务器会不开心。最典型的表现就是你突然收到一个429状态码意味着请求过多。源码里的案例特意演示了如何处理这个问题。核心思路是两点控制请求频率和设置重试策略。控制请求频率最简单的方法是使用time.sleep()import time import random for page in range(1, 11): url fhttps://example.com/list?page{page} resp requests.get(url, headersheaders, timeout10) # 处理数据... time.sleep(random.uniform(1, 3))这里使用随机延时而不是固定延时是因为固定间隔的请求更容易被识别为机器行为。随机延时让请求节奏看起来更接近人工操作。重试策略方面源代码演示了一种基础但有效的做法捕获异常后等待一段时间再重试同时设置最大重试次数避免无限循环import time max_retries 3 for attempt in range(max_retries): try: resp requests.get(url, headersheaders, timeout10) if resp.status_code 429: retry_after int(resp.headers.get(Retry-After, 5)) print(f触发429等待{retry_after}秒后重试...) time.sleep(retry_after) continue resp.raise_for_status() break except requests.RequestException as e: print(f请求异常{e}) time.sleep(2)如果你有多个请求要批量执行不建议使用简单的for循环串行请求那样效率很低。更好的方式是使用concurrent.futures线程池配合每线程独立的Session实例。不过在并发场景下必须更严格地控制整体QPS每秒请求数否则429会来得更快。我的实践做法是在线程池任务里加一个Token Bucket限流器确保全局请求速率稳定在合理范围内。一些更成熟的框架如tenacity可以直接实现重试逻辑但在学习阶段手写重试能够帮你更好地理解“指数退避”这些概念——每次重试间隔加倍而不是固定等待这样既能在短时间内快速重试又不会持续给服务器压力。3. BeautifulSoup解析从HTML里提取数据的正确姿势拿到了HTML接下来就是解析。BeautifulSoup在这套源码里承担的是“数据加工”的角色它把网页中结构混乱的文本整理成结构化数据。这一部分我结合源码里的典型案例聊聊选择器、解析器选型和实战写法。3.1 解析器选型与选择器用法BeautifulSoup支持多种解析器源码对比了html.parser和lxml的性能差异。站在实战角度我建议直接使用lxml解析器因为它的解析速度快容错性也更高。安装方式也很简单pip install beautifulsoup4 lxml使用时指定解析器from bs4 import BeautifulSoup soup BeautifulSoup(html_content, lxml)选择器方面find()和find_all()是核心方法但更高效的写法是用select()传入CSS选择器。比如想提取所有class为news-title的h3标签里的文本titles soup.select(h3.news-title a) for item in titles: print(item.get_text(stripTrue))这段代码的思路是先通过CSS选择器定位到所有目标标签再使用get_text()提取文本内容stripTrue参数自动去除首尾空白字符。选择器的学习关键在于多练习因为真实网页的HTML结构往往嵌套很深、class命名混乱有时候同样信息在页面上出现多次。我的调试习惯是先在浏览器开发者工具里用Elements面板定位目标元素复制它的选择器再放到BeautifulSoup里微调。这套源码里的案例都是这么调试出来的按这个思路来解析效率会高很多。3.2 实战案例用BeautifulSoup解析天气数据结合很多初学者关心的“Python爬虫实现天气预报”需求我特意写了一个贴合源码风格的实战案例。目标是从一个简单天气页面中解析出城市、日期和温度信息。假设页面结构大致如下div classweather-item span classcity北京/span span classdate2025-01-15/span span classtemp8°C/span /div解析代码如下from bs4 import BeautifulSoup import requests def parse_weather(html_content): soup BeautifulSoup(html_content, lxml) weather_list [] items soup.select(div.weather-item) for item in items: city item.select_one(span.city).get_text(stripTrue) date item.select_one(span.date).get_text(stripTrue) temp item.select_one(span.temp).get_text(stripTrue) weather_list.append({ city: city, date: date, temperature: temp }) return weather_list # 实际使用 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) } resp requests.get(https://example.com/weather, headersheaders, timeout10) if resp.status_code 200: data parse_weather(resp.text) for item in data: print(item)这段代码演示了一个完整链路请求页面、解析HTML、提取结构化字段、组装成列表。你拿到weather_list之后可以继续写入CSV、Excel或者数据库。源码里恰好提供了类似的数据保存示例。解析过程中的一个常见坑是目标标签可能不存在直接调用select_one()会返回None接下来调用get_text()就会报AttributeError。稳妥的做法是加空值判断city_el item.select_one(span.city) city city_el.get_text(stripTrue) if city_el else 真实页面里HTML结构的变化、缺字段、多字段的情况时有发生写代码时养成防御性判断的习惯能减少很多调试时间。这套源码里的解析案例也统一采用了这种写法初学者可以重点学习。4. Scrapy框架实战与进阶玩法Requests BeautifulSoup能解决单页面、小规模的爬取任务但当你面临数千上万个URL、需要并发下载、需要自动处理重试、数据需要清洗入库时Scrapy的价值就体现出来了。4.1 Scrapy项目结构与第一个爬虫源码里的Scrapy案例结构非常标准当你用scrapy startproject创建一个新项目后会得到类似这样的目录my_spider/ ├── scrapy.cfg ├── my_spider/ │ ├── __init__.py │ ├── items.py │ ├── middlewares.py │ ├── pipelines.py │ ├── settings.py │ ├── spiders/ │ │ └── example_spider.py理解这个目录结构是使用Scrapy的第一步我来逐一说明。items.py定义数据结构类似Python里的数据类用于声明爬虫要提取的字段。spiders/存放爬虫主体逻辑每个文件定义一个Spider继承scrapy.Spider或CrawlSpider。pipelines.py定义数据管道负责处理Spider提取出的Item比如清洗、去重、存储到数据库。middlewares.py定义中间件用于在请求发送前或响应返回后插入自定义逻辑最常见的用途是更换代理、添加UA、处理验证等。settings.py全局配置包括并发数、下载延迟、User-Agent、是否遵循robots规则等。源码里提供了一个简单的示例Spider核心逻辑是定义start_requests()或start_urls、解析响应并yield提取出的Item。和手写Requests脚本相比Scrapy最大的优势是它帮你把请求调度、失败重试、并发控制都做好了你只需要关心“如何提取数据”。4.2 中间件与动态页面处理在源码的Scrapy进阶部分值得重点学习的是Downloader Middleware的用法。它的执行时机是请求被发送之前和响应被返回之后是挂载代理、修改请求头、处理状态码的统一入口。一个常见的需求是给每个请求随机配置UA中间件的写法大致如下import random class RandomUserAgentMiddleware: def process_request(self, request, spider): user_agents [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..., Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ..., Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) ... ] request.headers[User-Agent] random.choice(user_agents)然后在settings.py里启用这个中间件DOWNLOADER_MIDDLEWARES { my_spider.middlewares.RandomUserAgentMiddleware: 400, }数字400是中间件的执行顺序数值越小越早执行官方中间件默认值大约是500左右设置400可以保证自定义逻辑优先执行。这个细节新手经常忽略结果发现代码没生效其实就是因为执行顺序没调对。再说动态页面处理。很多网站的数据不是直接写在HTML里的而是通过JavaScript动态加载有的还嵌套在iframe里。这类页面Requests拿不到真实数据因为页面先加载一个壳再通过Ajax请求后台接口拿到数据后渲染出来。遇到这种情况首先要做的是用浏览器的开发者工具查看网络请求尝试直接找到数据接口。大多数情况下接口返回的是JSON直接请求接口比渲染整个页面效率高得多也稳定得多。如果确实找不到接口或者数据经过复杂加密那就要考虑渲染方案。源码里提到了Scrapy如何配合Playwright模拟真实浏览器。基本思路是在Downloader Middleware中判断当前请求是否需要浏览器渲染如果需要就调用Playwright打开页面等待一定的网络空闲时间后返回渲染后的页面源码。这样可以绕过部分由JavaScript动态加载带来的解析难题。但需要注意Playwright这类浏览器自动化工具开销很大多开页面会占用大量内存并发设置要适当调低否则机器很容易被拖垮。4.3 Scrapy-Redis分布式扩展这是一套源码中占比不大但含金量很高的部分。当单机爬虫的带宽和处理能力达到瓶颈时就需要把任务分布到多台机器上。Scrapy本身是单机运行的要实现分布式主流方案是引入Scrapy-Redis。它的核心思想是用Redis作为共享的请求队列和去重队列多台机器上的多个Scrapy爬虫共享同一个调度队列谁有空就取一个请求去抓取这样天然就实现了任务的均衡分配。源码里的Scrapy-Redis示例展示的配置大致是SCHEDULER scrapy_redis.scheduler.Scheduler DUPEFILTER_CLASS scrapy_redis.dupefilter.RFPDupeFilter REDIS_URL redis://127.0.0.1:6379中心化的请求队列意味着即使其中一台机器宕机任务也不会丢失因为请求都缓存在Redis里。掌握这套方案后你就能理解大型爬虫为什么会设计成“调度中心 多Worker节点”的架构而Scrapy-Redis正是这套架构的入门实现。建议在学完基础Scrapy后再看这几个文件理解会更顺畅。5. 反爬应对与常见问题排查实录爬虫写了几年我最大的感受是爬虫的难点不在写代码而在处理各种预料之外的状况。这套源码里虽然没有专门的反爬章节但结合案例中遇到的情况我整理了一份高频问题排查清单希望能帮你少踩一些坑。5.1 高频错误速查表以我自己的经验来看初学者最容易遇到下面这些问题我整理了原因和解决方案。问题现象可能原因排查思路与解决方向403 Forbidden服务器拒绝请求通常是没有带UA或头部信息不全补全请求头使用完整浏览器UA与Accept字段429 Too Many Requests请求频率过高触发限流降低并发、增加延时、设置重试机制参考Retry-After响应头504 Gateway Timeout服务器响应超时可能是目标站点本身慢或出口IP不稳定增加超时时间检查请求URL是否正确适当重试返回百度安全验证页面目标站点要求人工验证常见于搜索场景降低频率避免短时间内大量请求考虑是否需要登录态数据解析为空但页面源码里能看到信息数据由JavaScript动态加载不在初始HTML里找数据接口或引入浏览器渲染方案中文乱码页面编码与resp.text默认编码不一致从响应头或HTML meta标签中确认编码设置resp.encoding5.2 我的排查思路和避坑经验第一个要强调的经验是出了问题先分阶段定位。很多初学爬虫的朋友在页面解析不到数据时第一反应是检查BeautifulSoup代码但实际上问题很可能出在请求阶段——页面压根没有正常返回。我建议把“请求”和“解析”当成两个独立环节来调试先打印响应状态码再打印一小段HTML源码确认结构最后才去调解析代码。这套源码的案例里在每个环节都留有print或日志正是为了方便这种分段排查。第二个经验是写爬虫一定要有频率控制意识。很多网站对爬虫的反制并不是立即生效的而是累计到一定量后才触发封禁。等到收到429甚至IP被封时往往已经迟了。合理设置下载延迟、随机延时是保护爬虫稳定运行的最基础手段。在Scrapy里DOWNLOAD_DELAY可以设置全局下载延迟比如设置成1.5秒每个请求之间就会至少间隔1.5秒。再配合CONCURRENT_REQUESTS控制并发数能有效降低被封风险。第三个经验我单独说遇到页面返回安全验证时不要试图脚本化破解。一套成熟的做法是先降低请求频率观察是否能恢复访问如果仍然需要验证考虑是否需要登录账号增加可信度。学习爬虫的核心是技术本身遵守目标网站的使用规则、控制合理频率才能让爬虫开发这条路走得更远。5.3 批量型、增量型与垂直型爬虫的应用场景在源码的案例设计里其实隐藏了三种常见的爬虫应用形态很多人没有注意到。搞清楚这三种形态的差异对理解爬虫架构设计很有帮助。批量型爬虫是最直观的形态一次性把目标数据全部抓下来通常是周期性运行。典型的场景是爬取一个分类下的所有商品、某个网站的历史文章列表。实现上只需要一个循环遍历所有分页URL就可以完成。增量型爬虫解决的是“持续更新”的问题第一次先抓全量数据之后每次运行只抓新增或变化的数据。这种设计在信息流、新闻、论文数据库等场景非常常见。实现核心是记录每次抓取的最新时间点或最新的ID下次从这个断点继续避免重复抓取。源码里虽然没有大规模展示增量设计但在Scrapy部分通过过滤重复URL的机制提到了去重对增量抓取的意义。垂直型爬虫则是面向特定领域、特定网站的专用爬虫只关注某类型的数据比如只爬招聘信息、只爬股票行情、只爬学术论文。垂直型爬虫通常深度依赖目标站点的页面结构一旦页面改版就需要调整解析逻辑。源码里的实战案例基本都属于这一类。理解了这三种形态你在设计自己的爬虫时就会更有数先想清楚自己的需求属于哪一类再决定用脚本还是框架。6. 这套代码还可以怎么扩展拿到一套完整源码学会跑通只是第一步更有价值的是基于它做扩展。以这套代码为基础我建议从以下几个方向继续深入。6.1 数据存储从JSON到数据库源码里的很多示例把结果存储为JSON或CSV文件这在学习阶段完全够用。但在实际项目中数据量一旦大了文件存储查询起来非常低效。你可以把Scrapy Pipeline中的存储逻辑改成写入MySQL或PostgreSQL定义一个存储类在process_item方法中执行INSERT语句。或者使用MongoDB这类文档数据库爬虫抓取的结构化数据天然适合以JSON文档形式存储。这个扩展能帮你把爬虫能力与后端开发能力衔接起来。6.2 爬虫调度与监控如果希望爬虫每天定时运行最简单的是用操作系统的cron计划任务Linux/macOS或任务计划程序Windows。但这种方法维护成本高没有运行状态可视化的能力。更进阶的做法是使用调度平台例如Apache Airflow或更轻量的方案把爬虫封装为可调度的任务节点配合失败告警比如钉钉/微信通知这就是一个接近生产环境的爬虫系统雏形。源码里对这部分没有涉及但这是工程化的必经之路。6.3 从学习项目到工程级爬虫的差距最后说一下我的个人看法。在这套源码里学到的东西能让你成为一个“会写爬虫的开发者”但距离“能维护大型爬虫系统”还有一段距离。工程级爬虫除了抓取和解析还需要考虑代理池管理、验证码识别策略在这里我只提防御思路不展开破解方法、任务调度、日志监控、异常告警等模块。这些并不是单点技术而是一套系统工程。我的建议是先把这套源码里的代码彻底吃透——每行代码都看得懂、能复现、知道为什么这么写然后再挑一个真实的小型网站独立完成一个从分析到部署的完整爬虫项目。这个过程走下来你才算真正入了爬虫开发的门。最后再分享两个实操细节。第一写爬虫时不要在一个文件里堆所有逻辑尽量把请求、解析、存储拆分成不同函数或模块这样出了问题好定位也方便复用。第二我建议始终维护一个config.py或配置文件把Headers、目标URL、延时范围、存储路径这些可变参数统一放在一处而不是散落在代码各处改起来会非常省心。这套源码的目录组织方式值得模仿但如果你要接手自己的项目在它基础上进一步细化你会收获更多。本文还有配套的精品资源点击获取