正则表达式爬取网页
用正则表达式从网页里抠数据,正则十次有八次会翻车。表达我上周想抓某电影网站的式爬评分,页面结构看着挺规整,取网<span class="rating_num">8.7</span> 这种。正则正则一写,表达rating_num.*?式爬(\d+\.\d+),跑起来确实能匹配到。取网但只跑了三页就发现,正则有的表达条目评分被包在 <strong> 里,有的式爬干脆是 8.7分 连在一起。改了三版正则,取网最后干脆换成了 BeautifulSoup。正则
这不是表达说正则没用。小规模、式爬结构稳定的页面,正则快得离谱。比如抓取某个固定格式的 API 返回的 JSON 片段,或者从日志文件里提取 IP 和访问时间,正则就是一把手术刀。但网页这东西,尤其是别人家的网页,今天和明天的 DOM 可能都不一样。你用正则去匹配 HTML,本质上是在用字符串模式对抗一棵随时会变的树。
我见过最离谱的案例,是有人用正则抓电商价格。写了个 price.*?(\d+\.\d{ 2}),结果商品描述里有一句“原价 199.00,现价 99.00”,正则贪婪匹配直接抓到了 199.00。改成非贪婪,又因为页面里有个隐藏的 data-price 属性先出现,抓了个空。这种 bug 调试起来特别费劲,因为正则不会告诉你它为什么匹配错了,它只会沉默地给你一堆垃圾数据。
那什么时候该用正则?我的判断标准很简单:如果目标文本是纯文本或者半结构化文本,正则合适。比如从 robots.txt 里提取 Disallow 路径,或者从 User-Agent 字符串里判断浏览器版本。但一旦涉及嵌套标签、属性顺序变化、注释干扰,正则的维护成本会指数级上升。
有个取巧的办法:先用正则把网页里最外层的目标块抠出来,再用 HTML 解析器处理内部。比如 <div class="content">([\s\S]*?)</div> 先拿到内容区,剩下的交给 lxml。这样既利用了正则的快速定位,又避免了它处理嵌套结构的短板。我抓某论坛帖子列表时就这么干,先匹配 <table id="threadlisttableid">([\s\S]*?)</table>,再对每一行用 XPath 提取标题和链接。比纯正则稳,比纯解析器快。
但正则有个坑得注意:HTML 里的换行和空格。你写 <div>.*?</div>,如果中间有换行,. 默认不匹配 \n,得加 re.S。还有属性值里的引号,单引号双引号混用,正则写起来就头疼。我一般会先 re.sub(r'\s+', ' ', html) 把空白压一下,再匹配,能省不少事。
说到底,正则表达式爬网页,适合做“提取”而不是“解析”。提取是从一堆文本里找特定模式,解析是理解文档结构。网页是结构化的,用正则去解析它,就像用螺丝刀撬罐头——有时候能开,但容易弄一手汁。真要做爬虫,还是老老实实上解析器。正则留着处理 URL 参数、清洗文本、验证格式,这些才是它的主场。
最后给个实际建议:如果你非要用正则抓网页,先写个测试脚本,拿十种不同页面的 HTML 跑一遍。能过八种,再上生产。过不了,趁早换工具。别跟正则较劲,它赢不了 HTML 的嵌套。
(责任编辑:探索)













