追書神器(一)—Scrapy入門

走川_發表於2019-03-04

前言

看小說是我這麼多年來一直保持的習慣。《盤龍》、《鬥破蒼穹》、《仙逆》、《凡人修仙傳》等等,陪伴了我整個學生時代。最近發現iOS上小說類app體驗都不好,經常出現廣告彈出、更新不及時、強制分享等情況。於是在一個下雨的晚上,我決定不再忍受這些app,自己強擼一個追書爬蟲。

Scrapy介紹

Scrapy是python主流爬蟲框架,可以很方便的通過url抓取web資訊,同時與傳統的requests庫相比,提供了更多的工具和更高的併發。推薦從官方學習網站上學習。

不過,你一點scrapy資料都不知道也沒有關係,讀完本文一樣能擼出來

Scrapy實戰

在開始前,我們需要準備好以下幾個東西:

  1. 想要爬取的小說url
  2. 環境搭建
  3. 入門級的python語法

選取網站

這裡我選的是 m.book9.net/。 選這個網站是因為它有是三個優點

  1. 更新速度比較快 (服務穩定)
  2. 頁面的結構簡單 (易於解析)
  3. 沒有做一些防爬蟲的保護 (操作簡單)

接下來,找到追更小說的主頁。

比如 辰東的《聖墟》

upload successful

假設現在我們要追更的話,需要每次開啟這個網站,然後點選最新章節的第一個選項條,連結到具體的小說章節。

仿造以上的步驟,我畫出了這樣一個流程:

upload successful

所以接下來,我們只需要根據這些流程來轉化成我們的程式碼就好了

搭建工程

我們將要搭建一個Scrapy殼工程,在這之前要先確保自己安裝了python的環境。框架自身相容2、3版本,所以不需要擔心版本的差異

我本地的環境是python3,所以可能會和2有細微的差異操作。

1.安裝Scrapy

> pip3 install scrapy
複製程式碼

2.建立爬蟲工程,將其命名為NovelCrawler

> scrapy startproject NovelCrawler
複製程式碼

3. 建立一個基於 url 的爬蟲服務

> scrapy genspider novel m.book9.net
複製程式碼

以上就是基本的工程建立過程,執行完畢之後就可以使用

> scrapy crawl novel
複製程式碼

命令去開啟爬蟲服務。不過當前我們的爬蟲還沒有實現任何的規則,所以即使執行了命令也不會做任何事,所以我們需要在工程中新增一些爬蟲規則。

爬蟲編寫

接下來我們用Pycharm來開啟剛剛建立好的工程。

upload successful

scrapy的所有爬蟲服務都集中在spiders目錄下,我們也在這裡添爬蟲檔案novel.py

請求小說主頁
upload successful

我們開啟檔案,新增一些基礎的配置

# encoding:utf-8

import scrapy

class NovelSpider(scrapy.Spider):
    #配置服務名稱,與上文建立服務的名稱相同
    name = `novel` 
    #允許訪問的域,與上文建立服務的名稱相同
    allowed_domains = [`m.book9.net`] 
    #發起請求的url,《聖墟》小說主頁
    start_urls = [`https://m.book9.net/wapbook/10.html`] 

	#預設的請求成功的回撥函式
    def parse(self, response): 
        pass

複製程式碼

上面的程式碼中,parse函式的入引數response物件裡面有什麼引數對我們來說是未知的,這也是初學python很頭疼的地方。這裡給大家提供一個方法:用Pycharm的debug功能檢視引數

upload successful

從上圖中我們可以發現,response包含了請求的html資訊。因此我們只需要其稍加處理,擷取出我們需要的部分。

獲取最新章節url

那麼如何解析我們需要的節點呢,response給我們提供了
xpath方法,我們只需要輸入的xpath規則就可以定位到相應html標籤節點。

不會xpath語法沒關係,Chrome給我們提供了一鍵獲取xpath地址的方法(右鍵->檢查->copy->copy xpath),如下圖:

upload successful

通過xpath,我們可以從這個頁面獲取到最新章節的地址

# encoding:utf-8

import scrapy

class NovelSpider(scrapy.Spider):
    name = `novel`
    allowed_domains = [`m.book9.net`]
    start_urls = [`https://m.book9.net/wapbook/10.html`]

    def parse(self, response):
    	#指定的<a>標籤的跳轉連結
        context = response.xpath(`/html/body/div[3]/div[2]/p[1]/a/@href`)   
        #提取陣列的第一個結果 即最新章節的url
        url = context.extract_first()  
        print(url) 
        pass

複製程式碼
請求章節資訊

有了連結之後,我們就可以跳轉到下一個頁面。而response也提供了follow方法,便於我們在站內進行短鏈的跳轉。

# encoding:utf-8

import scrapy

class NovelSpider(scrapy.Spider):
    name = `novel`
    allowed_domains = [`m.book9.net`]
    start_urls = [`https://m.book9.net/wapbook/10.html`]

    def parse(self, response):
        context = response.xpath(`/html/body/div[3]/div[2]/p[1]/a/@href`) 
        url = context.extract_first()
        #獲取短鏈後繼續請求,並將結果返回指定的回撥
        yield response.follow(url=url, callback=self.parse_article)
        
        #自定義回撥方法
    def parse_article(self,response): 
    	#這裡的response 就是我們具體的文章頁
        print(response)
        pass

複製程式碼

(如對程式碼中關鍵字yield感到疑惑的請點選傳送門

有了文章的頁面,我們只需要對他的html進行解析。這部分內容過於面向細節。只適用於這個網站,因此我不過多進行贅述。附上註釋程式碼:

# encoding:utf-8
import re

import os
import scrapy

class NovelSpider(scrapy.Spider):
    name = `novel`
    allowed_domains = [`m.book9.net`]
    start_urls = [`https://m.book9.net/wapbook/10.html`]

    def parse(self, response):
    	# 指定的<a>標籤的跳轉連結
        context = response.xpath(`/html/body/div[3]/div[2]/p[1]/a/@href`)  
        #獲取短鏈後繼續請求,並將結果返回指定的回撥
        url = context.extract_first() 
        yield response.follow(url=url, callback=self.parse_article)   

    def parse_article(self, response):
        #獲取文章的標題
        title = self.generate_title(response) 
        #構建文章的html
        html = self.build_article_html(title, response)  
        #將章節html存至本地
        self.save_file(title + ".html", html)
        #用自帶的瀏覽器開啟本地html
        os.system("open " + title.replace(" ", " ") + ".html")   
        pass

    @staticmethod
    def build_article_html(title, response):
        #獲取文章內容
        context = response.xpath(`//*[@id="chaptercontent"]`).extract_first()
        #過略文章中<a> 標籤跳轉內容
        re_c = re.compile(`<s*a[^>]*>[^<]*<s*/s*as*>`)
        article = re_c.sub("", context)  
        #拼接文章html
        html = `<html><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><div align="center" style="width:100%;text-alight:center"><b><font size="5">` 
               + title + `</font></b></div>` + article + "</html>"   
        return html

    @staticmethod
    def generate_title(response):
        title = response.xpath(`//*[@id="read"]/div[1]/text()`).extract()
        return "".join(title).strip()

    @staticmethod
    def save_file(file_name, context):
        fh = open(file_name, `wb`)
        fh.write(context.encode(encoding="utf-8"))
        fh.close()
        pass
複製程式碼

現在我們可在再當前目錄下執行以下命令:

> scrapy crawl novel
複製程式碼

展示視訊

image

思考

整體寫完下來,發現很難做到一份程式碼適用於多個站點。因此遇到在多站點抓取的需求時,為每個站點建立一個爬蟲檔案會更為適合。

原始碼地址(聽說年底點了star的年終會加倍哦)

相關文章