python爬蟲之正則表達式及開源許可證的爬取

 

- 通過正則表達式可以定義一些匹配規則,只要滿足匹配規則即認爲匹配成功,從而實現糊匹配

- 正則表達式中既可以包含普通字符,也可以包含由特殊字符指定的匹配模式。

- 在實際應用正則表達式進行匹配時,正則表達式中的普通字符需要做精確匹配,而特殊字符指定的匹配模式則對應了用於模糊匹配的規則。

 

常用的匹配模式  

 

 

 

 

我們通常在用於表示正則表達式的字符串前加上一個字符r,使得後面的字符串忽略轉義符。例如,對於字符串'\\bfoo\\b',我們可以寫作r'\bfoo\b'。

 

re模塊

compile 函數用於將一個字符串形式的正則表達式編譯成一個正則表達式對象,供 match、search以及其他一些函數使用。格式爲:

  •  
re.compile(pattern, flags=0)

 

其中,pattern是一個字符串形式的正則表達式;flags指定了匹配選項,

可以使用按位或(|)運算符將多個選項連接起來;flags的默認值爲0,

表示沒有任何匹配選項。例如,flags參數可以設置爲:

re.I 或 re.IGNORECASE # 匹配時不區分大小寫re.X或re.VERBOSE # 忽略正則表達式中的空格和#後面的註釋re.M或re.MULTILINE  # 使匹配模式中的^能夠匹配每行的開頭若干個字符,$能夠匹配每行的結尾若干個字符

當一個正則表達式在程序中被多次使用時,通過compile函數生成的正則表達式對象可重複使用、從而提高效率。

 

match函數用於對字符串開頭的若干字符進行正則表達式的匹配。格式爲:

  •  
re.match(pattern, string, flags=0)

 

其中,pattern是要匹配的正則表達式;string要作正則表達式匹配的

字符串;flags參數的含義與compile函數中的flags參數相同。如果匹

配成功,則返回一個Match對象;如果匹配失敗,則返回None。

  •  
import re 
result=re.match(r'python', '''我喜歡學習Python
Python是一門流行的編程語言''', re.I|re.M)
print('result:',result)
#輸出 result: None# 解釋:re.match函數只會對字符串開頭的若干字符作匹配,而不對後面行的開頭字符作匹配

除了直接調用re模塊中的match函數外,也可以使用compile函數生成的正則表達式對象中的match方法實現同樣的功能,格式爲:

Pattern.match(string[,pos[,endpos]])

 

其中,Pattern是compile函數返回的正則表達式對象;string是要做正則表達式匹配的字符串;可選參數pos指定了從string的哪個位置開始進行匹配,默認爲0;可選參數endpos指定了string的結束位置,match函數將對string中pos至endpos-1範圍的子串進行正則表達式匹配。

  •  
import re
pattern=re.compile(r'python', re.I) #生成正則表達式對象
result=pattern.match('我喜歡學習Python!',5)
print('result:',result)
#輸出 result: <re.Match object; span=(5, 11), match='Python'>#解釋:其中span是匹配的字符序列在字符串中的位置信息,match中保存了匹配到的字符序列信息。

Match對象方法

group([group1,…])根據傳入的組號返回對應分組的匹配結果。如果傳入一個組號,則返回一個字符串形式的匹配結果;如果傳入多個組號,則返回一個由多個匹配結果字符串組成的元組。如果傳入0或不傳入參數,則返回的是與正則表達式匹配的整個字符串

groups() 返回一個由所有分組的匹配結果字符串組成的元組

start(group=0)返回指定分組的匹配結果字符串在原字符串中的起始位置;如果group值爲0(默認值),則返回與正則表達式匹配的整個字符串在原字符串中的起始位置

end(group=0)返回指定分組的匹配結果字符串在原字符串中的結束位置;如果group值爲0(默認值),則返回與正則表達式匹配的整個字符串在原字符串中的結束位置

  •  
import re
str='''sno:#1810101#,name:#李曉明#,age:#19#,major:#計算機#
sno:#1810102#,name:#馬紅#,age:#20#,major:#數學#'''
rlt=re.search(r'name:#([\s\S]*?)#[\s\S]*?major:#([\s\S]*?)#',str, re.I)
if rlt: #判斷是否有匹配結果
    print('匹配到的整個字符串:', rlt.group())
    print('name:%s, startpos:%d, endpos:%d'%(rlt.group(1),rlt.start(1), rlt.end(1)))
    print('major:%s, startpos:%d, endpos:%d'%(rlt.group(2),rlt.start(2), rlt.end(2)))
    print('所有分組匹配結果:', rlt.groups())
else:
    print('未找到匹配信息')
#輸出 匹配到的整個字符串: name:#李曉明#,age:#19#,major:#計算機#name:李曉明, startpos:20, endpos:23major:計算機, startpos:41, endpos:44所有分組匹配結果: ('李曉明', '計算機')

 

search函數整個字符串進行掃描並返回第一個匹配的結果。格式爲:

re.search(pattern, string, flags=0)

 

re.search函數各參數的含義與re.match函數完全相同。如果匹配成功,則返回一個Match對象;否則,返回None。

 

不同於re.match函數(只匹配字符串開頭的若干字符),re.search函數可以對整個字符串從左向右掃描找到第一個匹配的字符序列。

  •  
import re
result=re.search(r'python', '我喜歡學習Python,Python簡單易用!', re.I)
print('result:',result)

#輸出 
result3: <re.Match object; span=(5, 11), match='Python'>

同Pattern.match方法一樣,也可以使用compile函數返回的正則表達式對象中的search方法實現re.search函數同樣的功能:

Pattern.search(string[,pos[,endpos]])

 

findall函數用於在字符串中找到所有與正則表達式匹配的子串。re.findall函數可以一次完成字符串中所有滿足正則表達式規則的子串的匹配。格式爲:

  •  
re.findall(pattern, string, flags=0)
  •  
import re
str='''sno:#1810101#,name:#李曉明#,age:#19#,major:#計算機#
sno:#1810102#,name:#馬紅#,age:#20#,major:#數學#'''
rlt=re.findall(r'name:#([\s\S]*?)#[\s\S]*?major:#([\s\S]*?)#', str, re.I)
print(rlt)

#輸出 
[('李曉明', '計算機'), ('馬紅', '數學')]

finditer函數與re.findall函數功能完全相同,唯一區別在於re.findall函

數返回列表形式的結果,而re.finditer返回迭代器形式的結果。格式爲:

 

re.finditer(pattern, string, flags=0)
  •  
  •  
import re
str='''sno:#1810101#,name:#李曉明#,age:#19#,major:#計算機#
sno:#1810102#,name:#馬紅#,age:#20#,major:#數學#'''
rlt=re.findall(r'name:#([\s\S]*?)#[\s\S]*?major:#([\s\S]*?)#', str, re.I)
for r in rlt1:
    print(r)
#輸出 
<re.Match object; span=(14, 45), match='name:#李曉明#,age:#19#,major:#計算機#'>
<re.Match object; span=(60, 89), match='name:#馬紅#,age:#20#,major:#數學#'>

貪婪匹配:Re庫默認採用貪婪匹配,即輸出匹配最長的子串。

>>> match = re.search(r'PY.*N', 'PYANBNCNDN')>>> match.group(0)PYANBNCNDN

最小匹配:只要長度輸出可能不同的,都可以通過在操作符後增加?變成最小匹配。

>>> match = re.search(r'PY.*?N', 'PYANBNCNDN')>>> match.group(0)PYAN*? 前一個字符0次或無限次擴展,最小匹配+? 前一個字符1次或無限次擴展,最小匹配?? 前一個字符0次或1次擴展,最小匹配{m,n}? 擴展前一個字符m至n次(含n),最小匹配

split函數用於將字符串按與正則表達式匹配的子串分割。格式爲:

re.split(pattern, string, maxsplit=0, flags=0)

其中,pattern是正則表達式;string是要分割的字符串;maxsplit是最

大分割次數,默認爲0表示不限制分割次數;flags與re.match等函數中

的flags參數含義相同。

import re
str='sno:1810101,name:李曉明,age:19,major:計算機'
rlt=re.split(r'\W+', str)
print(rlt)

#輸出 
['sno', '1810101', 'name', '李曉明', 'age', '19', 'major', '計算機']

 

sub函數用於替換字符串中與正則表達式匹配的子串。格式爲:

re.sub(pattern, repl, string, count=0, flags=0)

其中,pattern是正則表達式;repl是要將匹配子串替換成的字符串;string是待做替換操作的字符串;count是最大替換次數,默認爲0表示不限制替換次數(即將所有符合正則表達式的子串都替換成repl);flags與re.match等函數中的flags參數含義相同。

 

subn函數與re.sub函數功能完全相同,只是re.subn函數會以一個元組的形式同時返回替換匹配子串後得到的新字符串和替換的次數。格式爲:

re.subn(pattern, repl, string, count=0, flags=0)

 

爬蟲程序示例

開源中國網址中所有經過OSI認證的開源許可證爬取:

 

from urllib.request import urlopen
import re
import requests
from bs4 import BeautifulSoup

#獲取所有許可證的鏈接以及名字
def get_license():
    # 包含所有許可證網站的內容解析
    html = urlopen("https://opensource.org/licenses/alphabetical").read().decode('utf-8')
    soup = BeautifulSoup(html, 'lxml')
    opensource_list = soup.find('div', class_='field-item even')
    li_list = opensource_list.find_all('li')
    for li in li_list:
        # 獲取協議的鏈接信息
        license_href = 'https://opensource.org' + li.find('a')['href']
        license_name = li.text.replace('\n', '')
        reg = r'(.*)'
        license_name = re.findall(reg, license_name)[0]
        print(license_name + ":" + license_href)
        # 將各個協議寫入文件
        with open("All_license_link.txt" , 'a', encoding='utf-8') as f:
            f.write(license_name+ ":" + license_href+"\n")
        # 由於有些協議名字中含有“/",文件命名時不能識別,需要進行替換
        if license_name.find('/'):
            license_name = license_name.replace('/', ' ')
        filename = license_name + '.txt'
        #讀取各個對應的協議的內容
        html = requests.get(license_href).text
        soup = BeautifulSoup(html, 'lxml')
        container =soup.find('div', class_='field-item even')
        #由於某些協議文本顯示內容中有不相關的信息,所以需要就行內容的篩選
        all_href = container.find_all('p')
        s = ""
        s1 = ""
        for l in all_href:
            s += l.get_text()+"\n"
        all_href1 = container.find_all('pre')
        if all_href1==None:
            pass
        else:
            for l1 in all_href1:
                s1 += l1.get_text()+"\n"
        content = s+s1
        #將各個協議寫入文件
        with open("./licensefile/"+filename, 'w+', encoding='utf-8') as f:
                f.write(soup.h1.get_text()+"\n\n\n"+content)

def main():
    get_license()

if __name__ == '__main__':
    main()

 

提示 : requests模塊在使用前需要先安裝,可以在系統控制檯下輸入如下命令完成requests模塊的下載和安裝:

pip install requests -i http://pypi.douban.com/simple --trusted-host=pypi.douban.com

 

end

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章