ARTICLE DETAIL

资讯详情

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

Python爬虫入门实践:从环境搭建到数据存储全流程解析

Python爬虫入门实践:从环境搭建到数据存储全流程解析 网络爬虫是 Python 学习中最容易获得正反馈的方向之一。很多初学者学完变量、循环、函数和列表之后手里没有适合练手的项目爬虫恰好提供了一条完整链路用 requests 发送请求、用 Beautiful Soup 解析 HTML、用 CSV 或 SQLite 保存结果。这篇文章从安装 Python 开始带着你写一个能运行、能解析、能保存、能排查的最小爬虫并覆盖环境搭建、请求与解析、数据存储、常见报错和工程化建议。整篇内容面向零基础读者按顺序阅读即可不需要先掌握任何爬虫经验。需要先说清楚一点Python 生态的版本更新很快标题里的“最新版”指的是思路和流程适用于当前主流版本。实际安装时以下载页面显示的稳定版为准。文中所有代码都使用通用写法复制到自己的项目里时要把 URL、字段名、保存路径按实际情况调整。1. 先理解网络爬虫的工作原理与边界1.1 爬虫的核心链路只有三步爬虫做的事情本质上只有三件向目标服务器发出 HTTP 请求。接收服务器返回的响应通常是 HTML 文本也可能是 JSON、XML。从响应内容中提取自己需要的信息。浏览器做了同样的事情只不过它把 HTML 渲染成了带样式的页面爬虫不关心渲染只关心原始文本和数据结构。理解这一点很重要因为很多新手误以为爬虫要“操作浏览器”实际上大部分场景只需要模拟一次网络请求。1.2 从输入网址到拿到数据中间发生了什么用户在浏览器输入网址后系统会依次完成这些动作DNS 解析把域名解析为 IP 地址。建立 TCP 连接。发送 HTTP 请求。服务器返回 HTML 响应。浏览器解析 HTML、加载资源、渲染页面。爬虫走的是前四步第五步不做。它拿到 HTML 后直接用解析库从标签结构中定位数据。因此学习爬虫之前至少要理解 HTTP 请求方法、状态码、请求头和响应体这几个基础概念。1.3 服务端渲染与客户端渲染决定爬虫策略网页有不同的渲染方式这直接决定爬虫怎么写渲染方式页面内容来源爬虫策略服务端渲染服务器直接把内容拼进 HTML直接请求 URL解析 HTML客户端渲染HTML 只有脚本数据由前端 JS 调用接口获取先找 XHR 接口再请求 JSON 数据混合渲染首屏部分服务端输出部分接口加载分别处理静态内容和接口数据判断方式很简单在浏览器里右键查看网页源代码在源码中搜索页面上能看到的文字。如果搜不到说明数据很可能由 JS 动态加载需要去开发者工具的 Network 面板里找接口。1.4 写爬虫前要先确认合规边界爬虫属于网络请求实际使用中必须注意边界。学习阶段建议在练习站点或者自己有权限的站点上操作比如专门用于爬虫练习的quotes.toscrape.com。正式场景中至少要确认以下几点目标网站的服务条款是否允许自动抓取。抓取的数据是否涉及个人隐私或版权内容。请求频率是否会对目标服务器造成压力。数据用途是否符合法律法规和网站政策。遇到反爬机制时正确做法是降低频率、遵守站点规范、寻找官方 API 或联系网站方获取授权。不要抱着“绕过去”的心态去写爬虫这不安全也不是软件工程的正规做法。2. 从零搭建一套干净的 Python 爬虫环境2.1 安装 PythonWindows、macOS、Linux 都该怎么做爬虫不依赖某个特定 Python 版本但要求版本不能过旧。建议安装当前官方稳定版至少是 Python 3.9 以上。安装方式按系统区分。Windows 用户去官方网站python.org/downloads下载安装包。安装时有一个关键步骤在安装向导第一页勾选“Add python.exe to PATH”否则命令行里敲python会提示找不到命令。安装完成后打开 PowerShell 或 CMD 验证python --version python -m pip --version如果电脑里已经装了多个 Python 版本Windows 推荐使用自带的py启动器py -3.12 --version py -3.11 --versionmacOS 系统自带/usr/bin/python3但版本可能偏旧不一定适合安装最新依赖。建议通过官网安装包或 Homebrew 安装独立版本然后使用python3命令python3 --versionLinux 发行版通常自带 Python 3但需要确认pip和venv模块是否齐全。以 Ubuntu/Debian 为例sudo apt update sudo apt install python3 python3-pip python3-venv python3 --version注意安装完成后如果输入python报错而python3正常说明系统默认命令是python3。这与 Windows 的习惯不同不是安装失败。2.2 用虚拟环境隔离项目依赖爬虫项目会安装很多第三方库。如果所有项目都装到全局环境不同项目的依赖版本容易互相冲突。正确做法是每个项目单独创建一个虚拟环境。在项目目录下执行python -m venv venv激活虚拟环境# Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate激活后命令行提示符前面会出现(venv)表示当前使用的是项目独立的 Python 环境。之后的pip install都只会安装到这个虚拟环境里。新手最容易犯的一个错误就是跳过虚拟环境直接在系统环境里安装一堆包。短期看能跑换一个项目就可能出现“为什么这里能运行、那边报错”的问题。2.3 安装 requests、BeautifulSoup 与 lxml激活虚拟环境后安装三个最常用的爬虫库pip install requests beautifulsoup4 lxml三个库的分工是requests发送 HTTP 请求获取网页内容。beautifulsoup4解析 HTML提取节点。lxml高性能解析引擎Beautiful Soup 的底层解析器之一也提供 XPath 能力。如果下载速度慢可以临时指定国内镜像源pip install requests beautifulsoup4 lxml -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后检查版本确认环境正常pip list2.4 IDE 与浏览器调试工具写代码推荐使用 VSCode 或 PyCharm Community Edition。VSCode 安装官方 Python 插件后能提供语法提示、调试和终端集成PyCharm 社区版对 Python 的提示更完整。两者都够用选一个即可。比 IDE 更重要的是浏览器开发者工具。在 Chrome 或 Edge 里按 F12可以打开开发者工具其中 Network 面板能查看每个请求的 URL、请求头、响应状态和响应内容。调试爬虫时大部分时间都花在“确认页面的数据结构”上Network 面板是最可靠的参照。另外要区分两个概念浏览器“查看网页源代码”看到的是服务器返回的原始 HTML开发者工具 Elements 面板看到的是 JS 执行后渲染出来的 DOM。两者不一致时说明页面是动态渲染的。3. 用 requests 写出第一个可运行爬虫3.1 选一个适合练习的目标站点练习爬虫不能用真实网站高频请求也容易遇到验证码和页面结构频繁变动。推荐使用quotes.toscrape.com这是一个专门为爬虫练习设计的站点结构稳定首页展示多条带作者和标签的经典引用适合入门。这个站点的 HTML 结构很规整每条引用放在div classquote中引用内容在span classtext作者在small classauthor标签在a classtag。3.2 用 requests 发送第一个 GET 请求先写一个最简单的请求脚本目的不是解析而是确认请求链路能通import requests url https://quotes.toscrape.com/ resp requests.get(url, timeout10) print(resp.status_code) print(resp.headers.get(Content-Type)) print(resp.text[:200])运行后关键是看两部分resp.status_code应该是 200表示服务器正常返回。resp.text是响应正文也就是 HTML 源码的前 200 个字符。这里解释一下常用属性resp.status_codeHTTP 状态码。200 表示成功404 表示页面不存在403 表示被拒绝。resp.headers响应头包含 Content-Type、Server、Set-Cookie 等信息。resp.textrequests 根据响应头或自动推断的编码解码后的文本。resp.content未解码的原始字节通常在保存文件或处理二进制资源时使用。resp.url最终请求地址跟随重定向后可能变化。3.3 在解析前先处理编码问题中文网页经常会出现乱码核心原因是编码判断错误。requests 默认根据响应头中的charset解码如果响应头没有声明 charset或声明得不准确resp.text就可能出现乱码。推荐在处理文本前统一做一次编码修正resp.encoding resp.apparent_encodingapparent_encoding是 requests 根据内容本身推断出的编码比响应头更可靠代价是会额外消耗一点计算时间。对于爬虫程序来说这个代价完全可以接受。如果页面是 UTF-8通常不设置也不会出问题但显式设置可以避免后续代码在不同站点之间复用时报乱码。3.4 用 Beautiful Soup 完成最小闭环环境通了之后写出第一个完整爬虫请求页面、解析 HTML、提取引用文字和作者。import requests from bs4 import BeautifulSoup url https://quotes.toscrape.com/ resp requests.get(url, timeout10) resp.encoding resp.apparent_encoding soup BeautifulSoup(resp.text, html.parser) for quote in soup.select(.quote): text quote.select_one(.text).get_text() author quote.select_one(.author).get_text() print(f{author}: {text})这段代码做了四件事请求首页。用html.parser解析 HTML。用 CSS 选择器soup.select(.quote)找出所有 class 为 quote 的节点。在每个节点内部继续定位.text和.author取出纯文本。运行后控制台会输出带作者名的引用列表说明整条链路已经跑通。这里要注意select_one返回第一个匹配节点select返回所有匹配节点列表。如果页面里某个 class 对应多个元素想提取全部内容必须用select后遍历而不是select_one。3.5 GET 与 POST 请求的差异爬虫并非只会发 GET 请求。有的接口需要提交表单或 JSON 数据要用 POST 请求。requests 的写法差异不大import requests login_url https://httpbin.org/post data {username: test, password: 123456} resp requests.post(login_url, datadata, timeout10) print(resp.status_code) print(resp.text[:300])data参数用于提交表单格式的数据json参数用于提交 JSON 数据。选择哪种取决于目标接口的要求可以通过浏览器开发者工具的 Network 面板查看请求的 Content-Type 和请求体。4. 数据解析的三条路线正则、Beautiful Soup 与 XPath4.1 为什么不能直接用字符串切片解析 HTML新手常犯一个错误用find或字符串切片去截取数据。这种做法在页面结构固定的极简场景下能工作但真实网页的标签属性顺序、空白、注释、嵌套结构都可能变化字符串匹配一旦遇到换行或属性顺序调整就会失败。正确做法是把 HTML 解析成树形结构再用选择器定位节点。下面介绍三种常用方案。4.2 正则表达式适合简单且稳定的文本提取正则表达式适合在明确知道文本格式时使用比如提取邮箱、手机号、日期或提取结构非常稳定的片段。以练习站点为例可以这样提取引用内容import re pattern rspan classtext itemproptext(.*?)/span texts re.findall(pattern, resp.text, re.S) for text in texts: print(text)这里两个关键点是(.*?)是非贪婪匹配表示尽量少地匹配内容避免把多个 span 一起吞掉。re.S让点号可以匹配换行符因为 HTML 可能跨多行。正则的缺点是脆弱。只要标签结构一变表达式就需要重新调整而且复杂正则的可读性很差。建议只在提取纯文本片段时使用不要用它解析整张 HTML 页面。4.3 Beautiful Soup最适合新手的解析方式Beautiful Soup 把 HTML 变成树结构支持标签名、属性、CSS 选择器等多种定位方式。常用的查找方法# 根据 CSS 选择器查找 soup.select(div.quote) soup.select_one(span.text) # 根据标签和属性查找 soup.find_all(div, class_quote) soup.find(small, class_author) # 获取文本和属性 node.get_text() # 节点内所有文本 node.get(href) # 节点属性值get_text()会把节点下所有子节点的文本拼接起来适合提取整段内容get(href)用于提取链接比如下一页地址或图片地址。4.4 XPath用路径表达式定位节点XPath 是一种在 XML/HTML 树中定位节点的路径语言配合lxml使用。它适合习惯“从根到叶子”描述路径的场景。from lxml import etree tree etree.HTML(resp.text) texts tree.xpath(//div[classquote]/span[classtext]/text()) authors tree.xpath(//div[classquote]/small[classauthor]/text()) for author, text in zip(authors, texts): print(f{author}: {text})//表示从任意位置查找[classquote]表示筛选 class 属性等于 quote 的节点/text()表示取该节点的文本。XPath 表达能力强能写复杂的条件但可读性会随着表达式变长而下降。实践中Beautiful Soup 和 XPath 选一个深入学习即可另一个能读懂就够用。4.5 三种解析方式怎么选解析方式学习成本容错性适用场景备注正则表达式中低提取固定格式文本、简单片段HTML 结构变化时维护成本高Beautiful Soup低高大多数 HTML 页面解析选择器直观适合新手XPath / lxml中高复杂路径定位、批量提取节点搭配 lxml 性能较好实际项目里优先用 Beautiful Soup 或 XPath 完成 80% 的结构化解析正则只作为补充手段。5. 给爬虫加上请求头、超时与重试机制5.1 请求头的内容与作用直接使用 requests 默认请求时服务端收到的 User-Agent 是类似python-requests/2.x的标识。有些站点会因为这个标识返回异常内容或拒绝访问。一个常规做法是携带更接近浏览器的请求头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-Language: zh-CN,zh;q0.9,en;q0.8, } resp requests.get(url, headersheaders, timeout10)要强调的是设置 User-Agent 只是标识客户端身份是正常网络行为不是绕过限制。如果站点明确拒绝脚本访问正确做法是尊重站点规则而不是反复伪造身份。5.2 为请求加上超时和重试爬虫运行在网络环境中超时是迟早会遇到的异常。requests 如果不设置timeout请求可能长时间挂起导致程序卡死。生产代码必须显式设置超时resp requests.get(url, headersheaders, timeout10)遇到网络抖动时一次失败不代表目标不可用可以重试几次。一个简单的重试函数import time import requests def fetch(url, headersNone, timeout10, max_retries3): for attempt in range(1, max_retries 1): try: resp requests.get(url, headersheaders, timeouttimeout) resp.raise_for_status() return resp except requests.RequestException as exc: print(f第 {attempt} 次请求失败{exc}) if attempt max_retries: time.sleep(2 * attempt) return None这个实现做了两件重要的事raise_for_status()会在状态码为 4xx 或 5xx 时主动抛出异常避免拿到错误页面后继续解析。重试间隔用2 * attempt递增第一次等 2 秒第二次等 4 秒避免失败后立刻高频重试。5.3 控制请求频率做有节制的爬虫爬虫的请求频率直接影响目标服务器负载。即使抓取逻辑完全正确过高的请求频率也可能导致 IP 被临时限制。因此翻页抓取时每次请求之间应该加合理间隔import time time.sleep(1)异步并发、多线程爬虫虽然能提升速度但会成倍放大对目标站点的压力。入门阶段先养成流控意识后续再研究高并发场景。5.4 遇到动态页面和登录验证先判断再决定方案如果目标页面在浏览器里能看到数据但 requests 抓回来是空壳不要急着换工具按下面的顺序排查打开开发者工具切到 Network 面板刷新页面。找到加载数据的 XHR 请求查看它的 URL、参数和响应格式。如果接口返回的是 JSON直接用 requests 请求这个接口解析 JSON 往往比解析 HTML 更简单。如果接口需要签名、加密参数或页面交互复杂再考虑 Playwright 等浏览器自动化工具。无头浏览器能解决动态渲染问题但资源占用大、运行慢不是第一选择。能走普通 HTTP 请求解决的优先走普通请求。6. 把数据保存下来CSV、JSON 与 SQLite解析出数据之后还要考虑怎么落盘。不同格式适合不同用途下面用抓取首页引用数据为例分别演示 CSV、JSON 和 SQLite 的保存方式。6.1 保存 CSV 并解决 Excel 打开乱码CSV 适合表格数据可以用 Excel、WPS 直接打开。需要注意两个细节newline避免写入时出现空行encodingutf-8-sig让 Excel 正确识别 UTF-8 编码。import csv import requests from bs4 import BeautifulSoup url https://quotes.toscrape.com/ resp requests.get(url, timeout10) resp.encoding resp.apparent_encoding soup BeautifulSoup(resp.text, html.parser) rows [] for quote in soup.select(.quote): rows.append([ quote.select_one(.text).get_text(), quote.select_one(.author).get_text(), ]) with open(quotes.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([text, author]) writer.writerows(rows)用utf-8-sig而不是utf-8是因为 Excel 在打开无 BOM 的 UTF-8 文件时可能把中文识别成乱码。这个细节在交付数据文件时很常见。6.2 保存 JSON 保留结构的灵活性JSON 适合保存带嵌套结构的数据也方便后续程序读取和处理。import json data [] for quote in soup.select(.quote): data.append({ text: quote.select_one(.text).get_text(), author: quote.select_one(.author).get_text(), }) with open(quotes.json, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2)ensure_asciiFalse保证中文以原始字符写入而不是\uXXXX转义序列indent2让 JSON 文件可读。6.3 保存 SQLite 方便查询数据量较大或需要按条件查询时SQLite 是合适选择。它是 Python 内置支持的轻量数据库不需要额外安装服务。import sqlite3 conn sqlite3.connect(quotes.db) conn.execute( CREATE TABLE IF NOT EXISTS quotes ( id INTEGER PRIMARY KEY AUTOINCREMENT, author TEXT, text TEXT ) ) rows_for_db [ (quote.select_one(.author).get_text(), quote.select_one(.text).get_text()) for quote in soup.select(.quote) ] conn.executemany(INSERT INTO quotes (author, text) VALUES (?, ?), rows_for_db) conn.commit() conn.close()executemany可以一次性插入多条记录比循环执行单条插入效率更高。使用参数占位符?可以避免 SQL 注入问题。6.4 三种存储方式怎么选存储方式优点缺点适用场景CSV直观可用 Excel 查看结构简单不适合嵌套数据数据表格化、交付给非技术人员JSON支持嵌套结构通用性强不适合大量数据检索接口数据、配置文件、日志SQLite支持 SQL 查询适合中量数据需要额外学习 SQL数据量大、需要按条件过滤和统计爬虫新手可以从 CSV 起步理解数据落盘流程当项目变大后再逐步切换到 SQLite 或专业数据库。7. 常见报错与排查路径7.1 高频问题速查表爬虫运行中的错误绝大多数集中在请求、编码、解析、环境四个环节。下面是高频问题速查表问题现象常见原因检查方式解决建议中文乱码响应头 charset 缺失或错误打印resp.encoding和resp.apparent_encoding显式设置resp.encoding resp.apparent_encoding返回 403缺少 User-Agent 或请求频率过高打印状态码和响应头补充请求头降低频率返回空 HTML页面由 JS 动态渲染浏览器查看网页源代码搜索页面文字寻找 XHR 接口改用 JSON 解析请求超时网络抖动或目标站点响应慢用浏览器访问确认站点可用性设置 timeout实现重试逻辑解析结果为空CSS 选择器写错或页面结构变化打印soup的局部内容确认节点用浏览器 DevTools 核对 class 和层级pip 安装失败网络源连接不稳定查看报错信息中的超时或连接失败使用国内镜像源数据重复翻页逻辑边界处理错误统计记录数并与页面总数对比增加去重逻辑确认翻页终止条件7.2 从现象到根因的排查顺序遇到问题不要乱猜按下面的顺序逐层检查先确认输入URL 是否正确参数是否需要。再确认请求本身状态码是多少响应头返回了什么。检查编码resp.text是否乱码resp.encoding是否正确。检查内容响应里是否真的有目标数据还是被拦截页面。检查解析选择器是否匹配当前 HTML 结构。检查异常程序是否因为某个未捕获异常中断。最后确认环境依赖版本、Python 版本、虚拟环境是否激活。其中最常见的问题是拿浏览器里看到的 DOM 去对比 requests 抓回来的 HTML。记住浏览器 DOM 可能经过 JS 修改和服务器原始响应不一致。7.3 调试爬虫的几个实用技巧第一个技巧是保存中间结果。把resp.text直接写到本地文件再用编辑器的格式化功能查看比在控制台打印长 HTML 更容易定位结构with open(page.html, w, encodingutf-8) as f: f.write(resp.text)第二个技巧是打印关键节点。解析不到数据时先打印一小段 soup 内容确认选择器路径print(soup.select_one(.quote))第三个技巧是最小化复现。把出错逻辑抽出来单独写一个小脚本反复调整请求参数和选择器快速定位原因。不要在大程序里堆 print 调试效率太低。8. 学习路线、可复用清单与进阶方向8.1 从零到进阶的推荐学习路线爬虫学习不是一步到位的按下面的顺序推进最稳妥Python 基础语法变量、循环、函数、列表、字典、文件读写。requests 与 Beautiful Soup掌握请求、解析、数据提取。动态页面处理学习从 Network 面板找接口再尝试 Playwright。Scrapy 框架用框架管理请求、解析、管道存储。数据清洗与存储掌握 CSV、JSON、SQLite再延伸到 MySQL。工程化能力日志、配置外置、去重、调度、监控。每一步都要有实际产出。比如学完 requests就做一个小页面抓取学完 Beautiful Soup就做一个列表抓取学完 SQLite就把列表存进数据库并写一个查询。8.2 写爬虫前和执行中的可复用检查清单下面这份清单可以直接复制用于自己的项目[ ] 确认目标网站是否允许自动抓取阅读 robots.txt 和服务条款。[ ] 确认抓取内容不涉及个人隐私、版权和敏感数据。[ ] 使用练习站点或有权限的站点进行学习测试。[ ] 为每个请求设置 User-Agent 和 timeout。[ ] 为请求实现异常处理和重试。[ ] 控制请求频率翻页之间加合理间隔。[ ] 显式处理响应编码避免中文乱码。[ ] 保存中间 HTML 或日志便于排查。[ ] 检查解析结果是否为空不盲目继续写入存储。[ ] 对存储数据做去重避免重复记录。[ ] 定期检查页面结构选择器失效时能快速定位。8.3 进阶方向Scrapy、异步与工程化入门程序跑通后可以往三个方向继续深入。第一个方向是 Scrapy 框架。它把请求调度、解析、中间件、Item Pipeline 整合在一起适合规模化抓取。学习时重点理解 Item 的定义、Pipeline 的写入流程和下载中间件的作用。第二个方向是异步并发。使用aiohttp或asyncio能明显提升请求吞吐量但并发越高对目标站点的压力越大必须配合限速策略。第三个方向是工程化。爬虫只是数据采集的一部分完整的工程还包括把 URL、频率、存储路径做成配置项把运行状态写入日志把抓取任务做成定时任务对抓取结果做校验和去重。如果需要交付给非技术同事使用还可以用 PyInstaller 把脚本打包成可执行文件。但无论技术做到哪一步都要记住爬虫的价值在于稳定、合规、可维护地获取数据而不是想尽办法绕过站点的限制。真正值得花时间的是工程化能力和数据处理能力这两项才是长期积累的技术资产。
返回列表