這期的爬蟲是爬取「簡書」的搜索結果頁,篇幅將會分爲兩部分來寫,第一部分是爬蟲部分,主要涉及搜索文章的提取和數據保存,第二部分涉及基本的數據分析和可視化,本篇文章屬於爬蟲篇。html
首先看一下整個爬蟲的源代碼,每一個函數的用處已經寫在函數說明中,後面也會進行代碼解讀。python
# -*- coding: utf-8 -*- import requests import json from urllib.parse import quote from pymongo import MongoClient """ 簡書搜索爬蟲 輸入搜索關鍵詞,將搜索到的全部文章爬取下來 數據保存到 mongodb 中 """ class JianshuSearch(object): def __init__(self, db_name, coll_name, key, host='127.0.0.1', port=27017): self.headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/57.0.2987.110 Safari/537.36" } self.url = 'http://www.jianshu.com/search/do?q={key}&type=note&page={page}&order_by=default' self.key = quote(key) self.start_page = 1 self.host = host self.port = port self.db_name = db_name self.coll_name = coll_name def get_total_pages(self): '''提取總頁碼數''' url = self.url.format(key=self.key, page=self.start_page) html = requests.get(url, headers=self.headers).text data = json.loads(html) total_pages = data['total_pages'] return total_pages def get_infos(self, page): '''提取單個頁面的文章信息,格式爲dict''' url = self.url.format(key=self.key, page=page) html = requests.get(url, headers=self.headers).text data = json.loads(html) entries = data['entries'] for each in entries: self.save_infos(each) def save_infos(self, entry): '''保存一個文章的信息''' coon = MongoClient(host=self.host, port=self.port) coll = coon[self.db_name][self.coll_name] coll.insert(entry) def main(self): '''主函數,循環迭代進行翻頁,提取全部頁碼的信息並保存到數據庫''' total_pages = int(self.get_total_pages()) for i in range(1, total_pages + 1): self.get_infos(i) print('總計{}頁,已經爬完{}頁'.format(total_pages, i)) if __name__ == '__main__': DB_NAME = 'jianshu' COLL_NAME = 'search_result' key = 'python' spider = JianshuSearch(key=key, db_name=DB_NAME, coll_name=COLL_NAME) spider.main()
爬蟲的基本思路依然分爲3個主要步驟:ajax
首先在簡書的搜索框中輸入任意一個關鍵詞,例如「Python」,而後點擊搜索按鈕,查看結果頁面。mongodb
結果頁面以下圖所示:數據庫
能夠看到,搜索的結果包含了不少種類的信息,有「相關用戶」、「相關專題」和「相關文章」,而且文章的排序還能夠選擇條件。json
因爲這裏只須要提取搜索文章的信息,所以能夠先看一下搜索到的文章列表中包含哪些能夠收集的信息,以便後續查找和保存數據。瀏覽器
固然,通常來講,不少時候,咱們看到的信息其實並非眼前的頁面給咱們的信息,而是當前頁面從其餘地方「搬」過來的信息。若是使用或者知道ajax 的用法,這句話就很好理解,固然,不理解也無妨,並不影響後續操做。ide
打開瀏覽器的開發者界面,可使用快捷鍵 F12。刷新一下搜索的頁面,而後進入 Network,看看瀏覽器中加載了什麼頁面。函數
通常能夠先從 Doc中查看,也就是網頁源代碼,可是此次的爬蟲能夠看到源代碼中並無搜索的結果頁,因而能夠繼續查看 js,發現好像也沒有新加載頁面,繼續查看 XHR,終於找到了刷新頁面加載出來的頁面請求,具體看截圖:工具
看截圖中的編號,編號1就是頁面加載的地方,編號2能夠看到請求的 URL,編號3就是 URL 的組成,能夠從中查看連接的構成規律,這裏的規律就是連接中有2個關鍵參數,第一個參數就是搜索的關鍵詞,這裏用 q=Python表示,而後第二個參數就是當前頁碼,這裏是 page=2,從這裏就能夠直接聯想到,若是知道總頁碼數,那就可使用循環來獲得全部頁碼的 URL。編號4是請求的方式,這裏是 GET。
通過這個頁面,就能夠把整個爬蟲的思路理清楚了:
首先須要導入相關庫:
import requests import json from urllib.parse import quote from pymongo import MongoClient
這4個庫的做用分別是:
因爲這個爬蟲是建立了一個爬蟲類,因此須要按照 Python類的規範來,首先初始化類,經過函數來實現:
def __init__(self, db_name, coll_name, key, host='127.0.0.1', port=27017): self.headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/57.0.2987.110 Safari/537.36" } self.url = 'http://www.jianshu.com/search/do?q={key}&type=note&page={page}&order_by=default' self.key = quote(key) self.start_page = 1 self.host = host self.port = port self.db_name = db_name self.coll_name = coll_name
這裏給類傳遞了5個參數,其中3個是沒有給予默認值的參數,2個是給了默認值的參數,每一個參數的含義:
能夠經過爬取任意一個頁碼,而後打印一下提取的信息,看看信息的構成。
經過打印信息能夠發現,每一個頁面的信息都是一個字典格式,所以能夠經過json模塊來轉換成 Python 的 dict 格式,而後能夠發現每頁都有一個參數是「total_pages」,這個參數就提供了當前搜索的結果總頁碼數,所以能夠經過函數來提取這個參數的值:
def get_total_pages(self): '''提取總頁碼數''' url = self.url.format(key=self.key, page=self.start_page) html = requests.get(url, headers=self.headers).text data = json.loads(html) total_pages = data['total_pages'] return total_pages
因爲每一個頁面的信息都是一個 json 格式,因此信息的提取方式很簡單,直接從字典中提取就好了:
def get_infos(self, page): '''提取單個頁面的文章信息,格式爲dict''' url = self.url.format(key=self.key, page=page) html = requests.get(url, headers=self.headers).text data = json.loads(html) entries = data['entries'] for each in entries: self.save_infos(each)
函數的思路很簡單,首先經過 requests
請求網頁,而後獲得一個json類型的信息,可是因爲這個信息的格式是 str
, 因此須要使用 json.loads
方法轉換成 dict
格式。而後使用字典的鍵值對關係提取到總頁碼數就能夠了。
這個函數的最後面用到了一個保存信息的函數,也就是後面要說的將數據保存到數據庫中。
def save_infos(self, entry): '''保存一個文章的信息''' coon = MongoClient(host=self.host, port=self.port) coll = coon[self.db_name][self.coll_name] coll.insert(entry)
保存到數據庫的函數須要傳入一個參數,是一個 dict
類型,這個參數正好能夠理解爲提取的單個文章的信息。
首先,按照 mongodb
的鏈接方式創建一個鏈接,這個鏈接就使用到了建立類的時候傳入的數據庫的參數,而後使用 insert()
方法就能夠插入數據了。
講過上述的的函數,已經能夠提取總頁碼數,也能夠提取並保存單個頁碼的文章信息了,剩下的就是使用循環來提取全部頁碼的信息了,因而,能夠把這個過程寫到一個主函數中:
def main(self): '''主函數,循環迭代進行翻頁,提取全部頁碼的信息並保存到數據庫''' total_pages = int(self.get_total_pages()) for i in range(1, total_pages + 1): self.get_infos(i) print('總計{}頁,已經爬完{}頁'.format(total_pages, i))
爲了方便查看爬蟲的進度,在每爬取一頁信息,能夠打印當前的進度。
最後一段代碼就是運行爬蟲,首先給定一些須要傳遞到爬蟲類中的參數,而後啓動爬蟲的主程序便可看到爬蟲結果。
「Python」這個字段在簡書中有100頁的數據(若是沒有猜錯,這個100頁應該是簡書默認提供的最大頁碼數),因此爬蟲運行的時間並不長,爬完以後可使用可視化工具看看數據庫中的信息。
數據庫中信息如圖:
後記:從數據庫中能夠查看到一些有效信息,包括文字的標題、連接、發佈時間、做者的信息、評論數、閱覽量、喜歡數等。雖然這些數據並無什麼研究價值,可是本着練手的想法,後續會使用 Python 基本的數據分析工具來對爬取的信息進行可視化分析。