Scrapy

scrapy 框架

  • Scrapy是用純Python實現一個為了爬取網站數據、提取結構性數據而編寫的應用框架,用途非常廣泛。

  • 框架的力量,用戶只需要定制開發幾個模塊就可以輕松的實現一個爬蟲,用來抓取網頁內容以及各種圖片,非常之方便。

  • Scrapy 使用了 Twisted['tw?st?d] 異步網絡框架來處理網絡通訊,可以加快我們的下載速度,不用自己去實現異步框架,并且包含了各種中間件接口,可以靈活的完成各種需求。

Scrapy架構圖

scrapy.PNG
  • Scrapy Engine(引擎): 負責Spider、ItemPipeline、Downloader、Scheduler中間的通訊,信號、數據傳遞等。

  • Scheduler(調度器): 它負責接受引擎發送過來的Request請求,并按照一定的方式進行整理排列,入隊,當引擎需要時,交還給引擎。

  • Downloader(下載器):負責下載Scrapy Engine(引擎)發送的所有Requests請求,并將其獲取到的Responses交還給Scrapy Engine(引擎),由引擎交給Spider來處理,

  • Spider(爬蟲):它負責處理所有Responses,從中分析提取數據,獲取Item字段需要的數據,并將需要跟進的URL提交給引擎,再次進入Scheduler(調度器),

  • Item Pipeline(管道):它負責處理Spider中獲取到的Item,并進行進行后期處理(詳細分析、過濾、存儲等)的地方.

  • Downloader Middlewares(下載中間件):你可以當作是一個可以自定義擴展下載功能的組件。

  • Spider Middlewares(Spider中間件):你可以理解為是一個可以自定擴展和操作引擎和Spider中間通信的功能組件(比如進入Spider的Responses;和從Spider出去的Requests)

Scrapy的使用

  1. 創建項目

scrapy startproject 項目名

  1. 創建爬蟲文件

scrapy genspider 爬蟲名 域名

Scrapy Item pipeline(管道文件)使用

當Item在Spider中被收集之后,它將會被傳遞到Item Pipeline,這些Item Pipeline組件按定義的順序處理Item。

每個Item Pipeline都是實現了簡單方法的Python類,比如決定此Item是丟棄而存儲。以下是item pipeline的一些典型應用:

驗證爬取的數據(檢查item包含某些字段,比如說name字段)
查重(并丟棄)
將爬取結果保存到文件或者數據庫中

下載項目圖片

使用圖片管道 當使用 ImagesPipeline :

在一個爬蟲里,你抓取一個項目,把其中圖片的URL放入 image_urls 組內。
項目從爬蟲內返回,進入項目管道。
當項目進入 ImagesPipeline,image_urls 組內的URLs將被Scrapy的調度器和下載器(這意味著調度器和下載器的中間件可以復用)安排下載,當優先級更高,會在其他頁面被抓取前處理。項目會在這個特定的管道階段保持“locker”的狀態,直到完成圖片的下載(或者由于某些原因未完成下載)。
當圖片下載完,另一個組(images)將被更新到結構中。這個組將包含一個字典列表,其中包括下載圖片的信息,比如下載路徑、源抓取地址(從 image_urls 組獲得)和圖片的校驗碼。 images 列表中的圖片順序將和源 image_urls 組保持一致。如果某個圖片下載失敗,將會記錄下錯誤信息,圖片也不會出現在 images 組中。

爬蟲數據持久化保存

step1: items.py文件:自定義字段,確定要爬取的目標網站數據
step2:spiders/douban.py 文件: 爬蟲文件,在這里編寫爬蟲代碼,解析數據,獲取新的url
step3:數據持久化

方式一:將數據存入mongodb
settings.py文件: 設置文件,在這里設置User-Agent,激活管道文件
ITEM_PIPELINES = {
    'XXXXXXXXXXX': 300,
}

MONGODB 主機名
MONGODB_HOST = '127.0.0.1'
MONGODB 端口號
MONGODB_PORT= 27017
數據庫名稱
MONGODB_DBNAME = "XXX"
存儲數據的表名稱
MONGODB_SHEETNAME= "xxxx"

pipelines.py管道:這里進行數據的清洗和持久化
import pymongo
from scrapy.conf import settings

class DoubanPipeline(object):

    # 將數據存儲在mongodb中
    def __init__(self,host,port,dbname,sheetname):
        # 創建MONGODB數據庫鏈接
        client = pymongo.MongoClient(host=host, port=port)
        # 指定數據庫
        mydb = client[dbname]
        # 存放數據的集合名稱
        self.mysheet = mydb[sheetname]

    @classmethod
    def from_crawler(cls, crawler):
        host = crawler.settings["MONGODB_HOST"]
        port = crawler.settings["MONGODB_PORT"]
        dbname = crawler.settings["MONGODB_DBNAME"]
        sheetname = crawler.settings["MONGODB_SHEETNAME"]
    
        return cls(host,port,dbname,sheetname)

    def process_item(self,item,spider):
        data = dict(item)
        # mongodb數據插入語句,使用save保存數據的效率會很慢,因為它需要循環便利,操作費時
        self.mysheet.insert(data)
        return item
方式二:將數據存入mysql數據庫
settings.py文件: 設置文件,在這里設置User-Agent,激活管道文件

ITEM_PIPELINES = {
    'xxxxxxxxxxxxxxxxx': 300,
}

#關于數據庫的相關配置
MYSQL_HOST = '127.0.0.1'
MYSQL_PORT = 3306
MYSQL_USER = ''
MYSQL_PWD = ''
MYSQL_DB = ''

pipelines.py管道文件
import pymysql
class DoubanPipeline(object):

#     將數據存儲值mysql數據庫
#     _mysql_exceptions.OperationalError: (1366, 是因為數據庫中的字符集與charset="utf8"不符合

    def __init__(self,host,port,user,pwd,db,charset):
        self.client = pymysql.Connect(host,user,pwd,db,port,charset='utf8')
        self.cursor = self.client.cursor()
    
    @classmethod
    def from_crawler(cls,crawler):
        host = crawler.settings['MYSQL_HOST']
        port = crawler.settings['MYSQL_PORT']
        user = crawler.settings['MYSQL_USER']
        pwd = crawler.settings['MYSQL_PWD']
        db = crawler.settings['MYSQL_DB']

        return cls(host,port,user,pwd,db,charset)

    def process_item(self,item,spider):
        insert_sql = """
               insert into doubanmovie(title, playable, content, star, commentnum, inq)
               VALUE (%s, %s, %s, %s, %s, %s)
        """
        try:
            self.cursor.execute(insert_sql, (item['title'],  item['content'], item['score'], item['info']))
            self.client.commit()
        except Exception as err:
            print(err)
            self.client.rollback()
        return item

將sql語句和要插入的數據在item中定義一個方法返回,通過item調用,然后返回

class XxxxItem(scrapy.Item):

    #名稱
    title = scrapy.Field()

    def insert_data_to_db(self,dataDict):
        sql = """
        INSERT INTO caipu (%s)
        VALUES (%s)
        """ % (','.join(dataDict.keys()),','.join(['%s']*len(dataDict)))

        data = list(dataDict.values())

        return sql,data
mysql數據異步插入
#twisted是一個異步的網絡框架,這里可以幫助我們實現異步將數據插入數據庫

#adbapi里面的子線程會去執行數據庫的阻塞操作,當一個線程執行完畢之后,同時,原始線程能繼續進行正常的工作,服務其他請求。
import pymysql
from twisted.enterprise import adbapi

#異步插入數據庫
class DoubanPipeline(object):

    def __init__(self,dbpool):
        self.dbpool = dbpool

    #使用這個函數來應用settings配置文件。
    @classmethod
    def from_crawler(cls, crawler):
        parmas = {
        'host':crawler.settings['MYSQL_HOST'],
        'user':crawler.settings['MYSQL_USER'],
        'passwd':crawler.settings['MYSQL_PASSWD'],
        'db':crawler.settings['MYSQL_DB'],
        'port':3306,
        'charset':'utf8',
        }

        # **表示字典,*tuple元組,
        # 使用ConnectionPool,起始最后返回的是一個ThreadPool
        dbpool = adbapi.ConnectionPool(
            'pymysql',
            **parmas
        )
        return cls(dbpool)

    def process_item(self, item, spider):
        #這里去調用任務分配的方法
        query = self.dbpool.runInteraction(
            self.insert_data_todb,
            item,
            spider
        )
        #數據插入失敗的回調
        query.addErrback(
            self.handle_error,
            item
        )

        #執行數據插入的函數
        def insert_data_todb(self,cursor,item,spider):
            insert_str,parmas = item.insertdata()
            cursor.execute(insert_str,parmas)
            print('插入成功')

    def handle_error(self,failure,item):
        print(failure)
        print('插入錯誤')
        #在這里執行你想要的操作

    def close_spider(self, spider):
        self.pool.close()

Scrapy Shell 的使用

Scrapy終端是一個交互終端,我們可以在未啟動spider的情況下嘗試及調試代碼,也可以用來測試XPath或CSS表達式,查看他們的工作方式,方便我們爬取的網頁中提取的數據。

啟動Scrapy Shell

scrapy shell " "
scrapy shell -s USER_AGENT=" "

嘗試使用Selector

返回 xpath選擇器對象列表

response.xpath('')

使用 extract()方法返回 Unicode字符串列表(打印列表第一個元素,終端編碼格式顯示)

response.xpath(' ').extract()[0]

返回 xpath選擇器對象列表

response.xpath('//title/text()')[0].extract()

Scrapy Spider文件介紹

scrapy.Spider是最基本的類,所有編寫的爬蟲必須繼承這個類

主要用到的函數及調用順序為:
init() : 初始化爬蟲名字和start_urls列表

start_requests() 調用make_requests_from url():生成Requests對象交給Scrapy下載并返回response

parse():

解析response,并返回Item或Requests(需指定回調函數)。
Item傳給Item pipline持久化 , 而Requests交由Scrapy下載,并由指定的回調函數處理(默認parse()),一直進行循環,直到處理完所有的數據為止。

Scrapy CrawlSpiders介紹和使用

CrawlSpider它是Spider的派生類,Spider類的設計原則是只爬取start_url列表中的網頁,而CrawlSpider類定義了一些規則Rule來提供跟進鏈接的方便的機制,從爬取的網頁結果中獲取鏈接并繼續爬取的工作

#######rules
CrawlSpider使用rules屬性來決定爬蟲的爬取規則,并將匹配后的url請求提交給引擎,完成后續的爬取工作。

在rules中包含一個或多個Rule對象,每個Rule對爬取網站的動作定義了某種特定操作,比如提取當前相應內容里的特定鏈接,是否對提取的鏈接跟進爬取,對提交的請求設置回調函數等。

LinkExtractors (目的提取鏈接)
主要參數:

allow:滿足括號中“正則表達式”的URL會被提取,如果為空,則全部匹配。
deny:滿足括號中“正則表達式”的URL一定不提取(優先級高于allow)。
allow_domains:會提取的鏈接的domains。
deny_domains:一定不會被提取鏈接的domains。
restrict_xpaths:使用xpath表達式,和allow共同作用過濾鏈接。

Scrapy Request和Response相關參數介紹

Request先關參數介紹

url: 就是需要請求,并進行下一步處理的url

callback: 指定該請求返回的Response,由那個函數來處理。

method: 請求一般不需要指定,默認GET方法,可設置為"GET", "POST", "PUT"等,且保證字符串大寫

headers: 請求頭

cookies: cookies,模擬用戶登錄需要指定用戶的cookies,字典dict型

meta: 比較常用,在不同的請求之間傳遞數據使用的。字典dict型

encoding: 編碼類型,使用默認的 'utf-8' 就行。

dont_filter: 表明該請求不由調度器過濾。這是當你想使用多次執行相同的請求,忽略重復的過濾器。默認為False。

errback: 指定錯誤處理函數

Response相關參數介紹

status: 響應碼
body: 響應體
url:響應url
self.request (request對象)
self.headers (響應頭)

Scrapy DOWNLOADER_MIDDLEWARE 的使用

通常防止爬蟲被反主要有以下幾個策略

  • 動態設置User-Agent(隨機切換User-Agent,模擬不同用戶的瀏覽器信息)

*禁用Cookies(前提是爬取的網站不需要cookies參數)(也就是不啟用cookies middleware,不向Server發送cookies,有些網站通過cookie的使用發現爬蟲行為)

使用cookies池,自定義中間件

除非特殊需要,否則禁用cookies,防止某些網站根據Cookie來封鎖爬蟲。

COOKIES_ENABLED = False
設置延遲下載(降低訪問網站的頻率)(設置為2秒或更高)
DOWNLOAD_DELAY = 2
使用IP代理地址池:VPN和代理IP,現在大部分網站都是根據IP來反爬的。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容