91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用scrapy實現增量式爬取

發布時間:2022-06-21 11:56:30 來源:億速云 閱讀:184 作者:iii 欄目:開發技術

本篇內容主要講解“如何使用scrapy實現增量式爬取”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何使用scrapy實現增量式爬取”吧!

1.要爬取的信息

在scrapy中,信息通過item來封裝,這里我定義兩個item,一個用于封裝每本小說的信息,一個用于封裝每個章節的信息

1.BookItem

class BookItem(scrapy.Item):
    _id = scrapy.Field() #小說id,用于定位章節信息,章節唯一
    novel_Name = scrapy.Field() #小說名稱
    novel_Writer = scrapy.Field()#小說作者
    novel_Type = scrapy.Field()#小說類型
    novel_Status = scrapy.Field()#小說狀態,連載或者完結
    novel_UpdateTime = scrapy.Field()#最后更新時間
    novel_Words = scrapy.Field() #總字數
    novel_ImageUrl = scrapy.Field()#封面圖片
    novel_AllClick = scrapy.Field()#總點擊
    novel_MonthClick = scrapy.Field()#月點擊
    novel_WeekClick = scrapy.Field()#周點擊
    novel_AllComm = scrapy.Field()#總推薦
    novel_MonthComm = scrapy.Field()#月推薦
    novel_WeekComm = scrapy.Field()#周推薦
    novel_Url = scrapy.Field()#小說url
    novel_Introduction = scrapy.Field()#小說簡介

2.ChapterItem

 class ChapterItem(scrapy.Item):
    chapter_Url = scrapy.Field()#章節url
    _id = scrapy.Field()#章節id
    novel_Name = scrapy.Field()#小說名稱
    chapter_Name = scrapy.Field()#章節名稱
    chapter_Content = scrapy.Field()#內容
    novel_ID = scrapy.Field()#小說id
    is_Error = scrapy.Field()#是否異常

2.解析信息

這里我是用的是scrapy自帶的通用爬蟲模塊,只需要指定信息解析方式,需要跟進的url就夠了

1.指定需要跟進的url和回調函數

  allowed_domains = ["23us.so"] #允許爬取的域名
  start_urls = ["http://www.23us.so/xiaoshuo/414.html"]#種子url
  #跟進的url
  rules=(
    Rule(LinkExtractor(allow=("xiaoshuo/\d*\.html")),callback="parse_book_message",follow=True),
    Rule(LinkExtractor(allow=("files/article/html/\d*?/\d*?.index.html")),callback="parse_book_chapter",follow=True),
    Rule(LinkExtractor(allow=("files/article/html/\d*?/\d*?/\d*?.html")),callback="parse_chapter_content",follow=True),
    Rule(LinkExtractor(allow=(".*")),follow=True),
  )

2.解析方法

1.解析書籍信息方法

#解析小說信息頁面
  def parse_book_message(self,response):
    if not response.body:
      print(response.url+"已經被爬取過了,跳過")
      return;
    ht = response.body.decode("utf-8")
    text = html.fromstring(ht)
    novel_Url = response.url
    novel_Name = text.xpath(".//dl[@id='content']/dd[1]/h2/text()")[0].split(" ")[0] if response.xpath(".//dl[@id='content']/dd[1]/h2/text()") else "None"
    novel_ImageUrl = text.xpath(".//a[@class='hst']/img/@src")[0] if response.xpath(".//a[@class='hst']/img/@src") else "None"
    novel_ID = int(response.url.split("/")[-1].split(".")[0]) if response.url.split("/")[-1].split(".") else "None"
    novel_Type = text.xpath(".//table[@id='at']/tr[1]/td[1]/a/text()") if response.xpath(".//table[@id='at']/tr[1]/td[1]/a/text()") else "None"
    novel_Writer = "".join(text.xpath(".//table[@id='at']/tr[1]/td[2]/text()")) if response.xpath(".//table[@id='at']/tr[1]/td[2]/text()") else "None"
    novel_Status = "".join(text.xpath(".//table[@id='at']/tr[1]/td[3]/text()")) if response.xpath(".//table[@id='at']/tr[1]/td[3]/text()") else "None"
    novel_Words = self.getNumber("".join(text.xpath(".//table[@id='at']/tr[2]/td[2]/text()"))) if response.xpath(".//table[@id='at']/tr[2]/td[2]/text()") else "None"
    novel_UpdateTime = "".join(text.xpath(".//table[@id='at']/tr[2]/td[3]/text()")) if response.xpath(".//table[@id='at']/tr[2]/td[3]/text()") else "None"
    novel_AllClick = int("".join(text.xpath(".//table[@id='at']/tr[3]/td[1]/text()"))) if response.xpath(".//table[@id='at']/tr[3]/td[1]/text()") else "None"
    novel_MonthClick = int("".join(text.xpath(".//table[@id='at']/tr[3]/td[2]/text()"))) if response.xpath(".//table[@id='at']/tr[3]/td[2]/text()") else "None"
    novel_WeekClick = int("".join(text.xpath(".//table[@id='at']/tr[3]/td[3]/text()"))) if response.xpath(".//table[@id='at']/tr[3]/td[3]/text()") else "None"
    novel_AllComm = int("".join(text.xpath(".//table[@id='at']/tr[4]/td[1]/text()"))) if response.xpath(".//table[@id='at']/tr[4]/td[1]/text()") else "None"
    novel_MonthComm = int("".join(text.xpath(".//table[@id='at']/tr[4]/td[3]/text()"))) if response.xpath(".//table[@id='at']/tr[4]/td[2]/text()") else "None"
    novel_WeekComm = int("".join(text.xpath(".//table[@id='at']/tr[4]/td[3]/text()"))) if response.xpath(".//table[@id='at']/tr[4]/td[3]/text()") else "None"
    pattern = re.compile('<p>(.*)<br')
    match = pattern.search(ht)
    novel_Introduction = "".join(match.group(1).replace("&nbsp;","")) if match else "None"
     #封裝小說信息類
    bookitem = BookItem(
          novel_Type = novel_Type[0],
          novel_Name = novel_Name,
          novel_ImageUrl = novel_ImageUrl,
          _id = novel_ID,   #小說id作為唯一標識符
          novel_Writer = novel_Writer,
          novel_Status = novel_Status,
          novel_Words = novel_Words,
          novel_UpdateTime = novel_UpdateTime,
          novel_AllClick = novel_AllClick,
          novel_MonthClick = novel_MonthClick,
          novel_WeekClick = novel_WeekClick,
          novel_AllComm = novel_AllComm,
          novel_MonthComm = novel_MonthComm,
          novel_WeekComm = novel_WeekComm,
          novel_Url = novel_Url,
          novel_Introduction = novel_Introduction,
    )
    return bookitem

2.解析章節信息

def parse_chapter_content(self,response):
    if not response.body:
      print(response.url+"已經被爬取過了,跳過")
      return;
    ht = response.body.decode('utf-8')
    text = html.fromstring(ht)
    soup = BeautifulSoup(ht)
    novel_ID = response.url.split("/")[-2]
    novel_Name = text.xpath(".//p[@class='fr']/following-sibling::a[3]/text()")[0]
    chapter_Name = text.xpath(".//h2[1]/text()")[0]
    '''
    chapter_Content = "".join("".join(text.xpath(".//dd[@id='contents']/text()")).split())
    if len(chapter_Content) < 25:
      chapter_Content = "".join("".join(text.xpath(".//dd[@id='contents']//*/text()")))
    pattern = re.compile('dd id="contents".*?>(.*?)</dd>')
    match = pattern.search(ht)
    chapter_Content = "".join(match.group(1).replace("&nbsp;","").split()) if match else "爬取錯誤"
    '''
    result,number = re.subn("<.*?>","",str(soup.find("dd",id='contents')))
    chapter_Content = "".join(result.split())
    print(len(chapter_Content))
    novel_ID = response.url.split("/")[-2]
    return ChapterItem(
          chapter_Url = response.url,
          _id=int(response.url.split("/")[-1].split(".")[0]),
          novel_Name=novel_Name,
          chapter_Name=chapter_Name,
          chapter_Content= chapter_Content,
          novel_ID = novel_ID,
          is_Error = len(chapter_Content) < 3000
          )

3.scrapy中實現增量式爬取的幾種方式

1.緩存

通過開啟緩存,將每個請求緩存至本地,下次爬取時,scrapy會優先從本地緩存中獲得response,這種模式下,再次請求已爬取的網頁不用從網絡中獲得響應,所以不受帶寬影響,對服務器也不會造成額外的壓力,但是無法獲取網頁變化的內容,速度也沒有第二種方式快,而且緩存的文件會占用比較大的內存,在setting.py的以下注釋用于設置緩存

#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

這種方式比較適合內存比較大的主機使用,我的阿里云是最低配的,在爬取半個晚上接近27W個章節信息后,內存就用完了

2.對item實現去重

本文開頭的第一種方式,實現方法是在pipelines.py中進行設置,即在持久化數據之前判斷數據是否已經存在,這里我用的是mongodb持久化數據,邏輯如下

  #處理書信息
  def process_BookItem(self,item):
    bookItemDick = dict(item)
    try:
      self.bookColl.insert(bookItemDick)
      print("插入小說《%s》的所有信息"%item["novel_Name"])
    except Exception:
      print("小說《%s》已經存在"%item["novel_Name"])
  #處理每個章節
  def process_ChapterItem(self,item):
    try:
      self.contentColl.insert(dict(item))
      print('插入小說《%s》的章節"%s"'%(item['novel_Name'],item['chapter_Name']))
    except Exception:
      print("%s存在了,跳過"%item["chapter_Name"])
  def process_item(self, item, spider):
    '''
    if isinstance(item,ChaptersItem):
      self.process_ChaptersItem(item)
    '''
    if isinstance(item,BookItem):
      self.process_BookItem(item)
    if isinstance(item,ChapterItem):
      self.process_ChapterItem(item)
    return item

兩種方法判斷mongodb中是否存在已有的數據,一是先查詢后插入,二是先設置唯一索引或者主鍵再直接插入,由于mongodb的特點是插入塊,查詢慢,所以這里直接插入,需要將唯一信息設置為”_id”列,或者設置為唯一索引,在mongodb中設置方法如下

db.集合名.ensureIndex({"要設置索引的列名":1},{"unique":1})

需要用什么信息實現去重,就將什么信息設置為唯一索引即可(小說章節信息由于數據量比較大,用于查詢的列最好設置索引,要不然會非常慢),這種方法對于服務器的壓力太大,而且速度比較慢,我用的是第二種方法,即對已爬取的url進行去重

3.對url實現去重

對我而言,這種方法是最好的方法,因為速度快,對網站服務器的壓力也比較小,不過網上的資料比較少,后來在文檔中發現scrapy可以自定義下載中間件,才解決了這個問題

文檔原文如下

class scrapy.downloadermiddlewares.DownloaderMiddleware

process_request(request, spider) 當每個request通過下載中間件時,該方法被調用。

process_request() 必須返回其中之一: 返回 None 、返回一個 Response 對象、返回一個 Request對象或raise IgnoreRequest 。

如果其返回 None ,Scrapy將繼續處理該request,執行其他的中間件的相應方法,直到合適的下載器處理函數(downloadhandler)被調用, 該request被執行(其response被下載)。

如果其返回 Response 對象,Scrapy將不會調用 任何 其他的 process_request() 或process_exception() 方法,或相應地下載函數; 其將返回該response。 已安裝的中間件的process_response() 方法則會在每個response返回時被調用。

如果其返回 Request 對象,Scrapy則停止調用process_request方法并重新調度返回的request。當新返回的request被執行后,相應地中間件鏈將會根據下載的response被調用。

如果其raise一個 IgnoreRequest 異常,則安裝的下載中間件的 process_exception()方法會被調用。如果沒有任何一個方法處理該異常,則request的errback(Request.errback)方法會被調用。如果沒有代碼處理拋出的異常,則該異常被忽略且不記錄(不同于其他異常那樣)。

所以只需要在process_request中實現去重的邏輯就可以了,代碼如下

class UrlFilter(object):
  #初始化過濾器(使用mongodb過濾)
  def __init__(self):
    self.settings = get_project_settings()
    self.client = pymongo.MongoClient(
      host = self.settings['MONGO_HOST'],
      port = self.settings['MONGO_PORT'])
    self.db = self.client[self.settings['MONGO_DB']]
    self.bookColl = self.db[self.settings['MONGO_BOOK_COLL']]
    #self.chapterColl = self.db[self.settings['MONGO_CHAPTER_COLL']]
    self.contentColl = self.db[self.settings['MONGO_CONTENT_COLL']]
  def process_request(self,request,spider):
    if (self.bookColl.count({"novel_Url":request.url}) > 0) or (self.contentColl.count({"chapter_Url":request.url}) > 0):
      return http.Response(url=request.url,body=None)

但是又會有一個問題,就是有可能下次開啟時,種子url已經被爬取過了,爬蟲會直接關閉,后來想到一個笨方法解決了這個問題,即在pipeline.py里的open_spider方法中再爬蟲開啟時刪除對種子url的緩存

def open_spider(self,spider):            
    self.bookColl.remove({"novel_Url":"http://www.23us.so/xiaoshuo/414.html"})

4.結果

如何使用scrapy實現增量式爬取

如何使用scrapy實現增量式爬取

如何使用scrapy實現增量式爬取

如何使用scrapy實現增量式爬取

到此,相信大家對“如何使用scrapy實現增量式爬取”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

康乐县| 隆化县| 巴林左旗| 保亭| 祁连县| 固阳县| 陵川县| 磐石市| 莱阳市| 平顺县| 江达县| 玉林市| 梨树县| 阆中市| 舒兰市| 黔西县| 卢湾区| 龙里县| 东光县| 新泰市| 平凉市| 化隆| 绿春县| 盘山县| 临澧县| 永善县| 青海省| 涞源县| 车险| 江阴市| 原平市| 孟连| 宕昌县| 上思县| 新宁县| 宜兴市| 五峰| 衡山县| 灵寿县| 乐至县| 宝清县|