
爬虫这个词一听好像很玄乎什么“爬虫工程师”“反爬对抗”“数据采集”搞得跟黑客技术似的。其实把它拆开看核心动作就一句话写代码替你去访问网页再把网页里你需要的信息摘出来存好。说白了就是把你手动“打开页面-找信息-复制粘贴”这个过程自动化。我最早接触爬虫就是嫌一个个手动复制太蠢写了十几行代码跑完几分钟顶我以前一下午的活儿。这篇文章就基于“网络爬虫基础使用”这个主题把我这些年用爬虫攒下来的核心经验、实操步骤和踩过的坑一次性捋清楚从原理到能跑通的完整代码都给到你适合刚入门、想用爬虫解决重复劳动的朋友。这篇文章不讲虚的直接围绕几个最核心的问题展开爬虫到底怎么工作、请求和解析的细节有哪些坑、一个标准项目怎么落地、遇到403和乱码怎么排查。写完你会发现爬虫没那么神秘它就是一门“让数据自己跑到你硬盘里”的手艺活。1. 项目整体设计与思路拆解1.1 爬虫本质上在做什么从浏览器到代码的映射很多人一开始理解爬虫容易跑偏以为爬虫是“黑进网站后台去偷数据”。其实完全不是。爬虫做的事情和你用浏览器访问网站没有任何本质区别都是向服务器发一个HTTP请求然后服务器把HTML页面响应回来浏览器负责把HTML渲染成好看的样子爬虫则负责把HTML当作文本来解析数据。打个比方你就明白了。你去餐厅吃饭浏览器相当于你自己去柜台点菜服务员把菜端上来你直接在餐桌上享用爬虫相当于派了个机器人替你点菜机器人不关心菜好不好看它只关心“这道菜的食材构成是什么、价格标签上写的多少钱”然后把信息记录下来打包带走。餐厅不知道有什么区别因为你都是合规地走点菜流程。所以写爬虫的第一步就是搞清楚你平时浏览器访问一个页面时背后到底发生了什么。最简化的模型就三步你输入网址浏览器拼一个HTTP请求发出去服务器处理请求返回HTML文档浏览器解析HTML加载出图片、文字、样式。爬虫程序做的事一模一样只不过把“浏览器解析渲染”换成了“代码提取关键字段”。这也就是为什么爬虫的三大基本功永远是发请求、解析HTML、存储数据。1.2 为什么选Python生态与requests库基础爬虫的语言选型几乎不用犹豫直接用Python。倒不是说Python性能多好它的强项是生态和上手速度。一个爬虫项目里最麻烦的不是“发请求”而是“解析网页”Python在这块有天然优势。你只需要安装两个库就能覆盖爬虫入门90%的需求requests负责发HTTP请求BeautifulSoupbs4负责解析HTML。我自己也试过用Java写HttpClient、用Node写axios但解析HTML的时候Java得配jsoupNode得配cheerio代码量和思考成本都比Python高一个台阶。对初学者来说Python让你把精力集中在“爬虫逻辑”本身而不是被语言细节绊倒。你可能听过Scrapy这个框架它确实强大自带爬取、解析、调度、导出等功能适合大型采集项目。但入门阶段我强烈不建议一上来就上Scrapy。原因很简单Scrapy的异步机制和框架约定太多你还没搞懂普通爬虫的请求解析流程就直接面对框架的层层封装出了bug都不知道底层发生了什么。先用requests手写一个爬虫把每一步跑通了再去摸Scrapy你会觉得完全是降维打击。1.3 爬虫的常规流程获取、解析、存储三件套任何一个基础爬虫项目不管目标网站长什么样流程都逃不出这三步第一步是“获取”。用requests把目标网页的HTML拿下来。这个阶段核心是两件事把请求头Headers伪装得像真实浏览器以及处理反爬机制比如频率限制、验证码校验。第二步是“解析”。从拿到的HTML文本里把你要的数据摘出来。这个阶段最常用的是CSS选择器或XPath。BeautifulSoup的select()方法支持CSS选择器定位元素的写法和前端写样式差不多#id、.class、tag都适用上手很快。第三步是“存储”。把解析出的结构化数据保存下来。入门阶段最简单的方案是写CSV文件用Excel就能打开不需要额外装数据库。后期数据量大了可以再换成SQLite、MySQL或者直接对接pandas做数据分析。这三个阶段对应着一个爬虫项目的完整骨架。你只要把每一步的代码模块化后续换目标网站的时候只需要改“解析”部分的逻辑请求和存储基本可以复用。1.4 适用场景与边界意识基础爬虫最适用的场景一句话概括就是“把重复的信息收集工作自动化”。典型的有商品比价和价格监控、招聘信息聚合、行业报告收集、公开数据整理、舆情关键词监测等等。它的本质是代替人去做那些“打开网页-复制-粘贴”的重复劳动把时间留给真正需要判断力的工作。但边界意识必须从一开始就建立。爬虫能爬不代表应该爬这里有三条底线我建议你直接写进自己的项目规范里第一只爬公开数据不碰需要用户名密码才能访问的私密页面第二遵守目标网站的robots.txt协议和访问频率限制不搞暴力抓取第三爬下来的数据仅用于个人学习或研究不涉及商业用途更不碰个人隐私数据。这不仅是技术问题更是要刻在脑子里的职业习惯。后面第五部分我会展开讲哪些红线千万别碰。2. 核心细节解析与实操要点2.1 HTTP请求的本质你发请求服务器回数据进入实操之前必须先搞懂你发的那个“请求”到底是什么。HTTP协议本质上就一个套路客户端发一个请求报文服务端回一个响应报文。请求报文里最重要的几个部分是请求方法GET/POST、URL地址、请求头Headers、请求体Body。对基础爬虫来说90%的场景都在用GET请求也就是“我想获取某个页面的内容”。requests库发GET请求就一句话import requests url https://example.com response requests.get(url) print(response.status_code) print(response.text)response.status_code拿到的就是HTTP状态码200代表成功403代表服务器拒绝你访问404代表页面不存在。response.text拿到的是服务器返回的HTML源码这就是我们后续要解析的对象。注意一个新手常踩的坑response.text是requests根据响应头里的编码信息自动解码出来的字符串。如果服务器返回的编码声明不对或者干脆没声明response.text就会出现乱码。遇到这种情况不要死磕text属性改成用response.content拿到原始的bytes数据再手动指定编码解码html_bytes response.content html_text html_bytes.decode(utf-8, errorsignore)这个细节在第四部分排查乱码的时候还会重点讲。2.2 Headers与User-Agent别一上来就被拒用代码直接访问网站服务器一眼就能认出来“这不是正常人”因为requests默认的请求头里写着User-Agent: python-requests/2.x.x。这等于进店的时候大声喊“我不是人类”很多服务器出于安全考虑一看这个就直接拒绝访问返回403。解决的办法很简单把自己伪装成真实浏览器。你打开浏览器的开发者工具在Network面板里随便点开一个请求把Request Headers里的User-Agent和Accept-Language复制下来设置到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-Language: zh-CN,zh;q0.9,en;q0.8, } response requests.get(url, headersheaders)这里我多说一句很多教程喜欢贴一堆爬虫用的User-Agent列表让你随机切换。入门阶段你根本不需要搞那么复杂固定用一个真实浏览器的User-Agent就够了。反而频繁切换UA有时候会触发反爬因为正常人类不可能每刷新一次页面就换一个浏览器。实操经验是先不加任何请求头试一次看返回的状态码。如果正常返回200那就不用加如果被拒了再加User-Agent试试。一步一步叠加不要一上来就堆一堆参数否则出了问题你都分不清是哪一步引起的。2.3 状态码的语义200不等于成功403不等于没戏HTTP状态码是服务器和你说的话你得听懂它的意思。基础爬虫阶段你最常遇到的就这么几个200正常返回HTML拿到了。301/302重定向服务器告诉你“页面搬家了”。requests默认会自动跟随重定向所以你通常感知不到。403明白告诉你“我不欢迎你访问”。最常见的原因就是缺User-Agent或者IP被限流了。404这个页面不存在可能是URL写错了也可能是这个网站有规律的URL但具体某个ID对应的数据被删了。429请求太频繁触发了限流服务器让你“歇会儿”。这里有一个基础爬虫最常踩的坑看到200就以为数据没问题结果一解析发现HTML是空的或者全是乱码。为什么因为很多网站的反爬策略是“返回200但给你一个JS挑战页面”或者“返回一个需要验证的中间页”。所以状态码只能作为参考真正判断有没有拿到有效数据还是要看解析结果。后面第四部分我会给你一个完整的排查流程。2.4 编码问题乱码的根源与解决方案乱码是爬虫新手最容易崩溃的问题之一。好不容易把HTML请求回来了结果中文全成了“Ã¤Â¸Âæ–‡”一类的鬼东西瞬间没了兴致。乱码的根源一句话就能解释网页内容在服务器那边是某种编码比如UTF-8或GBK但你解码的时候用了另一种编码去解。就像一把锁配错了钥匙当然打不开。requests库处理编码有一套自己的逻辑先看响应头里的Content-Type字段有没有指定charset有就用它指定的编码没有就尝试从HTML内容里的meta charset...标签去猜。但有些网站的HTML写的规范和实际内容不一致requests猜错了乱码就出现了。排查和解决的办法也很直接。先用response.encoding看看requests认为的编码是什么再看一下response.apparent_encoding这个是requests根据网页内容“猜”出来的真实编码。如果两者不一致直接手动指定response.encoding response.apparent_encoding text response.text或者更简单粗暴的方案拿response.content的bytes数据用chardet这个库自动检测编码再手动decode。这里补充一个我自己的习惯凡是要做内容解析的HTML我都是直接用bytes然后手动decode成UTF-8不依赖requests自动解码省得后面统计字数的时候发现一串乱码。2.5 robots.txt与访问频率职业习惯从第一天开始robots.txt是网站放在根目录下的一个文本文件用来告诉爬虫“哪些路径你可以访问哪些你不准碰”。比如https://example.com/robots.txt你用浏览器直接打开就能看到内容。我在每个爬虫项目的文档里都会写死一条铁律动手写代码之前先看目标网站的robots.txt。这并不仅仅是合规问题它还能帮你避开很多大坑。比如有些网站会在robots.txt里明确写了Disallow: /user/你强行去爬用户信息不仅技术上容易触发风控数据性质上也容易惹麻烦。频率控制同样重要。你想想一个正常人类手动浏览网页再快也就一两秒点开一个页面。你的爬虫如果一秒发十个请求服务器日志里看起来就非常反常触发IP封禁是迟早的事。入门阶段最稳妥的做法就是每次请求之间强制休time.sleep()具体间隔视网站规模而定通常1到3秒比较合理。宁可爬得慢一点也别拿自己的IP赌。3. 实操过程与核心环节实现3.1 环境搭建虚拟环境与依赖安装实操阶段我们用Python 3.10以上版本配合虚拟环境安装依赖。用虚拟环境的习惯建议从现在就开始养成它能保证每个项目的依赖互不干扰。我的操作流程是这样mkdir books_spider cd books_spider python -m venv venv # Windows激活 venv\Scripts\activate # macOS/Linux激活 source venv/bin/activate pip install requests beautifulsoup4安装完成后可以用一句命令验证环境是否就绪python -c import requests, bs4; print(环境就绪)这里说一个小经验如果你只是做简单的脚本不需要装Jupyter Notebook直接在IDE里写.py文件运行就行。Notebook适合做数据分析探索但爬虫脚本要跑很多次还是普通py文件方便调试。3.2 目标站点选择与第一个请求理论知识再多不如跑一个真实案例。这里我用一个专门用于爬虫学习的公开练习网站https://books.toscrape.com/来做演示。这个站点的设计初衷就是让爬虫初学者练习没有复杂的反爬机制页面结构也规整非常适合把整套流程跑通。先用上一节说的方式发一个GET请求把首页HTML保存到变量里import requests url https://books.toscrape.com/ 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 } response requests.get(url, headersheaders, timeout10) print(状态码:, response.status_code) print(页面长度:, len(response.text)) print(页面内容预览:, response.text[:500])跑起来之后你会在控制台看到状态码200页面长度大概有几千字符。看到这里恭喜你第一步已经完成了你的代码已经成功从服务器拿到了HTML内容。这就是爬虫最底层的“获取”环节。3.3 数据解析用BeautifulSoup提取结构化数据拿到了HTML字符串接下来要做的就是“解析”把网页里的商品信息一条一条摘出来。用BeautifulSoup的话第一步是把HTML转成可查询的文档对象from bs4 import BeautifulSoup soup BeautifulSoup(response.text, html.parser)这里第二个参数html.parser是Python自带解析器不需要额外安装。你也可以换成lxml速度更快但需要额外安装。入门阶段用默认的就行。页面上每本图书都在article classproduct_pod这个标签里书名在h3下的a标签里价格在p classprice_color里。用CSS选择器可以这么提取books soup.select(article.product_pod) for book in books: title book.select(h3 a)[0][title] price book.select(p.price_color)[0].text print(title, price)这里有几个细节值得注意。第一select()返回的是列表哪怕只有一个元素你也得用[0]取第一个或者直接判断列表是否为空否则会抛IndexError。第二a标签的属性提取用[title]这种方式拿到的是属性值而不是标签文本。第三价格文本里通常会包含换行符和空格后面可以用strip()清理。关于定位元素我建议你多用浏览器的开发者工具。打开目标网页右键点你想要的数据选择“检查”DevTools就会自动帮你定位到对应的HTML代码。这比你自己对着HTML源码猜要高效得多。3.4 数据存储CSV落盘与异常处理解析出了数据下一步就是存储。CSV是入门阶段最友好的方案用Excel或者任意文本编辑器都能打开不需要配置数据库。Python自带的csv库就能写不依赖第三方模块。import csv with open(books.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([书名, 价格]) for book in books: title book.select(h3 a)[0][title] price book.select(p.price_color)[0].text.strip() writer.writerow([title, price])这里有一个非常容易踩的坑encoding参数不要写utf-8要写utf-8-sig。不加-sig的话你用Excel打开CSV文件中文字段名会变成乱码。原因在于Excel默认按ANSI编码读取CSV而带BOM头的UTF-8文件才能被正确识别。这个小细节我当年卡了将近半小时建议你一次到位。3.5 完整可运行的爬虫示例把请求、解析、存储三步合起来一个完整的爬虫脚本就成型了import csv import time import requests from bs4 import BeautifulSoup def fetch_page(url): 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 } response requests.get(url, headersheaders, timeout10) response.encoding response.apparent_encoding return response.text def parse_books(html): soup BeautifulSoup(html, html.parser) books [] for item in soup.select(article.product_pod): title item.select(h3 a)[0][title] price item.select(p.price_color)[0].text.strip() books.append({title: title, price: price}) return books def write_csv(books, filenamebooks.csv): with open(filename, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([书名, 价格]) for book in books: writer.writerow([book[title], book[price]]) if __name__ __main__: url https://books.toscrape.com/ html fetch_page(url) data parse_books(html) write_csv(data) print(f成功写入 {len(data)} 条数据) time.sleep(1)这个脚本的架构虽然简单但已经体现了模块化思想请求一个函数、解析一个函数、存储一个函数。后续你想爬更多页面只需要封一个循环把每一页的URL逐个传进去就行不需要改动内部逻辑。这也是我认为基础阶段最应该养成的代码习惯。4. 常见问题与排查技巧实录4.1 请求被拒或返回403先从伪装做起403是最常见的反爬信号。排查思路我建议按这个顺序来第一步确认是否设置了User-Agent没有的话先加上第二步检查是否缺少其他关键Header有些网站会校验Referer、Accept这些字段第三步确认访问频率是不是太快连续请求之间有没有做暂停。如果加了User-Agent还是403可以打开浏览器DevTools把你的真实请求头整个复制下来替换到代码里。这个方法在90%的轻量级反爬场景下都能解决问题。你可能会遇到有人提到需要换IP这种进阶操作但那属于另一层级的对抗了。基础阶段先把Headers伪装和频率控制做好大部分网站根本不会对你动手。4.2 拿到了HTML但是一片乱码乱码问题我在第二部分已经提过这里给一个标准排查流程。先看response.encoding和response.apparent_encoding是否一致不一致就手动把response.encoding设置为apparent_encoding的值。如果设置了还是乱码那可能是页面内容本身就是Base64编码或其他编码格式的文本需要进一步查看对应网页源码里的特殊处理。另一个很容易被忽略的情况是你控制台看到的乱码并不代表数据文件里是乱的。有时候是终端编码不支持中文显示但写到CSV里一切正常。所以出现乱码先别慌把数据落盘再看看文件别在控制台显示这一层耗太多时间。这里再给一个我自己维护的快速判断方法拿一小段疑似乱码的文本丢到搜索引擎里搜一下如果搜出来正好是奇怪的语言文章那就是编码错位了如果完全搜不到任何结果那可能是内容本身被加密了方向就不同了。4.3 请求超时与连接异常异常处理写全套爬虫跑着跑着突然抛TimeoutException或者ConnectionError这是常态。目标服务器不稳定、你的网络波动、目标网站对你做了限流都可能导致请求失败。入门阶段最常见的错误是不做异常处理让程序直接崩溃退出。标准做法是给每个请求设置超时时间并用try-except把异常捕获住import requests from requests.exceptions import RequestException try: response requests.get(url, headersheaders, timeout10) response.raise_for_status() except RequestException as e: print(f请求失败: {e}) else: html response.texttimeout10表示10秒内连接不上就放弃raise_for_status()会把4xx和5xx状态码变成异常抛出来。这样形成一个逻辑请求失败就记录日志并跳过程序继续跑不会五万条数据爬到一半突然整个挂掉。4.4 动态页面拿不到数据数据可能渲染在JavaScript里基础爬虫最大的拦路虎是你用requests拿到的HTML里根本找不到你想要的数据。最常见的原因是页面内容是JavaScript动态渲染的浏览器先把HTML框架加载出来再通过Ajax请求后台接口拿数据最后填充到页面上。你的requests只拿到了那个空壳子HTML自然什么都解析不到。遇到这种情况排查顺序是先打开浏览器的DevTools切到Network面板刷新页面后重点看XHR类型的请求。如果发现某个接口返回的是JSON格式的数据里面刚好包含你要的字段那就有救了直接请求那个接口解析JSON比解析HTML还简单。这种方法通常叫“直接请求接口”是基础爬虫进阶到中级的一个重要能力节点。如果数据在接口返回的JavaScript代码里就用正则去匹配。比如很多网站首屏数据会嵌在window.__INITIAL_STATE__ {...}这种变量里。用正则截取这段变量名和JSON数据再丢给json.loads()解析也是常见的套路。实在不行才考虑用Selenium或Playwright这类自动化浏览器方案但那是最后的手段不要在基础阶段轻易上因为开浏览器抓页面的效率和稳定性都远不如直接请求HTML或接口。4.5 常见问题速查表我把自己带新人时经常遇到的问答整理成了一张速查表按“问题-原因-解决方案”排在下面排查的时候对照着来就行。现象常见原因推荐排查顺序返回403缺少User-Agent或触发反爬规则添加浏览器请求头检查频率检查是否需要登录态返回200但页面为空数据由JS动态渲染检查Network里的XHR接口直接解析JSON接口中文乱码响应编码识别错误比对encoding和apparent_encoding手动指定UTF-8/GBK请求超时网络波动或服务器限流增加timeouttry-except捕获后重试延长sleep间隔数据解析不到CSS选择器写错或页面结构变了用DevTools重新确认元素位置验证选择器前的select()返回值长度爬了一阵突然被拒绝频率过高触发限流加大请求间隔随机休眠降低并发CSV打开中文乱码文件编码缺少BOM用utf-8-sig替代utf-8写入这张表涵盖了基础爬虫阶段80%的“为什么会这样”的困惑。你会发现大多数问题其实指向同一个根源你的行为和正常用户差异太大。5. 合规使用与进阶方向5.1 合规底线能不能爬先看这四条这一部分我必须用相对严肃的语气写因为新手最容易在这上面栽跟头。爬虫本身不违法但用爬虫的方式和目的决定了你做的事是不是稳妥的。我给自己定过四条标准也建议你参考第一只访问公开数据。需要登录、付费才能看的内容就不要去硬闯。技术上也许能破解登录态但那已经明确越过了边界风险从量变到质变。第二严格遵守robots.txt。网站怎么约定爬虫访问的规则就按它的规则来。第三控制请求频率和总量。你是去做数据收集不是去做压力测试别把一个站爬到瘫痪。第四用途限定。拿数据做个人学习、做公开信息的整理分析都是合理的拿去倒卖、批量生成内容、侵犯他人权益那是绝对的红线。我见过太多人问“为什么我的IP被封了”十个里有八个是开爬虫跑了一晚上没加sleep把目标站点的访问频率干到了极限。技术问题好解决习惯问题难改。慢就是快这是爬虫这行最需要记住的一句话。5.2 进阶方向从单页到规模化跑通基础流程之后你会遇到越来越多“不够用”的场景。这时候就自然进入进阶阶段了。主要方向有这么几个第一个是分布式爬虫。单机单线程爬得慢你可以用Redis加Scrapy-Redis来把任务分发到多台机器上跑。第二个是异步抓取。aiohttp这类异步框架能在单线程里同时发起成百上千个请求吞吐量比同步爬虫高一个量级但异步编程的调试难度也直线上升。第三个是反爬对抗。从基本的Headers伪装、Cookie处理到IP代理池、验证码识别再到指纹模拟这个方向深不见底也是我不建议新手一上来就碰的原因。第四个是数据管线的完善。把存储从CSV升级到数据库定时调度、增量更新、数据清洗这些都是数据工程师的日常了。我的建议是先把这本书示例项目扩展成多页爬取把50页的图书数据全部存进SQLite这一步能覆盖从“单页脚本”到“小批量采集任务”的跨越。再往后想走多深看你自己的职业方向和对技术的兴趣。5.3 一个能少熬夜的小技巧最后分享一个我一直在用的debug习惯在解析函数里加日志输出不要静默跑。每解析完一个item就往控制台打一条结构化日志import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logging.info(f解析完成: {title} {price})这样一个爬虫跑起来你能实时看见它到底跑到了哪一步、每一条数据长什么样。出错的瞬间日志就是你回溯现场的最佳工具。爬虫跑一晚上第二天醒来发现凌晨三点就崩了如果没有任何日志你连崩在哪一行都不知道。打开日志文件一眼定位这种习惯能帮你少熬不少夜。我在实际操作里的最大体会是爬虫的核心从来不是写代码而是理解网页和HTTP。代码只是那层皮你真正在做的是把“人怎么能看懂网页”这个逻辑翻译给程序听。所以每当我解析不到数据的时候我第一步永远是打开浏览器亲手看一遍页面想清楚数据从哪来再去看代码。这个思路几乎解决了我90%的困惑。希望这篇项目总结能帮你把上面这套流程跑通然后你会发现爬虫不过是个开始真正有意思的其实是“拿数据去做事”这个更大的世界。