热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

可有可无正则_Python爬虫|0x9字符串解析神器:正则表达式

(给抠腚男孩加星标,提升Python、Android技能)作者:CoderPig正则表达式(RegularExpression),一个根据
(给抠腚男孩加星标,提升Python、Android技能)
作者:CoderPig
正则表达式(Regular Expression),一个根据「特定语法结构编写的字符串」,常用于字符串的检索、替换及匹配验证。有啥用?来个简单的例子感受下:从下述字符串中提取手机号码水电费了开的斯洛13712345678伐克圣诞节要怎么提取?遍历字符串一个个字符判断?很麻烦吧,如果用正则的话,只需下述一串字符串:(0|86|17951)?(13[0-9]|14[579]|15[0-35-9]|17[01678]|18[0-9])[0-9]{8}随手打开一个在线正则测试工具,复制粘贴:

https://tool.oschina.net/regex/

de90e6a17ef4de63c86270934fbfddc5.png点击测试匹配后,可以看到手机号码已被提取出来了,强大如斯,看不懂上述表达式?没关系,本节就来系统地讲解下Python中正则的详细用法。9d031d629e0ab9515266fc8880df58e0.pngre模块Python中内置re模块用来处理正则表达式,上述在线测试的例子可以写成Python代码:

import re
reg_string = '水电费了开的斯洛13712345678伐克圣诞节'
reg = '(0|86|17951)?(13[0-9]|14[579]|15[0-35-9]|17[01678]|18[0-9])[0-9]{8}'
result = re.search(reg, reg_string)print(result)# 输出:#
上述用到的search()函数是re模块提供的正则匹配函数,一一介绍下re模块提供的几个常用函数~

// 0x1、常用函数 //

----------------:匹配re.match(pattern, string, flags=0)尝试从字符串的开头进行匹配,匹配成功返回匹配对象,否则返回None;re.search(pattern, string, flags=0)扫描整个字符串,返回第一个匹配对象,否则返回None;----------------:检索与替换re.findall(pattern, string, flags=0)扫描整个字符串,匹配所有能匹配的对象,以列表形式返回;re.finditer(pattern, string, flags=0)参数同findall,匹配所有能匹配的对象,但是是以迭代器形式返回;re.sub(pattern, repl, string, count=0, flags=0)将匹配的字符串替换为其他字符串,count为替换的最大次数,默认为0,替换所有。re.split(pattern, string, maxsplit=0, flags=0)将匹配的字符串进行分割,返回列表,maxsplit为分割的最大次数,默认为0,分割所有。----------------:编译Pattern对象对于多次用的正则表达式,可调用compile()函数将正则表达式编译成Pattern对象,调用时直接Pattern对象.xxx即可,以此提高复用性。----------------:flags修饰符在调用这些常用函数时,可传入flags参数(标志位修饰符),来控制匹配模式,有下表这些可供选择,如果想同时选择多个使用运算符"|"进行连接,比如:re.I|re.M
修饰符描述
re.IIGNORECASE → 忽略大小写
re.MMULTILINE → 多行匹配,影响^和$
re.SDOTALL → 使.匹配包括换行在内的所有字符
re.XVERBOSE → 忽略空白和注释,并允许使用'#'来引导一个注释
re.UUNICODE → 根据Unicode字符集解析字符,影响\w、\W、\b和\B
re.LLOCALE → 做本地化识别(locale-aware)匹配
9d031d629e0ab9515266fc8880df58e0.png正则语法详解

// 0x1、加在正则字符串前的'r' //

用于告知编译器这个string是raw string(原始字符串),不要转义反斜杠,比如r'\n'是两个字符:反斜杠+n,而不是换行!

// 0x2、字符规则 //

字符作用
.匹配任意一个字符(除\n外)
[...]匹配[]中列举的字符
[^...]匹配不在[]中列举的字符
\d匹配数字,0-9
\D匹配非数字
\s匹配空白,即空白与tab缩进
\S匹配非空白
\w匹配字母数字或下划线,a-z,A-Z,0-9,_
\W匹配非字母数字或下划线
-匹配范围,如a-f

// 0x3、数量规则 //

Tips:前三个做了优化,速度会更快,尽量使用它们~ ?‍♂️
字符作用
*前面的字符出现0次或无限次,即可有可无
+前面的字符出现1次或无限次,即最少一次
?前面的字符出现0次或一次,即要么不出现,要么只出现一次
{m}前面的字符出现m次
{m,}前面的字符至少出现m次
{m,n}前面的字符出现m到n次

// 0x4、边界规则 //

字符作用
^行的开始
$行的结束
\b单词边界,即单词和空格间的位置,如'er\b'可匹配'never'中的'er',但不能匹配'verb'中的'er'
\B匹配非单词边界
\A匹配字符串开头
\Z匹配字符串结尾,如果有换行,只匹配到换行前的结束字符串
\z匹配字符串结尾,如果有换行,会连换行符也匹配

// 0x5、分组 //

有时我们需要的可能是匹配字符串中的一部分内容,可以进行分组,使用括号()包裹,比如:从匹配的字符串中提取出区号和本地号码 → ^(\d{3})-(\d{3,8})$,具体规则如下表所示:
字符作用
l匹配左右任意一个表达式
(re)匹配括号内的表达式,也表示一个组
(?:re)同上,但不表示一个组
----------------:零宽度断言,难点,看不懂的可以跳过又称环视或预搜索,一种零宽度的匹配,匹配到的内容不会保存到匹配结果中去,最终匹配结果只是一个位置而已。作用是给指定位置添加一个限定条件,用来规定此位置前或后的字符需满足限定条件才能使用正则中的子表达式匹配成功。比如:abaZW123 → ab(?=[A-Z]) → 与后面跟着一个大写字母的ab匹配,这里的ab跟着小写的a,所以这里是不匹配的。具体完整语法如下所述:
  • (?=re)前向肯定断言,仅当子表达式在此位置的右侧匹配时才继续匹配,如:18(?=88) 与88结尾的18实例匹配。

  • (?!re)前向否定断言,仅当子表达式不在此位置的右侧匹配时才继续匹配,如:(?!88) 与不以88结尾的实例匹配,所以与1888不匹配。

  • (?<&#61;re)后向肯定断言&#xff0c;仅当子表达式在此位置的左侧匹配时才继续匹配&#xff0c;如&#xff1a;(?&#61;18)88 与 跟在18后的88实例匹配。

  • (?后向否定断言&#xff0c;仅当子表达式不在此位置的左侧匹配时才继续匹配&#xff0c;如&#xff1a;(?

----------------&#xff1a;re中的group()及其他函数在re模块中&#xff0c;可以调用group()函数获得每个分组匹配内容&#xff1a;

reg_pattern &#61; re.match(r&#39;^(\d{4})-(\d{3,8})$&#39;, &#39;0756-1234567&#39;)print(reg_pattern.group())print(reg_pattern.group(0))print(reg_pattern.group(1))print(reg_pattern.group(2))# 输出&#xff1a;0756-12345670756-123456707561234567除此之外&#xff0c;还有四个常用函数&#xff1a;

  • groups()&#xff1a;从group(1)开始往后的所有值&#xff0c;返回一个元组

  • start()&#xff1a;返回匹配的开始位置

  • end()&#xff1a;返回匹配的结束位置

  • span()&#xff1a;返回一个元组&#xff0c;表示匹配位置(开始&#xff0c;结束)

// 0x6、贪婪与非贪婪 //

正则匹配默认是贪婪匹配&#xff0c;即匹配尽可能多的字符&#xff0c;如&#xff1a;

re.match(r&#39;^(\d&#43;)(0*)$&#39;,&#39;12345000&#39;).groups()# 原意是向得到(&#39;12345&#39;, &#39;000&#39;)这样的结果的&#xff0c;但输出的却是&#xff1a;
(&#39;12345000&#39;, &#39;&#39;)# 由于贪婪匹配&#xff0c;直接把后面的0券给匹配了&#xff0c;结果0*只能匹配空字符串了&#xff0c;# 若果想尽可能少的匹配&#xff0c;可在\d&#43;后加上一个问号?&#xff0c;采用非贪婪匹配
re.match(r&#39;^(\d&#43;?)(0*)$&#39;,&#39;12345000&#39;).groups()# 输出结果&#xff1a;
(&#39;12345&#39;, &#39;000&#39;)

// 0x7、转义字符 //

当遇到用于正则匹配模式的特殊字符时&#xff0c;在前面加反斜线转义一下即可&#xff0c;比如\.&#xff0c;\(&#xff0c;\)等。9d031d629e0ab9515266fc8880df58e0.png正则练习

// 0x1、简单验证手机号码格式 //

# ① 开头可能是&#xff1a;0(长途)、86(天朝国际区号)、17951(国际电话)中的# 一个或者一个都没有
(0|86|17951)?# ② 接着1xx&#xff0c;有13x&#xff0c;14x&#xff0c;15x&#xff0c;17x&#xff0c;18x&#xff0c;后面这个x的取值范围是不一样的# 13x&#xff1a;0123456789# 14x&#xff1a;579# 15x&#xff1a;012356789# 17x&#xff1a;01678# 18x&#xff1a;0123456789
(13[0-9]|14[579]|15[0-35-9]|17[01678]|18[0-9])# ③ 剩下的8个数字
[0-9]{8}# 将匹配字符串拼接到一起&#xff1a;
phone_ number_regex &#61; re.compile("^(0|86|17951)?(13[0-9]|14[579]|15[0-35-9]|17[01678]|18[0-9])[0-9]{8}$")# Tips&#xff1a;0-9也可以直接用\d匹配

// 0x2、身份证号码验证 //

# 身份证分两代&#xff1a;# 一代&#xff1a;15位号码组成 → xxxxxx yy mm dd pp s# 二代&#xff1a;18个号码组成 → xxxxxx yyyy mm dd ppp s# 把这两种情况分开&#xff0c;先推算18位# 前6位 → 地址编码(省市县)&#xff0c;第1位1-9&#xff0c;其他五位0-9
[1-9]\d{5}# ② 7-10位 → 年&#xff0c;范围1800-2099
(18|19|20)\d{2})# ③ 11-12位 → 月&#xff0c;1-9月需补零0&#xff0c;10&#xff0c;11&#xff0c;12
(0[1-9]|10|11|12)# ④ 13-14位 → 日&#xff0c;13位可能为0、1、2&#xff0c;16位0-9&#xff0c;还需补上10、20、30、31
([012][1-9]|10|20|30|31)# ⑤ 15-17位 → 顺序码&#xff0c;对同年同月同日出生的人编订的顺序号&#xff0c;奇数分给男的&#xff0c;偶数分给女的&#xff1b;
\d{3}# ⑥ 18位 → 校验码&#xff0c;0-9或x和X
[0-9Xx]# 将字符串拼接下得出18位身份证号码的过滤正则字符串&#xff1a;
[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|10|11|12)([012][1-9]|10|20|30|31)\d{3}[0-9Xx]# 18位的推算出来了&#xff0c;15也不在话下了
[1-9]\d{5}\d{2}(0[1-9]|10|11|12)([012][1-9]|10|20|30|31)\d{2}[0-9Xx]# 最后用|连接符将两个正则表达式拼接&#xff1a;
id_card_regex &#61; re.compile(r"^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|10|11|12)([012][1-9]|10|20|30|31)\d{3}[0-9Xx]|[1-9]\d{5}\d{2}(0[1-9]|10|11|12)([012][1-9]|10|20|30|31)\d{2}[0-9Xx]$")

// 0x3、实用正则收集 //

# 摘自&#xff1a;https://juejin.cn/post/6844904182835757064&#xff0c;更多可自行打开源链接查阅# 网址(url,支持端口和"?&#43;参数"和"#&#43;参数)
((ht|f)tps?://)?[\w-]&#43;(\.[\w-]&#43;)&#43;([\w.,&#64;?^&#61;%&:/~&#43;#-]*[\w&#64;?^&#61;%&/~&#43;#-])?# 24小时制时间(HH:mm:ss)
(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d# 12小时制时间(hh:mm:ss)
(?:1[0-2]|0?[1-9]):[0-5]\d:[0-5]\d# date(日期)
\d{4}(-)(1[0-2]|0?\d)\1([0-2]\d|\d|30|31)# email(邮箱)
(([^<>()\[\]\\.,;:\s&#64;"]&#43;(\.[^<>()\[\]\\.,;:\s&#64;"]&#43;)*)|(".&#43;"))&#64;((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]&#43;\.)&#43;[a-zA-Z]{2,}))# 中文/汉字
(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])&#43;# 匹配连续重复的字符
(.)\1&#43;
9d031d629e0ab9515266fc8880df58e0.png实战&#xff1a;爬取城市编码列表

// 0x1、爬取分析 //

城市编码在很多地方用到&#xff0c;比如查询某个地区的天气&#xff1a;

http://www.weather.com.cn/weather1dn/101280601.shtml

后面跟着的101280601就是深圳的城市编码&#xff0c;网上搜到大部分城市编码大全大多比较旧不全&#xff0c;中国天气网既然提供查询接口&#xff0c;那么应该是有完整的最新的城市列表&#xff0c;当然不会直接暴露&#xff0c;需要用间接的方法获取到。打开国内天气预报文字版地址&#xff1a;

http://www.weather.com.cn/textFC/hb.shtml#

看到下图所示列表&#xff1a;83ea5ddd2bb3bf544f33b6a581dde0de.png点击北京&#xff0c;跳转至&#xff1a;

http://www.weather.com.cn/textFC/beijing.shtml

faefa306df82882a850e184c9c977836.png再点击表里的北京&#xff0c;跳转至&#xff1a;

http://www.weather.com.cn/weather/101010100.shtml

这里的101010100就是北京的城市编码&#xff0c;打开海淀&#xff0c;城市编码为101010200&#xff0c;所以我们要做就是&#xff1a;
  • ① 获取所有省的跳转地址列表&#xff1b; 

  • ② 循环遍历每个跳转地址&#xff0c;提取后面的城市编码保存起来&#xff1b;

逻辑弄懂了&#xff0c;直接写出完整代码。

// 0x2、完整代码实现 //

# -*- coding: utf-8 -*-# !/usr/bin/env python"""------------------------------------------------- File : city_code_spider.py Author : CoderPig date : 2020-12-02 00:03 Desc : 城市编码爬取-------------------------------------------------"""import requests as rimport re
origin_url &#61; &#39;http://www.weather.com.cn/textFC/hb.shtml&#39;# 提取省跳转城市的正则
province_pattern &#61; re.compile(r&#39;)
province_set &#61; set() # 用set的原因是去重# 非省排除列表
province_remove_list &#61; [&#39;hb&#39;, &#39;db&#39;, &#39;hd&#39;, &#39;hz&#39;, &#39;hn&#39;, &#39;xb&#39;, &#39;xn&#39;, &#39;gat&#39;]# 省地址
province_base_url &#61; &#39;http://www.weather.com.cn/textFC/{}.shtml&#39;# 提取城市编码及城市名的正则
city_pattern &#61; re.compile(r&#39;(101\d{6}).shtml.*?>(.*?))
city_dict &#61; dict() # 用dict的原因同样是去重
headers &#61; {&#39;Host&#39;: &#39;www.weather.com.cn&#39;,&#39;Referer&#39;: &#39;http://www.weather.com.cn/textFC/db.shtml&#39;,&#39;User-Agent&#39;: &#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) &#39;&#39;Chrome/83.0.4103.97 Safari/537.36 &#39;
}# 获取省def extract_province():
    resp &#61; r.get(origin_url, headers&#61;headers)print("爬取&#xff1a;", resp.url)if resp is not None:
        resp_text &#61; resp.text
        province_list &#61; province_pattern.findall(resp_text)for province in province_list:if province not in province_remove_list:
                province_set.add(province)# 提取市及城市编码def extract_city(extract):
    resp &#61; r.get(province_base_url.format(extract), headers&#61;headers)print("爬取&#xff1a;", resp.url)if resp is not None:
        resp.encoding &#61; &#39;utf-8&#39; # 需设置编码&#xff0c;否则中文会乱码
        resp_text &#61; resp.text
        city_list &#61; city_pattern.findall(resp_text)for city in city_list:if city[1] !&#61; &#39;详情&#39;:
                city_dict[city[0]] &#61; city[1]# 写入文件def write_to_file(content, file_path):with open(file_path, &#39;w&#43;&#39;, encoding&#61;&#39;utf-8&#39;) as f:
        f.write(content)if __name__ &#61;&#61; &#39;__main__&#39;:
    extract_province()    for province in province_set:
        extract_city(province)# 根据编码顺序排序
    asc_city_dict &#61; {}for c in sorted(city_dict):
        asc_city_dict[c] &#61; city_dict[c]
    write_content &#61; &#39;&#39;for (key, value) in asc_city_dict.items():
        write_content &#43;&#61; &#39;{}: {}\n&#39;.format(key, value)
    write_to_file(write_content, "city_code.txt")
运行后&#xff0c;控制台陆续打印出爬取的url&#xff1a;eb2751dc3fc9fe0055bd6c76ba683833.png打开本地生成的city_code.txt文件&#xff0c;可以看到城市编码信息已保存到本地459877bc6dbd88b2133424546a62816a.png

- EOF -




推荐阅读
  • Python爬虫中使用正则表达式的方法和注意事项
    本文介绍了在Python爬虫中使用正则表达式的方法和注意事项。首先解释了爬虫的四个主要步骤,并强调了正则表达式在数据处理中的重要性。然后详细介绍了正则表达式的概念和用法,包括检索、替换和过滤文本的功能。同时提到了re模块是Python内置的用于处理正则表达式的模块,并给出了使用正则表达式时需要注意的特殊字符转义和原始字符串的用法。通过本文的学习,读者可以掌握在Python爬虫中使用正则表达式的技巧和方法。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文详细介绍了Python中正则表达式和re模块的使用方法。首先解释了转义符的作用,以及如何在字符串中包含特殊字符。然后介绍了re模块的功能和常用方法。通过学习本文,读者可以掌握正则表达式的基本概念和使用技巧,进一步提高Python编程能力。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • node.jsrequire和ES6导入导出的区别原 ... [详细]
  • Python中的PyInputPlus模块原文:https ... [详细]
  • Yii framwork 应用小窍门
    Yiiframework应用小窍门1.YiiFramework]如何获取当前controller的名称?下面语句就可以获取当前控制器的名称了!Php代码 ... [详细]
  • MySQL多表数据库操作方法及子查询详解
    本文详细介绍了MySQL数据库的多表操作方法,包括增删改和单表查询,同时还解释了子查询的概念和用法。文章通过示例和步骤说明了如何进行数据的插入、删除和更新操作,以及如何执行单表查询和使用聚合函数进行统计。对于需要对MySQL数据库进行操作的读者来说,本文是一个非常实用的参考资料。 ... [详细]
  • 本文介绍了在Go语言中可见性与scope的规则,包括在函数内外声明的可见性、命名规范和命名风格,以及变量声明和短变量声明的语法。同时,还介绍了变量的生命周期,包括包级别变量和局部变量的生命周期,以及变量在堆和栈上分配的规则和逃逸分析的概念。 ... [详细]
  • ***Createdbyjiachenpanon161118.**合法uri*exportfunctionvalidateURL(textval){consturlregex^( ... [详细]
author-avatar
mobiledu2502912781
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有