微信閱讀:網羅天下之~正則表達html
在線預覽:http://github.lesschina.com/python/spider/re/1.網羅天下之-正則表達.htmlpython
字符 | 含義 |
---|---|
. |
匹配任意1個字符(除了\n ) |
[] |
匹配[ ] 中列舉的1個字符(^ 能夠取反) |
\d |
匹配數字(0~9 ) |
\D |
匹配非數字(非數字 ) |
\s |
匹配空白(空格、Tab鍵、回車 ) |
\S |
匹配非空白 |
\w |
匹配單詞字符,即a-z、A-Z、0-九、_ (包括單箇中文字符) |
\W |
匹配非單詞字符 |
注意:git
\s
並不匹配""
<re.Match object; span=(0, 1), match='\t'>
match=xxx
,就是咱們ret.group()
的結果# 定義一個通用測試方法
import re
def my_match(re_str, input_str):
ret = re.match(re_str, input_str)
if ret:
print(f"[匹配結果:{ret.group()}]")
else:
print(f"[{input_str}不匹配]")
return ret
# Python中字符串前面加上 r 表示原生字符串(不轉義)
# \s 驗證
# 空格匹配驗證
my_match("\s"," ")
# Tab鍵匹配驗證
my_match("\s","\t")
# 回車匹配驗證
my_match("\s","\n")
# 不匹配驗證:(空字符串)
my_match("\s","")
# \d 驗證
# 匹配單個數字
my_match("\d","1") # 一點要變成字符串
# 多個數字則只能匹配一個字符
my_match("\d","11") # 注意
# 解決:以^開頭,以$結尾
my_match("^\d$","11")
# [] 驗證
# 不是一、二、3則不匹配
my_match("[1-3]","4")
# 匹配1~3,6~9
my_match("[1-36-9]","7")
# 不匹配驗證
my_match("[1-36-9]","5")
# 只匹配數字和字母(大小寫)
my_match("[\da-zA-Z]","7")
my_match("[\da-zA-Z]","b")
my_match("[\da-zA-Z]","B")
# 不匹配驗證
my_match("[\da-zA-Z]","_")
# [] 取反擴展
# \d ==> [0-9]
# \D ==> [^0-9]
# 非二、四、6
my_match("[^246]","3")
my_match("[^246]","@")
# 錯誤驗證
my_match("[^246]","4")
# 非 1~6
my_match("[^1-6]","7")
my_match("[^1-6]","#")
# 錯誤驗證
my_match("[^1-6]","5")
# \w在UTF8下會匹配中文的驗證:
my_match("\w","滾")
# .匹配任意字符,不包括\n的驗證:
my_match(".","\n")
# 除了\n,能夠匹配任意一個字符
my_match(".","\t")
# 擴展
# 若是想讓.支持\n,再多傳個flag:re.S
re.match(".","\n",re.S)
字符 | 含義 |
---|---|
* |
1個字符出現次數:>=0 |
+ |
1個字符出現次數:>=1 |
? |
1個字符出現次數:1 or 0 |
{m} |
1個字符出現m 次 |
{m,n} |
1個字符出現從[m,n] 次 |
\ |
轉義特殊字符 |
^ |
匹配字符串開頭 |
$ |
匹配字符串結尾 |
多個字符,通常都是以^
開頭,以$
結尾,否則容易出Bug(re.match
方法默認以^
開頭)github
PS:vi
命令模式下,輸入^
和$
,光標會跳轉到頭和尾正則表達式
Python中r""
表明不轉義字符串:數據庫
# r"",若是包含轉義字符\就容易出錯了,這時候r""就上場了
re.match("\\mmd","\mmd")
# 緣由分析
# \是有特殊含義的,想要沒有特殊含義就再加個\,
# 那加上的這個\又有特殊含義,因此就蛋疼了,r""這時候就上場了
# 解決方法
re.match(r"\\mmd","\\mmd") # 下面有案例
# * 、 + 、?
# *:0個或者多個
my_match(r"\d*","")
my_match(r"\d*","11")
# +:1個或者多個
# 不匹配驗證:
my_match(r"\d+","")
# 匹配驗證:
my_match(r"\d+","1")
my_match(r"\d+","11")
# ?:0個或者1次
# 不匹配驗證:
my_match(r"^\d?$","11")
# 匹配驗證
my_match(r"^\d?$","")
my_match(r"^\d?$","1")
# 爲何用^和$包裹,看下面兩個奇葩案例就知道了
my_match("\d","123333")
my_match("\d*","a") # ==> "a" ==> """a"
# {}指定位數驗證
# ? ==> {0,1}
# 1位數字或者2位數字
my_match(r"^\d{1,2}$","7")
# 1位數字或者2位數字
my_match(r"^\d{1,2}$","17")
# 錯誤驗證
my_match(r"^\d{1,2}$","777")
# 指定位數 eg:10位數字
my_match(r"^\d{10}$","1234567890")
# 錯誤驗證 ~ 9位
my_match(r"^\d{10}$","123456789")
# 錯誤驗證 ~ 非整數
my_match(r"^\d{10}$","A123456789")
# {} 擴展
# * ==> {0,}
# + ==> {1,}
# \d 至少3個
my_match(r"\d{3,}","123")
my_match(r"\d{3,}","1234")
# 錯誤驗證
my_match(r"\d{3,}","12")
# ^ $ 案例
# 驗證變量命名
my_match(r"^[a-zA-z_]\w*$","a_bbp")
my_match(r"^[a-zA-z_]\w*$","_")
# 測試一個就知道爲何用\w了
def test蛋():
print("mmd")
test蛋() # Python Code
# 若是沒有加開頭和結尾的Bug測試
# 沒有判斷結尾的Bug案例
my_match(r"[a-zA-z_]\w*","a_b#w")
# 測試Bug,這個也匹配了
my_match(r"[a-zA-z_]+@qq.com","mmd@qq.comcom")
# 改進 ~ 如今不匹配了
my_match(r"^[a-zA-z_]\w*$","a_b#w")
# 轉義字符 \ 引入案例
# 測試Bug,這個也匹配了
my_match(r"[a-zA-z_]+@qq.com","mmd@qq#comcom")
# 改進 ~ 如今不匹配了 (開頭結尾+\轉義)
my_match(r"^[a-zA-z_]+@qq\.com$","mmd@qq#comcom")
# r"",若是包含轉義字符\就容易出錯了,這時候r""就上場了
try:
my_match("\\mmd","\mmd")
except Exception as ex:
print(ex)
# 緣由分析
# \是有特殊含義的,想要沒有特殊含義就再加個\,
# 那加上的這個\又有特殊含義,因此就蛋疼了,r""這時候就上場了
# 解決方法
my_match(r"\\mmd", "\\mmd")
字符 | 含義 |
---|---|
| |
匹配左右任意一個表達式 |
\b |
匹配一個單詞的邊界(字母與空格間的位置) |
\B |
匹配非單詞的邊界 |
( ) |
將括號中字符做爲一個分組 |
\num |
引用分組num匹配到的字符串 |
(?P<name>) |
分組起別名 |
(?P=name) |
引用別名爲name分組匹配到的字符串 |
# 匹配邊界
# \b 匹配以net結尾的單詞
my_match(r"\w+net\b","dotnet")
my_match(r"\w+net\b","dotnet crazy")
# 不匹配驗證
my_match(r"\w+net\b","dotnetcrazy")
# 後面會講
re.findall(r"\w+net\b","dotnet crazy aspnet")
# 不匹配驗證:\b、\B、^、$只是表明邊界,並不表示空格
my_match(r"\w+\bnet\b","dot net crazy")
# 正確修改
my_match(r"\w+\s\bnet\b","dot net crazy")
# 把上面換成\B,則表明單詞間必須是 非空格的字符
my_match(r"\w+\Bnet\B","dotnetcrazy")
my_match(r"\w+\Bnet\B","dotnetAcrazy")
my_match(r"\w+\Bnet\B","dotnet1crazy")
# 不匹配驗證
my_match(r"\w+\Bnet\B","dotnet#crazy")
# | 匹配左右任意一個表達式
# 匹配小明或者小張
my_match(r"^小明|小張$","小明")
my_match(r"^小明|小張$","小張")
# 不匹配驗證
my_match(r"^小明|小張$","小潘")
# () 將括號中字符做爲一個分組
# group(1) 返回第1個括號匹配內容
my_match(r"^[a-zA-Z0-9_]+@(qq|163)\.com$","mmd@163.com").group(1)
# HTML的標籤匹配匹配檢查
my_match(r"^<([a-zA-Z1-9]+)>.*</\1>$","<h1>萌萌噠</h1>").group(1)
# groups返回全部的匹配結果
my_match(r"^<([a-zA-Z1-9]+)><([a-zA-Z1-9]+)>(.*)</\2></\1>$","<p><font>我去</font></p>").groups()
# 匹配 qq.com 和 163.com (別忘記轉義.)
ret = my_match(r"(^[a-zA-Z0-9_]+)@(qq|163)\.com$","mmd@qq.com")
print(ret.groups())
ret = my_match(r"(^[a-zA-Z0-9_]+)@(qq|163)\.com$","mmd@163.com")
print(ret.groups())
# 不匹配驗證
my_match(r"^(^[a-zA-Z0-9_]+)@(qq|163)\.com$","@163.com")
my_match(r"^(^[a-zA-Z0-9_]+)@(qq|163)\.com$","mmd@123.com")
# 別名案例(不經常使用)
my_match(r"<(?P<mmd>\w*)><(?P<dnt>.*)>.*</(?P=dnt)></(?P=mmd)>","<html><h1>萌萌噠</h1></html>").group(2)
# 不匹配驗證
my_match(r"<(?P<mmd>\w*)><(?P<dnt>.*)>.*</(?P=dnt)></(?P=mmd)>","<html><h1>萌萌噠</h2></html>")
# 練練手
# 1~100之間的數字:(1,100)
my_match(r"^[1-9]\d?$","0")
my_match(r"^[1-9]\d?$","7") # 十位只能是1~9
my_match(r"^[1-9]\d?$","77")
# 不匹配驗證
my_match(r"^[1-9]\d?$","07")
my_match(r"^[1-9]\d?$","777")
# 0~100的數字:[0,100]
re_str=r"^([1-9]?\d?|100)$" # ^([1-9]\d?|100|0)$
my_match(re_str,"0")
my_match(re_str,"1")
my_match(re_str,"70")
my_match(re_str,"100")
# 不匹配驗證
my_match(re_str,"07")
my_match(re_str,"170")
my_match(re_str,"700")
上面的都是通用系列,下面的才能體現爲啥爬蟲是Python的優點:編程
re.match
:和其餘語言用法一致(默認從頭開始匹配)re.search
:匹配第一個並返回(若是加了^
和$
就和match同樣了)re.findall
:返回全部匹配的列表re.sub
:將匹配到的數據進行替換,再返回新的字符串
re.split
:正則切割函數(相似於字符串的split)re.compile
:正則字符串編譯成正則表達式對象# 匹配第一個就結束了
ret = re.search(r"\d","個人名字叫小明,今年23,88")
print(ret.group())
# 若是加了開頭結尾就和match同樣了
print(re.search(r"^\d$","個人名字叫小明,今年23,88"))
# 返回全部匹配的列表
re.findall(r"\d","個人名字叫小明,今年23,88")
re.split(r",|。","個人名字叫小明,今年23。88")
# sub案例:批量替換1
re.sub(r"\d+","***","我上次買的時候98.5塊,如今30就拿到了,差評!")
# sub案例:批量替換2 ~ 拿到分組內容並進行處理
re.sub(r"(\d+)",r"400\1","我是小明,客服電話是:6789688")
# sub案例:函數處理
def shit_test(result):
# 返回類型必須是str
return str(float(result.group())*2)
re.sub(r"\d+",shit_test,"我上次買的時候98.5塊,如今30就拿到了,差評!")
# 擴展內容
pattern = re.compile(r"A.*Z",re.S) # 表達式複用
print(re.match(pattern,"ABZ").group())
print(re.match(pattern,"ACZ").group())
# 練手小案例
# 提取單詞
input_str = "Python Golang NetCore JavaScript"
print(re.split(" ",input_str))
re.findall("[a-zA-Z]+",input_str)
# 提取文字 """ 保留字符串原始格式
html_str = """
<div>
<h3>職位描述</h3>
<div>
崗位職責: <br>1. 負責公司數據管理制度、規範、流程的設計,參與數據開發平臺的建設和管理<br>2. 規劃數據倉庫工做方向,持續提高團隊工做目標和工做效率<br>3. 負責全面瞭解公司業務,進行深層次的數據分析,爲數據開發項目提供指導性的意見,從數據角度爲公司產品開發、業務運營提供決策支持建議<br>4. 掌握業界技術動向,組織研究大數據相關前沿技術,用於指導實際的數據支持項目<br>任職要求:<br>1. 精通數據倉庫實施理論,生命週期管理,具有大型互聯網數據倉庫架構設計、模型設計、ETL設計經驗,以及海量數據處理和優化經驗<br>2. 深刻理解Hadoop/Hive/Spark/Storm/Kylin等大數據相關技術和原理<br>3. 有實際使用Hive/MR/Spark等大數據處理技術解決大數據相關問題的項目經驗,具有豐富的性能調優經驗<br>4 熟悉OLAP工具和數據分析技能,對數據敏感,可以進行數據分析,挖掘數據價值<br>5. 邏輯思惟能力強,有較強的學習能力和創新思惟,可以解決複雜的商業問題<br>6. 優秀的溝通能力和文字表達能力,有較強的團隊管理能力
</div>
</div>
"""
# 清除HTML標籤(`/?`:`/`出現0次或者1次)
re.sub(r"</?\w+>|\n| ","",html_str).strip()
# 貪婪演示
# 貪婪模式下會盡量匹配:
input_str = "我叫小明,歡迎撥打客服:4006789678"
ret = re.match(r"(.+)(\d+)", input_str)
print("[提取的號碼爲:]",ret.group(2))
print("[貪婪的字符串:]",ret.group(1))
# 解決方法 .+? or .*?
ret = re.match(r"(.+?)(\d+)", input_str)
print("[提取的號碼爲:]",ret.group(2))
print("[貪婪的字符串:]",ret.group(1))
# 練手小案例
# 增強版提取案例 ~ BOSS
html_str = """
<div class="detail-content">
<div class="job-sec">
<h3>職位描述</h3>
<div class="text">
崗位職責: <br>1. 負責公司數據管理制度、規範、流程的設計,參與數據開發平臺的建設和管理<br>2. 規劃數據倉庫工做方向,持續提高團隊工做目標和工做效率<br>3. 負責全面瞭解公司業務,進行深層次的數據分析,爲數據開發項目提供指導性的意見,從數據角度爲公司產品開發、業務運營提供決策支持建議<br>4. 掌握業界技術動向,組織研究大數據相關前沿技術,用於指導實際的數據支持項目<br>任職要求:<br>1. 精通數據倉庫實施理論,生命週期管理,具有大型互聯網數據倉庫架構設計、模型設計、ETL設計經驗,以及海量數據處理和優化經驗<br>2. 深刻理解Hadoop/Hive/Spark/Storm/Kylin等大數據相關技術和原理<br>3. 有實際使用Hive/MR/Spark等大數據處理技術解決大數據相關問題的項目經驗,具有豐富的性能調優經驗<br>4 熟悉OLAP工具和數據分析技能,對數據敏感,可以進行數據分析,挖掘數據價值<br>5. 邏輯思惟能力強,有較強的學習能力和創新思惟,可以解決複雜的商業問題<br>6. 優秀的溝通能力和文字表達能力,有較強的團隊管理能力
</div>
</div>
<div class="job-sec">pass</div>
<div class="job-sec">xx</div>
<div class="job-sec company-info"pass</div>
<div class="job-sec">pass</div>
</div>
"""
# 先找到第一個job-sec(正則思路:直接定位匹配,寫幾個關鍵詞,其餘都是偷懶寫法.*?)
ret = re.search(r'<div.*?job-sec">.*?text">(.*?)</div>', html_str, re.S)
new_str = ret.group(1)
print(new_str)
# 再處理下多餘的HTML標籤
re.sub(r"<br>|\s","",new_str)
# 再來一例 ~ 拉勾
html_str = """
<dd class="job_bt">
<h3 class="description">職位描述:</h3>
<div>
<p>崗位職責:<br>1. 爲政企客戶和合做夥伴提供騰訊互聯網+總體解決方案技術層面的售前架構諮詢服務; <br>2. 爲政府、企業提供騰訊大數據等項目的規劃、諮詢服務,協助合做夥伴及產品部門進行大數據等項目的落地; <br>3. 配合BD等團隊發展生態合做夥伴,將騰訊能力與合做夥伴方案進行方案融合,爲合做夥伴提供諮詢、培訓、方案融合服務; <br>4. 針對客戶互聯網+需求,深度定製互聯網+解決方案並制定實施計劃,把握全局項目進度,協調相關資源、協助實施團隊完成方案Demo系統搭建,PoC測試及項目落地工做; <br>5. 負責互聯網+案例、技術方案的更新維護,以及佈道工做。</p>
<p><br>崗位要求:<br>1. 本科以上學歷,5年(碩士3年)以上大數據等售前諮詢相關的工做經驗; <br>2. 熟悉Hadoop、Spark等開源大數據技術體系,熟悉Oracle、PostgreSQL等數據庫。要求至少有3個以上政企大數據項目規劃與落地經驗; <br>3. 具備宏觀思惟,有高層彙報能力。熟悉醫療、公安等行業大數據優先; <br>4. 具有優秀的文檔能力,清晰明瞭地表達架構意圖,可以熟練編寫各種技術文檔; <br>5. 良好的溝通、協調及資源整合能力; <br>6. 有針對行業ISV的渠道支持經驗優先。</p>
</div>
</dd>
"""
# 匹配須要的內容(正則思路:快速定位,而後.*?偷懶寫法走起)
ret = re.search('<h3.*?p>(.*?)</p>.*?p>(.*?)</p>',html_str,re.S)
# 再處理下多餘的HTML標籤
for item in (ret.group(1),ret.group(2)):
print(re.sub(r"\s| |<br>", "", item))
第一階段併發、網絡、爬蟲、DB的網撒完了,如今準備慢慢收網網絡
這幾個系列相關性挺強,隨便深刻哪個專題都得扯到其餘專題
因此最後網子就有點大了,不過不用慌,慢慢來~
相關係列文章以下:
Python3 與 C# 併發編程之~ 上篇(Net專欄)
Queue引入篇 [看看就行]
線程篇之~增強篇收尾(Event、Timer、Barrier)