實戰 Python 網絡爬蟲:美團美食商家信息和用戶評論
一、網站分析及項目設計
美食是人類的畢生追求,說到美食,我們總會想起美團美食,面對類型眾多的商家,應如何選擇優質的商家,使消費最大合理化。在本 Chat 里,將講述如何爬取美團商家信息。
廢話不多說,我們直接在瀏覽器打開美團美食的網址,然后打開谷歌的開發者工具,並刷新網頁,重新捕捉請求資源,如圖所示:
根據店名在 Network 選項卡的各個分類標簽下查找數據所在的 HTML 源碼位置,在每個請求信息的 Response 下使用 Ctrl+F 快速查找店名(初漾台味黑糖),最終在 Doc 標簽下找到相關信息,如圖所示:
從圖上的信息可以看到,地址欄的 gz 代表區域“廣州”,全國的美食商家是以城市進行划分的。而商家的數據是以 JSON 格式表示,網頁上顯示的信息都可以在此找到。在這個網頁中,我們是要查找這個商家的 URL 地址,從而進入商家詳細頁。
但從美團美食的首頁中,我們能獲取的信息就這么多,因此,我們先訪問店家詳細頁,發現商家詳細頁的 URL 地址帶有一串數字。不同的商家,數字內容都不一樣,如圖所示:
通過對比發現,每個商家詳細頁的 URL 地址只有末端的數字串是不相同的,這應該是美團給商家標記的 id,我們取其中一個商家 id 回到美團首頁查找,發現可找到相關信息,如圖所示:
根據上述分析,我們可以在美團美食首頁里獲取商家 id,通過 id 來構建商家詳細頁的 URL 地址。
得到了商家詳細頁的 URL 地址后,下一步是在商家詳細頁里進行數據爬取。數據爬取分為兩部分:商家信息和顧客評論,如圖所示:
首先,我們找出商家信息所在的請求信息,在開發者工具的 Network 選項卡的 doc 標簽下找到相關信息,商家信息是在 doc 標簽下找到,並且也是以 JSON 格式表示,如圖所示:
接着是分析顧客評論所在的請求信息,最終在 XHR 標簽下找到相關的請求信息,如圖所示:
綜合上述,我們需要從三個請求信息里獲取數據,三個請求信息的說明如下:
- 美團美食的首頁地址,獲取每個商家的 id
- 商家詳細頁地址,獲取商家信息
- 顧客評論的 AJAX 接口,獲取顧客評論信息
目前只是簡單分析了三個請求信息,每個請求的請求參數和請求頭會在功能實現的過程中詳細講解。
根據現有的分析,我們創建項目文件夾 meituan,項目文件分別有 Insql.py、meishi.conf 和 meishi.py,文件說明如下:
- Insql.py 是將數據入庫處理,由 ORM 框架 SQLAlchemy 實現
- meishi.conf 設置區域信息,如廣州(gz)或北京(bj)等,從而控制爬蟲的爬取方向
- meishi.py 實現爬蟲功能
二、爬取所有商家信息
簡單分析網頁后,接下來我們先實現所有商家的信息爬取。由於商家詳細頁只需要商家 id 即可,因此爬取所有商家信息只需爬取商家 id 即可。
從美團美食的首頁得知,其 URL 地址的“gz”代表廣州。首頁的底部設有分頁功能,當點擊第二頁的時候,URL 末端新增下級目錄 pn2,第三頁的下級目錄為 pn3,以此類推,新增的下級目錄代表分頁的頁數。當遍歷 32 次即可得到當前城市所有美食商家信息,如圖所示:
根據 URL 的變化規律和商家信息的 HTML 源碼結構,所有商家信息的爬取功能定義為函數 get_all(),函數參數 city_list 代表各個城市信息並以字符串表示,如 gz、bj 等,函數代碼如圖所示:
函數 get_all() 所實現的功能說明如下:
- 首先將參數 city_list 以英文逗號進行截取,得到各個城市的拼音縮寫。
- 然后遍歷各個城市,分別爬取各個城市的美食信息,每個城市只顯示 32 頁的美食信息,因此需要遍歷 32 次。
- 每次遍歷都會對當前分頁發送 HTTP 請求,請求頭設有 Upgrade-Insecure-Requests、Host 和 Referer 屬性,這些屬性最好寫入請求頭,這樣可以避開反爬蟲檢測。特別是 Host 屬性,因為 URL 的域名設有城市信息,如 gz.meituan.com,而 Host 屬性是為 URL 指定相應的域名,使其一一對應。
- 從當前請求中獲取響應內容,並用正則表達式提取當前分頁所有的商家 id(即find_poiId)以及訪客信息 find_uuid。
- 調用函數 get_info(),將爬取的數據作為函數參數傳入。函數 get_info() 是進入商家詳細頁,爬取商家的基本信息。
三、分別爬取每個商家的信息和用戶評論信息
在函數 get_all() 里,我們調用了函數 get_info(),它是進入訪問商家詳細頁的,主要爬取商家的基本信息。商家詳情頁的 URL 地址為 http://www.meituan.com/meishi/%s/,其中 %s 代表商家 id。
注意:如果對商家詳細頁發送 HTTP 請求,這里涉及了一個反爬蟲機制——Cookies 的使用,我們查看該請求的請求頭內容。如圖所示:
商家詳細頁的請求頭與一般的請求頭並無太大差異,按照以往的開發模式,首先構架 URL 地址,然后對 URL 發送請求,最后從請求里獲取響應內容並提取目標數據。按照該思路,商家的基本信息爬取功能如圖所示:
當運行程序的時候,程序是沒有提取到商家信息了,這說明該請求的響應內容不是商家詳細頁的網頁內容,肯定遇到反爬蟲檢測。在請求頭里,除了尚未加入 Cookies 之外,其余屬性已添加,因此,我們嘗試加入 Cookies,發現可以提取到商家信息。
但是只使用一個 Cookies 也會中斷爬取過程,原因在於訪問頻繁。為了降低訪問頻繁,引入 Cookies 池,將代碼的請求部分進行修改,如下所示:
從函數 get_info() 里可到,它調用了函數 get_comment(),並將商家 ID 和 find_uuid 分別傳入,find_uuid 是從函數 get_all()提取出來的數據,這兩個函數參數都是構建顧客評論的 AJAX 接口的請求參數,如圖所示:
- 請求參數 uuid 是函數參數 find_uuid
- 請求參數 originUrl 是商家詳細頁的 URL 地址
- 請求參數 id 是商家 id
因此,函數 get_comment() 的代碼如圖所示:
四、ORM 框架實現數據持久化存儲
爬蟲的核心功能大致已實現,接着講解數據存儲方面。數據存儲以 MySQL 為例,存儲過程使用 ORM 框架 SQLAlchemy,這樣可實現數據持久化,它的優點此處不一一講述。
首先在 Insql.py 里導入 ORM 框架 SQLAlchemy,並創建相關對象,通過 SQLAlchemy 連接本地 MySQL 數據庫。數據庫編碼設為 UTF8mb4,因為評論信息里可能出現手機表情這類特殊內容,這些特殊內容是超出 UTF-8 的編碼范圍。代碼如下:
將商家信息和顧客評論信息分別存儲在數據表 meituan_shop 和 meituan_comment。數據表之間存在一對多的數據關系,一個商家會有多條顧客評論,映射類的定義如下:
上述只是定義映射類,數據存儲的功能尚未實現。數據存儲由函數 shop_db() 和函數 comment_db() 實現,兩者會對待存儲的數據進行判斷,如果數據已存在數據庫,則進行更新處理,反之新增一條數據。代碼如下:
五、設置配置文件,動態控制爬取方向
配置文件給爬蟲程序 meishi.py 讀取,用於控制爬蟲的爬取方向,比如爬取北京、上海等城市的美食信息。將配置文件命名為 meishi.conf,配置信息如下:
配置文件只設置配置屬性 city,屬性值是將每個城市編號以英文逗號的形式拼接起來,城市編號是首個字母拼音開頭組成的。函數 get_all() 的函數參數 city_list 就是配置屬性 city。
在爬蟲文件 meishi.py 里,文件運行函數 _main_ 主要讀取配置文件的配置屬性 city,並調用函數 get_all(),代碼如下:
