1. 程式人生 > >Python爬蟲與正則表示式

Python爬蟲與正則表示式

Python爬蟲與正則表示式


一.Python中萬用字元的使用

1.表示方式

表示 意義
* 匹配0到任意字元
? 匹配單個字元
. 當前
.. 當前的上一級
[0-9] 0到9的任意一個數字
[a-z] a到z的任意一個字母
[A-Z] A到Z的任意一個字母
[a-zA-Z] a到z或者A到Z之間的任意一個字母

注意:以下內容在Linux Shell中可以識別,但在Python中不能被識別

表示 意義
[[:digit:]] 匹配一個數字
[[:algha:]] 匹配一個字母
[[:upper:]] 匹配一個大寫字母
[[:lower:]] 匹配一個小寫字母
[[:space:]] 匹配一個空格

2.Python中的使用

glob模組

glob模組可以使用Unix shell風格的萬用字元匹配符合特定格式的檔案和資料夾,跟windows的檔案搜尋功能差不多。glob模組並非呼叫一個子shell實現搜尋功能,而是在內部呼叫了os.listdir()fnmatch.fnmatch()

(1).glob.glob: 返回所有匹配正則的路徑(返回的是一個列表)

print(glob.glob('/etc/*.conf'))
print(glob.glob('/etc/????.conf', recursive=True))
print(glob.glob('/etc/*[0-9]*.conf'))
print(glob.glob('/etc/*[A-Z]*.conf'))
print(glob.glob('/etc/*[0-9A-Z]*.conf'))

在這裡插入圖片描述
(2).glob.iglob: 返回所有匹配正則的路徑(返回的是一個生成器)

print(glob.iglob('/etc/*[0-9A-Z]*.conf'))

在這裡插入圖片描述

二.正則表示式


python標準庫中用於正則表示式的為re模組

re = regular expression 正則表示式

作用: 對於字串進行處理, 會檢查這個字串內容是否與你寫的正則表示式匹配,如果匹配, 拿出匹配的內容;如果不匹配, 忽略不匹配內容。

1.編寫正則的規則

即將需要匹配的字串的樣式表示出來,一般為pattern = r'str' (str為需要匹配的字串)
pattern(模式)

2.findall方法

在表達了所要搜尋的字串之後,使用findall()方法在指定範圍(字串或前端程式碼)中尋找。

import re

s = "kiosk/home/kiosk/westosanaconda2/envs/blog/bin/python3.6/home/kiosk/Desktop/villa"
# 1. 編寫正則的規則
pattern1 = r'villa'
pattern2 = r'kiosk'

# 2. 通過正則去查詢匹配的內容
print(re.findall(pattern1, s))
print(re.findall(pattern2, s))

在這裡插入圖片描述

3.match方法

match()嘗試從字串的起始位置開始匹配

  • 如果起始位置沒有匹配成功, 返回一個None
  • 如果起始位置匹配成功, 返回一個物件
print(re.match(pattern1, s))
matchObj = re.match(pattern2, s)
# 返回match匹配的字串內容;
print(matchObj.group())

在這裡插入圖片描述

4.search方法

search()會掃描整個字串, 只返回第一個匹配成功的內容

  • 如果能找到, 返回一個物件, 通過group方法獲取對應的字串
match1Obj = re.search(pattern1, s)
print(match1Obj.group())
match2Obj = re.search(pattern2, s)
print(match2Obj.group())

在這裡插入圖片描述

5.split方法

split()方法: 指定多個分隔符進行分割

# split()方法: 指定多個分隔符進行分割;
import re
ip = '172.25.254.250'   #與一般的split方法進行比較
print(ip.split('.'))
s = '12+13-15/16'
print(re.split(r'[\+\-\*/]', s))

在這裡插入圖片描述

6.sub方法

sub()方法:指定內容進行替換

s = 'villa is a soccer player'
print(s.replace('villa', 'messi'))

# 希望替換的是數字, 但數字的值不固定, 則通過正則來實現;
s = "本次轉發數為100"
print(re.sub(r'\d+', '0', s))


# 自動會給addNum傳遞一個引數, 引數時匹配到的SRE物件
def addNum(sreObj):
    """在原有基礎上加1"""
    # 年末任情況字串中匹配到的內容還是字串
    num = sreObj.group()  # ‘100’ ‘99’
    new_num = int(num) + 1
    return  str(new_num)

s1 = "本次轉發數為100, 分享數量為99"
print(re.sub(r'\d+', addNum, s1))

在這裡插入圖片描述

7.正則表示式特殊字元類

特殊序列符號 意義
\A 只在字串開始進行匹配
\Z 只在字串結尾進行匹配
\b 匹配位於開始或結尾的空字串
\B 匹配不位於開始或結尾的空字串
\d 相當於[0-9]
\D 相當於[^0-9]
\s 匹配任意空白字元:[\t\n\r\r\v]
\S 匹配任意非空白字元:[^\t\n\r\r\v]
\w 匹配任意數字和字母:[a-zA-Z0-9]
\W 匹配任意非數字和字母
import  re
# 字元類:
print(re.findall(r'[^0-9]', 'villa123villa'))
print(re.findall(r'[0-9]', 'villa123villa'))

# 特殊字元類 .
print(re.findall(r'.', 'villa\n'))


# 特殊字元類\d
print(re.findall(r'\d', '當前文章閱讀量為8'))
# 等待學習匹配次數的規則
print(re.findall(r'\d', '當前文章閱讀量為8000'))


# 特殊字元類\D
print(re.findall(r'\D', '當前文章閱讀量為8'))
# 等待學習匹配次數的規則
print(re.findall(r'\D', '當前文章閱讀量為8000'))



# 特殊字元類\s, \S
print(re.findall(r'\s', '\n當前\r文章閱\t讀量為8'))
# 等待學習匹配次數的規則
print(re.findall(r'\S', '\n當前\r文章閱\t讀量為8'))



# 特殊字元類\w, \W
print(re.findall(r'\w', '12當前villa文章閱_讀量為8&'))
# 等待學習匹配次數的規則
print(re.findall(r'\W', '12當前villa文章閱_讀量為8&'))

效果如下:
在這裡插入圖片描述

8.指定字元出現指定次數

表示 意義
: 代表前一個字元出現0次或者無限次 d*.*
+ 代表前一個字元出現一次或者無限次 d+
? 代表前一個字元出現1次或者0次; 假設某些字元可省略, 也可以不省略的時候使用 d?
{m } 前一個字元出現m次
{m,} 前一個字元至少出現m次 * == {0,} + ==={1,}
{m,n} 前一個字元出現m次到n次? === {0,1}
import re

print(re.findall(r'd*', ''))
print(re.findall(r'd*', 'ddd'))
print(re.findall(r'd*', 'dwww'))
print(re.findall(r'.*', 'westos'))



print(re.findall(r'd+', ''))
print(re.findall(r'd+', 'ddd'))
print(re.findall(r'd+', 'dwww'))
print(re.findall(r'd+', 'westos'))


print(re.findall(r'188-?', '188 6543'))
print(re.findall(r'188-?', '188-6543'))
print(re.findall(r'188-?', '148-6543'))


pattern = r'\d{3}[\s-]?\d{4}[\s-]?\d{4}'
print(re.findall(pattern,'188 6754 7645'))
print(re.findall(pattern,'18867547645'))
print(re.findall(pattern,'188-6754-7645'))

在這裡插入圖片描述
在這裡插入圖片描述

三.正則表示式案例

1.匹配郵箱

首先寫出所需要郵箱的正則表示式:pattern = r'[A-z]\w{5,11}@qq\.com'

ps:字串中新增一些字串干擾
import re

pattern = r'[A-z]\w{5,11}@qq\.com'

s = """

各種格式的郵箱入下所示:

[email protected] 

2. [email protected] 

[email protected] 
[email protected] 

3. [email protected] 


4. [email protected] 
[email protected] 
5. [email protected]

6. [email protected] 

7. [email protected] 

8. [email protected]

9. [email protected]

具體釋義入下:

1.163郵箱 




提供以@163.com為字尾的免費郵箱,3G空間,支援超大20兆附件,280兆網盤。精準過濾超過98%的垃圾郵件。 


2.新浪郵箱 




提供以@sina.com為字尾的免費郵箱,容量2G,最大附件15M,支援POP3。 



3.雅虎郵箱 




提供形如@yahoo.com.cn的免費電子郵箱,容量3.5G,最大附件20m,支援21種文字。 


4.搜狐郵箱 

提供以@sohu.com結尾的免費郵箱服務,提供4G超大空間,支援單個超大10M附件。強大的反垃圾郵件系統為您過濾近98%的垃圾郵件。 


5.QQ郵箱 

提供以@qq.com為字尾的免費郵箱,容量無限大,最大附件50M,支援POP3,提供安全模式,內建WebQQ、閱讀空間等。 

"""
dataLi = re.findall(pattern, s)
with open('email.txt', 'w') as f:
    for email in dataLi:
        f.write(email + '\n')

結果如下:
在這裡插入圖片描述

2.匹配IP地址

pattern = r'[1-9]\d{0,2}\.[1-9]\d{0,2}\.[1-9]\d{0,2}\.[1-9]\d{0,2}'

import re

pattern = r'[1-9]\d{0,2}\.[1-9]\d{0,2}\.[1-9]\d{0,2}\.[1-9]\d{0,2}'
print(re.findall(pattern, '172.25.0.2'))
print(re.findall(pattern, '172.25.1.2'))
print(re.findall(pattern, '172.25.1.278'))

#  | 代表或者的意思
pattern1 = r'^(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)$'
# print(re.findall(pattern1, '172.25.1.278'))
# print(re.findall(pattern1, '172.25.1.178'))

Obj = re.match(pattern1, '172.25.1.178')
if Obj:
    print("查詢到匹配的內容:", Obj.group())
else:
    print('No Found')

Obj = re.match(pattern1, '172.25.1.278')
if Obj:
    print("查詢到匹配的內容:", Obj.group())
else:
    print('No Found')

在這裡插入圖片描述

四.初入爬蟲


爬蟲簡述:
網路爬蟲是一個自動提取網頁的程式,它為搜尋引擎從全球資訊網上下載網頁,是搜尋引擎的重要組成。傳統爬蟲從一個或若干初始網頁的URL開始,獲得初始網頁上的URL,在抓取網頁的過程中,不斷從當前頁面上抽取新的URL放入佇列,直到滿足系統的一定停止條件。聚焦爬蟲的工作流程較為複雜,需要根據一定的網頁分析演算法過濾與主題無關的連結,保留有用的連結並將其放入等待抓取的URL佇列。然後,它將根據一定的搜尋策略從佇列中選擇下一步要抓取的網頁URL,並重覆上述過程,直到達到系統的某一條件時停止。另外,所有被爬蟲抓取的網頁將會被系統存貯,進行一定的分析、過濾,並建立索引,以便之後的查詢和檢索;對於聚焦爬蟲來說,這一過程所得到的分析結果還可能對以後的抓取過程給出反饋和指導。

1.爬取貼吧中某一帖子的所有郵箱

第一步通過爬蟲獲取該網址的內容

使用urlopen開啟指定頁面
再使用.read()讀取頁面內容
最後decode(‘utf-8’)使用utf-8的解碼方式使頁面內容解碼為unicode

第二步通過網頁內容, 使用正則表示式獲得符合正則的所有郵箱
import re
def getPageContent(url):
    """
        獲取網頁原始碼
    :param url: 指定url內容
    :return: 返回頁面的內容(str格式)
    """
    with urlopen(url) as html:
        return  html.read().decode('utf-8')

def parser_page(content):
    """
    根據內容獲取所有的貼吧總頁數;
    :param content: 網頁內容
    :return: 貼吧總頁數
    """
    pattern = r'<span class="red">(\d+)</span>'
    data = re.findall(pattern, content)
    return  data[0]


def parser_all_page(pageCount):
    """
    根據貼吧頁數, 構造不同的url地址;並找出所有的郵箱
    :param pageCount:
    :return:
    """
    emails = []
    for page in range(int(pageCount)):
        url = 'http://tieba.baidu.com/p/xxxxxxxxxxxxpn=%d' %(page+1)    # 此處網站省略
        print("正在爬取:%s" %(url))
        content = getPageContent(url)
        # pattern = r'\w[-\w.+]*@[A-Za-z0-9][-A-Za-z0-9]+\.+[A-Za-z]{2,14}'
        pattern = r'[a-zA-Z0-9][-\w.+]*@[A-Za-z0-9][-A-Za-z0-9]+\.+[A-Za-z]{2,14}'
        findEmail = re.findall(pattern, content)
        print(findEmail)
        emails.append(findEmail)
    return  emails
def main():
    url = 'http://tieba.baidu.com/p/xxxxx'
    content = getPageContent(url)
    pageCount = parser_page(content)
    emails = parser_all_page(pageCount)
    print(emails)
    with open('tiebaEmail.txt', 'w') as f:
        for tieba in chain(*emails):
            f.write(tieba + '\n')
main()

在這裡插入圖片描述
(此處資料省略)

2.爬取貼吧中某一帖子的所有圖片

import re
from urllib.request import urlopen


def get_content(url):
    """
        獲取網頁內容
    :param url:
    :return:
    """
    with urlopen(url) as html:
        return  html.read()

def parser_get_img_url(content):
    """
    解析貼吧內容, 獲取所有風景圖片的url
    :param content:
    :return:
    """
    pattern = r'<img class="BDE_Image".*?src="(http://.*?\.jpg)".*?>'
    imgUrl = re.findall(pattern, content.decode('utf-8').replace('\n', ' '))
    return  imgUrl

def main():
    url = 'http://tieba.baidu.com/p/5437043553'
    content = get_content(url)
    imgLi = parser_get_img_url(content)
    for index,imgurl in enumerate(imgLi):
        # 根據圖片的url獲取每個圖片的內容;
        content = get_content(imgurl)
        with open('img/%s.jpg' %(index+1), 'wb') as f:
            f.write(content)
            print("第%s個圖片下載成功...." %(index+1))
main()

在這裡插入圖片描述
在這裡插入圖片描述

3.儲存cookie資訊

cookie:某些網站為了辨別使用者身份, 只有登陸之後才能訪問某個頁面;
進行一個會話跟蹤, 將使用者的相關資訊包括使用者名稱等儲存到本地終端

# 1. 如何將Cookie儲存到變數中, 或者檔案中;
# 1). 宣告一個CookieJar ---> FileCookieJar --> MozillaCookie
cookie = cookiejar.CookieJar()
# 2). 利用urllib.request的HTTPCookieProcessor建立一個cookie處理器
handler = HTTPCookieProcessor(cookie)
# 3). 通過CookieHandler建立opener
# 預設使用的openr就是urlopen;
opener = request.build_opener(handler)
# 4). 開啟url頁面
response = opener.open('http://www.baidu.com')
# 5). 列印該頁面的cookie資訊
print(cookie)
for item in cookie:
    print(item)
# 2. 如何將Cookie以指定格式儲存到檔案中
# 1). 設定儲存cookie的檔名
cookieFilename = 'cookie.txt'
# 2). 宣告一個MozillaCookie,用來儲存cookie並且可以寫入文進阿
cookie = cookiejar.MozillaCookieJar(filename=cookieFilename)
# 3). 利用urllib.request的HTTPCookieProcessor建立一個cookie處理器
handler = HTTPCookieProcessor(cookie)
# 4). 通過CookieHandler建立opener
# 預設使用的openr就是urlopen;
opener = request.build_opener(handler)
# 5). 開啟url頁面
#response = opener.open('http://www.baidu.com')
# 6). 列印cookie,
print(cookie)
print(type(cookie))
# ignore_discard, 即使cookie資訊將要被丟棄。 也要把它儲存到檔案中;
# ignore_expires, 如果在檔案中的cookie已經存在, 就覆蓋原檔案寫入;
cookie.save(ignore_discard=True, ignore_expires=True)

在這裡插入圖片描述

儲存到檔案中效果如下圖時示:
在這裡插入圖片描述

從檔案中獲取cookie並訪問

# 1). 指定cookie檔案存在的位置
cookieFilename = 'cookie.txt'
# 2).宣告一個MozillaCookie,用來儲存cookie並且可以寫入檔案, 用來讀取檔案中的cookie資訊
cookie = cookiejar.MozillaCookieJar()
# 3). 從檔案中讀取cookie內容
cookie.load(filename=cookieFilename)
# 4). 利用urllib.request的HTTPCookieProcessor建立一個cookie處理器
handler = HTTPCookieProcessor(cookie)
# 5). 通過CookieHandler建立opener
# 預設使用的openr就是urlopen;
opener = request.build_opener(handler)
# 6). 開啟url頁面
response = opener.open('http://www.baidu.com')
#7). 列印資訊
print(response.read().decode('utf-8'))

在這裡插入圖片描述