簡書搜索爬蟲

這期的爬蟲是爬取「簡書」的搜索結果頁,篇幅將會分爲兩部分來寫,第一部分是爬蟲部分,主要涉及搜索文章的提取和數據保存,第二部分涉及基本的數據分析和可視化,本篇文章屬於爬蟲篇。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

  1. 找到入口,即起始頁面的 URL
  2. 找到翻頁的規律
  3. 選擇有效的方式進行翻頁,提取全部信息

查看頁面主要信息

首先在簡書的搜索框中輸入任意一個關鍵詞,例如「Python」,而後點擊搜索按鈕,查看結果頁面。mongodb

結果頁面以下圖所示:數據庫

搜索結果

能夠看到,搜索的結果包含了不少種類的信息,有「相關用戶」、「相關專題」和「相關文章」,而且文章的排序還能夠選擇條件。json

因爲這裏只須要提取搜索文章的信息,所以能夠先看一下搜索到的文章列表中包含哪些能夠收集的信息,以便後續查找和保存數據。瀏覽器

找到入口——第一個 URL

固然,通常來講,不少時候,咱們看到的信息其實並非眼前的頁面給咱們的信息,而是當前頁面從其餘地方「搬」過來的信息。若是使用或者知道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個庫的做用分別是:

  1. 網頁請求
  2. 將字符串格式轉換成 json 格式
  3. 將通常字符串轉換成 URL 能夠讀取的格式
  4. python 鏈接 mongodb 的依賴

爬蟲類的建立

因爲這個爬蟲是建立了一個爬蟲類,因此須要按照 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個是給了默認值的參數,每一個參數的含義:

  • key:搜索的關鍵詞
  • db_name:數據庫名稱
  • coll_name:數據表名稱
  • host,port:連接數據庫的地址和端口

提取總頁碼數

能夠經過爬取任意一個頁碼,而後打印一下提取的信息,看看信息的構成。

經過打印信息能夠發現,每一個頁面的信息都是一個字典格式,所以能夠經過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 基本的數據分析工具來對爬取的信息進行可視化分析。

查看原文

相關文章
相關標籤/搜索