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