ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MATLAB爬虫实战:从webread到JSON接口的轻量级数据采集指南

MATLAB爬虫实战:从webread到JSON接口的轻量级数据采集指南

1. 为什么我推荐用MATLAB做爬虫

1.1 别小看MATLAB在爬虫领域的定位

先回答一个最常被问到的问题:MATLAB也能写爬虫?能,而且比很多人想象的要顺手。

我在某个数据分析项目里需要批量采集公开气象站点数据,一开始也想直接上Python,但数据拿到之后还是要交给MATLAB做建模分析,来回导文件的过程实在折磨人。后来我干脆把爬虫直接写在MATLAB里,数据抓下来就落在工作区里,清洗、插值、建模一气呵成,整个流程瞬间清爽了很多。

MATLAB爬虫的定位,说白了就是“轻量、顺手、够用就好”。它不像那些专门做爬虫的编程语言那样灵活,但对付静态网页、JSON接口、小批量数据抓取,完全够用了。尤其适合本来就以MATLAB为主要工具的人,特别是做科研、算法验证、课程设计的群体,与其为了抓一次数据就折腾一套新环境,不如直接用手上现成的工具。

当然,如果你是准备搞大型数据采集系统、需要处理复杂的JS渲染页面、维护几十个代理IP轮换,那我还是建议去用专门的语言。MATLAB爬虫的优点在快速、直接、与数据分析无缝衔接,而不是高并发和反反爬。

1.2 MATLAB爬虫能解决哪些实际问题

结合我自己的使用经验,MATLAB爬虫最适合这几类场景:

  1. 抓取公开的JSON接口数据,比如天气数据、股票行情、论文元数据。
  2. 抓取静态网页中的表格、文本内容,比如统计数据、排行榜、公告列表。
  3. 批量下载固定格式的公开文件,比如按编号规律排列的PDF、CSV、NC数据文件。
  4. 配合定时任务做定期数据更新,抓完直接进MATLAB的预处理流程。

我做过一个行业信息监测的小项目,每天定时从几个公示平台抓取最新公告列表,用MATLAB爬虫在15分钟内完成抓取、清洗、入库、生成日报的全流程。整个过程也就一百多行代码,维护成本极低,跑了一年多都没出过大问题。

1.3 学习MATLAB爬虫需要什么基础

技术要求不高,只需要:

  • 对MATLAB基本语法有概念,知道怎么定义变量、调用函数、用循环和条件判断。
  • 了解网页的基本结构,知道HTML标签大概长什么样,知道JSON和数组的对应关系。
  • 有一点正则表达式基础,或者愿意花半小时学一下常用规则。

只要满足这三条,剩下的就是按照本文的路线一步步操作。我尽量把每个步骤的原理和细节讲清楚,跟着走一遍,你就能具备独立完成简单爬虫任务的能力。

2. 先把核心函数摸透:webread、webwrite、websave与weboptions

2.1 四个基础函数的定位与分工

MATLAB爬虫的底层实现依托于内置网络函数,核心就四个:webread、webwrite、websave、weboptions。搞清这几个函数的定位,基本就打通了爬虫的主干。

  • webread:发送GET请求,读取网页或接口返回的内容,是日常使用频率最高的函数。返回值可以直接赋给变量,如果你请求的是JSON接口,再用jsondecode解析成MATLAB的struct或cell数组即可。
  • webwrite:发送POST请求,用于向服务器提交数据。需要登录、提交表单参数的场景中会用到。
  • websave:把请求到的内容直接保存为文件,适合下载图片、压缩包、PDF等二进制资源,省去手动写文件操作。
  • weboptions:配置请求参数的万能选项,可以设置超时时间、请求头信息、编码方式、证书验证等,是爬虫“是否能顺利把数据拿到”的关键变量。

别看函数少,组合起来威力不小。下面我逐个拆解用法。

2.2 weboptions:解决八成爬虫问题的关键配置

不少刚用MATLAB爬虫的初学者,一上来直接webread(url),结果遇到中文乱码、请求超时、服务器拒绝访问,就以为MATLAB爬虫不行。多数情况下是没配置weboptions。

weboptions的核心配置项包括:

配置项作用常用值建议
Timeout请求超时秒数,默认10秒,很多网页10秒根本响应不完30或更长
UserAgent自定义请求头中的浏览器标识设置为常见浏览器UA字符串
CharacterEncoding指定网页编码格式,解决中文乱码的关键UTF-8、GBK等
ContentType声明返回内容的类型auto、text、json等
CertificateAuthorityHTTPS证书验证方式信任证书时保持默认;不信任时可关

写一段标准配置示例:

opts = weboptions(... 'Timeout', 30, ... 'UserAgent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', ... 'CharacterEncoding', 'UTF-8');

在后面的每个示例中,我基本都会带上这个配置。先把它固定下来,爬虫之旅会顺畅很多。

2.3 第一个示例:抓取一个网页的原始内容

先来一个最简单的示例,随便找一个公开静态网页,把它的HTML源码抓到MATLAB里看看:

url = 'https://example.com'; % 仅为示例,换成你实际要抓的网址 opts = weboptions('Timeout', 30, 'UserAgent', 'Mozilla/5.0'); htmlText = webread(url, opts); % 查看前500个字符 disp(htmlText(1:min(500, numel(htmlText))))

这段代码完成的事很简单:向指定网址发起GET请求,拿到服务器返回的HTML文本,打印前500个字符确认内容。

要注意,webread拿到的是char类型文本,不是MATLAB的string。如果你用string(htmlText)转换过,后面做正则匹配时某些函数的行为会不一样,建议保持char类型操作,避免踩坑。

到这里,你已经完成了MATLAB爬虫的“握手”。接下来进入正题,我会带你完成三种最常见的抓取任务。

3. 静态网页数据抓取:从HTML中提取结构化信息

3.1 静态网页抓取的整体流程

静态网页的特点是:服务器返回的HTML里直接包含你需要的所有数据,不需要额外执行JS脚本。抓取静态网页的标准流程分四步:

  1. 确定目标网址,用浏览器开发者工具(按F12)查看网页结构。
  2. 用webread获取网页HTML源码。
  3. 用正则表达式或htmlTree解析HTML,提取目标数据。
  4. 把提取到的数据整理成表格结构,存入MATLAB变量。

我以抓取某个公开排行榜数据为例,详细走一遍这个过程。

先说思路:一个典型的排行榜页面,每一行包含排名、名称、数值信息,在HTML源码中通常以固定规律的标签重复呈现。只要找到包含数据的标签模式,用正则表达式就能批量提取。

3.2 确定目标与网页结构分析

假设我们要抓取某公开技术论坛的“热门话题排行榜”。打开目标页面后,按F12进入开发者工具,在Elements面板里找到话题名称所在的HTML标签,发现每条话题的结构大致如下:

<li class="topic-item"> <span class="rank">1</span> <a class="title">MATLAB爬虫实战技巧</a> <span class="views">12345</span> </li>

这个结构非常规律,排名、标题、浏览数分别在class为rank、title、views的标签内。这种规律的HTML结构,正是正则可以发挥的分寸。

3.3 用正则表达式提取数据

正则表达式的核心在于“用模式匹配有规律的内容”。针对上面的HTML结构,我写以下正则:

% 抓取页面HTML url = 'https://example.com/hot-topics'; % 示例网址 opts = weboptions('Timeout', 30, 'UserAgent', 'Mozilla/5.0', 'CharacterEncoding', 'UTF-8'); htmlText = webread(url, opts); % 提取排名 rankPattern = '<span class="rank">(.*?)</span>'; ranks = regexp(htmlText, rankPattern, 'tokens', 'dotexceptnewline'); ranks = str2double([ranks{:}]); % 提取标题 titlePattern = '<a class="title">(.*?)</a>'; titles = regexp(htmlText, titlePattern, 'tokens', 'dotexceptnewline'); titles = [titles{:}]; % 提取浏览数 viewsPattern = '<span class="views">(.*?)</span>'; viewsStr = regexp(htmlText, viewsPattern, 'tokens', 'dotexceptnewline'); views = str2double([viewsStr{:}]);

这套正则的关键在于(.*?),这是一种非贪婪匹配,意思是从前一个匹配点开始,尽可能少地匹配字符,直到遇到下一个模式的第一个字符。这样就能精确定位两个固定标签之间的内容。

提示:非贪婪匹配.*?建议配合'dotexceptnewline'选项使用,否则如果标签之间有换行,.会匹配不到换行符以外的内容,造成结果为空。

执行后,ranks、titles、views三个变量分别是数值数组、字符向量元胞数组、数值数组。把它们组合成表格:

resultTable = table(ranks', titles', views', ... 'VariableNames', {'Rank', 'Title', 'Views'}); disp(resultTable);

这样就完成了一个最简单的静态网页数据抓取。整个过程不复杂,但对细节有要求,比如正则表达式里不要多留空白字符,否则匹配会失败。

3.4 更稳妥的方案:用htmlTree解析网页

正则表达式在处理高度规律的HTML时快速有效,但遇到结构嵌套复杂、属性顺序不固定的网页时会很吃力。MATLAB其实内置了一套HTML解析器,对应的函数是htmlTree和findElement。

% 将HTML文本解析为DOM树 tree = htmlTree(htmlText); % 找到所有class为"topic-item"的li元素 items = findElement(tree, 'li.topic-item'); % 遍历每个元素,提取子内容 numItems = numel(items); titles = cell(numItems, 1); views = zeros(numItems, 1); for i = 1:numItems titleElem = findElement(items(i), 'a.title'); titles{i} = extractHTMLText(titleElem); viewsElem = findElement(items(i), 'span.views'); views(i) = str2double(extractHTMLText(viewsElem)); end

htmlTree的用法有些像网页编程里DOM操作,熟悉document.querySelectorAll的开发者会很容易上手。它的优势是结构清晰、不易误匹配,缺点是解析速度较慢、调用层次略深。我的建议是:简单重复结构用正则,复杂嵌套结构用htmlTree,两者结合使用才是最高效的。

4. JSON接口数据抓取:获取数据的最佳路径

4.1 为什么优先找JSON接口

在爬虫任务中,JSON接口是“优先于”HTML网页的。静态网页的HTML里,数据与标签混在一起,需要额外解析;而JSON接口直接返回结构化数据,用MATLAB的jsondecode一句话就能转成变量。省时省力,还不容易出错。

很多网站虽然页面看起来是富交互的,背后其实都有JSON接口在提供数据。找到一个JSON接口,比从渲染后的HTML里做层层提取要优雅得多。

如何发现JSON接口?最简单的方法还是用浏览器的开发者工具,切换到Network面板,刷新页面后观察网络请求,凡是响应类型为json、返回内容结构清晰的请求,就是我们要找的接口。在Fetch/XHR标签里通常能直接看到这些请求。

4.2 用webread加jsondecode完成抓取

假设我找到一个公开的天气接口,返回的数据结构大致是:

{"city":"某市","temperature":26.5,"humidity":60,"wind":"北风3级"}

MATLAB端代码非常简单:

url = 'https://api.example.com/weather/city=101010100'; % 示例接口 opts = weboptions(... 'Timeout', 30, ... 'UserAgent', 'Mozilla/5.0', ... 'ContentType', 'json'); jsonStr = webread(url, opts); data = jsondecode(jsonStr); disp(data.city); disp(data.temperature);

jsondecode会把JSON对象映射为MATLAB的struct,JSON数组映射为struct数组或cell数组。访问字段直接用点号语法,与MATLAB习惯完全一致。

注意:有些JSON接口的字段名包含不合法变量名字符,比如数字开头、带短横线。jsondecode后可以通过fieldnames(data)查看实际字段名,然后用data.('字段名')的方式访问,绕开语法限制。

4.3 实战:批量抓取多个接口并组装成表格

实际场景中,一个接口往往只返回一天或一条记录,我们需要循环抓取多天数据。下面模拟一个批量抓取近七天股票行情的流程:

symbol = '600000'; % 示例股票代码 baseUrl = 'https://api.example.com/stock?code=%s&date=%s'; opts = weboptions('Timeout', 30, 'UserAgent', 'Mozilla/5.0'); dates = datesstr = cellstr(datestr(datetime(2024,12,1):caldays(6):datetime(2024,12,7), 'yyyy-mm-dd')); n = numel(dates); allData = struct([]); for i = 1:n url = sprintf(baseUrl, symbol, dates{i}); try jsonStr = webread(url, opts); tmp = jsondecode(jsonStr); allData(i).date = tmp.date; allData(i).close = tmp.close; allData(i).volume = tmp.volume; catch ME warning('日期%s抓取失败: %s', dates{i}, ME.message); end end result = struct2table(allData);

这段代码有几个值得细说的点:

  1. sprintf用于拼接URL,把变量嵌入网址中,是批量抓取的基础操作。
  2. try-catch包裹请求,单次失败不影响整体循环,避免某个接口异常导致整批数据中断。
  3. 循环体之外统一把struct数组转成table,方便后续分析。

很多初学者会忽视try-catch,一旦某个请求意外失败,整个脚本就直接中断。批量抓取场景中,加一层容错是基本素养。

5. 进阶操作:webwrite发送POST请求与认证

5.1 POST请求的适用场景

GET请求适合获取数据,但有些接口要求通过POST方式提交参数,才能返回目标内容。常见于带有搜索条件的查询接口、需要登录后才能访问的用户接口。

POST请求与GET请求的本质区别在于:GET把参数放在URL的查询字符串里,POST把参数放在请求体中。因此前者在网址上能看到完整参数,后者对网址不可见。

5.2 用webwrite实现POST请求

MATLAB的webwrite是发送POST请求的手段。使用方式很直接:

url = 'https://api.example.com/search'; opts = weboptions(... 'RequestMethod', 'post', ... 'MediaType', 'application/json', ... 'Timeout', 30); % 构造POST数据(MATLAB struct 转 JSON) postData = struct('keyword', 'MATLAB爬虫', 'page', 1, 'pageSize', 20); jsonData = jsonencode(postData); response = webwrite(url, jsonData, opts); result = jsondecode(response);

注意几个关键细节:

  • RequestMethod必须设置为post,否则webwrite默认使用的POST可能不生效或不规范。
  • MediaType要与服务器期望的请求体格式一致。如果接口要求application/json,就设置成application/json;如果要求application/x-www-form-urlencoded,则设置成对应类型,且请求体通常改为struct,MATLAB会自动编码。
  • jsonencode与jsondecode互为逆操作,前者把MATLAB结构转为JSON字符串,后者把JSON字符串解回结构。

5.3 带Token认证的请求处理

很多接口需要认证才能访问,常见方式是请求头中加入Authorization字段。在weboptions中可以用HeaderFields配置自定义请求头:

opts = weboptions(... 'Timeout', 30, ... 'HeaderFields', {'Authorization', 'Bearer your_token_here'}); jsonStr = webread(url, opts);

这里有个小坑:HeaderFields的值必须是以偶数个字符串排列的cell数组,两两配对构成“字段名-字段值”。不能像Python requests那样直接传字典,写错参数结构就会报错。

遇到需要在网页登录后抓取数据的情况,一般思路是先观察登录接口,用webwrite模拟登录,从响应中获取Cookie或Token,再在后续请求的HeaderFields中携带这个凭证。这个过程在MATLAB里完全可行,但涉及登录的接口往往还有验证码等机制,初级教程先不做深挖,够用就好。

6. 常见问题与排查技巧实录

6.1 中文乱码:编码问题

用webread抓中文网页,经常出现乱码。核心原因在于:目标页面使用了GBK或其他非UTF-8编码,而MATLAB默认按UTF-8解码。

排查步骤:

  1. 在浏览器中打开目标网页,查看源码或响应头,确认页面的charset值。
  2. 在weboptions中显式设置CharacterEncoding为对应的编码。
  3. 如果是GBK页面,设置:
opts = weboptions('CharacterEncoding', 'GBK'); htmlText = webread(url, opts);

设置后,htmlText里的中文就能正常显示了。

另一种情况是接口返回的中文在JSON中被转义成\uXXXX形式,这类在jsondecode后通常会自动转回实际字符,无需手动处理。

提示:如果页面同时存在两种编码的片段,或者因为服务器配置问题导致响应头与实际内容不一致,以网页源码<meta charset="...">标签中声明的编码为准。

6.2 请求超时与断线

网页响应慢或网络不稳定,webread会抛出超时错误。解决方法很简单,调大Timeout参数,或者加上自动重试机制。

我这里提供一个自行封装的带重试的读取函数写法,大家可以直接复用:

function out = robustWebread(url, opts) maxRetries = 3; for attempt = 1:maxRetries try out = webread(url, opts); return; catch ME if attempt == maxRetries rethrow(ME); else pause(2 * attempt); % 递增等待时间 end end end end

重试之间加等待时间,是避免连续请求失败后又立刻重试导致压力过大。pause(2 * attempt)让每次重试间隔递增,也更不容易被服务器视为异常请求。

6.3 HTTPS证书验证失败

访问用自签名证书或非公共证书的HTTPS站点时,MATLAB可能因为证书验证失败报错。

处理方式是在weboptions中调整证书验证相关选项,例如设置:

opts = weboptions('CertificateAuthority', false);

不过要提醒一下:关掉证书验证意味着连接不再验证服务器身份,仅建议在可信环境中使用,不要随便对所有网站关闭验证。

6.4 反爬机制的简单应对

这里说的反爬机制是指服务器通过基础手段限制非浏览器来源的访问。最常见的就是检查User-Agent、检查请求频率、要求特定请求头。

初级的应对方法:

设置一个常见浏览器的User-Agent:

opts = weboptions('UserAgent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');

控制请求频率,避免短时间内高频请求:

pause(rand * 2 + 1); % 每次请求后随机暂停1到3秒

随机暂停比固定暂停更贴近人工操作习惯,能有效降低被识别为机器的概率。

6.5 抓回来的数据是乱码或空值怎么办

这是新手最常遇到的组合问题。现象是:网页明明有数据,webread返回的字符串却是空或乱码。排查顺序如下:

  1. 确认URL是否完整、是否需要携带请求头。
  2. 打印返回文本的前几百字符,看是否是正常HTML。
  3. 用浏览器开发者工具查看目标页面发出的是GET还是POST,是否带了额外参数。
  4. 检查目标页面是否依赖JavaScript动态渲染。如果是,返回的HTML可能只是一个空壳,所有数据都是JS异步加载的。这时不能直接抓页面正文,要转而去找背后真正的JSON接口。

动态渲染问题在MATLAB里没有太好的浏览器内核方案去模拟执行JS,所以面对这类网站,最实际的策略就是找JSON接口。这也是我反复强调“优先找接口”的原因。

7. 实操总结与个人体会

7.1 我日常使用的固定套路

经过多次项目实践,我总结了一套固定套路,几乎可以应对大多数轻量级抓取需求:

  1. 打开目标网站,按F12进入Network面板,先看有没有JSON接口。
  2. 有JSON接口,直接确定URL规律,用webread加jsondecode抓取。
  3. 没有JSON接口,再看页面HTML结构是否规律。
  4. 结构很规律,用正则表达式或htmlTree提取。
  5. 结构混乱或依赖JS动态加载,放弃直接抓HTML,回到第一步找更深层的接口。

这套流程执行下来,80%以上的轻量级数据抓取问题都能在半小时内解决。

7.2 数据抓完只是开始

MATLAB爬虫最大的优势不止在于“抓”,更在于抓完之后立刻能进行数据分析。表格拿到工作区中,table转数组、画图、拟合、统计,一气呵成。这种全流程在同一个环境中完成的感觉,是用其他语言抓完再导来导去时很难体会到的顺畅。

我建议大家把爬虫脚本封装成函数,输入参数是URL或日期范围,输出是整理好的table。这样逻辑清晰,复用方便,定时任务可以直接调用。配合MATLAB的定时任务工具,自动抓取、处理和生成报告完全可以无人值守。

7.3 几点提醒

尊重目标网站的访问规则,合理控制请求频率,不要给服务器造成不必要的压力。公开数据抓取也存在一定的合规边界,抓取前确认网站的使用条款,个人学习用途相对宽松,但如果要用于商业项目,请务必确认数据使用的合法性。

爬虫这件事,很多人一上来就追求技术上的复杂,工具链堆得很高。但以我的经验,能简单解决的问题尽量简单,抓到数据才是核心目的,工具只是手段。MATLAB爬虫这把手锤,在合适的钉子面前,永远是趁手的。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进