☰
Python爬虫零基础入门:Requests请求、数据解析与存储全攻略
2026/10/17 13:10:08 网站建设 项目流程

简介:面向零基础新手和希望提升数据获取能力的技术爱好者,这套Python爬虫全攻略以循序渐进的方式,系统讲解爬虫架构中URL管理、网页下载、网页解析、数据存储与调度器五大组件,并配套requests、BeautifulSoup、Selenium等常用工具的真实代码示例,帮助读者快速上手静态网页采集、动态页面渲染攻破以及常见反爬措施应对。资源为单个PDF压缩包,大小441KB,内容编排紧凑、便于离线查阅,可作为从入门到进阶的随身速查手册。目前已有307人学习下载,热度持续走高。文中通过新闻标题抓取、JS动态内容提取、代理IP与随机延迟策略等三个由易到难的实战案例,完整展示数据采集流程,同时以专节强调Robots协议、请求频率控制与数据合法使用等法律红线,让读者在合规前提下掌握可落地的自动化采集能力。

1. 零基础入门Python爬虫:为什么说这项网页数据采集技术最值得先学

很多人一听到“爬虫”就想到复杂的命令行和看不懂的加密算法,实际上零基础入门爬虫,只需要三样东西:一台能跑Python的电脑、一个目标网页、一份能把网页内容按规律抽出来的代码。它解决的具体问题是“每周自动汇总某类公开信息”“定时采集价格变动”这类手工复制粘贴效率极低的重复劳动。学完Python基础语法之后,爬虫往往是最快能见到实际成果的方向。

这项技术适合两类人:刚学完循环和字符串操作、想快速做出真东西的新手;以及需要定期获取网页数据、但没打算深入前端开发的业务人员。不用等学完所有Python知识再动手。请求、解析、清洗、入库这条链路每走通一步,都会加深对文本处理和程序结构的理解,而且每一步都有看得见的结果。

这篇攻略按“发请求、做解析、清数据、防翻车”的顺序展开。每一章的代码都可以直接复制运行,注释里标注了新手最容易漏的参数,后面还会单独讲几个高频踩坑现场。

2. 把请求原理讲透:Requests库的六个必懂参数与最小抓取命令

2.1 HTTP请求的底层顺序:URL、请求方法、状态码

爬虫的第一步是拿到网页内容。浏览器地址栏里输入网址回车,本质上是在向服务器发一条“请把某个资源给我”的请求。这条请求包含三块关键信息:请求方法(最常见的是GET和POST)、目标URL、以及一堆由浏览器自动附带的请求头字段。服务器处理完后,返回一个状态码和响应正文。状态码决定了程序下一步怎么做:200直接解析正文;403说明服务器不欢迎这次来访;404说明目标页面不存在;503常见于服务器过载或正在维护。

URL本身也有结构。拿“协议 + 域名 + 路径 + 查询参数”来说,路径决定取哪个页面,查询参数往往决定页面里展示什么内容。做爬虫时,很多人只盯着网页主体,忽略了查询参数其实是分页与筛选的开关。很多资讯列表页,第二页就是在URL后面加一个类似page=2的参数,掌握这个规律后,批量抓取就从“手动翻十次页面”变成“循环改一个参数”。

还有一点容易被忽略:响应头里的字段是有价值的排查线索。比如Content-Type告诉你返回的是HTML还是JSON,Set-Cookie说明服务器下发了一个Cookie,Content-Encoding告诉你正文是否经过了gzip压缩。第一次抓某个站点时,我会先把响应头完整打印一遍再动手写解析逻辑,这比盯着正文猜要快得多。

2.2 最小抓取命令:GET请求、timeout、headers

Requests是Python里最常用的HTTP请求库,它把底层的socket通信封装成了几个简单函数。先看一个可以直接运行的最小例子。

import requests # 最基本的GET请求 resp = requests.get('https://example.com/news', timeout=10) # 打印状态码,确认服务器是否正常返回 print(resp.status_code) # 打印前500个字符,确认返回内容是不是预期的页面 print(resp.text[:500])

这段代码的逻辑很直接:get()发起请求,服务器返回的响应对象保存在resp里;status_code用来判断请求结果,.text保存响应正文内容。代码里最容易被新手忽略的是timeout=10,它表示最多等10秒,超过就抛异常。没有这个参数时,遇到响应慢的服务器,程序可能一直挂在那里,看起来像死掉了,实际是在等一个永远不会回来的数据包。

实际使用中,这么裸写经常拿不到内容。不少服务器会检查来访客户端的User-Agent字段。默认情况下Requests会发送一串以python-requests开头的标识,服务器识别出来就直接拒绝。常见的做法是请求时带上一个浏览器风格的请求头:

import requests # 用字典结构模拟浏览器请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0', 'Accept-Language': 'zh-CN,zh;q=0.9', } resp = requests.get('https://example.com/news', headers=headers, timeout=10) print(resp.status_code) print(resp.text[:500])

这里把User-Agent写成了常见浏览器的形式,服务器看到它来自主流浏览器内核,才会按正常页面逻辑给出内容。Accept-Language表示希望收到中文内容。注意一个原则:UA只是一段自报身份的字符串,它解决的是“正常浏览器访问”这一类的拦截,不是万能钥匙。服务器如果做了账号权限控制,加再多请求头也进不去。合规爬虫只对公开页面做低频采集,这个边界一开始就要立住。

2.3 还有四个必懂参数:params、Session、重试、编码

除了headers和timeout,还有四个参数几乎天天遇到。先看参数表。

参数 / 对象作用使用建议
params以字典形式传URL查询参数自动做URL编码,替代手拼字符串
Session保持Cookie和会话状态需要登录态或连续请求时使用
verify控制SSL证书校验默认True;只访问自签名证书的测试站点时才改为False
encoding指定返回文本的解码方式中文乱码时优先试gb18030

params把查询参数拆成字典传进去,比如{'page': 1, 'size': 10}。Requests会自动把这些键值对拼到URL后面,并对中文和特殊字符做编码处理。手拼字符串经常漏掉编码环节,参数里一旦出现中文,拼出来的URL就是坏的。代码里写法是:

import requests params = {'page': 1, 'size': 10} resp = requests.get('https://example.com/news', params=params, timeout=10)

Session用来保持会话状态。某些站点首次访问会下发Cookie,后续页面靠这个Cookie识别访客。直接用requests.get是无状态的,Cookie会丢。改成“先建一个会话,再用会话发请求”,Cookie就会自动保留:

import requests session = requests.Session() session.headers.update({'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}) resp = session.get('https://example.com/news', timeout=10)

Session的核心用法是把公共请求头统一放在session.headers里,后续所有请求自动带上。爬一个需要翻很多页的列表站,用Session写起来最顺手。

verify参数大多数时候不用动。只有访问企业内网、自签名证书的测试站点时,不关掉证书校验会报SSL错误。正常公网站点建议保持默认,关掉校验等于放弃了加密链路的安全确认。

编码问题是中文站点的高频坑。服务器返回的是字节流,Requests在响应头没有明确charset时,会按自己的猜测解码。很多老站点的实际编码是gbk,被默认按utf-8解码后中文就变成乱码。处理方式很简单:

resp.encoding = resp.apparent_encoding

apparent_encoding会根据返回内容的字节分布做统计推断,多数情况下比默认值准。遇到推断失败的,直接在代码里写死resp.encoding = 'gb18030'。gb18030是GBK的超集,覆盖字符更全,不少做过几年的爬虫脚本里都能看到这一行的影子。

最后补一个重试机制。网络请求随时可能因为波动失败,单次失败就退出会让整个采集任务半途而废。我一般会在请求外层包一个三次重试的小循环:

import time for attempt in range(3): try: resp = requests.get('https://example.com/news', timeout=10) resp.raise_for_status() break except requests.exceptions.RequestException: print(f'第{attempt + 1}次请求失败,等待2秒后重试') time.sleep(2)

raise_for_status()会在服务器返回4xx或5xx时主动抛异常,这样重试逻辑不用手动判断状态码。每次失败后停2秒再试,避免对服务器造成瞬时压力。注意重试只对瞬时波动有效,服务器明确拒绝时,无限重试只是把报错时间推迟,正确做法是停下来检查请求头或调整采集对象。

2.4 GET与POST:什么时候该换一种请求方法

GET适合查询、分页、筛选这类只读操作,参数拼在URL里;POST把参数放在请求体里,适合提交表单、触发搜索这类操作。有些搜索页面用浏览器打开时地址栏看不到参数,大概率就是POST:

import requests # POST请求示例:表单数据放在请求体里 payload = {'keyword': 'Python爬虫'} resp = requests.post('https://example.com/search', data=payload, timeout=10) print(resp.status_code) print(resp.text[:200])

data参数用于表单格式,json参数用于提交JSON格式的API数据。判断标准是看服务器要求什么,浏览器开发者工具里提交请求时能看到Content-Type的值,如果写的是application/x-www-form-urlencoded就用data,如果是application/json就用json。初学阶段把握一个准则:先从浏览器能正常打开的页面入手,GET能解决就先用GET,POST留给明确的搜索表单场景。请求层稳了,后面解析才有意义。

3. 数据解析的三条路线:正则、XPath、CSS选择器怎么选

3.1 解析的本质:把HTML标签当树看

爬虫拿到的是整段HTML文本,里面除了数据还有大量标签、属性和样式。解析要做的就是按规则从这段文本里取出目标数据。把HTML的嵌套结构理解成树是关键:每个标签都是树上的一个节点,标签之间是父子关系。看下面这段HTML:

<div class="news-list"> <a class="title" href="/news/1">第一条标题</a> <a class="title" href="/news/2">第二条标题</a> </div>

div是父节点,两个a标签是它的子节点,class="title"给了这组标签一个可识别的名字。解析工具的职责,就是按“哪个标签、哪个class、哪个层级”的路径找到对应节点。这个树状视角决定了选型:如果只用正则全文搜索,HTML在眼里就是纯文本,只要标签顺序变化或属性顺序调整,匹配就断裂;用结构化选择器,可以按标签树的身份属性来定位,页面改版时只换选择器表达式,主干代码不用动。

3.2 正则表达式提取:适合藏在标签里的连续文本

正则的优点是灵活度高,不依赖HTML结构也能提取。适合从一段文本里抽出日期、编号、价格、数字这类有明确格式特征的连续内容。示例:

import re html = '<span class="price">¥12.50</span>' prices = re.findall(r'¥([\d.]+)', html) print(prices)

re.findall返回所有匹配的分组,模式写的是取货币符号后面由数字和小数点组成的连续文本。输出结果是['12.50']。正则最典型的用法是“格式规整、独立成文本”的数据点,比如从正文里抽日期、从列表页里抽文章ID。

正则的边界也要说清楚:一旦数据夹在多层标签里,或者class的顺序经常变化,正则会变得极其脆弱,写出来的匹配模式又长又难维护。初学阶段建议把正则定位为“解析工具里的补位选手”,主力交给XPath和CSS选择器,只有它们取不到符合直觉的结果时,再考虑用正则做文本层面的兜底。

3.3 XPath与lxml:入门爬虫最推荐的一条路线

XPath用路径表达式在HTML树里定位节点,配对使用的lxml解析库在性能上也比纯Python的解析器好。先看代码:

from lxml import html doc = html.fromstring(resp.text) # 选取所有class包含title的a标签 items = doc.xpath('//a[contains(@class, "title")]') for a in items: title = a.text_content().strip() link = a.xpath('./@href') print(title, link)

//a表示在文档任意位置找a标签,contains(@class, "title")是条件筛选,表示class属性里包含title这个词。这里用contains而不是精确等号,是因为实际页面里class经常同时挂多个类名,比如class="title red",精确匹配会漏掉。.text_content()取出标签内的全部文本,.strip()去掉首尾空白。第二行a.xpath('./@href')是相对当前节点的子路径查询,拿到a标签的href属性。

XPath真正上手之后是三个方案里表达能力最强的,支持布尔条件、多路径并集、按位置索引。踩坑的地方集中在两点:一是路径写死,比如/html/body/div[3]/div[1]/div[2],这种绝对路径在页面改版时必崩,要养成用class和属性特征做锚点的习惯;二是HTML不规范时,fromstring会自动修正标签结构,导致某些位置和你肉眼看到的不一样,排查时优先相信代码输出而不是自己数标签层级。

3.4 CSS选择器与BeautifulSoup:改代码最顺手的方式

BeautifulSoup的select()方法支持CSS选择器语法,句法更短,人类友好度最高。示例:

from bs4 import BeautifulSoup soup = BeautifulSoup(resp.text, 'html.parser') items = soup.select('a.title') for a in items: print(a.get_text(strip=True), a.get('href'))

soup.select('a.title')表示选取所有class为title的a标签,和XPath版做的事一样,但写起来更短。.get_text(strip=True)取文本并清空白,.get('href')取属性。BeautifulSoup还提供find和find_all,适合“只取第一个”或“按文本内容找”这类场景。日常开发里我经常两个混用:单条数据用find,批量数据用select,偶尔用XPath处理复杂条件。

三个方案没有绝对的优劣,只有场景的适配。下面这张表是我自己选型时对照的:

对比维度正则XPathCSS选择器
学习成本中等初学略高最低
定位能力文本连续匹配任意节点、复杂条件常见节点选择
页面改版后的维护性很差较好较好
最适合的场景取ID、日期、数字复杂层级页面日常大部分列表页

入门阶段最稳的组合是Requests + lxml + XPath,理由很简单:XPath在多数爬虫实战教程与AI辅助生成代码里都通用,换项目时迁移成本最低。等XPath用熟练了,再花半天补CSS选择器,两条路线互为备份。

4. 从网页到数据表:清洗、去重、入库一条龙

4.1 爬下来的数据为什么不能直接用

费了劲把数据从HTML里取出来,直接存表格通常会后悔。页面原始数据有三个常见脏点:文本里夹杂标签残留和换行符;金额、日期、数字都带着单位或符号;同一页面多跑几遍,重复条目成倍累积。这些问题不处理,后续做统计分析时就会遇到:价格字段排不了序,因为它是字符串;日期格式五花八门,pandas认不出时间序列;重复数据让汇总表失真。

清洗这步很枯燥,但能省后面十倍时间。标准化处理做到什么程度,取决于数据的最终去向:只是看一眼就删的,清洗到能读就行;要入库做周报月报的,字段类型、空值、去重必须一次到位。我一般按固定顺序处理:先补空值,再清格式,然后去重,最后做类型转换。

4.2 用Pandas做字段清洗:从字符串到可统计的标准表格

Pandas是Python里最常用的数据处理库,核心结构叫DataFrame,可以理解成内存里的Excel表格。看一个典型清洗过程:

import pandas as pd rows = [ {'标题': 'A产品', '价格': '¥12.50', '日期': '2025年1月3日'}, {'标题': None, '价格': '¥13.00', '日期': '2025-01-03'}, {'标题': 'A产品', '价格': '¥12.50', '日期': '2025/1/3'}, ] df = pd.DataFrame(rows) # 空值填充 df['标题'] = df['标题'].fillna('未知') # 去除货币符号并转成浮点数 df['价格'] = df['价格'].str.replace('¥', '').astype(float) # 统一日期格式 df['日期'] = pd.to_datetime(df['日期']) # 整行去重 df = df.drop_duplicates() print(df)

这一段集中展示了四个最常见操作:fillna把空标题替换成占位文本,避免后续操作被空值打断;str.replace去掉货币符号,再用astype(float)把字符串转成真正的数字;pd.to_datetime把三种不同的日期写法统一成时间格式;drop_duplicates按整行内容去重,三条记录跑完只剩两条。运行结果里可以看到“2025年1月3日”“2025-01-03”“2025/1/3”被统一成了同一种时间戳格式。

实际项目中日期格式往往远多于这三种,pd.to_datetime有一个errors='coerce'参数,处理不了的置为NaT而不是抛异常中断。数据量大时,先停一次脚本观察清洗结果,再决定后续是放宽规则还是补映射表。

4.3 三种存档方式的取舍与入库代码

清洗完的数据要落盘。CSV、SQLite、Excel是三个主流选择,各有明确边界:

存档方式优点缺点适合场景
CSV通用,可直接用表格软件打开编码兼容容易出问题一次性数据交付
SQLite可查询、支持增量更新需要一点SQL基础每日增量采集
Excel业务人员直接看图频繁写入容易损坏文件小批量分析结果

日常爬虫任务里,我默认用SQLite。单文件、免安装、Python内置支持,对中小规模数据完全够用。写入代码:

import sqlite3 # 连接数据库文件,不存在时会自动创建 conn = sqlite3.connect('data.db') df.to_sql('items', conn, if_exists='append', index=False) conn.commit() conn.close()

to_sql第一个参数是表名,if_exists='append'表示表已存在时追加数据,index=False表示不把DataFrame的索引写成单独一列。第一次运行时表会自动创建,之后每次运行都是追加,不需要手动建表。

SQLite有一个需要规避的毛病:并发写能力弱。多个脚本同时往同一个库文件里写,会报database is locked。解决方法只有一个核心思路——写完立刻commit并关闭连接,把持有连接的时间压到最短。对于采集频率不高的任务,这个方案足够了。

4.4 把前几章串成一个最小采集脚本:列表页到SQLite

把请求、解析、清洗、入库连起来跑通一次,比单独学每个环节更有价值。下面这个脚本抓一个公开新闻列表页的第一页,提取标题和链接后直接入库:

import requests from lxml import html import pandas as pd import sqlite3 # 1. 请求 headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0'} resp = requests.get('https://example.com/news', headers=headers, timeout=10) resp.encoding = resp.apparent_encoding # 2. 解析 doc = html.fromstring(resp.text) items = doc.xpath('//a[contains(@class, "title")]') rows = [{'标题': a.text_content().strip(), '链接': a.xpath('./@href')[0]} for a in items] # 3. 清洗 df = pd.DataFrame(rows).drop_duplicates() # 4. 入库 conn = sqlite3.connect('news.db') df.to_sql('news', conn, if_exists='append', index=False) conn.commit() conn.close() print(f'入库 {len(df)} 条')

这个脚本看起来短,但把四个环节完整串起来了。请求层设置了UA和超时,解析层用相对路径取文本与属性,清洗层做整行去重,存储层用完即关。初学阶段建议在这个骨架之上扩展:加一个for循环翻页,把链接里的href补全成完整URL,再在入库前按链接查一遍库做幂等。这四个扩展点做完,爬虫链路的基本功就算过关了。

5. 高频Web采集排障避坑手册:四个翻车现场与根治方案

5.1 请求头顺序玄学:为什么同样的UA却返回403

现象:某开发者在请求头里加了浏览器UA,状态码还是403,服务器明确拒绝访问。反复核对代码没有语法错误,请求头看起来也齐全,问题始终复现不了。

原因:服务器侧的身份校验并不只看UA一个字段,还会综合校验请求头里的其他字段、字段顺序以及Cookie。有些校验规则甚至会对某个请求头做交叉验证,比如判断Referer与当前访问路径是否匹配。单加一个UA,只解决了最基础的识别问题。如果同一个IP短期内访问次数过多,还会触发频率层面的限流,此时请求头怎么改都没用。

解决:打开浏览器开发者工具,刷新目标页面,在网络面板里找到这次页面请求,把完整请求头逐项复制到代码的headers字典里。改一项测一次,观察状态码变化,用二分法定位是哪个字段在起作用。如果完整请求头都贴上仍然403,大概率是IP层面被限制,最稳妥的做法是把抓取频率降下来,换成合规的数据来源。请求头顺序这类问题在业内常被叫“玄学”,实际上服务器的规则引擎不会随机变化,只是规则不透明。遇到玄学问题,先确认是不是自己请求频率超标,再逐项核对请求头。

5.2 中文乱码:apparent_encoding的救与坑

现象:页面内容打印出来,中文全部变成类似产å“的乱码,英文和数字正常。程序代码没有问题,请求也成功了,问题出现在解码环节。

原因:服务器返回的HTTP响应头里没带charset字段,Requests的默认编码是UTF-8,但目标站点实际用的是GBK或GB2312。字节流按错误的编码表解码,中文自然全乱。

解决:代码里加上一句resp.encoding = resp.apparent_encoding,再重新打印。如果还是乱,就手动指定:resp.encoding = 'gb18030'。gb18030是GBK的超集,能覆盖更多生僻字和特殊符号,中文站点乱码时优先用它。这个坑在早期HTML站里非常普遍,近几年新站点基本都切到UTF-8了,但老系统、行业站、公告列表页里仍大量存在。判断站点用的是什么编码,可以在浏览器里右键查看网页源代码,看meta标签的charset声明。

5.3 页面抓下来解析却为空:先看HTML再怪代码

现象:XPath或CSS选择器怎么写都取不到内容,返回结果一直是空列表。代码确认没写错,selector也从官方示例改过来了,但就是什么都拿不到。

原因:目标数据根本不在resp.text里。现在大量页面是前端JavaScript动态渲染的,浏览器里看到的列表内容由JavaScript向后端接口请求JSON后动态填充,而爬虫拿到的HTML只是空壳框架。另外,需要登录后可见的内容,未登录状态下返回的页面本身就不含数据。

解决:右键查看网页源代码,搜索目标关键词。如果源代码里搜不到,说明数据来自异步接口。打开开发者工具的Network面板,刷新页面,在过滤框输入目标关键词,找到返回JSON的XHR请求。这个请求的URL、参数和返回格式,就是爬虫真正该抓的东西。判断标准很简单:直接在浏览器地址栏输入接口URL,能看到JSON文本,说明参数够用。这个方向第6章会专门展开。

5.4 同一套代码刚才还能跑,现在全部超时

现象:脚本刚运行的前几分钟一切正常,跑到中途请求开始成片抛超时或连接错误。重跑一次,发现连第一页都抓不下来了。

原因:短时间内的请求频率超过了服务器阈值,被限流了。也可能是本地网络波动造成的暂时性断连。很多新手会把全部页面的抓取放在一个紧循环里,循环间隔只有几百毫秒,等于用最大速率轰炸服务器,被限制是必然结果。

解决:每次请求前随机sleep 1到3秒,把请求间隔拉开。配合第2章的重试循环,单个请求失败后自动重试。再把任务拆成小批次运行,每批之间停更长时间。正确处理方式永远是降低自身频率,而不是研究如何对抗服务器限制。这里明确一个边界:本攻略只讨论公开数据的低频采集,涉及登录可见、明确声明禁止抓取的内容不在讨论范围内。遵守目标站点的使用条款是职业底线,不是可选项。

5.5 入库数据翻倍:忘了去重和主键

现象:同一个脚本跑了几天,查询数据库发现记录条数远超预期。对比源站数据,发现同一篇文章出现了多次。

原因:列表页里的条目会跨页重复,或者脚本中途失败重启后,已经把同一批数据重复写入了。如果入库时没有去重逻辑,append模式会无条件追加,数据翻倍是必然。

解决:在入库前按唯一字段查库。最简单的方式是给SQLite表加主键约束,或者用INSERT OR IGNORE语法让数据库层面的重复数据自动被忽略:

import sqlite3 conn = sqlite3.connect('news.db') conn.execute('CREATE TABLE IF NOT EXISTS news (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, url TEXT UNIQUE)') conn.execute('INSERT OR IGNORE INTO news (title, url) VALUES (?, ?)', ('标题', 'https://example.com/1')) conn.commit() conn.close()

url TEXT UNIQUE把链接设成唯一约束,INSERT OR IGNORE在遇到重复URL时直接跳过而不是报错。这比在代码里先查询再插入更省事,也让幂等兜底由数据库保证。写爬虫脚本时把这个习惯养成,后面每次重启脚本都不用担心数据重复。

6. 动态加载页面的接口直取:用XHR数据源替代HTML解析

6.1 从开发者工具里找到真实接口

动态页面的数据藏在异步请求里,直接解析HTML是徒劳的。正确的做法是让浏览器帮你找出真实接口。打开开发者工具的Network面板,刷新页面,在过滤框输入页面里肉眼可见的关键词,比如某条新闻的标题文本。面板里会出现一个XHR请求,点开它的Preview标签,如果能看到JSON格式的数据,这就是目标接口。

把接口的URL完整复制出来,单独在浏览器地址栏打开。如果直接返回JSON,说明请求所需的参数都在URL里;如果返回空数据或报错,则需要带上请求头里额外的参数,比如某个token字段。这个排查步骤是整个动态页面爬虫的核心,花十分钟搞清楚一个接口,比写两个小时HTML解析器更可靠。习惯上我先看Query String参数,再看Headers里的请求头字段,这两处基本涵盖绝大多数接口的鉴权信息。

6.2 用Requests直接拿JSON数据

找到接口规律后,代码反而比HTML解析更简单,因为JSON已经是结构化数据,不需要再写选择器。

import requests headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0', 'X-Requested-With': 'XMLHttpRequest', } params = {'page': 1, 'page_size': 10, 'category': 'news'} resp = requests.get('https://example.com/api/list', params=params, headers=headers, timeout=10) data = resp.json() for item in data.get('items', []): print(item.get('title'), item.get('url'))

resp.json()把响应体直接解析成Python字典和列表,后面用get方法按字段名取值即可。headers里加X-Requested-With: XMLHttpRequest是因为部分后端会区分普通页面请求和异步请求,不加可能返回一段跳转HTML而不是JSON。params字典控制了分页参数,翻页时只需要循环修改page的值。

接口直取之后,JSON数据直接对接第4章的DataFrame清洗和SQLite入库流程,中间省掉整个HTML解析环节。数据质量也更好,字段名是后端定义好的,不用再从标签里抽。这个方法是我目前处理所有动态页面的首选方案。

做接口直取,记住一个原则:接口地址和参数结构属于站点的业务逻辑,会随着版本迭代变化。我现在凡是要长期抓的页面,都会把接口地址和关键参数抽到一个配置字典里,改版时只改配置不改代码。这个习惯救过我很多次,别让30行代码和站点的一次升级绑得太死。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询