python3 爬蟲入門

renke發表於2021-09-09

這裡爬取貓眼電影 TOP100 榜的資訊,作為學習的第一個Demo


有目標才有驅動力

個人覺得python這門語言,入門最好的方式就是直接實戰,在做專案的時候去學習,做到每個知識點有的放矢。    就像給你一把鋤頭,告訴你20米深處埋了幾百萬現金,那你肯定會有源源不斷的動力去做這件事


今天開始接觸的python,從爬蟲開始。 語言相對來說比較簡單,環境配置到是花了不少時間。

  • 作為新人,建議先選擇一個好的開發工具,vim相對來說不是太好用

  • 我用的是-------.如果註冊碼失效,請選擇License server,然後裡面填就OK了

有個要注意的點是在引入beautifurSoup庫的時候會報錯,因為3.x的庫需要引入的是beautifurSoup4.


到這一步環境配置基本上OK了,可以開始進入正題了

這裡我跟的一個教程是

知識點比較廣,從一個爬蟲事例開始.

目標

提取出貓眼電影 TOP100 榜的電影名稱、時間、評分、圖片等資訊,提取的站點 URL 為:,提取的結果以檔案形式儲存下來。

準備工作

新增Requests依賴庫,注意不是Request

抓取分析

本節我們需要抓取的目標站點為:,開啟之後便可以檢視到榜單的資訊,如圖

圖片描述

3-11.jpg


網頁下滑到最下方可以發現有分頁的列表,我們點選一下第二頁觀察一下頁面的URL和內容發生了怎樣的變化,如圖


圖片描述

3-12.jpg

可以發現頁面的 URL 變成了:,相比之前的URL多了一個引數,那就是 offset=10,而目前顯示的結果是排行 11-20 名的電影,初步推斷這是一個偏移量的引數,再點選下一頁,發現頁面的 URL 變成了:,引數 offset 變成了 20,而顯示的結果是排行 21-30 的電影。

由此可以總結出規律,offset 代表了一個偏移量值,如果偏移量為 n,則顯示的電影序號就是 n+1 到 n+10,每頁顯示 10 個。所以如果想獲取 TOP100 電影,只需要分開請求 10 次,而 10 次的 offset 引數設定為 0,10,20,...,90 即可,這樣獲取不同的頁面結果之後再用正規表示式提取出相關資訊就可以得到 TOP100 的所有電影資訊了。

抓取首頁

接下來我們用程式碼實現這個過程,首先抓取第一頁的內容,我們實現一個 get_one_page() 方法,傳入 url 引數,然後將抓取的頁面結果返回,然後再實現一個 main() 方法呼叫一下,初步程式碼實現如下:

import requests

def get_one_page(url):
    response = requests.get(url)    if response.status_code == 200:        return response.text    return None

def main():
    url = ''
    html = get_one_page(url)    print(html)

main()

一般網站都會有反爬蟲機制,如果發現資料無法抓取下來,那應該是檢測到了你的ip爬蟲,這時候可以使用代理爬蟲,這一塊涉及的比較多,包括代理池,到後面再慢慢了解,先給倆獲取代理ip的函式,ip來自西刺

def get_ip_list(url, headers):
    web_data = requests.get(url, headers=headers)
    soup = BeautifulSoup(web_data.text, 'lxml')
    ips = soup.find_all('tr')
    ip_list = []    for i in range(1, len(ips)):
        ip_info = ips[i]
        tds = ip_info.find_all('td')
        ip_list.append(tds[1].text + ':' + tds[2].text)    return ip_list


def get_random_ip(ip_list):
    proxy_list = []    for ip in ip_list:
        proxy_list.append('http://' + ip)
    proxy_ip = random.choice(proxy_list)
    proxies = {'http': proxy_ip}    return proxies#呼叫

  url = ''
    headers = {        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
    }
    ip_list = get_ip_list(url, headers=headers)
    proxies = get_random_ip(ip_list)    print(proxies)# 這個 proxies  就是要填入的代理IP# requests.get(url1, proxies=proxies, headers=headers)

正則提取

接下來網頁看一下頁面的真實原始碼

圖片描述

3-13.jpg

檢視其中的一個條目的原始碼如圖

圖片描述

3-14.jpg

可以看到一部電影資訊對應的原始碼是一個 dd 節點,用正規表示式來提取這裡面的一些電影資訊,首先需要提取它的排名資訊,而它的排名資訊是在 class 為 board-index 的 i 節點內,所以所以這裡利用非貪婪匹配來提取 i 節點內的資訊,正規表示式寫為:


.*?board-index.*?>(.*?)

隨後需要提取電影的圖片,可以看到在後面有個 a 節點,其內部有兩個 img 節點,經過檢查後發現第二個 img 節點的 data-src屬性是圖片的連結,在這裡提取第二個 img 節點的 data-src屬性,所以正則可以改寫如下:


.*?board-index.*?>(.*?).*?data-class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="(.*?)"

再往後我們需要提取電影的名稱,它在後面的 p 節點內,class 為 name,所以我們可以用 name 做一個標誌位,然後進一步提取到其內 a 節點的正文內容,正則改寫如下:


.*?board-index.*?>(.*?).*?data-class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="(.*?)".*?name.*?a.*?>(.*?)

隨後如果需要再提取主演、釋出時間、評分等內容的話都是同樣的原理,最後正規表示式寫為:


.*?board-index.*?>(.*?).*?data-class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="(.*?)".*?name.*?a.*?>(.*?).*?star.*?>(.*?).*?releasetime.*?>(.*?).*?integer.*?>(.*?).*?fraction.*?>(.*?).*?

這樣我們一個正規表示式可以匹配一個電影的結果,裡面匹配了 7 個資訊,接下來我們透過呼叫 findall() 方法提取出所有的內容,實現一個 parse_one_page() 方法如下:

def parse_one_page(html):
    pattern = re.compile(        '
.*?board-index.*?>(.*?).*?data-class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="(.*?)".*?name.*?a.*?>(.*?).*?star.*?>(.*?).*?releasetime.*?>(.*?).*?integer.*?>(.*?).*?fraction.*?>(.*?).*?
',         re.S)     items = re.findall(pattern, html)    print(items)

這樣我們就可以成功將一頁的 10 個電影資訊都提取出來,但這樣還不夠,資料比較雜亂,再將匹配結果處理一下,遍歷提取結果並生成字典,方法改寫如下:

def parse_one_page(html):
    pattern = re.compile(        '
.*?board-index.*?>(.*?).*?data-class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="(.*?)".*?name.*?a.*?>(.*?).*?star.*?>(.*?).*?releasetime.*?>(.*?).*?integer.*?>(.*?).*?fraction.*?>(.*?).*?
',         re.S)     items = re.findall(pattern, html)    for item in items:         yield {            'index': item[0],            'image': item[1],            'title': item[2].strip(),            'actor': item[3].strip()[3:] if len(item[3]) > 3 else '',            'time': item[4].strip()[5:] if len(item[4]) > 5 else '',            'score': item[5].strip() + item[6].strip()         }

這樣就可以成功提取出電影的排名、圖片、標題、演員、時間、評分內容了,並把它賦值為一個個的字典,形成結構化資料,執行結果如下:

{'image': '', 'actor': '張國榮,張豐毅,鞏俐', 'score': '9.6', 'index': '1', 'title': '霸王別姬', 'time': '1993-01-01(中國香港)'}
{'image': '', 'actor': '蒂姆·羅賓斯,摩根·弗里曼,鮑勃·岡頓', 'score': '9.5', 'index': '2', 'title': '肖申克的救贖', 'time': '1994-10-14(美國)'}
{'image': '', 'actor': '讓·雷諾,加里·奧德曼,娜塔莉·波特曼', 'score': '9.5', 'index': '3', 'title': '這個殺手不太冷', 'time': '1994-09-14(法國)'}
{'image': '', 'actor': '格利高利·派克,奧黛麗·赫本,埃迪·艾伯特', 'score': '9.1', 'index': '4', 'title': '羅馬假日', 'time': '1953-09-02(美國)'}
{'image': '', 'actor': '湯姆·漢克斯,羅賓·懷特,加里·西尼斯', 'score': '9.4', 'index': '5', 'title': '阿甘正傳', 'time': '1994-07-06(美國)'}
{'image': '', 'actor': '萊昂納多·迪卡普里奧,凱特·溫絲萊特,比利·贊恩', 'score': '9.5', 'index': '6', 'title': '泰坦尼克號', 'time': '1998-04-03'}
{'image': '', 'actor': '日高法子,坂本千夏,糸井重裡', 'score': '9.2', 'index': '7', 'title': '龍貓', 'time': '1988-04-16(日本)'}
{'image': '', 'actor': '馬龍·白蘭度,阿爾·帕西諾,詹姆斯·凱恩', 'score': '9.3', 'index': '8', 'title': '教父', 'time': '1972-03-24(美國)'}
{'image': '', 'actor': '周星馳,鞏俐,鄭佩佩', 'score': '9.2', 'index': '9', 'title': '唐伯虎點秋香', 'time': '1993-07-01(中國香港)'}
{'image': '', 'actor': '柊瑠美,入野自由,夏木真理', 'score': '9.3', 'index': '10', 'title': '千與千尋', 'time': '2001-07-20(日本)'}

到此為止我們就成功提取了單頁的電影資訊

寫入檔案

隨後將提取的結果寫入檔案,在這裡直接寫入到一個文字檔案中,透過 json 庫的 dumps() 方法實現字典的序列化,並指定 ensure_ascii 引數為 False,這樣可以保證輸出的結果是中文形式而不是 Unicode 編碼,程式碼實現如下:

def write_to_json(content):
    with open('result.txt', 'a') as f:        print(type(json.dumps(content)))
        f.write(json.dumps(content, ensure_ascii=False,).encode('utf-8'))

透過呼叫 write_to_json() 方法即可實現將字典寫入到文字檔案的過程,此處的 content 引數就是一部電影的提取結果,是一個字典。

整合程式碼

def main():
    url = ''
    html = get_one_page(url)    for item in parse_one_page(html):
        write_to_json(item)

分頁爬取

遍歷一下給這個連結傳入一個 offset 引數,實現其他 90 部電影的爬取,新增如下呼叫即可:

if __name__ == '__main__':    for i in range(10):
        main(offset=i * 10)

將 main() 方法修改一下,接收一個 offset 值作為偏移量,然後構造 URL 進行爬取,實現如下

def main(offset):
    url = '?offset=' + str(offset)
    html = get_one_page(url)    for item in parse_one_page(html):        print(item)
        write_to_file(item)

完整程式碼

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

def get_one_page(url):
    try:
        response = requests.get(url)        if response.status_code == 200:            return response.text        return None
    except RequestException:        return None

def parse_one_page(html):
    pattern = re.compile('
.*?board-index.*?>(d+).*?data-class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="(.*?)".*?name">.*?star">(.*?).*?releasetime">(.*?)'                          + '.*?integer">(.*?).*?fraction">(.*?).*?
', re.S)     items = re.findall(pattern, html)    for item in items:         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 write_to_file(content):     with open('result.txt', 'a', encoding='utf-8') as f:         f.write(json.dumps(content, ensure_ascii=False) + 'n') def main(offset):     url = '?offset=' + str(offset)     html = get_one_page(url)    for item in parse_one_page(html):        print(item)         write_to_file(item)if __name__ == '__main__':    for i in range(10):         main(offset=i * 10)         time.sleep(1)

最後 跑起來,能得到一個txt檔案,差不多是這樣

圖片描述

3-15-1.jpg


萬事開頭難,然後中間難,最後也難。

願每一個程式設計師都能長命白歲,哈哈



作者:PetitBread
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/430/viewspace-2809783/,如需轉載,請註明出處,否則將追究法律責任。

相關文章