1. 程式人生 > >Python爬了一半的資料出BUG了,等等!這一半資料還能用

Python爬了一半的資料出BUG了,等等!這一半資料還能用

相信你一定有過這樣的經歷:大晚上好不容易寫好一個爬蟲,添加了種種可能出現的異常處理,測試了很多遍都沒有問題,點選了 RUN 開始正式執行 。

第二天早上一睜眼就滿心歡喜地衝到電腦前,結果發現爬蟲半夜斷了,你氣得想要砸電腦,然後你看了一下 MongoDB 中爬了一半的資料,在想是刪掉重新爬,還是保留下來接著爬。

Python爬了一半的資料出BUG了,等等!這一半資料還能用

 

到這兒問題就來了,刪掉太可惜,接著爬很可能會爬到重複資料,雖然後期可以去重,但你有強迫症,就是不想爬到重複資料,怎麼辦呢?

這就遇到了「爬蟲斷點續傳」問題,關於這個問題的解決方法有很多種,不過本文主要介紹資料儲存到 MongoDB 時如何做到只插入新資料,而重複資料自動過濾不插入。

先來個簡單例子,比如現在有兩個 list ,data2 中的第一條資料和 data 列表中的第一條資料是重複的,我們想將這兩個 list 依次插入 MnogoDB 中去, 通常我們會使用 insert_one() 或者 insert_many() 方法插入,這裡我們使用 insert_one() 插入,看一下效果。

 1data = [
2{'index':'A','name':'James','rank':'1' },
3{'index':'B','name':'Wade','rank':'2' },
4{'index':'C','name':'Paul','rank':'3' },
5]
6
7data2 = [
8{'index':'A','name':'James','rank':'1' },
9{'index':'D','name':'Anthony','rank':'4' },
10]
11
12import pymongo
13client = pymongo.MongoClient('localhost',27017)
14db = client.Douban
15mongo_collection = db.douban
16
17for i in data:
18 mongo_collection.insert_one(i)

插入第一個 list :

Python爬了一半的資料出BUG了,等等!這一半資料還能用

 

 

插入第二個 list :

Python爬了一半的資料出BUG了,等等!這一半資料還能用

 

 

你會發現,重複的資料 A 被插入進去了,那麼怎麼只插入 D,而不插入 A 呢,這裡就要用到 update_one() 方法了,改寫一下插入方法:

1for i in data2:
2 mongo_collection.update_one(i,{'$set':i},upsert=True)

 

Python爬了一半的資料出BUG了,等等!這一半資料還能用

 

 

這裡用到了 $set 運算子,該運算子作用是將欄位的值替換為指定的值,upsert 為 True 表示插入。這裡也可以用 update() 方法,但是這個方法比較老了,不建議使用。另外嘗試使用 update_many() 方法發現不能更新多個相同的值。

1for i in data2:
2 mongo_collection.update(i, i, upsert=True)

下面舉一個豆瓣電影 TOP250 的例項,假設我們先獲取 10 個電影的資訊,然後再獲取前 20 個電影,分別用 insert_one() 和 update_one() 方法對比一下結果。

insert_one() 方法會重複爬取前 10 個電影,最終生成 30 個數據:

Python爬了一半的資料出BUG了,等等!這一半資料還能用

 

 

update_one() 方法則只會插入新的 10 個電影,最終生成 20 個數據:

Python爬了一半的資料出BUG了,等等!這一半資料還能用

 

 

這就很好了對吧,所以當我們去爬那些需要分頁的網站,最好在爬取之前使用 update_one() 方法,這樣就算爬蟲中斷了,也不用擔心會爬取重複資料。

程式碼實現如下:

1import requests

2import json

3import csv

4import pandas as pd

5from urllib.parse import urlencode

6import pymongo

7

8client = pymongo.MongoClient('localhost', 27017)

9db = client.Douban

10mongo_collection = db.douban

11class Douban(object):

12 def __init__(self):

13 self.url = 'https://api.douban.com/v2/movie/top250?'

14

15 def get_content(self, start_page):

16 params = {

17 'start': start_page,

18 'count': 10

19 }

20 response = requests.get(self.url, params=params).json()

21 movies = response['subjects']

22 data = [{

23 'rating': item['rating']['average'],

24 'genres':item['genres'],

25 'name':item['title'],

26 'actor':self.get_actor(item['casts']),

27 'original_title':item['original_title'],

28 'year':item['year'],

29 } for item in movies]

30

31 self.write_to_mongodb(data)

32

33 def get_actor(self, actors):

34 actor = [i['name'] for i in actors]

35 return actor

36

37 def write_to_mongodb(self, data):

38 for item in data:

39 if mongo_collection.update_one(item, {'$set': item}, upsert=True):

40 # if mongo_collection.insert_one(item):

41 print('儲存成功')

42 else:

43 print('儲存失敗')

44

45 def get_douban(self, total_movie):

46 # 每頁10條,start_page迴圈1次

47 for start_page in range(0, total_movie, 10):

48 self.get_content(start_page)

49

50if __name__ == '__main__':

51 douban = Douban()

52 douban.get_douban(10)

文章就到這裡啦~

寫在最後

Python爬了一半的資料出BUG了,等等!這一半資料還能用