1. 程式人生 > >python網路爬蟲(web spider)系統化整理總結(二):爬蟲python程式碼示例(兩種響應格式:json和html)

python網路爬蟲(web spider)系統化整理總結(二):爬蟲python程式碼示例(兩種響應格式:json和html)

        上一篇部落格(入門知識篇),對爬蟲有了一個基本的瞭解,但是具體怎麼實現一個爬蟲程式呢?

        一般情況下,我們在瀏覽器獲取資訊,是向伺服器傳送一個http請求,要麼返回html頁面,要麼是ajax請求返回一串json資料,以更新當前網頁中區域性資訊。這裡用兩個例子分別學習下爬蟲的基本操作。

         以下程式碼是基於python3.6環境。


一、百度線上翻譯的自動聯想功能

百度線上翻譯有這樣一個功能,如下圖,輸入一個字母w,下面黃框裡實時就會聯想出來幾個w開頭的單詞並有相應的翻譯,分析可見,是網頁自動傳送了一個post請求"https://fanyi.baidu.com/sug",並且只有一個引數。那麼我們怎麼利用這個API實現我們自己的聯想功能呢?

引數列表:

 

再看下其返回結果:

我們模擬瀏覽器傳送這個請求,將獲得這樣的json字串,解析之後,就可以拿到聯想到的單詞和相應的翻譯了:

# 使用requests
import requests
import json

def fanyi(keyword):

    url = 'https://fanyi.baidu.com/sug'

    # 定義請求引數
    data = {
        'kw' : keyword
    }
    # 通過設定header頭,偽裝瀏覽器使用者
    headers = {"User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)"}
    # 傳送post請求,抓取資訊
    res = requests.post(url,data=data,headers=headers)

    # 解析結果並輸出
    # 獲取響應的json字串
    str_json = res.text
    # 把json轉字典
    myjson = json.loads(str_json)
    # 先判斷data裡面是否有內容
    if len(myjson['data'])==0:
        print("結果:沒有這個詞")
        print()
    else:
        # 遍歷結果,輸出單詞和相應的翻譯
        for v in myjson['data']:
            print("--聯想到的單詞:"+v['k'])
            print("--翻譯:"+v['v'])
            print()

if __name__ == '__main__':
    while True:
        keyword = input('輸入翻譯的單詞:')
        if keyword == 'q':
            print("-- 結束 --")
            break
        fanyi(keyword)

輸出結果如下:

二、使用requests分頁爬取貓眼電影中榜單欄目中TOP100榜的所有電影資訊,並將資訊寫入txt檔案和資料庫中(原始碼有詳細的註釋)

和上一個例子不同的是,這次返回的結果是html程式碼,我們需要通過正則表示式匹配解析出需要的內容;

這裡有兩點值得注意的:

1、使用requests方式請求得到的html原始碼,有可能和直接在瀏覽器選取其中一個元素看到的程式碼不一致也有可能和右鍵檢視頁面原始碼不一致,對解析會造成一定的干擾。 這是因為瀏覽器可能是通過Ajax區域性更新的資料,解析之前要注意區分。

2、python中yield的使用(對python使用不是很熟悉的情況下,這個應用還是很新鮮、很特別的):

     關於python中yield的使用說明

     一個帶有 yield 的函式就是一個 generator,它和普通函式不同,生成一個 generator 看起來像函式呼叫,但不會執行任何函式程式碼,直到對其呼叫 next()(在 for 迴圈中會自動呼叫 next())才開始執行。雖然執行流程仍按函式的流程執行,但每執行到一個 yield 語句就會中斷,並返回一個迭代值,下次執行時從 yield 的下一個語句繼續執行。看起來就好像一個函式在正常執行的過程中被 yield 中斷了數次,每次中斷都會通過 yield 返回當前的迭代值。

      yield 的好處是顯而易見的,把一個函式改寫為一個 generator 就獲得了迭代能力,比起用類的例項儲存狀態來計算下一個 next() 的值,不僅程式碼簡潔,而且執行流程異常清晰。

 

以下是第二個例子的原始碼:

# URL地址:http://maoyan.com/board/4 其中引數offset表示分頁的每頁起始條數-1
# 獲取資訊:{排名,圖片,標題,主演,放映時間,評分}

from requests.exceptions import RequestException
import requests
import re,time,json
import pymysql

def getPage(url):
    '''爬取指定url頁面資訊'''
    try:
        #定義請求頭資訊(模擬瀏覽器)
        headers = {
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'
        }
        # 執行爬取
        res = requests.get(url,headers=headers)
        #判斷響應狀態,並響應爬取內容
        if res.status_code == 200:
            return res.text
        else:
            return None
    except RequestException:
        return None

def parsePage(html):
    '''解析爬取網頁中的內容,並返回欄位結果
       這裡有個問題,使用requests獲取的網頁html和在瀏覽器中從頁面中選擇一個元素對應的html程式碼可能不一致
    '''
    #定義解析正則表示式(關於正則表示式的寫法用法,後面有機會再探討)
    pat = '<i class="board-index board-index-[0-9]+">([0-9]+)</i>.*?' \
          '<img data-src="(.*?)" alt="(.*?)" class="board-img" />.*?' \
          '<p class="star">(.*?)</p>.*?' \
          '<p class="releasetime">(.*?)</p>.*?' \
          '<i class="integer">([0-9\.]+)</i>' \
          '<i class="fraction">([0-9]+)</i>'
    # print(html)
    #執行解析
    items = re.findall(pat,html,re.S)
    #遍歷封裝資料並返回
    for item in items:
        print("item : ",item)
        yield {
            'index':item[0],
            'image':item[1],
            'title':item[2],
            'actor':item[3].strip()[3:],
            'time':item[4].strip()[5:],
            'score':item[5]+item[6],
        }

def writeDB(content):
    """
    寫入資料庫
    :param content:
    :return:
    """
    print(content)
    print(content["index"])

    db = pymysql.connect("127.0.0.1", "root", "root", "myspider_001")
    cursor = db.cursor()
    sql = "insert into tb_results (index0,image,title,actor,time,score) \
           values ('%s','%s','%s','%s','%s','%s')"% \
          (content['index'],content['image'],content['title'],content['actor'],content['time'],content['score'])
    print(sql)
    try:
        cursor.execute(sql)
        print("qq")
        db.commit()
    except:
        print("q")
        db.rollback()


def writeFile(content):
    '''執行檔案追加寫操作'''
    #print(content)
    with open("./result.txt",'a',encoding='utf-8') as f:
        f.write(json.dumps(content,ensure_ascii=False) + "\n")
        #json.dumps 序列化時對中文預設使用的ascii編碼.想輸出真正的中文需要指定ensure_ascii=False

def main(offset):
    ''' 主程式函式,負責排程執行爬蟲處理 '''
    url = 'http://maoyan.com/board/4?offset=' + str(offset)
    #print(url)
    html = getPage(url)
    #判斷是否爬取到資料,並呼叫解析函式
    if html:
        for item in parsePage(html):
            writeFile(item)
            writeDB(item)


# 判斷當前執行是否為主程式執行,並遍歷呼叫主函式爬取資料
if __name__ == '__main__':
    # 前100名,每頁10名,分頁引數offset 0 10 20 ...90
    for i in range(10):
        main(offset=i*10)
        time.sleep(1)

 

 

學完這兩個基本的例子,就對爬蟲有了一個初步的認識。

後面還會繼續學習,寫下來,一方面是個總結和記錄,一方面希望有可能幫到和我一樣的初學者。