ARTICLE DETAIL

资讯详情

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

Python爬虫入门:CSDN博客文章列表抓取实战

Python爬虫入门:CSDN博客文章列表抓取实战 很多朋友第一次接触 Python不是被语法劝退就是被所谓的“数据分析”劝退。我倒建议从爬虫开始见效最快也最能建立正反馈。这篇文章要做的主题很具体零基础用 Python 爬取 CSDN 博客文章列表。我把一个完整项目从思路、环境、请求、解析到翻页、反爬一步步拆开讲你跟着抄作业就能跑出结果。爬虫不是魔法就是把你手动复制粘贴几百条数据的过程用代码自动跑一遍。CSDN 作为国内流量很大的技术博客平台页面结构复杂也有反爬机制拿它练手既贴近实战又不会太难特别适合刚学完 Python 基础语法、想找第一个完整小项目的读者。我会把整个项目拆成五六个环节每个环节讲清楚“为什么这么做”和“怎么落地”还会把我在实际抓取中踩过的坑、总结的排查技巧一起放进来。你不需要一开始就理解所有细节先跑起来再回头看原理。1. 先搞清楚爬虫能做什么以及边界在哪里爬虫的本质就是模拟浏览器向服务器发请求拿到 HTML 响应后再把里面有用的字段提取出来。用生活化的话说你不是用眼睛去看网页而是让代码替你去网页上看然后按你定好的规则抄下来。CSDN 文章列表页面上有标题、链接、发布时间、阅读量、点赞数手动复制一百条得花半小时脚本几秒钟就搞定。这也是爬虫最典型的使用场景批量获取一个作者的全部文章目录做整理或者做后续分析。1.1 一句话讲清楚爬虫原理一个正常的网页请求流程是这样的浏览器输入网址DNS 把域名解析成 IP向服务器发送 HTTP 请求服务器返回 HTML 文档浏览器再把 HTML 渲染成你能看到的样子。爬虫做的事情和浏览器几乎一样唯一区别是浏览器拿到 HTML 后渲染成页面爬虫拿到 HTML 后直接当纯文本处理。Python 里有几个知名库可以完成这件事requests负责发请求BeautifulSoup4或者lxml负责解析 HTMLpandas负责把结果整理成表格导出。CSDN 首页和个人主页都是服务端渲染加部分前端异步渲染的混合体。个人博客的文章列表页核心的文章列表数据基本是服务端渲染好的所以请求返回的 HTML 里可以直接看到文章标题和链接。这个特性让初学者的爬虫难度大幅降低你不需要用 Selenium 去模拟浏览器也不需要逆向分析接口签名只靠requests加BeautifulSoup就能完成任务。1.2 关于 CSDN 页面结构和 robots 协议记得先说合规。任何一个网站都有一个robots.txt文件相当于网站的“访客须知”。CSDN 的robots.txt对爬虫有明确规则搜索引擎爬虫和普通个人脚本的权限不同。你如果只是小规模、低频、用于个人学习的数据抓取控制好请求频率不要影响网站正常服务一般不会有问题。但如果你要把爬下来的内容批量发布、商用、或者大规模抓取那就需要慎重评估合规风险了。尽量不要去爬需要登录才能看的付费内容或者私密数据这是底线。CSDN 个人博客页的 URL 规则很规律一般是https://blog.csdn.net/用户名/article/list/页码。这个规律是很容易从浏览器地址栏观察出来的。文章详情页的 URL 则是一串长数字 ID列表页里就有。我们做的第一个爬虫项目就从列表页开始因为列表页信息密度高一篇博客的标题、发布时间、阅读量都挤在一个标签结构里非常适合练手。2. 环境准备零基础也能装好的 Python 工具箱开始写代码之前先把环境准备好。很多新手把时间浪费在环境问题上后面所有例子都会走样。我用的是 Python 3.9 以上版本官方安装包安装的时候记得勾选“Add Python to PATH”这一步能省掉后面很多麻烦。2.1 Python 安装与虚拟环境如果还没装 Python去 python.org 下载对应系统的安装包一路点下一步就能装好。装好后打开命令行输入python --version能输出版本号就算成功。我不太建议一开始就用 Anaconda对爬虫这种轻量项目来说有点重标准 Python 加 pip 就够了。项目要单独建目录目录下建议用虚拟环境。虚拟环境的作用是把当前项目依赖的库和系统里的全局库隔离开避免后面项目多了互相冲突。Windows 下用python -m venv venv创建进入虚拟环境后再用pip install安装库就只装到本项目的环境里清爽很多。注意Linux 或者 macOS 下进入虚拟环境的命令是source venv/bin/activateWindows 下是venv\Scripts\activate。2.2 必备库清单这个项目需要三个库requests发 HTTP 请求替代浏览器的网络请求能力。beautifulsoup4解析 HTML提取结构化数据。lxml作为 BeautifulSoup 的解析引擎解析速度更快。安装命令一条搞定pip install requests beautifulsoup4 lxml装完可以用pip list看一眼版本。我实测下来比较稳定的是 requests 2.31.x、beautifulsoup4 4.12.x、lxml 4.9.x这几个版本兼容性很好按这套组合来不会遇到奇怪报错。除了这几个库后面为了排查问题可能还要用到json和time这两个是 Python 内置库不需要额外安装。如果想把导出的数据做得更漂亮可以再装一个pandas不过那不是必需项等基础流程跑通了再考虑也不迟。3. 第一步先发送一个请求把页面“拿”下来写爬虫最兴奋也最容易出问题的一步就是第一次拿到网页的 HTML。很多新手在这里卡住不是代码写错而是被反爬机制挡住了。我们用 requests 先拿一个列表页拿回来再打印出来看看。3.1 用 requests 发送 GET 请求一个最简单的请求代码是import requests url https://blog.csdn.net/你的用户名/article/list/1 response requests.get(url) print(response.status_code) print(response.text[:2000])如果你用的是别人博客的 URL应该能拿到 200 状态码和一段 HTML。如果你用了自己的博客 URL 还没发布任何文章页面可能是空列表甚至被重定向。所以我建议先拿一个大佬的博客列表页练手比如那些文章很多的作者数据够多结构也完整。这里有个重要概念response对象里存的不只是网页源码还包括状态码、响应头、Cookie 等信息。状态码 200 表示请求成功403 表示被拒绝418 表示被识别成爬虫这个后面详细说。第一次打印出来如果是一大团 HTML别慌接下来的任务就是从这个大团里抠出想要的信息。3.2 加上响应头模拟真实浏览器纯requests.get有一个问题服务器看到请求头里没有浏览器标识很容易识别出你不是真人。CSDN 的反爬不是特别严格但最好一开始就把请求做得专业点。常规做法是设置User-Agent就是告诉服务器“我是一个浏览器”。我从浏览器里复制一串真实的 UA直接放进请求headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 } response requests.get(url, headersheaders)这里有个细节UA 里的操作系统、浏览器版本要尽量真实有些反爬系统会对 UA 做版本校验太老的版本也可能被拒。我用的是 Chrome 的 UA兼容性非常好。还有一个常见的坑是编码问题。CSDN 的页面编码是 UTF-8正常情况requests会自动处理。但如果你打印出来是乱码可以手动指定编码response.encoding utf-8这一步要放在访问response.text之前。我自己调试的时候一般的经验是先把状态码、编码、响应内容前几百个字符都打印出来确认拿到的不再是验证码或者跳转页面再做下一步。4. 第二步用 BeautifulSoup 解析列表页提取文章标题、链接、发布时间页面已经拿到手现在是核心环节解析 HTML。HTML 本质是一堆标签嵌套我们要做的就是从这堆标签里精准找到文章列表所在的位置。BeautifulSoup 提供了非常友好的 API哪怕是完全没有前端经验的读者也可以按照选择器这套逻辑来操作。4.1 观察 HTML 结构解析之前要先知道目标数据长什么样。最快的办法是在浏览器里打开目标列表页按下 F12 打开开发者工具用鼠标点一下文章标题观察它在 HTML 里是怎么包裹的。CSDN 的列表页结构在不同时期有过改版目前我测试的版本里每一篇文章是一个div classarticle-item-box里面嵌套了标题h4、链接a href...、时间span classdate、阅读数span classread-num。这就是爬虫里常说的“定位”过程。找到根节点再一层层往下找子节点。BeautifulSoup 的做法是先找到所有符合根节点的元素再用find或者select在这个元素内部继续查。4.2 提取核心字段下面这段代码展示了最基础的提取方式from bs4 import BeautifulSoup soup BeautifulSoup(response.text, lxml) items soup.select(.article-item-box) for item in items: title_tag item.select_one(h4 a) title title_tag.get_text(stripTrue) if title_tag else 无标题 link title_tag.get(href) if title_tag else 无链接 date_tag item.select_one(.date) date date_tag.get_text(stripTrue) if date_tag else 无时间 print(title, link, date)这里用到了 CSS 选择器select返回所有匹配的元素select_one返回第一个匹配的元素。.article-item-box中的点号表示 class 属性h4 a表示 h4 标签里的 a 标签。get_text(stripTrue)是把标签里所有文字提取出来并去掉首尾空白。.get(href)是取 a 标签的链接属性。如果你运行之后发现items是空列表那大概率是页面结构变了或者被反爬拦截。这时候回到第 3 步检查状态码和返回内容别急着改选择器。4.3 清洗数据和统一格式提取出来的字符串通常会夹杂换行、空格、甚至制表符。比如标题可能带了一堆空白阅读数可能是“1万”这样的数据直接存进表格会很难看。清洗这一步虽然在第一个项目里看着不必要但对后续学习非常重要养成习惯能受益很久。title title.strip().replace(\n, ).replace(\r, ) if 万 in read_num: read_num str(int(float(read_num.replace(万, )) * 10000))遇到“1万”这种格式可以先去掉“万”转成浮点数再乘一万最后转成字符串这样数据就变成统一的纯数字。日期字段同理CSDN 的日期有时候是“2024-12-25 10:00:00”有时候是“6天前”要统一成标准格式还得做一些判断。第一个版本可以先原样存储跑通之后再优化。我把列表页解析结果先存到一个列表里result [] for item in items: data {} data[title] title data[link] link data[date] date data[read_num] read_num result.append(data)最终打印或者导出都很方便。这个result列表就是你抓下来的结构化数据后面存 CSV 或者 JSON 都是顺手的事。4.4 用函数封装解析逻辑爬虫代码最容易越写越长如果不做函数封装后面想调整一个解析逻辑时会非常痛苦。我建议从一开始就把每一步拆开一个函数负责发请求一个函数负责解析一个函数负责保存数据。哪怕代码现在还小但等你要抓更多字段、更多页面的时候这个架构的价值就会立刻体现出来。def fetch_page(url): response requests.get(url, headersheaders, timeout5) response.encoding utf-8 return response.text def parse_page(html): soup BeautifulSoup(html, lxml) result [] items soup.select(.article-item-box) ... return result这种写法最大的好处是定位问题成本低。如果某个页面解析失败你只需要测parse_page这个函数不用连带着看请求逻辑里有没有问题。初学者看到这段代码也许会觉得绕但相信我你后面改起需求时会感谢当初把代码写下得像个项目而不是一段脚本。5. 第三步翻页抓取多页并处理反爬常见问题单个列表页只有二十来篇文章真正要抓完整一个作者的所有文章必须处理翻页。CSDN 的翻页规律非常好猜URL 里最后一位数字就是页码第一页是 list/1第二页是 list/2以此类推。所以翻页本质就是循环改变 URL 里的页码数字。5.1 翻页逻辑用 for 循环来控制页码一个非常朴素的实现是这样for page in range(1, 6): url fhttps://blog.csdn.net/用户名/article/list/{page} html fetch_page(url) page_data parse_page(html) result.extend(page_data) time.sleep(1)这里的range(1, 6)表示抓取前 5 页。time.sleep(1)是每抓一页之后歇一秒这么做是为了降低请求频率不给对方服务器增加压力。很多人容易忽略这个细节一次性瞬间发几百个请求很容易触发反爬IP 被封之后再想做就难了。翻页过程中有个隐藏问题翻到后面页面可能没有数据了继续循环只是在浪费请求。更好的做法是判断当前页解析出来的items是空列表就提前退出循环if not parse_page(html): print(已经没有更多文章了) break这个判断逻辑看着简单实际在抓取动态数据时特别有用。有些博客作者只有两三页文章硬要抓 10 页纯粹是给自己找风险。5.2 请求频率控制与异常处理time.sleep是控制频率最简单的手段但这里面的数值选择有讲究。如果对方服务器比较宽容0.5 秒间隔就能接受如果遇到较严格的反爬可能需要 2 到 3 秒。我自己的习惯是至少 1 秒起步结合随机数让请求间隔不固定更像真人的浏览节奏import random time.sleep(random.uniform(1, 2))这样既不会给服务器压力也让请求时间分布更自然。这一步是新手很容易忽略的经验技巧等被封了才追悔莫及。同时要处理网络异常。网络请求不是永远成功的超时、连接重置、偶尔的 5xx 错误都很正常。不处理异常程序会直接崩溃在中间某个页面。稳妥做法是给请求加上try...except...并且设置超时参数try: response requests.get(url, headersheaders, timeout5) response.raise_for_status() except requests.exceptions.RequestException as e: print(请求失败, url, e) return None这里timeout5表示超过 5 秒没响应就放弃raise_for_status会在状态码是 4xx/5xx 时主动抛异常。这种防御性编程看着啰嗦但实际抓取几百页时它就是稳定性的保障。一次失败不要直接退出记录一下 URL继续跑剩余的页面最后统一处理失败项是更成熟的方案。5.3 常见状态码解读跑脚本时你会遇到各种 HTTP 状态码每个状态码都在向你传递信息。状态码含义常见原因处理建议200请求成功正常响应继续解析301/302重定向URL 规则变了或触发了登录跳转检查 URL 是否带 Cookie403没有权限IP 被封或 UA 被限制换 UA、加延时或换 IP404页面不存在用户名或页码错误修正 URL418被识别为爬虫请求特征太明显伪装请求头或换策略500/502服务器错误对方服务器临时故障休息后重试注意降低频率418 是个很特殊的错误码它源自一个著名的咖啡壶协议彩蛋但在现在的反爬体系里它基本上就是“服务器识别你是爬虫”的代称。遇到 418先不要急着换 IP检查一下自己的请求头是不是太裸了再确认请求频率是不是太快。很多时候从这两点入手就够了。6. 常见问题与排查技巧实录说来也巧我最早写这个爬虫项目时几乎把所有坑都踩了一遍。有些问题当时搜索很久才找到答案现在整理出来希望能帮你少走弯路。这里挑几个高频问题每一个我都有可复现的排查步骤。6.1 返回空列表但浏览器里明明有文章这是新手遇到概率最高的现象。代码写完了流程也没报错但items就是空。第一步先确认返回的 HTML 里是不是真的包含.article-item-box。方法很简单把html存成 txt 文件用编辑器搜索一下这个 class 名with open(debug.html, w, encodingutf-8) as f: f.write(html)如果文件里搜不到说明页面结构变了或者你拿到的是一个跳转后的空壳页面。如果搜得到说明解析代码里的选择器写错了。CSDN 页面改版不算太频繁但不同用户主题可能影响 HTML 结构中部分 class 名称你用别人的博客页面学习时结构和使用自己账号时的版本可能略有差异一切以实际页面为准别迷信老教程里的固定写法。6.2 编码问题导致标题乱码标题里的中文变成了一堆乱码字符多半是解码方式不对。CSDN 的页面是 UTF-8但有时服务器响应头里没标明requests 就会用默认的 ISO-8859-1 去解码结果就是乱码。解决办法就是在代码里明确指定response.encoding utf-8这个坑在 Python 爬虫里非常经典凡是遇到中文乱码第一反应永远是检查编码而不是换解析库。6.3 阅读数或者日期提取不到CSDN 列表页的阅读数和日期标签在不同页面版本里可能有差异。有时候同一个作者的页面里老文章和新文章的 HTML 结构不完全一致。比如第一篇文章的日期是.date类第二篇可能是.time类如果你只写了.date就会有一部分数据缺失。对策是抓取前先做一个数据抽样检查打印出前两篇文章的完整 HTML 片段肉眼确认标签确实存在再写正式的选择器。具体操作可以在浏览器开发者工具里对某个元素右键点“Copy selector”把复制到的选择器先测试一轮再批量应用。6.4 访问量太大触发反爬被限制访问我遇到过一次 418 错误当时是连续抓了差不多一百页没有停顿结果 IP 被临时限制后面所有请求都开始返回 418。那时候才意识到频率控制不是可有可无的装饰。排查时我先观察返回的 HTML 内容发现里面不是文章的列表而是一个请求验证页面。用浏览器访问同样的地址填一下验证码又恢复了但脚本再跑还是会被拒。解决思路分三层第一是降低请求频率把time.sleep从 1 秒提到 2 秒以上第二是强化请求头把Accept、Accept-Language、Referer这些字段都补上伪装得更像浏览器第三是如果频率降到很低仍然被限制考虑切换 IP 或加代理但这就是另外一套方案了。对于初学者项目我强烈建议先从第一层和第二层入手绝大多数场景都能解决。6.5 请求结果被重定向到登录页有些用户的博客可能设置了访问限制或者你的请求触发了登录拦截。此时requests返回的 URL 变成了一个 login 页面地址。排查时可以打印实际请求后的最终 URLprint(response.url)如果这个 URL 里面出现login或者passport之类的关键词说明被引导去登录了。此时最简单的办法是换个不需要登录的公开用户页面学习阶段先不碰需要登录态的接口。后面如果确实需要登录数据那是另一个关于会话管理和 Cookie 的项目进阶之后再搞。重要的经验学习爬虫永远从最少依赖、最公开的数据页面开始。不要第一步就挑战高难度的登录、验证码、字体反爬先把基础链路跑通信心建立了后面研究难题才有底气。结束语从抓取到整理的完整流程以及下一步可以怎么做跑通上面的流程之后你已经能把一个作者的文章标题、链接、发布日期、阅读量抓下来。通常我会建议再补一个步骤把数据存到 CSV 文件import csv with open(articles.csv, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[title, link, date, read_num]) writer.writeheader() writer.writerows(result)这里utf-8-sig编码是为了让 Excel 打开 CSV 时中文不乱码一个小细节能省掉很多麻烦。有了这份 CSV你可以统计这个作者的文章发布频率、阅读量变化趋势甚至可以按标题里的关键词做分类。你会发现爬虫只是开始数据清洗、分析、可视化才是更大的宝库。我个人在实际操作中的体会是初学者做爬虫项目最容易犯的错不是代码不会写而是太心急。先学会打印、调试、保存中间结果每一步都确认数据是对的再做下一步整个项目就会顺畅很多。还有一个小技巧每次抓取前先在浏览器里刷新一次目标页面确保页面结构没变这样能避免很多无意义的报错调试。如果你已经跑通了这篇文章里的代码下一个值得挑战的方向是把抓取目标从列表页扩展到文章详情页提取正文内容和摘要然后尝试对正文做简单的关键词统计或者生成词云。再往后可以学习用selenium处理那些需要 JavaScript 动态渲染的页面也可以学用scrapy构建更稳定的分布式抓取框架。但不管走哪个方向今天这套请求、解析、清洗、控制频率的基本功都会是你最常用的底层能力。爬虫这东西入门看似简单想做得又快又稳、还不给对方服务器添麻烦里面每一环都有值得打磨的细节。希望这篇文章能成为你踩完第一个坑之后的垫脚石。
返回列表