速看 !Python小說爬蟲,有目錄,有文字極簡處理,還有IP解鎖方式!

朋友爬了本小說讓我看看,評價一番,“操蛋,你從哪裏學的?這就是一坨屎的爬蟲·······”


至於我爲什麼發這麼火,原因如下:

  1. 朋友的小說全部放在文件夾中,就是一章一個text文本, 沒有目錄的存在,而且目錄章節混亂,我大致看了下,第五章就跳到第七章了, 中間的不知道怎麼沒了。
  2. 打開小說一看, 密密麻麻擠着一起,沒有一點段落感,而且還有一些html代碼,沒有處理乾淨,最重要的,居然還有一大批網址在裏面,簡直就是不走心
  3. 第三,爬取的小說根本就不能算一整本,我看了一下文件數量,足足少了二十多章,我評價到看個鬼,你自己看看能不能行 !
  4. 朋友看完,自己也很無語,至於他爲什麼會出現這種情況,我想很多爬蟲新手們,都會出現這樣的情況,而且這些問題都是出自細節中,新手們,不怎麼會考慮周到,那麼既然朋友也出現了這樣的問題,他讓我幫他改改,於是改完之後,我寫下這篇博客,讓更多人真正的爬小說,而不是爬一坨屎,自己都看不下去的那種坨屎!!!!

看了一下朋友的代碼,發現問題的所在之處,都是細節問題!

爬蟲不注意細節,你談什麼爬蟲!

在這裏插入圖片描述


解決方案如下:

這一期還是做一個乾貨分享,都是我半年以來的爬蟲經驗,需要教學可以私聊我,在校學生還是需要一點經濟支持的!

如何產生章節:

在這裏插入圖片描述
自己問自己,有多少人爬取小說是這樣的,你自己能看嗎?

寫代碼的時候大部分人是這樣的

with open('**.text', 'w') as fw:
	fw.write(r.text)

關於文件的寫入, ‘w’ 的方式 是覆蓋寫, 沒有就創建, 那麼我們寫小說就不需要用這個, 使用
‘a’ 追加寫的模式, 然後添加適當的分隔符, 只有文本中添加目錄, 整個txt在手機中才會顯示出目錄的存在 ,我的實例如下: 最好加點文字提示, 代表這章節下載成功到總txt中 !

path = 'D://爬取小說//'
        os.chdir(path)  # 進入這個文件夾
        with open('酒神.txt', 'a+', encoding='utf-8') as fw:
            fw.write(''.join(items['title']) + '\n\n\n' + '- ' * 40)
            fw.write(''.join(items['text']))
        print(f'{items["title"]} 下載完成!')

如何章節順序穩定:

一般我們都是從目錄url 中, 去獲取我們需要的章節網址, 這裏拿我爬取的網址實例:

在這裏插入圖片描述
這裏我們就能獲取所有章節的網址了, 只要進行適當的拼接處理, 那麼很多人就舒服了,直接獲取整個url列表, 直接返回給調用函數, 舒服吧? 你電腦不舒服, 關於returnyield 你真的懂嗎? 如果你請求的網址很多, 一定時間請求次數過多,就容易丟失網址,我想大部分人都是返回列表,然後遍歷列表吧? 萬一丟了一個章節網址呢? 是不是就會出現章節混亂的情況,使用yield 就不需要考慮這麼多因素了, 它可以直接調用一次返回一次,有效的避免了可能的因素, 我覺得很好用, 我也推薦使用, 不是所有的返回都是用return!!!

示例如下:

page_url = html.xpath('//div[@id="list"]/dl/dd/a/@href')
            for url in page_url[222:]:
                url = f'http://www.biquge.info/11_11079/{url}'
                yield url

如何保證文字的處理:

爬取小說這樣的文字量很多的情況下, 文字的處理顯得極爲重要了,爬取小說不推薦使用正則re,也不推薦使用soup, 原因你獲取不了網頁自帶的換行符和縮進符, 比如 \xboo 之類的, 如果你獲取不了文本自帶的這些,那麼你就得自己添加,可以使用re的sub進行適當的替換換行,這就很麻煩,這裏一定推薦使用xpath的text() 去匹配, 方便快捷,爬小說的小助手!

示例如下:

text = html.xpath('//div[@id="content"]/text()')

非常的簡單, 而且xpath的匹配非常的快,不會的朋友一定要學,soup真的不推薦,大工程起來真的慢!


如何應對ip限制等因素:

他在爬小說的時候,也出現了這樣的問題,在一個urls列表中, 竟然有的能請求成功, 有的不能請求成功, 明明都是網址沒有錯, 我也能打得開,但就是出現了這樣的bug,很鬧騰,但是後面我還是修復了。

bug 出現 : 所有的網址 一定是正確的, 我可以確保。

在這裏插入圖片描述

爲什麼會出現這樣的情況,高頻發的請求網址,如果不是使用框架, 那麼就需要設置延遲時間了。比如sleep() 或者 request 加入參數 timeout ,不然很容易被網址通過請求次數 進而識別出來 這是一個爬蟲程序, 也就是非人類操作, 那麼他就可以不讓你請求,從而你就爬蟲失敗!

這樣搞:

多次請求,模塊分離,你搞我, 我也可以搞你!

將獲取文本的函數 分離出來, 在parse_page 進行三次判斷,失敗就回溯,三次回溯,實測,請求被擋住的概率大幅降低。這是一個不錯的方法, 當然, 還需要加入 sleep(), 來控制代碼的延遲時間。

    def parse_page_error(self, r):
        # 爲處理異常:

        r.encoding = r.apparent_encoding
        html = etree.HTML(r.text)
        title = html.xpath('//div[@class="bookname"]/h1/text()')
        text = html.xpath('//div[@id="content"]/text()')
        items = {}
        items['title'] = title
        items['text'] = text
        self.save_text(items)

    def parse_page(self):
        """
        分析每一章節  然後下載, 次數過快 容易炸ip 三次保底請求 !
        """
        for url in self.get_page():
            r = requests.get(url, headers=self.headers)
            time.sleep(0.7)
            if r.status_code == 200:
                self.parse_page_error(r)
            else:
                print(f'該 {url}未下載成功! 再次請求')
                rr = requests.get(url, headers=self.headers)
                if rr.status_code == 200:
                    self.parse_page_error(rr)
                else:
                    print("第三次請求!")
                    rrr = requests.get(url, headers=self.headers)
                    self.parse_page_error(rrr)
        print('全部下載完成!')


這一期乾貨就到此爲此, 主要還是滿足自己的需求,2020年了,看小說還不會爬蟲,你有多撈!

爬蟲全程序:

# -*- coding :  utf-8 -*-
# @Time      :  2020/6/2  16:13
# @author    :  沙漏在下雨
# @Software  :  PyCharm
# @CSDN      :  https://me.csdn.net/qq_45906219

import requests
from lxml import etree
import os
import time


class Spider:
    def __init__(self):
        self.start_url = 'http://www.biquge.info/11_11079/'
        self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                                      'AppleWebKit/537.36 (KHTML, like Gecko)'
                                      ' Chrome/81.0.4044.129 Safari/537.36',
                        'Host': 'www.biquge.info',
                        'Referer': 'http://www.biquge.info/11_11079/5216668.html',
                        'Cookie': 'clickbids=11079; Hm_lvt_6dfe3c8f195b43b8e667a2a2e5936122=1591085546;'
                                  ' Hm_lvt_c979821d0eeb958aa7201d31a6991f34=1591085539,1591085553,1591085815; '
                                  'Hm_lpvt_6dfe3c8f195b43b8e667a2a2e5936122=1591087376; '
                                  'Hm_lpvt_c979821d0eeb958aa7201d31a6991f34=1591087377'}

    def get_page(self):
        """
        獲得每一章節的網址
        yield 回去
        """
        r = requests.get(self.start_url, headers=self.headers)
        if r.status_code == 200:
            r.encoding = r.apparent_encoding
            html = etree.HTML(r.text)
            page_url = html.xpath('//div[@id="list"]/dl/dd/a/@href')
            for url in page_url[222:]:
                url = f'http://www.biquge.info/11_11079/{url}'
                yield url

    def save_text(self, items):
        """
        根據章節下載'
        """
        path = 'D://爬取小說//'
        os.chdir(path)  # 進入這個文件夾
        with open('酒神.txt', 'a+', encoding='utf-8') as fw:
            fw.write(''.join(items['title']) + '\n\n\n' + '- ' * 40)
            fw.write(''.join(items['text']))
        print(f'{items["title"]} 下載完成!')

    def parse_page_error(self, r):
        # 爲處理異常:

        r.encoding = r.apparent_encoding
        html = etree.HTML(r.text)
        title = html.xpath('//div[@class="bookname"]/h1/text()')
        text = html.xpath('//div[@id="content"]/text()')
        items = {}
        items['title'] = title
        items['text'] = text
        self.save_text(items)

    def parse_page(self):
        """
        分析每一章節  然後下載, 次數過快 容易炸ip 三次保底請求 !
        """
        for url in self.get_page():
            r = requests.get(url, headers=self.headers)
            time.sleep(0.7)
            if r.status_code == 200:
                self.parse_page_error(r)
            else:
                print(f'該 {url}未下載成功! 再次請求')
                rr = requests.get(url, headers=self.headers)
                if rr.status_code == 200:
                    self.parse_page_error(rr)
                else:
                    print("第三次請求!")
                    rrr = requests.get(url, headers=self.headers)
                    self.parse_page_error(rrr)
        print('全部下載完成!')


jiushen = Spider()
jiushen.parse_page()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章