1. 程式人生 > >Python網路爬蟲入門案例

Python網路爬蟲入門案例

#一、什麼是網路爬蟲? 網路爬蟲又稱網路蜘蛛、網路螞蟻、網路機器人等,本質上是一段程式或指令碼,可以自動化瀏覽網路中的資訊,瀏覽資訊時程式會按照一定的規則去瀏覽,這些規則我們稱之為網路爬蟲演算法。 作用:

  • 定製搜尋引擎
  • 自動去廣告
  • 爬取圖片、文字
  • 爬取金融資訊進行投資分析

#二、前置知識

  • Http協議
  • Html
  • 正則表示式
  • 一門程式語言(建議Python)

#三、網路爬蟲的核心步驟

  • 選定爬取範圍
  • 分析網站結構特徵
  • 設計爬蟲規則
  • 編寫爬蟲指令碼

#四、案例 湖北師範大學“學校要聞” 這裡寫圖片描述 這個案例的的目標就是將上圖紅框所示中,學校要聞的新聞全部爬取到本地的txt檔案中。案例網站連結

好,現在已經選定了爬取範圍,接下來就開始分析網站的特徵結構,以及爬蟲策略

按住Fn+F11調出控制檯,在Element面板中檢視網頁的html標籤結構,如下所示 這裡寫圖片描述

可以看到,頁面主要的分為三個**<div>,其中<div class=“content”>中包含了代表新聞列表的<ul>標籤,內部每個<li>標籤都包含了一個<a>標籤,裡面href屬性中的值就是我們需要的,代表每一篇新聞頁的地址**。仔細觀察的話還可以發現,新聞標題也在標籤內部。

寫到這裡我們已經發現該頁面的結構特徵了,可以先動手寫一段指令碼來爬取這些內容。內容就暫時限定為新聞標題和對應的超連結,新聞內容的爬取待會再完善。

我們現在可以先寫出下面的程式碼:

import urllib.request
import re

url = 'http://www.news.hbnu.edu.cn/second/1'  # 構造網頁的url
response = urllib.request.urlopen(url)  # 呼叫庫來訪問該url,獲得響應
page = response.read().decode('utf-8')  # 讀取響應內容,並解碼為utf-8,得到html文件

print('===================爬取下來網頁的html文件===================')
print(page)  # 在控制檯打印出來將完整的html
# 如果不使用re.S引數,則只在每一行內進行匹配,如果一行沒有,就換下一行重新開始,不會跨行。 # 而使用re.S引數以後,正則表示式會將這個字串作為一個整體。 page = re.findall('<div class="content">.+?</div>', page, re.S) page = re.findall('<ul>.+?</ul>', str(page), re.S) page = re.findall('<a.+?</a>', str(page)) print('===================爬取下來網頁的html文件===================') for item in page: print(item)

執行上述程式碼,可以在控制檯中看到,包含新聞標題正文超連結的**<a>標籤全部被爬下來,以陣列的形式儲存,最後通過for迴圈將陣列每一項列印至控制檯。如下圖所示: 這裡寫圖片描述 現在我們要做的就很清晰了,通過超連結訪問新聞正文,將新聞標題和正文爬取到本地的txt檔案中**。

繼續擴充套件程式碼,使用超連結獲取新聞正文頁的響應,再繼續分析html結構特徵,爬取標題和正文內容。

程式碼如下:

import urllib.request
import re

url = 'http://www.news.hbnu.edu.cn/second/1'  # 構造網頁的url
response = urllib.request.urlopen(url)  # 呼叫庫來訪問該url,獲得響應
page = response.read().decode('utf-8')  # 讀取響應內容,並解碼為utf-8,得到html文件

# 如果不使用re.S引數,則只在每一行內進行匹配,如果一行沒有,就換下一行重新開始,不會跨行。
# 而使用re.S引數以後,正則表示式會將這個字串作為一個整體。
page = re.findall('<div class="content">.+?</div>', page, re.S)
page = re.findall('<ul>.+?</ul>', str(page), re.S)
page = re.findall('<a.+?</a>', str(page))
for item in page:
    href = re.findall('<a href="(.+?)">', item)[0]  # 匹配<a>標籤中href屬性中的內容
    article_url = 'http://www.news.hbnu.edu.cn/' + href  # 拼接成完整的新聞超連結URL
    response = urllib.request.urlopen(article_url)  # 獲得正文頁相應
    html = response.read().decode('utf-8')  # 得到新聞正文頁的完整html
    title = re.findall('<div class="content">(.+?)</div>', html, re.S)
    title = re.findall('<h1>(.+?)</h1>', str(title))[0]  # 匹配標題內容
    article = re.findall('<p class="MsoNormal".+?>(.+?)</p>', str(html))  # 匹配正文內容
    content = ''  # 定義存放正文內容的變數
    for p in article:
        p = re.findall('>(.+?)<', p)  # 儘量只匹配標籤之間的純文字內容
        if len(p) != 0:  # 去除空元素,防止下標越界
            for text in p:
                if '<' not in text:  # 跳過包含標籤的元素
                    content += text
    # 再次過濾按照之前規則沒法過濾掉的html元素
    content = content.replace('&ldquo;', '').replace('&rdquo;', '').replace('&nbsp;', '')
    # 列印文章標題
    print('==========' + title[0] + '==========')
    # 列印文章內容
    print(content)

執行上述程式碼,可以在控制檯看到純淨的新聞標題和正文文字,如下圖所示: 這裡寫圖片描述 接下來我們要做的就是將標題和正文輸出到檔案中。

程式碼修改如下:

import urllib.request
import re

import os

url = 'http://www.news.hbnu.edu.cn/second/1'  # 構造網頁的url
response = urllib.request.urlopen(url)  # 呼叫庫來訪問該url,獲得響應
page = response.read().decode('utf-8')  # 讀取響應內容,並解碼為utf-8,得到html文件

# 如果不使用re.S引數,則只在每一行內進行匹配,如果一行沒有,就換下一行重新開始,不會跨行。
# 而使用re.S引數以後,正則表示式會將這個字串作為一個整體。
page = re.findall('<div class="content">.+?</div>', page, re.S)
page = re.findall('<ul>.+?</ul>', str(page), re.S)
page = re.findall('<a.+?</a>', str(page))
for item in page:
    href = re.findall('<a href="(.+?)">', item)[0]  # 匹配<a>標籤中href屬性中的內容
    article_url = 'http://www.news.hbnu.edu.cn/' + href  # 拼接成完整的新聞超連結URL
    response = urllib.request.urlopen(article_url)  # 獲得正文頁相應
    html = response.read().decode('utf-8')  # 得到新聞正文頁的完整html
    title = re.findall('<div class="content">(.+?)</div>', html, re.S)
    title = re.findall('<h1>(.+?)</h1>', str(title))[0]  # 匹配標題內容
    article = re.findall('<p class="MsoNormal".+?>(.+?)</p>', str(html))  # 匹配正文內容
    content = ''  # 定義存放正文內容的變數
    for p in article:
        p = re.findall('>(.+?)<', p)  # 儘量只匹配標籤之間的純文字內容
        if len(p) != 0:  # 去除空元素,防止下標越界
            for text in p:
                if '<' not in text:  # 跳過包含標籤的元素
                    content += text
    # 再次過濾按照之前規則沒法過濾掉的html元素
    content = content.replace('&ldquo;', '').replace('&rdquo;', '').replace('&nbsp;', '')
    # 列印進度
    print('==========正在寫入新聞《' + title + '》到TxT檔案==========')
    # 在原始碼同目錄建立名“新聞”為資料夾
    existed = os.path.exists('./新聞')
    if not existed:
        os.makedirs('./新聞')
    # 寫入內容至檔案
    txt = open("./新聞/" + title + ".txt", "wb")
    txt.write(content.encode())
    txt.close()

執行程式碼後,控制檯可以看到爬蟲進度這裡寫圖片描述 在程式碼同目錄可以看到生成了“新聞”資料夾,裡面存放已經寫入完畢的txt檔案: 這裡寫圖片描述 開啟txt檔案,可以看到新聞正文內容: 這裡寫圖片描述 這麼快就要大功告成了嗎?

好了,別忘了,這個新聞頁面是可以翻頁的!!!!

你可以看到這個欄目一共有261頁,我們不可能針對每一個頁面去寫一個爬蟲,這樣就失去了爬蟲的意義。

所以現在我們需要做另一件事,繼續分析翻頁行為的細節。

首先觀察,在翻頁過程中,發現瀏覽器的URL並不包含頁碼資訊,也沒有發生改變,這說明這個案例不可能通過URL實現翻頁效果。

那好吧,有些難度了,繼續檢視控制檯,切換到NetWork面板,點選翻頁,檢視翻頁動作觸發了哪些http請求內容: 這裡寫圖片描述 直覺得到,最可疑的就是那名字為**“1”**的請求了,點進去檢視,果不其然,這是一個post請求,**請求data裡面還包含了請求的頁碼資訊。**如上圖紅框所示。

那麼我們就用Python構造個post請求來試一試,看看結果吧。

程式碼如下:

import requests

url = 'http://www.news.hbnu.edu.cn/second/1'
# 還有我們準備用Post傳的值,這裡值用字典的形式
values = {
    'type_id': '1,2',
    'page': '2'
}

html = requests.post(url, data=values).text  # 獲得新聞列表頁html
# 列印頁面html至控制檯
print(html)


執行上述程式碼,還真得到了下一頁面的html文件!而且文件結構特徵和第一頁完全一致!

這樣就好辦了,回到之前沒寫完的程式碼,繼續補完利用post請求翻頁的程式碼:

import urllib.request
import urllib.parse
import urllib.error
import re

import os

import requests

url = 'http://www.news.hbnu.edu.cn/second/1'  # 構造網頁的url

index = 0  # 開始頁碼
while 1:
    # 列印當前頁碼
    print('正在爬取第' + str(index + 1) + '頁')
    # 還有我們準備用Post傳的值,這裡值用字典的形式
    values = {
        'type_id': '1,' + str(index),
        'page': str(index)
    }
    index = index + 1  # 頁碼加1
    page = ''
    # 為了避免碰上網路堵塞URLError: <urlopen error timed out>的異常,加上這個迴圈多試幾次
    while 1:
        try:
            page = requests.post(url, data=values).text  # 獲得新聞列表頁html
            break
        except urllib.error.URLError as e:
            continue
    # 如果不使用re.S引數,則只在每一行內進行匹配,如果一行沒有,就換下一行重新開始,不會跨行。
    # 而使用re.S引數以後,正則表示式會將這個字串作為一個整體。
    page = re.findall('<div class="content">.+?</div>', page, re.S)
    page = re.findall('<ul>.+?</ul>', str(page), re.S)
    page = re.findall('<a.+?</a>', str(page))
    for item in page:
        href = re.findall('<a href="(.+?)">', item)[0]  # 匹配<a>標籤中href屬性中的內容
        article_url = 'http://www.news.hbnu.edu.cn/' + href  # 拼接成完整的新聞超連結URL
        response = ''
        # 為了避免碰上網路堵塞URLError: <urlopen error timed out>的異常,加上這個迴圈多試幾次
        while 1:
            try:
                response = urllib.request.urlopen(article_url)  # 獲得正文頁相應
                break
            except urllib.error.URLError as e:
                continue
        html = response.read().decode('utf-8')  # 得到新聞正文頁的完整html
        title = re.findall('<div class="content">(.+?)</div>', html, re.S)
        title = re.findall('<h1>(.+?)</h1>', str(title))[0]  # 匹配標題內容
        article = re.findall('<p class="MsoNormal".+?>(.+?)</p>', str(html))  # 匹配正文內容
        content = ''  # 定義存放正文內容的變數
        for p in article:
            p = re.findall('>(.+?)<', p)  # 儘量只匹配標籤之間的純文字內容
            if len(p) != 0:  # 去除空元素,防止下標越界
                for text in p:
                    if '<' not in text:  # 跳過包含標籤的元素
                        content += text
        # 再次過濾按照之前規則沒法過濾掉的html元素
        content = content.replace('&ldquo;', '').replace('&rdquo;', '').replace('&nbsp;', '')
        # 列印進度
        print('==========正在寫入新聞《' + title + '》到TxT檔案==========')
        # 在原始碼同目錄建立名“新聞”為資料夾
        existed = os.path.exists('./新聞')
        if not existed:
            os.makedirs('./新聞')
        # 寫入內容至檔案
        txt = open("./新聞/" + title + ".txt", "wb")
        txt.write(content.encode())
        txt.close()



程式碼整合在一起,看起來稍微複雜,可以執行看看觀看最終效果,結合程式碼幫助自己理解。

最終執行結果: 這裡寫圖片描述