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/,分析一下我們需要爬取模塊的代碼:

 可以看到,我們需要爬取的數據都在 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 requests
2 from html.parser import HTMLParser
3 from html.entities import name2codepoint
4 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 = True
20 
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] = value
28             if tag == 'img':
29                 for (key, value) in attrs:
30                     if key == 'src':
31                         self.listItem['imgUrl'] = value
32 
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 = False
41 
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'] = value
54     
55     # 標簽文本
56     def handle_data(self, data):
57         if self._allowRun:
58             if self.tagType == 'a':
59                 self.listItem['title'] = data
60                 self.taga = False
61             elif self.tagType == 'span':
62                 self.listItem['type'] = data
63        
64     # 注釋
65     def handle_comment(self, data):
66         pass
67 
68     # HTML entity 字符
69     def handle_entityref(self, name):
70         pass
71         
72     # Numeric 字符
73     def handle_charref(self, name):
74         pass
75 
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:

儲存數據

接下來就是將數據儲存到本地文件中,而寫入數據也是非常簡單:

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

 

在當前目錄里打開 hotList.json 文件,可以看到如下數據:

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

 

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",
 5             "href": "https://m.douban.com/time/column/164?dt_time_source=douban-web_anonymous",
 6             "title": "傷別離與共春風——唐宋詞的情感世界",
 7             "type": "音頻專欄"
 8         },
 9         {
10             "imgUrl": "https://img1.doubanio.com/dae/niffler/niffler/images/511ccf86-b8fc-11e9-b188-0242ac110008.jpg",
11             "href": "https://m.douban.com/time/column/163?dt_time_source=douban-web_anonymous",
12             "title": "世界記憶大師教你快速提升記憶力",
13             "type": "視頻專欄"
14         },
15         {
16             "imgUrl": "https://img1.doubanio.com/dae/niffler/niffler/images/74897a9e-880c-11e9-bd82-0242ac11001b.jpg",
17             "href": "https://m.douban.com/time/column/159?dt_time_source=douban-web_anonymous",
18             "title": "黑白之間:二十八堂書法練習課",
19             "type": "視頻專欄"
20         },
21         {
22             "imgUrl": "https://img3.doubanio.com/dae/niffler/niffler/images/6f488990-a773-11e9-b587-0242ac110011.jpg",
23             "href": "https://m.douban.com/time/column/161?dt_time_source=douban-web_anonymous",
24             "title": "馬伯庸的冷門書單",
25             "type": "音頻專欄"
26         },
27         {
28             "imgUrl": "https://img1.doubanio.com/dae/niffler/niffler/images/6c46cb9c-ac61-11e9-97e2-0242ac11000c.jpg",
29             "href": "https://m.douban.com/time/column/162?dt_time_source=douban-web_anonymous",
30             "title": "聽!解說式音樂會——古典音樂聆聽指南",
31             "type": "視頻專欄"
32         },
33         {
34             "imgUrl": "https://img3.doubanio.com/dae/niffler/niffler/images/ebd421cc-9968-11e9-ad2c-0242ac110006.jpg",
35             "href": "https://m.douban.com/time/column/158?dt_time_source=douban-web_anonymous",
36             "title": "從格里菲斯到諾蘭——影迷都在看的電影結構大師課",
37             "type": "視頻專欄"
38         },
39         {
40             "imgUrl": "https://img3.doubanio.com/dae/niffler/niffler/images/fa83f054-9633-11e9-a82e-0242ac110006.jpg",
41             "href": "https://m.douban.com/time/column/157?dt_time_source=douban-web_anonymous",
42             "title": "打開電影聲音的魔盒——好萊塢聲音設計大師課",
43             "type": "視頻專欄"
44         },
45         {
46             "imgUrl": "https://img3.doubanio.com/dae/niffler/niffler/images/81788c8e-8e53-11e9-b51e-0242ac110010.jpg",
47             "href": "https://m.douban.com/time/column/156?dt_time_source=douban-web_anonymous",
48             "title": "一劇之本——好萊塢編劇教父大師課",
49             "type": "視頻專欄"
50         },
51         {
52             "imgUrl": "https://img3.doubanio.com/dae/niffler/niffler/images/5d7d70aa-8b25-11e9-a08f-0242ac110012.jpg",
53             "href": "https://m.douban.com/time/column/155?dt_time_source=douban-web_anonymous",
54             "title": "老葉說電影——90分鍾看懂中國電影產業",
55             "type": "視頻專欄"
56         },
57         {
58             "imgUrl": "https://img3.doubanio.com/dae/niffler/niffler/images/e2e59078-828e-11e9-a465-0242ac110012.jpg",
59             "href": "https://m.douban.com/time/column/154?dt_time_source=douban-web_anonymous",
60             "title": "好萊塢特效大師課——從概念藝術到3D建模",
61             "type": "視頻專欄"
62         }
63     ]
64 }

這樣就只有兩個字:噓服。

 總結

這個例子只是用來熟悉爬蟲基本操作和思維邏輯,真正用到項目中還是得結合其他框架,如 Beautiful Soup,就可以獲取指定代碼片段進行解析而不需要像我們上面那樣設置開關或標志位。有興趣的朋友可以自己動手試試。

與諸君共勉。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM