看小說是我這麼多年來一直保持的習慣。《盤龍》、《鬥破蒼穹》、《仙逆》、《凡人修仙傳》等等,陪伴了我整個學生時代。最近發現iOS上小說類app體驗都很差,常常出現廣告彈出、更新不及時、強制分享等狀況。因而在一個下雨的晚上,我決定再也不忍受這些app,本身強擼一個追書爬蟲。html
Scrapy是python主流爬蟲框架,能夠很方便的經過url抓取web信息,同時與傳統的requests庫相比,提供了更多的工具和更高的併發。推薦從官方學習網站上學習。python
不過,你一點scrapy資料都不知道也沒有關係,讀完本文同樣能擼出來git
在開始前,咱們須要準備好如下幾個東西:github
這裏我選的是 m.book9.net/。 選這個網站是由於它有是三個優勢web
接下來,找到追更小說的主頁。shell
好比 辰東的《聖墟》數組
假設如今咱們要追更的話,須要每次打開這個網站,而後點擊最新章節的第一個選項條,連接到具體的小說章節。瀏覽器
仿造以上的步驟,我畫出了這樣一個流程:bash
因此接下來,咱們只須要根據這些流程來轉化成咱們的代碼就行了併發
咱們將要搭建一個Scrapy殼工程,在這以前要先確保本身安裝了python的環境。框架自身兼容二、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來打開剛剛建立好的工程。
scrapy的全部爬蟲服務都集中在spiders目錄下,咱們也在這裏添爬蟲文件novel.py
# 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功能查看參數
從上圖中咱們能夠發現,response包含了請求的html信息。所以咱們只須要其稍加處理,截取出咱們須要的部分。
那麼如何解析咱們須要的節點呢,response給咱們提供了 xpath方法,咱們只須要輸入的xpath規則就能夠定位到相應html標籤節點。
不會xpath語法不要緊,Chrome給咱們提供了一鍵獲取xpath地址的方法(右鍵->檢查->copy->copy xpath),以下圖:
經過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*a\s*>')
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
複製代碼
總體寫完下來,發現很難作到一份代碼適用於多個站點。所以遇到在多站點抓取的需求時,爲每一個站點創建一個爬蟲文件會更爲適合。
源碼地址(據說年末點了star的年終會加倍哦)