十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Python爬虫入门:requests库基础使用与实战指南

Python爬虫入门:requests库基础使用与实战指南 1. 项目概述从零开始理解网络爬虫的基石如果你刚开始接触Python爬虫或者在网上搜索“requests爬虫”时被一堆零散的代码片段和报错信息搞得晕头转向那么你来对地方了。今天我们不谈那些复杂的框架和高级技巧就聚焦在“request基础”这四个字上。这里的“request”通常指的就是Python中那个大名鼎鼎的requests库它是几乎所有Python爬虫的起点和核心。很多人觉得爬虫就是“请求-获取”这么简单但真正上手后却常常卡在“stream disconnected before completion”、“request returned 500”或者“由于触发安全风控策略该次访问请求被拒绝”这类错误上。这恰恰说明打好requests的基础远比盲目复制粘贴代码要重要得多。简单来说requests库就是让你用Python模拟浏览器向网站服务器发送一个“请求”Request然后接收服务器返回的“响应”Response。这个过程就是爬虫最本质的动作。无论是抓取知乎的文章、抖音的视频列表还是采集上市公司的报告数据底层都是这个逻辑。但为什么你的代码会失败为什么同样的网址别人能爬到你却被拒绝核心往往在于你对“请求”这个动作的理解不够深入。本文将带你彻底拆解requests库的基础使用不止是教你写出一行requests.get()更要让你明白这行代码背后发生了什么以及当它出错时你该如何像侦探一样排查问题。无论你是想写一个简单的公众号文章采集脚本还是为后续更复杂的“人狗大作战”这类趣味项目获取数据扎实的requests基础都是你绕不开的第一步。2. 核心需求解析我们到底要用requests做什么在深入代码之前我们必须先想清楚目标。使用requests进行爬虫核心是为了自动化、程序化地获取互联网上的公开数据。这听起来简单但衍生出几个层次的需求理解这些需求能帮你更好地使用工具。2.1 获取数据最基本的“读”操作这是最直观的需求。比如你想获取某个博客首页的HTML内容或者一个公开API返回的JSON数据例如天气信息。对应的就是requests.get()方法。你需要告诉它目标网址URL它帮你把内容拿回来。但这里有个关键点你拿到的是服务器愿意给你的“原始响应”。如果网站需要登录才能看或者数据是通过JavaScript动态加载的一个简单的get可能就拿不到你想要的东西。这就引出了下一个需求。2.2 模拟交互让服务器“以为”你是真人浏览器现代网站充满了交互。登录、点击按钮、翻页、搜索这些操作在浏览器里会附带大量的额外信息给服务器比如Cookies、特定的请求头Headers、表单数据Form Data或JSON参数。requests库的强大之处在于它能高度定制化每一个请求。你需要登录后才能爬取QQ空间或小红书那就先用requests.post()模拟登录保存服务器返回的Cookies在后续请求中带上。你发现直接请求被拒绝提示“安全风控策略”很可能是因为你的请求头太“假”缺少了User-Agent告诉服务器你是什么浏览器等关键信息。模拟一个真实的浏览器会话是绕过基础反爬机制的关键。2.3 处理响应从一堆字节中提取有效信息成功拿到响应Response对象后工作才完成一半。响应里包含状态码200表示成功404表示找不到500表示服务器内部错误、响应头包含了内容类型、编码等信息和最重要的响应体就是你想要的HTML或JSON数据。你需要正确地解码这些数据特别是处理中文可能遇到的乱码问题然后根据内容类型text/html,application/json用不同的方式如response.text获取文本response.json()解析JSON来提取信息。很多新手会在这里踩坑比如对JSON数据用了text或者没处理编码导致中文乱码。2.4 应对异常与错误构建健壮的爬虫网络世界充满不确定性。连接超时、服务器宕机、IP被暂时封锁、请求频率过快被限制……一个健壮的爬虫必须能妥善处理这些异常。requests库内置了异常处理机制比如requests.exceptions.Timeout,requests.exceptions.ConnectionError。你需要学会使用try...except来捕获它们并设计重试、等待、切换代理等策略。看到“error sending request for url”或“connection failed”这类错误时不至于手足无措。3. 环境准备与requests库安装工欲善其事必先利其器。在开始写爬虫之前一个干净、独立的Python环境是高效工作和避免依赖冲突的保障。我强烈建议新手从这一步开始养成良好的习惯。3.1 Python环境搭建与验证首先确保你的系统已经安装了Python。打开终端Windows上是CMD或PowerShellmacOS/Linux上是Terminal输入python --version或python3 --version。如果能看到类似“Python 3.8.10”的版本号说明已安装。我推荐使用Python 3.6及以上版本requests库对新版本支持更好。注意如果你在Windows上同时安装了Python 2和Python 3命令可能是py -3来启动Python 3。在命令行里多试试python、python3、py这几个命令看看哪个能调出Python 3的解释器。如果未安装请前往Python官网python.org下载安装程序。安装时务必勾选“Add Python to PATH”这个选项这能让你在命令行中直接使用python命令省去后续很多麻烦。3.2 使用虚拟环境隔离项目这是很多教程会跳过但极其重要的一步。虚拟环境相当于为你的爬虫项目建立一个独立的“工作间”在这个工作间里安装的requests库或其他库不会影响到系统全局或其他项目。这能完美解决“项目A需要requests 2.25项目B需要requests 2.28”的版本冲突问题。创建虚拟环境非常简单。在你的项目文件夹下打开终端执行# 对于Python 3.3可以使用内置的venv模块 python -m venv venv这条命令会在当前目录创建一个名为venv的文件夹里面包含了一个独立的Python环境。接下来激活这个环境Windows (CMD/PowerShell):venv\Scripts\activatemacOS/Linux:source venv/bin/activate激活后你的命令行提示符前面通常会显示(venv)表示你已经进入了虚拟环境。在这个环境下执行的所有pip install操作都只会影响当前项目。3.3 安装requests库在激活的虚拟环境中安装requests库只需要一行命令pip install requestspip是Python的包管理工具。执行后它会自动从PyPIPython官方的软件仓库下载requests库及其依赖如urllib3,chardet,certifi等并安装。为了验证安装是否成功可以启动Python交互界面测试一下python在出现的提示符后输入import requests print(requests.__version__)如果没有报错并且打印出版本号如2.28.1那么恭喜你requests库已经准备就绪。实操心得我习惯在项目根目录下创建一个requirements.txt文件里面写上requests2.25.1。这样以后在任何新环境比如部署到服务器中只需要运行pip install -r requirements.txt就能一键安装所有指定版本的依赖非常方便。这是管理项目依赖的行业通用做法。4. requests库核心方法与参数全解安装好环境我们终于可以进入正题。requests库的API设计非常优雅最常用的就是get(),post(),put(),delete()这几个方法对应HTTP协议的几种主要请求方式。其中get()和post()在爬虫中占据了99%的使用场景。理解它们的每一个关键参数是你从“能用”到“精通”的必经之路。4.1 GET请求获取资源的主力军requests.get()用于向指定URL请求数据参数附加在URL之后。它的基本形式是import requests response requests.get(https://www.example.com)但这行简单的代码背后你可以通过参数进行精细控制。核心参数详解params (字典或字节序列):用于构造查询字符串URL中?后面的部分。这是GET请求传递参数的标准方式。payload {key1: value1, key2: value2} r requests.get(https://httpbin.org/get, paramspayload) print(r.url) # 输出https://httpbin.org/get?key1value1key2value2为什么用它它比手动拼接URL更安全、更清晰requests会自动处理特殊字符的编码比如空格转成%20。headers (字典):定制HTTP请求头。这是模拟浏览器、对抗基础反爬的重中之重。headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, } r requests.get(https://www.zhihu.com, headersheaders)关键点User-Agent是很多网站判断请求来源是程序还是浏览器的首要依据。使用一个常见的浏览器UA字符串能大幅降低被直接拒绝的风险。你可以通过浏览器的开发者工具F12 - Network - 点击一个请求 - Headers找到真实的请求头信息进行复制。cookies (字典或RequestsCookieJar):发送Cookies。通常用于在已登录的状态下访问需要权限的页面。cookies {session_id: 123456abcde} r requests.get(https://www.weibo.com, cookiescookies)注意更常见的做法是通过一个requests.Session()对象来保持会话它会自动管理Cookies后面会详细讲。timeout (浮点数或元组):设置请求超时时间。这是必须设置的参数否则你的程序可能会因为一个无响应的服务器而永远挂起。# 连接超时和读取超时都设为5秒 r requests.get(https://www.example.com, timeout5) # 分别设置连接超时和读取超时 (连接超时3.05秒 读取超时27秒) r requests.get(https://www.example.com, timeout(3.05, 27))经验之谈对于不稳定的网站或网络环境设置一个合理的timeout如10秒并配合重试机制是保证爬虫稳定运行的基础。超时是网络爬虫最常见的异常之一。proxies (字典):设置代理服务器。用于隐藏真实IP应对IP访问频率限制。proxies { http: http://10.10.1.10:3128, https: http://10.10.1.10:1080, } r requests.get(https://www.example.com, proxiesproxies)重要提示代理服务器的稳定性和匿名性差异很大需要谨慎选择和使用。公开的免费代理往往速度慢且不可靠。allow_redirects (布尔值):是否允许重定向默认为True。如果设为False请求在遇到3xx状态码时将不会自动跳转而是返回这个状态码的响应。verify (布尔值或字符串):是否验证SSL证书默认为True。对于使用自签名证书的HTTPS网站你可能需要将其设为False但这会带来安全风险。更好的做法是指定一个CA证书包的路径。# 不推荐除非你明确知道风险且目标网站可信 r requests.get(https://internal.example.com, verifyFalse)4.2 POST请求提交数据的关键requests.post()用于向指定URL提交数据通常用于登录、提交表单、上传文件等操作。它的参数大部分与get()相同但多了一个核心参数data或json。核心参数详解data (字典、元组列表、字节或文件对象):发送表单数据application/x-www-form-urlencoded。# 模拟登录 login_data {username: your_name, password: your_pass} r requests.post(https://example.com/login, datalogin_data)这是最常见的POST数据格式对应网页表单的提交。json (字典):发送JSON格式的数据application/json。现代Web API如很多手机App的后台接口广泛使用JSON。api_data {query: Python, page: 1} r requests.post(https://api.example.com/search, jsonapi_data)使用json参数requests会自动将字典序列化为JSON字符串并设置请求头Content-Type: application/json。这比手动用json.dumps()处理再放入data中要方便和安全得多。files (字典):上传文件。files {file: open(report.xls, rb)} r requests.post(https://httpbin.org/post, filesfiles)GET vs POST 如何选择简单来说获取数据用GET提交/修改数据用POST。GET请求的参数在URL中可见有长度限制且可以被浏览器缓存、收藏。POST请求的参数在请求体内更安全相对无长度限制。在爬虫中你通常通过观察浏览器实际发出的请求来决定用哪个。在开发者工具的Network面板中查看请求的“Method”字段。4.3 其他请求方法requests.put()、requests.delete()、requests.head()、requests.patch()等方法使用频率较低但原理相通。head()方法只获取响应头不下载响应体适合快速检查资源是否存在或是否被修改过。5. 响应对象解析与数据提取发送请求后你会得到一个Response对象。这个对象包含了服务器返回的所有信息。能否正确地从它身上提取出你需要的数据是爬虫成功的关键。5.1 响应状态码与请求历史首先必须检查请求是否成功。r requests.get(https://httpbin.org/status/404) print(r.status_code) # 输出404 print(r.reason) # 输出Not Found # 便捷的属性状态码在200到400之间为True if r.ok: print(请求成功) else: print(f请求失败状态码{r.status_code})常见的状态码200 OK: 请求成功。301/302 Found: 重定向。requests在allow_redirectsTrue时会自动跟随。400 Bad Request: 客户端请求有语法错误。401 Unauthorized: 请求未经授权需要身份验证。403 Forbidden: 服务器理解请求但拒绝执行。常见于反爬策略生效。404 Not Found: 请求的资源不存在。500 Internal Server Error: 服务器内部错误。看到这个通常是网站的问题可以稍后重试。502/503/504: 网关或服务暂时不可用。如果发生了重定向可以通过r.history查看重定向链r requests.get(http://github.com, allow_redirectsTrue) # 会重定向到https print(r.history) # 包含一个Response对象的列表 print(r.url) # 最终URL: https://github.com/5.2 响应头信息响应头包含了服务器关于响应的元信息比如内容类型、编码、Cookies等。print(r.headers) # 返回一个字典-like的对象 print(r.headers[Content-Type]) # 获取特定的头信息如 text/html; charsetutf-8 print(r.headers.get(Content-Type)) # 更安全的获取方式如果不存在返回NoneContent-Type非常重要它决定了你应该用r.text还是r.json()来解析内容。5.3 响应体内容提取这是我们的主要目标。根据Content-Type的不同提取方式也不同。文本内容 (HTML, XML, 纯文本等):使用r.textr requests.get(https://www.example.com) html_content r.text print(html_content[:500]) # 打印前500个字符编码问题r.text会自动根据响应头中的编码信息r.encoding来解码字节流。如果响应头中没有指定或者指定错误导致中文乱码你需要手动指定编码。# 如果出现乱码可以尝试手动设置编码 r.encoding gbk # 或者 utf-8, gb2312 等 print(r.text)一个常见的技巧是使用chardet库requests已依赖自动检测编码import chardet r requests.get(some_url) r.encoding chardet.detect(r.content)[encoding]二进制内容 (图片、视频、文件):使用r.contentr requests.get(https://www.example.com/image.jpg) with open(image.jpg, wb) as f: # 必须以二进制写入模式打开文件 f.write(r.content)r.content是原始的字节流bytes适合保存非文本文件。JSON内容 (API接口返回):使用r.json()r requests.get(https://api.github.com/events) json_data r.json() # 直接解析为Python字典或列表 print(json_data[0][id]) # 访问数据重要只有当响应内容确实是合法的JSON时才能使用.json()否则会抛出requests.exceptions.JSONDecodeError异常。稳妥的做法是先判断状态码和内容类型或者使用try...except。if r.headers.get(Content-Type, ).startswith(application/json): try: data r.json() except ValueError: print(响应内容不是有效的JSON)原始响应流:使用r.raw和iter_content对于大文件如视频为了避免一次性加载到内存可以流式下载。r requests.get(https://example.com/big_file.zip, streamTrue) with open(big_file.zip, wb) as f: for chunk in r.iter_content(chunk_size8192): # 每次迭代返回指定大小的字节块 if chunk: # 过滤掉保持连接的空块 f.write(chunk)设置streamTrue后requests不会立即下载整个响应体而是先获取响应头。iter_content方法允许你按块迭代内容非常适合下载大文件。5.4 Cookies管理服务器通过响应头Set-Cookie下发的Cookies会被自动保存在Response对象中。r requests.get(https://www.example.com) print(r.cookies) # 这是一个RequestsCookieJar对象 print(r.cookies.get(session_id)) # 获取特定cookie的值RequestsCookieJar对象用起来和字典很像但它提供了更完整的Cookie规范支持。你可以将它传递给下一个请求的cookies参数以维持会话状态。6. 会话维持与高级技巧Session对象如果你需要连续访问同一个网站的多个页面比如先登录再访问个人中心那么使用requests.Session()是比手动传递Cookies更优雅、更强大的方式。6.1 为什么需要Session一个Session对象会跨请求自动保持某些参数和状态最典型的就是Cookies。它还会复用底层的TCP连接从而在发起多个请求到同一主机时提升性能。对比一下无Session (繁琐且易错):# 第一次请求登录 login_resp requests.post(..., datalogin_data) cookies_received login_resp.cookies # 第二次请求必须手动带上cookies profile_resp requests.get(..., cookiescookies_received)使用Session (简洁高效):session requests.Session() # 登录cookies会自动保存在session中 session.post(..., datalogin_data) # 后续请求自动使用session中的cookies profile_resp session.get(...)6.2 Session的实战应用模拟一个完整的登录并访问受保护页面的流程import requests # 1. 创建会话 s requests.Session() # 2. 可选为本次会话的所有请求设置统一的请求头 s.headers.update({ User-Agent: Mozilla/5.0..., Accept-Language: zh-CN,zh;q0.9 }) # 3. 首先可能访问登录页获取一些初始token或cookie某些网站需要 # 例如有些网站登录时需要先获取一个csrf_token login_page s.get(https://example.com/login) # 这里假设我们需要从登录页HTML中解析出一个csrf_token (实际中可用BeautifulSoup) # csrf_token extract_csrf_token(login_page.text) # 4. 构造登录数据并提交 login_data { username: your_username, password: your_password, # csrf_token: csrf_token, } login_response s.post(https://example.com/login_action, datalogin_data) # 5. 检查登录是否成功根据实际情况判断如状态码、响应内容、跳转等 if login_response.status_code 200 and 登录成功 in login_response.text: print(登录成功) # 6. 使用同一个session访问需要登录的页面 dashboard s.get(https://example.com/dashboard) print(dashboard.text[:200]) # 打印部分内容 else: print(登录失败)6.3 Session级别的配置你可以在创建Session时或之后为其设置默认参数这些参数会应用到该Session发起的所有请求。s requests.Session() s.proxies {http: http://proxy.example.com:8080} s.verify False # 谨慎使用仅为示例会禁用所有请求的SSL验证 s.timeout 10 # 为所有请求设置默认超时 # 后续的 s.get() 或 s.post() 都会自动使用这些配置这非常适合需要统一代理、统一超时时间等场景。注意事项Session对象会长期保持连接。如果你的爬虫程序运行时间很长并且向很多不同的主机发送请求可能会占用大量系统资源。对于一次性或简单的请求直接使用requests.get()更轻量。对于复杂的、需要保持状态的连续操作Session是首选。7. 异常处理与错误排查实战网络请求充满了不确定性。一个健壮的爬虫必须能妥善处理各种异常并从错误信息中快速定位问题。requests库定义了一系列清晰的异常类型。7.1 常见异常类型及处理使用try...except块来捕获和处理异常是最佳实践。import requests from requests.exceptions import Timeout, ConnectionError, HTTPError, RequestException url https://www.example.com try: response requests.get(url, timeout5) # 如果响应状态码不是200主动抛出HTTPError异常 response.raise_for_status() # 处理响应内容 print(response.text[:100]) except Timeout: print(f请求 {url} 超时。可能是网络慢或服务器无响应。) # 可以加入重试逻辑 # for i in range(3): # try: ... except Timeout: ... else: break except ConnectionError: print(f连接 {url} 失败。可能是DNS解析失败、服务器拒绝连接或网络中断。) # 检查网络连接或更换代理/重试 except HTTPError as e: print(fHTTP错误发生: {e}。状态码: {response.status_code}) # 处理特定的状态码如403禁止访问可能需要更换User-Agent或IP if response.status_code 403: print(访问被拒绝可能触发了反爬机制。) elif response.status_code 404: print(请求的资源不存在。) elif response.status_code 500: print(服务器内部错误可稍后重试。) except RequestException as e: print(f请求发生未知错误: {e}) # RequestException是所有requests库异常的基类可以捕获所有相关错误 except Exception as e: print(f发生了其他非requests异常: {e})关键点解析response.raise_for_status(): 这是一个非常实用的方法。如果响应状态码是4xx客户端错误或5xx服务器错误它会抛出一个HTTPError异常。这让你能把错误处理统一到异常捕获流程中代码更清晰。Timeout: 务必为每个请求设置timeout参数这是防止程序无限期挂起的基本保障。ConnectionError: 涵盖从DNS解析失败到TCP连接被拒等各种底层网络问题。7.2 错误排查实战指南当你的爬虫脚本报错时不要慌张按照以下步骤进行排查就像医生问诊一样看错误信息TracebackPython的错误信息会告诉你异常发生在哪一行是什么类型的错误。这是第一手资料。检查URL是不是写错了是不是需要https而你写了http手动在浏览器中打开这个URL看看是否正常。检查网络和代理你的电脑能正常上网吗如果使用了代理代理是否还有效尝试用curl或wget命令测试一下连通性。打印请求详情在发送请求前后打印出关键信息这是最有效的调试手段。import requests import json # 更详细的调试启用requests的日志可选信息量很大 # import logging # logging.basicConfig(levellogging.DEBUG) url https://httpbin.org/post data {test: data} headers {Custom-Header: my-value} # 发送请求前打印你准备发送的内容 print(f[准备请求] URL: {url}) print(f[准备请求] Data: {data}) print(f[准备请求] Headers: {headers}) try: r requests.post(url, jsondata, headersheaders, timeout10) # 收到响应后打印关键信息 print(f[响应状态] 状态码: {r.status_code}) print(f[响应头] Content-Type: {r.headers.get(Content-Type)}) print(f[响应体] 前500字符: {r.text[:500]}) # httpbin.org会返回我们发送的请求信息非常利于调试 if application/json in r.headers.get(Content-Type, ): print(f[响应JSON] {json.dumps(r.json(), indent2, ensure_asciiFalse)}) except Exception as e: print(f[请求异常] {type(e).__name__}: {e})通过对比你发送的和服务器返回的往往能发现端倪。比如你发现服务器返回403而你的请求头里缺少User-Agent。模拟浏览器行为用浏览器如Chrome的开发者工具F12 - Network正常访问一次目标页面。查看浏览器实际发送的请求Request Method: 是GET还是POSTRequest Headers: 复制完整的请求头特别是User-Agent,Cookie,Referer,Content-Type等。Request Payload/Form Data: 如果是POST查看它提交了什么数据。Response: 查看服务器返回的真实数据格式。 然后尽量让你的requests代码复现浏览器的这个请求。这是破解很多反爬机制的起点。处理常见反爬User-Agent检测添加常见的浏览器UA。频率限制在请求间加入随机延时time.sleep(random.uniform(1, 3))避免请求过快。IP封锁使用代理IP池轮换。验证码遇到验证码通常意味着需要更复杂的处理如打码平台、机器学习识别或考虑放弃该网站。动态加载JavaScript渲染requests只能获取初始HTML。如果数据是JS加载的你需要分析其背后的API接口仍在Network中找XHR/Fetch请求或者使用Selenium、Playwright等浏览器自动化工具。实操心得我习惯为重要的爬虫项目写一个简单的“调试模式”。在脚本开头设置一个DEBUG True的变量。当它为True时会打印出每个请求的URL、状态码和耗时当它为False时则安静运行。这能帮助我在开发阶段快速定位问题上线时又不会产生冗余输出。8. 综合实战案例构建一个简单的图片爬虫理论讲得再多不如动手写一个。我们设计一个简单的实战案例爬取一个图片网站例如我们以免费的示例图片网站https://picsum.photos为例它提供随机图片的图片列表并将前10张图片下载到本地。这个案例会综合运用我们讲到的GET请求、参数传递、异常处理、文件保存等知识点。8.1 目标分析与步骤拆解目标从https://picsum.photos/v2/list获取图片列表JSON格式并下载列表中的前10张图片。分析该API返回一个JSON数组每个元素是一个图片对象包含id,author,download_url等字段。我们需要解析JSON提取download_url然后用requests下载图片二进制内容并保存。步骤 a. 发送GET请求获取图片列表数据。 b. 解析JSON响应提取前10个图片的下载链接。 c. 遍历这10个链接发送GET请求下载图片。 d. 将图片的二进制内容response.content保存为本地文件。 e. 加入异常处理和友好提示。8.2 代码实现与逐行解析import requests import time import os from requests.exceptions import RequestException def download_images_from_picsum(limit10, save_dir./downloaded_images): 从Picsum.photos下载指定数量的图片。 Args: limit (int): 要下载的图片数量默认10张。 save_dir (str): 图片保存的本地目录。 # 0. 创建保存目录如果不存在 if not os.path.exists(save_dir): os.makedirs(save_dir) print(f[信息] 创建保存目录: {save_dir}) # 1. 定义图片列表API的URL和参数 list_api_url https://picsum.photos/v2/list params { page: 1, limit: limit # 限制返回的数量正好符合我们的需求 } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } print(f[步骤1] 正在请求图片列表...) try: # 2. 发送GET请求获取图片列表 list_response requests.get(list_api_url, paramsparams, headersheaders, timeout15) list_response.raise_for_status() # 如果状态码不是200抛出HTTPError # 3. 解析JSON数据 image_list list_response.json() print(f[信息] 成功获取到 {len(image_list)} 张图片的信息。) # 4. 遍历列表下载每张图片 for idx, image_info in enumerate(image_list, start1): image_url image_info.get(download_url) image_id image_info.get(id, unknown) author image_info.get(author, unknown) if not image_url: print(f[警告] 第{idx}张图片缺少下载链接跳过。) continue print(f[步骤2] 正在下载第 {idx}/{len(image_list)} 张: ID{image_id}, Author{author}) try: # 5. 发送请求下载图片流式传输适合可能的大文件 # 注意这里我们直接使用原图URL有些API可能需要拼接或处理 img_response requests.get(image_url, streamTrue, timeout30) img_response.raise_for_status() # 6. 从URL或响应头中提取文件名 # 简单处理使用图片ID作为文件名并假设是jpg格式根据实际情况调整 # 更健壮的做法是从响应头Content-Disposition或URL后缀获取 file_extension .jpg # 假设格式 filename f{image_id}_{author.replace( , _)}{file_extension} filepath os.path.join(save_dir, filename) # 7. 以二进制写入模式保存图片 with open(filepath, wb) as f: # 使用iter_content分块写入避免大文件占用过多内存 for chunk in img_response.iter_content(chunk_size8192): if chunk: f.write(chunk) print(f - 已保存至: {filepath}) except RequestException as e: print(f[错误] 下载图片 {image_url} 失败: {e}) continue # 跳过这张继续下一张 except IOError as e: print(f[错误] 保存文件 {filename} 失败: {e}) continue # 8. 礼貌性延时避免对服务器造成过大压力即使对示例网站也应保持良好习惯 time.sleep(0.5) # 暂停0.5秒 print(f[完成] 所有图片下载任务结束。文件保存在 {save_dir} 目录。) except RequestException as e: print(f[严重错误] 获取图片列表失败请检查网络或API地址: {e}) except ValueError as e: print(f[严重错误] 解析JSON响应失败API可能返回了错误格式: {e}) # 运行函数 if __name__ __main__: download_images_from_picsum(limit10, save_dir./picsum_images)8.3 代码要点与避坑指南参数化与函数化我们将功能封装成函数并接受limit和save_dir参数。这使得代码更灵活、可复用。在实际项目中这应该是基本操作。目录创建使用os.makedirs(save_dir, exist_okTrue)可以安全地创建目录如果目录已存在也不会报错。exist_okTrue参数在Python 3.2中可用更简洁。使用params传递参数构造API查询参数时使用params字典让requests自动编码比手动拼接URL更规范。流式下载大文件在下载图片的请求中我们设置了streamTrue并使用iter_content分块写入文件。这对于下载大图或文件至关重要可以防止一次性将整个文件加载到内存中导致内存溢出。异常处理的粒度我们进行了两层异常处理。外层try...except捕获获取列表时的致命错误如网络不通、API失效。内层try...except包裹每张图片的下载过程这样即使某一张图片下载失败也不会影响其他图片的下载提高了程序的健壮性。设置延时time.sleep(0.5)是一个简单的礼貌性延时。在爬取任何网站时在请求间加入随机延时是一个好习惯可以显著降低被服务器识别为恶意爬虫的风险。对于真实网站延时应该更长且更随机例如time.sleep(random.uniform(1, 3))。文件名处理本例简单使用了id和author组合成文件名。在实际爬取中文件名可能包含非法字符如/,:,?需要清洗。最好从URL或响应头Content-Disposition中提取原始文件名或者使用hashlib生成唯一文件名。8.4 案例扩展思考这个案例虽然简单但涵盖了爬虫的核心流程。你可以基于此进行扩展爬取其他网站将list_api_url和解析逻辑换成其他图片网站或API。增加代理支持在requests.get()中添加proxies参数。增加重试机制使用tenacity库或自己写循环在请求失败时重试几次。异步加速对于成百上千张图片使用aiohttp库进行异步请求可以极大提升下载速度。图形界面使用tkinter或PyQt为这个小工具做一个简单的界面。通过这个从分析到实现的完整过程你应该对如何使用requests库构建一个实用的爬虫脚本有了更直观的认识。记住爬虫的核心逻辑万变不离其宗构造请求 - 发送请求 - 处理响应 - 提取数据 - 保存数据。requests库完美地解决了前三个步骤为你打下了最坚实的基础。
返回列表