正则表达式 - 匹配开头、结尾、中间 - 某天气网站网页源代码分析

news2024/11/17 0:05:18

背景

爬取某天气网站数据,使用 Selenium 能够得到渲染数据后的页面源代码。特定日期的真实数据肯定只有1份,展示在页面表格中,但是源代码中提供了3个都有数据的 Table,而其中2个Table 的数据是通过 math.random 生成后填充,然后通过 css 样式设置了隐藏。

为了拿到真实数据,要么直接提取包含真实数据的 Table,要么剔除2个伪数据 Table,然后才能进一步提取 tr 以及 td 标签内的文本。鉴于使用了Scrapy框架,函数之间传递的是 HtmlResponse, 所以我们采用剔除2个伪数据 Table的方式,保留网页源代码其余部分,而没有提取真实数据再封装或者改用String 传递。

不管哪种方式,关键是要找出真数据 Table 和 伪数据 Table 之间的差异。

经过反复对比,我们发现用伪造数据填充的2个 Table, 都有在 class 中设置了 position : absolute 属性,所以可以用正则表达式来匹配出全部Table,然后直接将这2个 Table,替换为空。

实现

2步走方案

其实最开始是打算一步到位的,但是实在搞不定1步到位的正则表达式,所以决定还是先用笨办法,拿到结果再看怎么优化。所以用了2步,

  • 第1步,正则匹配得到3个 Table,这里的正则表达式有2种写法
    • 第1种:r"<table[\s\S]*?<\/table>",匹配到的结果是 table 标签内的内容,不包含table 标签
    • 第2种:r"(?=<table)[\s\S]*?(?<=<\/table>)" —— 这里用到了正则表达式的断言写法,匹配到的结果是 包含开头和结尾 table 标签的完整1部分
    • 但是我们的最终目的是分离这3个Table,无论最后是替换完整的 table 标签,还是只替换 table 标签内部,都能达到我们剔除伪数据的目的
  • 第2步,在3个 Table 基础上,进行 position 匹配,然后 replace 为空。这里的匹配就很简单了,只匹配是否包含 position 字符串即可
# 先匹配table 标签
# regex_table = r"<table([\s\S]*?)<\/table>"
# regex_table = r"<table[\s\S]*?<\/table>"
# 中间有括号是设置分组,但是这里只需要确认有没有匹配,不需要分组
# ?= 在开头是包含,在结尾是不包含
# ?<= 在开头是不包含,在结尾是包含
regex_table = r"(?=<table)[\s\S]*?(?<=<\/table>)"
tables = re.findall(regex_table, page_source)
print('{} tables been matched.'.format(len(tables)))

# 样式中没有设置 position 的Table,就是包含真实数据的,保留
# 另外2个 替换为空
regex_table_class = r"position"
for table in tables:
	if re.findall(regex_table_class, table):
		page_source = page_source.replace(table, '')

1步到位方案

在2步走方案成功拿到数据后,还是忍不住对效率提升,方案简化的渴望,于是又开始折腾,不断查找资料,甚至还无助地到知乎 发帖 求助 [笑哭] —— 是的,网上能找到很多理论,但是没有现成可借鉴方案。

在花了近一天时间付出数百次失败尝试理解高深理论最终头昏脑胀后,我终于还是 —— 没有放弃。借助无意间发掘出的新工具 Regex Debugger,对正则匹配的逻辑有了进一步认识,终于在工具加持以及半小时休息神智稍微清醒后,写出了这个匹配开头、结尾、中间 —— 简直前不见古人,后不见来者的正则表达式

 我在 知乎 上自问自答,对这个正则表达式做了解释。这里再赘述一遍,也能让自己再加深一遍印象。

开头和结尾的圆括号,其实和2步走方案一致,匹配以 <table 开头和以 </table> 结尾。

中间部分,因为要匹配包含 position 的字符串,所以最简单的写法就是 [\s\S]*?position[\s\S]*? ——  [\s\S]*? 用来做非贪婪匹配任意字符,因为html 代码中是有换行的,所以不能用 . 来匹配 。

但是这么写是拿不到正确结果的,如下图。第1个实际没有包含 position 的 table 标签也被包含了进来。所以需要在 以 <table 开头到遇到 position 之间,再加1个条件,把</table> 过滤掉  

 于是把<table 后面,position 前面的部分改为 (?:(?!<\/table>)[\s\S])*? —— 其中

(?!<\/table>) 表示不包含字符串 </table>,

(?!<\/table>)[\s\S] 表示不包含字符串 </table>的任意字符串

(?:(?!<\/table>)[\s\S]) 表示匹配但不提取

然后就可以得到正确的结果了。

因为<table 的闭合尖括号 > 也能把前面的这个 table 分割出来,所以正则可以简化成: (?=<table)(?:(?!>)[\s\S])*?position[\s\S]*?(?<=<\/table>)  —— 就是我们在代码里使用的形式。是的,1步到位,就是这么清爽。

# 一步到位的正则表达式
re_one_step = r"(?=<table)(?:(?!>)[\s\S])*?position[\s\S]*?(?<=<\/table>)"
res_one_step = re.sub(re_one_step,'', page_source)

我把替换前的原始 html 代码(下图左)和替换后的代码(下图右)保存到了本地,可以对比看到,处理后的代码只保留了1个 Table,另外2个被成功移除。

 

方案对比

从代码简洁度看,毫无疑问,1步到位方案的代码最简洁,只有2行,而2步走方案用了6行代码。

那性能上呢?直观上,1步到位方案是直接全文匹配,2步走方案是不断缩小匹配范围 —— 感觉上2步走似乎应该快。

但做了2次程序运行时间的粗略计算,实际还是1步到位方案更快。

第1次:

solution 2 exhaust 0.004988193511962891s
solution 1 exhaust 0.0019943714141845703s

第2次:

solution 2 exhaust 0.007024049758911133s
solution 1 exhaust 0.001960277557373047s

鉴于我们纯粹出于好奇求知学习的目的,所以性能就先步考虑了。无论如何,我们成功实现了对伪数据的剔除,拿到了包含真实数据的 Table。

这里留个引子,因为拿到的只是包含真实数据的 Table,并不代表里面全是真实数据。后面会再写一篇文章来分析如何从包含真实数据的 Table 里进一步剔除伪数据。不要问我怎么会有这种网站...... 我只想给网站的前端程序员点赞。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/160601.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ASP.NET Core 3.1系列(25)——Autofac中的泛型注册和程序集注册

1、前言 在实际开发业务中&#xff0c;泛型的应用非常广泛&#xff0c;而这也就产生了一个问题&#xff1a;泛型类和泛型接口该怎么注册&#xff1f;难道要开发者一行一行去写泛型构造参数吗&#xff1f;同时&#xff0c;实际业务中往往也会对项目进行分层设计&#xff0c;例如…

【十】Netty WebSocket协议栈开发

Netty WebSocket协议栈开发背景介绍HTTP 协议的弊端WebSocket 介绍WebSocket 特点WebSocket 连接建立Socket生命周期WebSocket关闭开发WebSocket 服务端功能介绍流程图代码实现jar 依赖WebSocket 服务端启动类 WebSocketServer服务端业务处理类 WebSocketServerHandlerWebSocke…

nacos的部署以及nacos启动报错“Unable to start embedded Tomcat”(部分解决)

这几天有一个基于yshop改编的SpringBoot的项目开发需求&#xff0c;本地需要下载使用nacos-server进行部署和开发&#xff0c;于是下载了nacos-server-2.0.3,并将其解压在一个没有中文路径的地方。 接下来根据nacos需求&#xff0c;设置了JAVA_HOME的环境变量&#xff1a; 修…

防火墙NAT综合实验

实验要求 1.内网网段配置动态pat将内网192.168.1.0网段映射到防火墙外网接口上 2.dmz区域服务器做静态pat将两台服务器对应到一个外网接口的不同端口 3.开启nat 控制 4.对内网网段192.168.2.0或豁免能够发访问外网 5.并通过远程进行验证 实验命令 ciscoasa# conf t cisco…

如何快速做好SEO优化?怎样综合查询seo?

本篇接着讲新手优化网站的技巧及应该注意什么方面&#xff0c;一起来看看吧&#xff01; 6.优化你的图片 从可读性的角度来看&#xff0c;图像非常重要。他们帮助可视化你的内容&#xff0c;帮助读者更容易理解。但它们对SEO也很重要&#xff0c;因为它们可以帮助你的网站被抓…

MySQL——SQL逻辑语句相同但是性能相差巨大?

在 MySQL 中&#xff0c;有很多看上去逻辑相同&#xff0c;但性能却差异巨大的 SQL 语句。对这些语句使用不当的话&#xff0c;就会不经意间导致整个数据库的压力变大。 下面通过三个案例对SQL语句进行分析&#xff1a; 案例一&#xff1a;条件字段函数操作 假设你现在维护了…

51单片机特性概览

51单片机指的是使用英特尔8051指令集的微控制器。 首先要了解什么是微控制器。 一、什么是微控制器&#xff1f; 微控制器包括&#xff1a; CPU其他组件(可能包括RAM,ROM,I/O端口&#xff0c;定时器、计数器、通信端口)&#xff0c; 而微处理器只包含CPU。 一开始只有微处…

适应性学习率

目录 适应性学习率 Adaptive learning rate 为什么不是临界点仍会导致训练停止 示例一示例二 RMSRMSPropAdam学习率还和时间有关 Learin Rate DecayWarm up 2021 - 类神经网络训练不起来怎么办(三) 自动调整学习率 (Learning Rate)适应性学习率 Adaptive learning rate 一般…

团队协作软件如何整合内容营销

每个内容营销团队都以不同的方式管理他们的流程和工作流程——无论是为成长中的团队扩展编辑流程&#xff0c;还是视频和社交媒体活动规划。优化内容营销项目管理就是降低复杂性和更有效地管理工作。 但是&#xff0c;为什么正确地做到这一点如此重要呢&#xff1f;与对外营…

第一个uni-app程序小结

工院喵开发小结 这是一篇关于uniapp新手写微信小程序的收获和踩坑总结。 目录工院喵开发小结一、架构二、收获1. 一些扩展组件的使用a. uni-uib. uni-listc. 栅格系统d. uni.scss辅助样式e. uni-easyinput 增强输入框f. uni-fab悬浮按钮g. swiper2. api管理3. 页面跳转传参4. …

iOS 语言基础初探 Xcode 工具

前言&#xff1a; 作为 iOS 开发的主要应用工具之一&#xff0c;Xcode 已经越来越被业内认可&#xff0c;本章节将针对此官方开发工具&#xff0c;为同学解读 Xcode 的基本情况&#xff0c;认识 Xcode 的工程体系&#xff0c;带领大家进入 iOS 开发第一步。 &#x1f3b6;文章目…

SAP入门技术分享四:模块化程序

模块化程序1.子程序概要2.子程序定义3.子程序参数&#xff08;1&#xff09;传递参数的方法&#xff08;2&#xff09;定义参数类型&#xff08;3&#xff09;参数与结构体&#xff08;4&#xff09;参数与内表4.调用子程序&#xff08;1&#xff09;调用程序内部子程序&#x…

vue npm link关联本地组件库

什么是 npm link 就是把你在本地开发好的文件做一个映射和链接&#xff0c;当你在 本地开发一个a项目&#xff0c;你的本地b项目想使用a项目下的组件 这时候就是需要进行npm link链接起来 a项目的运行效果 b项目的运行效果&#xff1a; 想要实现的效果&#xff1a;(在b项目上…

Oracle SQL Developer使用dbms_output.put_line显示输出

dbms输出 点击DBMS输出左侧的号&#xff0c;选择需要输出的数据库&#xff0c;点击确定 与步骤2选择相同数据库&#xff0c;右击数据库&#xff0c;选择打开SQL工作表(T) 在工作表中执行语句 declare --定义&#xff0c;相当于声明属性。t_a varchar2(20);--声明自定义属…

怎么写一篇计算机SCI论文初稿? - 易智编译EaseEditing

一、SCI论文的要求 SCI论文的核心是创新性。对于这个方面来说主要就是针对于论文的观点正确&#xff0c;文字通畅&#xff0c;逻辑严密&#xff0c;结构合理&#xff0c;结论有创新等等。 二、SCI论文格式规范 每一个SCI期刊都有自己特定的宗旨、栏目和专业定位&#xff0c;投…

TCP通信机制:三次握手、四次挥手、滑动窗口

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 TCP通信机制1. TCP三次握手2. TCP四次挥手3. TCP连接与数据传输过程4. TCP滑动窗口机制5. server服务端…

这套设备管理方案助你效率10倍提升

车间工厂等货物人员密集场所&#xff0c;对消防安全的要求很高。消防设备管理自然是生产制造型企业的核心之一。消防设施的有效管理&#xff0c;既要保证日常巡检工作的有效性&#xff0c;又要在设备出现故障后及时响应。在此基础上还要对整体管理情况进行数据分析&#xff0c;…

振弦采集模块的各种参数操作

振弦采集模块的各种参数操作 固件版本读取 点击指令区【 读取版本】 按钮&#xff0c;读取当前连接模块的固件版本信息&#xff0c;读取到的版本信息显示于按钮右侧。 VMTool 会根据读取到的版本不同对功能和界面做出调整&#xff0c;故此&#xff0c; 在使用 VMTool 时&#…

Workfine新手入门:给图片加水印

哈喽&#xff0c;我是办公助手小W&#xff0c;又到了跟大家分享办公小技巧的时候啦&#xff01; 最近Workfine5.0最新版本上线后&#xff0c;一直有人问到底有啥新功能啊&#xff1f;与往期版本有何不同呢&#xff1f;小W亲自去体验了一番&#xff0c;最大的一个亮点就是新增了…

90、【树与二叉树】leetcode ——104. 二叉树的最大深度:层次遍历+DFS+子问题分解(C++版本)

解题思路 原题链接&#xff1a;104. 二叉树的最大深度 解题思路 1、迭代法&#xff1a;层次遍历BFS /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), rig…