天天看點

python爬取ul下的li是空的_python爬取豆瓣首頁熱門欄目詳細流程

記錄一下爬取豆瓣熱門專欄的經過,通過這篇文章,你能學會requests,HTMLParser,json的基本使用,以及爬取網頁内容的基本思路。

使用子產品

1,擷取豆瓣首頁代碼:首先我們需要通路豆瓣頁面,擷取首頁的源碼。這裡推薦使用第三方庫:requests,相比python内置的 urllib 子產品,requests使用起來更簡單,功能更全面

2,對擷取的代碼進行解析:對于解析html代碼,已經有很多功能強大的架構能使用,如Scrapy,PySpider,Beautiful Soup等,這裡我們隻是學習下爬蟲的基本使用,是以内建的 HTMLParser 足夠使用了

3,對擷取的資料進行處理: json

思路分析

既然我們需要的隻是熱門專欄子產品的資料,那麼我們需要一個标志來告訴我們:下面的代碼就是專欄子產品了,準備擷取資料。同樣我們需要知道目前

讀取的是圖檔、标題還是欄目類别,以此将資料儲存到相應的字段中。總的來說,我們最起碼應該通過代碼來實作以下幾點:

1,擷取網頁源碼

2,通過自定義方法解析html

3,通過标志位判斷目前資料是否是我們需要的資料

4,通過分析代碼結構決定将要儲存的資料結構

5,将資料按照特定格式進行本地儲存

豆瓣官網:https://www.douban.com/,分析一下我們需要爬取子產品的代碼:

python爬取ul下的li是空的_python爬取豆瓣首頁熱門欄目詳細流程

可以看到,我們需要爬取的資料都在 ul.time-list 這個代碼塊裡,那麼我們的标志位就是:當開始标簽為 ul并且具有類名 time-list時,我們就要擷取資料了,當結束标簽為 ul 時,停止解析,繼續分析代碼結構,每個 li 裡面包含了對應資料裡面的 詳情頁跳轉連結,圖檔位址,标題以及專欄類别,那麼我們的資料結構到這裡也就很清楚了:一個 li 塊對應一條資料,每條資料由四個字段組成:

詳情頁跳轉連結 href --> 這裡我們考慮了一下, 還是通過第二個a标簽來擷取,它具有統一的類名title,同時我們還能擷取 标題title,

圖檔位址 imgUrl --> 通過每個li代碼塊裡面唯一img标簽的src屬性可以輕松擷取,

标題 title --> 通過 a.title擷取,

專欄類别 type --> 唯一的 span 标簽擷取

tip:像上面我們選取資料的标志位一樣,img的alt可以擷取标題,a标簽的文本也可以擷取标題,兩個a标簽都能擷取跳轉連結不管是爬蟲還是平時其他的開發,我們經常會遇到,同一個需求有多種方法實作,這時候我們就需要思考一下哪一種方法更簡潔,冷靜分析後的編碼不一定最優秀,但自己肯定印象深刻(說遠了,回歸正題)。

編碼實作

通過上面的準備工作,我們已經确定了需要引入的子產品,解析事件觸發标志位,需要擷取的資料,儲存的資料結構,可以正式開始編碼了:

requests是第三方庫,需要另外安裝,其他的是内置子產品,直接引入即可:

1 import requests2 from html.parser import HTMLParser3 from html.entities import name2codepoint4 import json
           

擷取豆瓣首頁源碼:

1 r = requests.get('https://www.douban.com/', timeout = 3)
           

是的,通過 requests擷取網頁隻需要一行代碼,timeout為擷取頁面逾時時間,通過 r.text 就是我們需要的html源碼,r.encoding可以擷取網頁編碼格式,當然requests還有其他的方法供我們使用,

如 帶參數的url: r = requests.get(url, params={.....}),擷取資料等

解析豆瓣首頁源碼:

HTMLParser 裡已經封裝好了針對html的各種事件處理,如 開始标簽,結束标簽,标簽屬性,标簽文本,注釋,特殊字元,不了解的可以看下這個:

https://www.liaoxuefeng.com/wiki/1016959663602400/1017784593019776,很簡單很清晰

1 class MyHTMLParser(HTMLParser): 2 def __init__(self): 3 super().__init__() 4 # 是否開始解析 5 self._allowRun = False 6  7 # 建立dist備用:儲存資料 8 self.hotList = {'data': []} 9 10 # 每一個 li 塊資料儲存11 self.listItem = {}12 13 # 目前解析标簽類型的标志位14 self.tagType = ''15 16 # 開始标簽及 标簽屬性17 def handle_starttag(self, tag, attrs):18 if tag == 'ul' and ('class', 'time-list') in attrs:19 self._allowRun = True20 21 # 若目前是開啟解析狀态22 if self._allowRun:23 if tag == 'a' and ('class', 'title') in attrs:24 self.tagType = 'a'25 for (key, value) in attrs:26 if key == 'href':27 self.listItem[key] = value28 if tag == 'img':29 for (key, value) in attrs:30 if key == 'src':31 self.listItem['imgUrl'] = value32 33 if tag == 'span':34 self.tagType = 'span'35 36 # 結束标簽37 def handle_endtag(self, tag):38 self.tagType = ''39 if tag == 'ul':40 self._allowRun = False41 42 if tag == 'li':43 if len(self.listItem) != 0:44 self.hotList['data'].append(self.listItem)45 self.listItem = {}46 47 # 空标簽及 标簽屬性48 def handle_startendtag(self, tag, attrs):49 if self._allowRun:50 if tag == 'img':51 for (key, value) in attrs:52 if key == 'src':53 self.listItem['imgUrl'] = value54 55 # 标簽文本56 def handle_data(self, data):57 if self._allowRun:58 if self.tagType == 'a':59 self.listItem['title'] = data60 self.taga = False61 elif self.tagType == 'span':62 self.listItem['type'] = data63 64 # 注釋65 def handle_comment(self, data):66 pass67 68 # HTML entity 字元69 def handle_entityref(self, name):70 pass71 72 # Numeric 字元73 def handle_charref(self, name):74 pass75 76 parser = MyHTMLParser()77 parser.feed(r.text)
           

代碼說明:我們必須知道在解析過程中,執行個體方法是按照源碼順序循環執行的,也就是說在同一個執行個體方法裡,我們可以針對不同的标簽或其他條件來進行不同的操作。我們所有的解析操作都是針對 ul.time-list 代碼塊的,是以我們需要一個開關,目前代碼是 ul.time-list時才執行我們自定義的解析操作,這個開關就是上面代碼裡的 _allowRun,當開始标簽是 ul.time-list的是否為 True,當結束标簽是 ul 的是否為False,而隻有當 _allowRun 為 True的時候,我們才繼續解析目前的标簽是 a 還是 img 或者 span。由于我們要在 文本解析事件 handle_data 中擷取 a 标簽的文本作為字段 title 的值,span标簽的文本作為字段 type 的值,是以我們需要一個标志位變量來供我們在執行 handle_data 的時候判斷目前解析的文本是屬于 a 還是 span,這個标志位變量就是上面代碼中 tagType,在 handle_starttag 中指派,在 handle_endtag 中清空。我們将每一條資料儲存在 listItem 中,當結束标簽為 li 時,說明我們的對一個 li 代碼塊解析完畢,listItem 儲存了我們需要的單挑資料,将 listItem 添加到 hotList中并清空 listItem 。執行上面代碼,我們已經将資料儲存在執行個體屬性 hotList裡面,我們可以在終端輸出 parser.hotList:

python爬取ul下的li是空的_python爬取豆瓣首頁熱門欄目詳細流程

儲存資料

接下來就是将資料儲存到本地檔案中,而寫入資料也是非常簡單:

1 with open('hotList.json', 'w') as f:2 json.dump(parser.hotList, f)
           

在目前目錄裡打開 hotList.json 檔案,可以看到如下資料:

python爬取ul下的li是空的_python爬取豆瓣首頁熱門欄目詳細流程

資料倒是寫入了,但是中文卻沒有如願顯示,而且對于追求美觀的我們來說也無法接受,是以我們需要指定寫入編碼格式,以及格式化:

1 with open('hotList.json', 'w', encoding="utf-8") as f:2 json.dump(parser.hotList, f, ensure_ascii = False, indent = 4)
           

我們在寫入的時候指定編碼格式為 utf-8: encoding="utf-8",在 json.dump寫入資料時增加了兩個參數:ensure_ascii = False 禁止進行 ascii轉碼,indent = 4:按縮進為 4個機關格式化資料,當然我們還可以将字段進行排序,隻需要加上字段:sort_keys = True,按需選擇即可,再打開 hotList.json 檔案檢視:

1 { 2 "data": [ 3 { 4 "imgUrl": "https://img1.doubanio.com/dae/niffler/niffler/images/1c6e77ec-c493-11e9-84c0-0242ac110008.jpg