1. 程式人生 > >Scrapy實現去重,使用Redis實現增量爬取

Scrapy實現去重,使用Redis實現增量爬取

一、使用場景:

定時爬取某網站的資料,每次爬取只爬取並儲存新增的資料到資料庫中,之前存過的資料不再入庫。

scrapy官方文件的去重模組,只能實現對當前抓取資料的去重,並不會和資料庫裡的資料做對比。當有一天需求變了,在你向mysql 資料庫儲存的時候,發現已經有一部分已經存在,有一部分新的資料,你又需要新增到mysql資料庫中,這時候你就需要通過redis來作為中介軟體,通過url來確保爬過的資料不會再爬,做到增量爬取。

二、準備工作:

1、電腦端安裝和啟動Redis服務:(參考http://www.runoob.com/redis/redis-install.html) 不啟動將無法訪問redis伺服器

2、安裝python的Redis模組:pip install Redis

三、需要知道的知識點

1、pymysql 如何連線、操作資料庫

2、Pandas  如何讀取mysql資料       

 pandas.read_sql(sql,connection)

3、Redis hash 如何儲存資料的以及一些方法

Redis是一個鍵值對的資料庫;Redis hash 是一個string型別的field和value的對映表,是redis提供的一種資料結構儲存方式。

redis雜湊結構:

結構:key field(欄位) value

對應:redis_data_dict   url(實際的url)   0(程式碼裡設定成了0)

key field value三者的關係:把key想象成一個字典的名稱,field想象成鍵,value想象成值,不過這裡我們關心的是field欄位,也就是關心的是鍵。這個value值設定有什麼用處沒搞清楚。

還需要搞清楚flushdb、Hlen、Hset、hexists。參考http://www.runoob.com/redis/redis-hashes.html

四、程式碼實現

Redis資料庫其實就是一箇中間件,因為爬蟲爬取的資料並不能直接拿去和mysql中的資料進行比較。

實現原理就相當於將mysql資料庫現有資料備份出來儲存在一個有鍵值對的Redis資料庫中,再將爬取到的資料和redis資料庫中的資料進行比較,若redis資料庫中的已經存在資料則丟棄,若redis資料庫中不存在該條資料則儲存進入mysql資料庫。每執行一次redis資料庫就會被重置一次。

分為幾個步驟:

  • 匯入模組

  • 連線redis  

  • 連線mysql

  • 清空redis裡的key,將mysql資料庫中的現有資料寫入redis

  • 將新爬到的資料與redis中的資料進行比對,如重複則丟棄,如不重複則寫入mysql資料庫。

import pymysql
import redis
import pandas

redis_db = redis.Redis(host='127.0.0.1',port=6379,db=1) # 連線本地redis,db資料庫預設連線到0號庫,寫的是索引值
redis_data_dict = ''  # key的名字,裡面的內容隨便寫,這裡的key相當於字典名稱,而不是key值。為了後面引用而建的


class MysqlRemovePipeline(object):
    def __init__(self):
        self.conn = pymysql.connect('localhost','root','Abcd1234','test')  # 連線mysql
        self.cursor = self.conn.cursor()  # 建立遊標
        # print(redis_db)
        redis_db.flushdb()  # 清空當前資料庫中的所有 key,為了後面將mysql資料庫中的資料全部儲存進去
        # print(redis_db)
        if redis_db.hlen(redis_data_dict) == 0:  # 判斷redis資料庫中的key,若不存在就讀取mysql資料並臨時儲存在redis中
            sql = 'select url from test_zxf'  # 查詢表中的現有資料
            df = pandas.read_sql(sql,self.conn)  # 讀取mysql中的資料
             # print(df)
            for url in df['url'].get_values():
                redis_db.hset(redis_data_dict,url,0) # 把每個url寫入field中,value值隨便設,我設定的0  key field value 三者的關係

    def process_item(self,item,spider):
        """
        比較爬取的資料在資料庫中是否存在,不存在則插入資料庫
        :param item: 爬取到的資料
        :param spider: /
        """
        if redis_db.hexists(redis_data_dict,item['url']): # 比較的是redis_data_dict裡面的field
            print("資料庫已經存在該條資料,不再繼續追加")
        else:
            self.do_insert(item)

    def do_insert(self, item):
        insert_sql = """
                insert into test_zxf(quote,author,tags,url,born_date,born_location) VALUES(%s,%s,%s,%s,%s,%s)
                            """
        self.cursor.execute(insert_sql, (item['quote'], item['author'], item['tags'], item['url'],
                                         item['born_date'], item['born_location']))
        self.conn.commit()  # 提交操作,提交了才真正儲存到資料庫中
        return item

    def close_spider(self,spider):
        self.cursor.close()  # 關閉遊標
        self.conn.close()    # 關閉連線

再配置一下settings檔案即可。

這裡每次抓取資料都需要儲存下url,儘管不是你所需要的資料。然後去比對url來判斷是否重複爬取,爬取過的url將不再爬取。

實現增量爬取的方式還有很多,其它的有時間再研究,後續還需要比較下這些方式的不同之處,使用場景分別是什麼。加油,吼