爬蟲之Scrapy框架

来源:https://www.cnblogs.com/Golanguage/archive/2020/03/26/12571714.html
-Advertisement-
Play Games

框架:具有很強的通用性,且封裝了一些通用實現方法的項目模板 (非同步框架): 高性能的網路請求 高性能的數據解析 高性能的持久化存儲 高性能的全站數據爬取 高性能的深度爬取 高性能的分散式 Scrapy環境安裝 IOS和Linux windows 安裝完成後,輸入 測試一下,出現如下圖顯示,即安裝成功 ...


  • 框架:具有很強的通用性,且封裝了一些通用實現方法的項目模板
  • scrapy(非同步框架):
    • 高性能的網路請求
    • 高性能的數據解析
    • 高性能的持久化存儲
    • 高性能的全站數據爬取
    • 高性能的深度爬取
    • 高性能的分散式

Scrapy環境安裝

IOS和Linux

  • pip install scrapy

windows

      a. pip3 install wheel

      b. 下載twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
      
      # Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl; Python是3.5版本的就選擇cp35下載

      c. 進入下載目錄,執行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl
    
      # 安裝失敗可能是這個文件的版本導致的,即使Python版本都是對的,可以重新下載一個32位的試試
      # 還安裝失敗的話就下載其python版本的,總有一個能成功

      d. pip3 install pywin32

      e. pip3 install scrapy

安裝完成後,輸入``scrapy`測試一下,出現如下圖顯示,即安裝成功。

Scrapy的基本使用

創建工程

  • scrapy startprojct proNmame

    cd proNmame進入到工程目錄下執行爬蟲文件

proName		# 工程名字
	spiders		# 爬蟲包(文件夾)
		__init__.py
    __init__.py
    items.py
    middlewares.py
    pipelines.py
    settings.py		# 創建好的工程的配置文件
scrapy.cfg		# scrapy的配置文件,不用修改

創建爬蟲文件

  • 創建爬蟲文件是py源文件
  • scrapy genspider spiderName www.xxx.com 網址後期可以修改
    • spiders包下創建一個py文件
# -*- coding: utf-8 -*-
import scrapy


class FirstSpider(scrapy.Spider):	# scrapy.Spider所有爬蟲類的父類
    # name表示的爬蟲文件的名稱,當前爬蟲文件的唯一標識
    name = 'first'
    
    # 允許的功能變數名稱,通常會註釋掉
    # allowed_domains = ['www.xx.com']
    
    # 起始的url列表,最開始要爬的網址列表
    # 作用:可以將內部的列表元素進行get請求的發送
    start_urls = ['http://www.sougou.com/','www.baidu.com']

    # 調用parse方法解析數據,方法調用的次數由start_urls列表元素個數決定的
    def parse(self, response):	# response表示一個響應對象,
        pass

基本配置

  • UA偽裝

  • robots協議的不遵從

    settings.py中將ROBOTSTXT_OBEY = True修改為False

  • 指定日誌等級

    settings.py中添加LOG_LEVEL = 'ERROR'

執行工程

  • scrapy crawl spiderName

  • 執行工程是不展示日誌文件

    scrapy crawl spiderName --nolog

    這種方式下程式報錯,不會展示;設置好日誌等級後直接執行工程即可。

數據解析

  • response.xpath('xpath表達式')

  • etree的不同之處:

    取文本/屬性:返回的是一個Selector對象,文本數據是存儲在該對象中

    • Selector對象[0].extract()返回字元串
    • Selector對象.extract_first()返回字元串
    • Selector對象.extract()返回列表

常用操作

  • 如果列表只有一個元素用Selector對象.extract_first(),返回字元串
  • 如果列表有多個元素Selector對象.extract(),返回列表,列表裡裝的是字元串

spiderName.py文件

# -*- coding: utf-8 -*-
import scrapy

class DuanziSpider(scrapy.Spider):
    name = 'duanzi'
    # allowed_domains = ['www.xx.com']
    start_urls = ['https://duanziwang.com/']

    def parse(self, response):
        article_list = response.xpath('/html/body/section/div/div/main/article')  # 基於xpath表達式解析
        for article in article_list:
            title = article.xpath('./div[1]/h1/a/text()')[0]    # 返回一個Selector對象
            # <Selector xpath='./div[1]/h1/a/text()' data='關於健康養生、延年益壽的生活諺語_段子網收錄最新段子'>
            title = article.xpath('./div[1]/h1/a/text()')[0].extract()    # 返回字元串
            # 關於健康養生、延年益壽的生活諺語_段子網收錄最新段子
            title = article.xpath('./div[1]/h1/a/text()').extract_first()   # 返回字元串
            # 關於健康養生、延年益壽的生活諺語_段子網收錄最新段子
            title = article.xpath('./div[1]/h1/a/text()').extract()     # 返回列表
            # ['關於健康養生、延年益壽的生活諺語_段子網收錄最新段子']
            print(title)
            break

持久化存儲

基於終端指令的持久化存儲

  • 只可以將parse方法的返回值存儲到指定尾碼的文本文件中

    指定尾碼:'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle',通常用csv

    指令scrapy crawl spiderName -o filePath

案例:將文本數據持久化存儲

# -*- coding: utf-8 -*-
import scrapy

class DuanziSpider(scrapy.Spider):
    name = 'duanzi'
    # allowed_domains = ['www.xx.com']
    start_urls = ['https://duanziwang.com/']

    # 基於終端指令的持久化存儲
    def parse(self, response):
        article_list = response.xpath('/html/body/section/div/div/main/article')  # 基於xpath表達式解析
        all_data = []
        for article in article_list:
            title = article.xpath('./div[1]/h1/a/text()').extract_first()
            content = article.xpath('./div[2]/p//text()').extract()
            content = ''.join(content)
            dic = {
                "title": title,
                "content": content
            }
            all_data.append(dic)
        return all_data
# 終端指令
# scrapy crawl spiderName -o duanzi.csv

基於管道的持久化存儲

scrapy建議使用管道持久化存儲

實現流程

  • 數據解析(spiderName .py

  • 實例化item類型對象(items.py

    items.py的item類中定義相關的屬性

    fieldNmae = scrapy.Field()

  • 將解析的數據存儲封裝到item類型的對象中(spiderName .py

    item['fileName'] = value 給item對象的fieldNmae屬性賦值

  • 將item對象提交給(spiderName .py

    yield item 將item提交給優先順序最高的管道

  • 在管道中接收item,可以將item中存儲的數據進行任意形式的持久化存儲(pipelines.py

    process_item():負責接收item對象且對其進行持久化存儲

  • 在配置文件settings.py中開啟管道機制

    找到如下代碼,取消註釋

    ITEM_PIPELINES = {
        # 300表示的是優先順序,數值越小,優先順序越高
       'duanziPro.pipelines.DuanziproPipeline': 300,
    }
    

案例:將文本數據持久化存儲

按上述在settings.py找到管道代碼,取消註釋。

spiderName .py

# -*- coding: utf-8 -*-
import scrapy
from duanziPro.items import DuanziproItem

class DuanziSpider(scrapy.Spider):
    name = 'duanzi'
    # allowed_domains = ['www.xx.com']
    start_urls = ['https://duanziwang.com/']
    # 基於管道的持久化存儲
    def parse(self, response):
        article_list = response.xpath('/html/body/section/div/div/main/article')  # 基於xpath表達式解析
        for article in article_list:
            title = article.xpath('./div[1]/h1/a/text()').extract_first()
            content = article.xpath('./div[2]/pre/code//text()').extract()
            content = ''.join(content)
            print(content)
            # 實例化item對象
            item = DuanziproItem()
            # 通過中括弧的形式訪問屬性給其賦值
            item['title'] = title
            item['content'] = content
            # 向管道提交item
            yield item

items.py

import scrapy

class DuanziproItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 使用固有屬性定義了兩個屬性
    # Field是一個萬能數據類型
    title = scrapy.Field()
    content = scrapy.Field()

pipelines.py

class DuanziproPipeline(object):
    # 重寫父類的該方法:該方法只會在爬蟲開始的時候執行一次
    fp = None
    
    # 打開文件
    def open_spider(self, spider):
        print('open spider')
        self.fp = open('./duanzi.txt', 'w', encoding='utf-8')

    # 關閉文件
    def close_spider(self, spider):
        print('close spider')
        self.fp.close()

    # 接收爬蟲文件返回item對象,process_item方法每調用一次可接收一個item對象
    # item參數:接收到的某一個item對象
    def process_item(self, item, spider):
        # 取值
        title = item['title']
        content = item['content']
        self.fp.write(title + ":" + content + "\n")
        return item

管道存儲細節處理

  • 管道文件中的管道類表示的是什麼?

    一個管道類對應的就是一種存儲形式(文本文件,資料庫)

    如果想要實現數據備份,則需要使用多個管道類(多種存儲形式:MySQL,Redis)

  • process_item中的 retutn item

    將item傳遞給下一個即將被執行(按照配置文件中ITEM_PIPELINES得權重排序)的管道類

存儲到MySQL

pipelines.py中添加如下代碼

import pymysql

class MysqlPipeline(object):
    conn = None
    cursor = None

    def open_spider(self, spider):
        self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123', db='spider',
                                    charset='utf8')

    def process_item(self, item, spider):
        # 取值
        title = item['title']
        content = item['content']
        self.cursor = self.conn.cursor()
        # sql語句
        sql = 'insert into duanzi values ("%s","%s")' % (title, content)
        try:
            self.cursor.execute(sql)
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()
        return item

    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()

settings.py中將MysqlPipeline類註冊到ITEM_PIPELINES中

ITEM_PIPELINES = {
    # 300表示的是優先順序,數值越小,優先順序越高
    'duanziPro.pipelines.DuanziproPipeline': 300,
    'duanziPro.pipelines.MysqlPipeline': 301,
}

存儲到Redis

  • 因為redis有的版本不支持存儲字典,下載2.10.6版本

    pip install redis==2.10.6

pipelines.py中添加如下代碼

from redis import Redis


class RedisPipeline(object):
    conn = None

    def open_spider(self, spider):
        self.conn = Redis(host='127.0.0.1', port=6379, password='yourpassword')

    def process_item(self, item, spider):
        self.conn.lpush('duanziList', item)
        # 報錯:因為redis有的版本不支持存儲字典,pip install redis==2.10.6

settings.py中將RedisPipeline類註冊到ITEM_PIPELINES中

ITEM_PIPELINES = {
    # 300表示的是優先順序,數值越小,優先順序越高
    'duanziPro.pipelines.DuanziproPipeline': 300,
    'duanziPro.pipelines.RedisPipeline': 301,
}

手動發送請求

  • 可以在start_urls這個列表中添加url,但是比較繁瑣

  • get請求發送

    yield scrapy.Request(url,callback)

    • url:指定好請求的url
    • callback:callback指定的回調函數一定會被執行(數據解析)
  • post請求發送

    yield scrapy.FormRequest(url,callback,formdata)

    • formdata存放請求參數,字典類型
  • 父類中start_requests請求發送的原理

# 簡單模擬父類的方法,主要看yield
def start_requests(self):
    for url in self.start_urls:
        # 發起get請求
        yield scrapy.Request(url=url,callback=self.parse)
        # 發起post請求,formdata存放請求參數
        yield scrapy.FormRequest(url=url,callback=self.parse,formdata={})

代碼實現

  • 主要是在spiderName .py中使用遞歸方法,且明確遞歸結束的條件;

    使用父類yield實現全站爬取

# -*- coding: utf-8 -*-
import scrapy
from duanziPro.items import DuanziproItem


class DuanziSpider(scrapy.Spider):
    name = 'duanzi'
    # allowed_domains = ['www.xx.com']
    start_urls = ['https://duanziwang.com/']
    
    # 手動請求的發送,對其他頁碼的數據進行請求操作
    # 定義通用url模板
    url = "https://duanziwang.com/page/%d/"
    pageNum = 2

    def parse(self, response):
        article_list = response.xpath('/html/body/section/div/div/main/article')  # 基於xpath表達式解析
        all_data = []
        for article in article_list:
            title = article.xpath('./div[1]/h1/a/text()').extract_first()
            content = article.xpath('./div[2]/pre/code//text()').extract()
            content = ''.join(content)
            # 實例化item對象
            item = DuanziproItem()
            # 通過中括弧的形式訪問屬性給其賦值
            item['title'] = title
            item['content'] = content
            # 向管道提交item
            yield item
        if self.pageNum < 5:
            new_url = format(self.url%self.pageNum)
            self.pageNum += 1
            # 遞歸實現全站數據爬取,callback指定解析的方法
            yield scrapy.Request(url=new_url, callback=self.parse)
  • pipelines.py中實現數據持久化存儲
class DuanziproPipeline(object):
    # 重寫父類的該方法:該方法只會在爬蟲開始的時候執行一次
    fp = None

    def open_spider(self, spider):
        print('open spider')
        self.fp = open('./duanzi.txt', 'w', encoding='utf-8')

    # 關閉fp
    def close_spider(self, spider):
        print('close spider')
        self.fp.close()

    # 接收爬蟲文件返回item對象,process_item方法每調用一次可接收一個item對象
    # item參數:接收到的某一個item對象
    def process_item(self, item, spider):
        # 取值
        title = item['title']
        content = item['content']
        self.fp.write(title + ":" + content + "\n")
        # 將item轉交給下一個即將被執行的管道類
        return item
  • settings.py中開啟管道類
ITEM_PIPELINES = {
    # 300表示的是優先順序,數值越小,優先順序越高
    'duanziPro.pipelines.DuanziproPipeline': 300,
}

yield在scrapy中的使用

  • 向管道中提交item對象

    yield item

  • 手動請求發送

    yield scrapy.Request(url,callback)

五大核心組件

  • 引擎(Scrapy Engine)

    處理整個系統的數據流,觸發事物(框架核心)。

  • 調度器(Scheduer)

    用來接收引擎發過來的請求,壓入隊列中,併在引擎再次請求的時候返回。

  • 下載器(Downloader)

    用於下載網頁內容,並將網頁內容返回給蜘蛛(Scrapy下載器是建立在twisted這個高效模型上的)。

  • 爬蟲(Spiders)

    爬蟲主要是幹活的,用於從特定的網頁中提取自己需要的信息,即所謂的實體(item)。用戶也可以從中提取出鏈接,讓Scrapy繼續抓取下一個頁面

  • 管道(item Pipeline)

    負責處理爬蟲從網頁抽取的實體,主要的功能是持久化實體、驗證實體的有效性、清除不需要的信息。當頁面被爬蟲解析後,將被髮送到項目管道,並經過幾個特定的次序處理數據。

五大核心組件的工作流程


當執行爬蟲文件時,5大核心組件就在工作了

首先執行爬蟲文件spider,spider的作用是
(1)解析(2)發請求,原始的url存儲在於spider中
1:當spider執行的時候,首先對起始的url發送請求,將起始url封裝成請求對象
2:將請求對象傳遞給引擎
3:引擎將請求對象傳遞給調度器(內部含有隊列和過濾器兩個機制),調度器將請求存儲在隊列(先進先出)中
4:調度器從隊列中調度出url的相應對象再將請求傳遞給引擎
5:引擎將請求對象通過下載中間件發送給下載器
6:下載器拿到請求到互聯網上去下載
7:互聯網將下載好的數據封裝到響應對象給到下載器
8:下載器將響應對象通過下載中間件發送給引擎
9:引擎將封裝了數據的響應對象回傳給spider類parse方法中的response對象
10:spider中的parse方法被調用,response就有了響應值
11:在spider的parse方法中進行解析代碼的編寫;
(1)會解析出另外一批url,(2)會解析出相關的文本數據
12: 將解析拿到的數據封裝到item中
13:item將封裝的文本數據提交給引擎
14:引擎將數據提交給管道進行持久化存儲(一次完整的請求數據)
15:如果parder方法中解析到的另外一批url想繼續提交可以繼續手動進行發請求
16:spider將這批請求對象封裝提交給引擎
17:引擎將這批請求對象發配給調度器
16:這批url通過調度器中過濾器過濾掉重覆的url存儲在調度器的隊列中
17:調度器再將這批請求對象進行請求的調度發送給引擎

引擎作用:
1:處理流數據 2:觸發事物
引擎根據相互的數據流做判斷,根據拿到的流數據進行下一步組件中方法的調用

下載中間件: 位於引擎和下載器之間,可以攔截請求和響應對象;攔截到請求和響應對象後可以
篡改頁面內容和請求和響應頭信息。
爬蟲中間件:位於spider和引擎之間,也可以攔截請求和響應對象,不常用。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • RPG系統構造 通過對於鬥羅大陸小說的游戲化過程,熟悉Angular的結構以及使用TypeScript的面向對象開發方法。 "Github項目源代碼地址" 人物 和其他RPG游戲類似,游戲裡面的人物角色大致有這樣的一些屬性:生命值,魔法值(魂力),攻擊力,防禦力,速度。RPG游戲中的角色隨著等級的提 ...
  • 這個不是無限級評論,只有兩層,實現起來比較簡單點,所有評論的parent_id都是對應的第一級評論的id,新增評論的時候,就在對應的評論下麵追加,並且用prepend()把最新評論放到最前面 資料庫設計(我這裡沒有真正用到資料庫,為了測試,用的都是寫死的假數據,所以後臺請求的介面只是返回了一個cod ...
  • 按照國際慣例先放效果圖 index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale ...
  • 在 HTML 文檔中,標題很重要;HTML 標題可以用來呈現文檔結構,設置得當的標題有利於用戶瀏覽您的網頁。標題(Heading)是通過 <h1> - <h6> 標簽進行定義的.<h1>定義最大的標題,<h6> 定義最小的標題。 註釋: A.瀏覽器會自動地在標題的前後添加空行。B.預設情況下,HTM ...
  • HTML 屬性:屬性是為 HTML 元素提供的附加信息。 HTML 元素可以設置屬性 屬性可以在元素中添加附加信息 屬性一般描述於開始標簽 屬性總是以名稱/值對的形式出現,比如:name="value"。 e.g:<a href=https://www.cnblogs.com/dhnblog/">這 ...
  • 圖解Java設計模式之外觀模式 影院管理項目 傳統方式解決影院管理 傳統方式解決影院管理問題分析 外觀模式基本介紹 外觀模式原理類圖 外觀模式解決影院管理 外觀模式的註意事項和細節 影院管理項目 組建一個家庭影院 :DVD 播放器、投影儀、自動屏幕、環繞立體聲、爆米花機,要求完成使用家庭影院的功能, ...
  • 問題引入:電腦里安裝了從官網下載的python3.8.0,。先使用了菜鳥教程的方法2安裝。https://www.runoob.com/numpy/numpy-install.html 發現產生錯誤。先是提示我的pip工具沒有更新到最新版本,於是根據提示更新後,發現仍有錯誤,於是放棄使用這方法里的命 ...
  • 需求 基於Spring, SpringMVC, Mybatis 實現一個類似京東商城的3C電子商城系統, 能夠實現商品管理與展示, 加入購物車, 支付購買等功能 運行環境 jdk1.8,tomcat8.5,mysql5.6,EclispseEE 項目技術 spring springmvc, myba ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...