一個神器大幅提升爬取效率

      網友投稿 653 2022-05-30

      在做爬蟲的時候,我們往往可能這些情況:

      網站比較復雜,會碰到很多重復請求。

      有時候爬蟲意外中斷了,但我們沒有保存爬取狀態,再次運行就需要重新爬取。

      還有諸如此類的問題。

      那怎么解決這些重復爬取的問題呢?大家很可能都想到了 “緩存”,也就是說,爬取過一遍就直接跳過爬取。

      那一般怎么做呢?

      比如我寫一個邏輯,把已經爬取過的 URL 保存到文件或者數據庫里面,每次爬取之前檢查一下是不是在列表或數據庫里面就好了。

      是的,這個思路沒問題,但有沒有想過這些問題:

      寫入到文件或者數據庫可能是永久性的,如果我想控制緩存的有效時間,那就還得有個過期時間控制。

      這個緩存根據什么來判斷?如果僅僅是 URL 本身夠嗎?還有 Request Method、Request Headers 呢,如果它們不一樣了,那還要不要用緩存?

      如果我們有好多項目,難道都沒有一個通用的解決方案嗎?

      的確是些問題,實現起來確實要考慮很多問題。

      不過不用擔心,今天給大家介紹一個神器,可以幫助我們通通解決如上的問題。

      介紹

      它就是 requests-cache,是 requests 庫的一個擴展包,利用它我們可以非常方便地實現請求的緩存,直接得到對應的爬取結果。

      GitHub:https://github.com/reclosedev/requests-cache

      PyPi:https://pypi.org/project/requests-cache/

      官方文檔:https://requests-cache.readthedocs.io/en/stable/index.html

      下面我們來介紹下它的使用。

      安裝

      安裝非常簡單,使用 pip3 即可:

      1

      pip3 install requests-cache

      安裝完畢之后我們來了解下它的基本用法。

      基本用法

      下面我們首先來看一個基礎實例:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      import requests

      import time

      start = time.time()

      session = requests.Session()

      for i in range(10):

      session.get('http://httpbin.org/delay/1')

      print(f'Finished {i + 1} requests')

      end = time.time()

      print('Cost time', end - start)

      這里我們請求了一個網站,是 http://httpbin.org/delay/1,這個網站模擬了一秒延遲,也就是請求之后它會在 1 秒之后才會返回響應。

      這里請求了 10 次,那就至少得需要 10 秒才能完全運行完畢。

      運行結果如下:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      Finished 1 requests

      Finished 2 requests

      Finished 3 requests

      Finished 4 requests

      Finished 5 requests

      Finished 6 requests

      Finished 7 requests

      Finished 8 requests

      Finished 9 requests

      Finished 10 requests

      Cost time 13.17966604232788

      可以看到,這里一共用了 13 秒。

      那如果我們用上 requests-cache 呢?結果會怎樣?

      代碼改寫如下:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      import requests_cache

      import time

      start = time.time()

      session = requests_cache.CachedSession('demo_cache')

      for i in range(10):

      session.get('http://httpbin.org/delay/1')

      print(f'Finished {i + 1} requests')

      end = time.time()

      print('Cost time', end - start)

      這里我們聲明了一個 CachedSession,將原本的 Session 對象進行了替換,還是請求了 10 次。

      運行結果如下:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      Finished 1 requests

      Finished 2 requests

      Finished 3 requests

      Finished 4 requests

      Finished 5 requests

      Finished 6 requests

      Finished 7 requests

      Finished 8 requests

      Finished 9 requests

      Finished 10 requests

      Cost time 1.6248838901519775

      可以看到,一秒多就爬取完畢了!

      發生了什么?

      這時候我們可以發現,在本地生成了一個 demo_cache.sqlite 的數據庫。

      我們打開之后可以發現里面有個 responses 表,里面多了一個 key-value 記錄,如圖所示:

      我們可以可以看到,這個 key-value 記錄中的 key 是一個 hash 值,value 是一個 Blob 對象,里面的內容就是 Response 的結果。

      可以猜到,每次請求都會有一個對應的 key 生成,然后 requests-cache 把對應的結果存儲到了 SQLite 數據庫中了,后續的請求和第一次請求的 URL 是一樣的,經過一些計算它們的 key 也都是一樣的,所以后續 2-10 請求就立馬返回了。

      是的,利用這個機制,我們就可以跳過很多重復請求了,大大節省爬取時間。

      Patch 寫法

      但是,剛才我們在寫的時候把 requests 的 session 對象直接替換了。有沒有別的寫法呢?比如我不影響當前代碼,只在代碼前面加幾行初始化代碼就完成 requests-cache 的配置呢?

      當然是可以的,代碼如下:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      import time

      import requests

      import requests_cache

      requests_cache.install_cache('demo_cache')

      start = time.time()

      session = requests.Session()

      for i in range(10):

      session.get('http://httpbin.org/delay/1')

      print(f'Finished {i + 1} requests')

      end = time.time()

      print('Cost time', end - start)

      這次我們直接調用了 requests-cache 庫的 install_cache 方法就好了,其他的 requests 的 Session 照常使用即可。

      我們再運行一遍:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      Finished 1 requests

      Finished 2 requests

      Finished 3 requests

      Finished 4 requests

      Finished 5 requests

      Finished 6 requests

      Finished 7 requests

      Finished 8 requests

      Finished 9 requests

      Finished 10 requests

      Cost time 0.018644094467163086

      這次比上次更快了,為什么呢?因為這次所有的請求都命中了 Cache,所以很快返回了結果。

      后端配置

      剛才我們知道了,requests-cache 默認使用了 SQLite 作為緩存對象,那這個能不能換啊?比如用文件,或者其他的數據庫呢?

      自然是可以的。

      比如我們可以把后端換成本地文件,那可以這么做:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      import time

      import requests

      import requests_cache

      requests_cache.install_cache('demo_cache', backend='filesystem')

      start = time.time()

      session = requests.Session()

      for i in range(10):

      session.get('http://httpbin.org/delay/1')

      print(f'Finished {i + 1} requests')

      end = time.time()

      print('Cost time', end - start)

      這里我們添加了一個 backend 參數,然后指定為 filesystem,這樣運行之后本地就會生成一個 demo_cache 的文件夾用作緩存,如果不想用緩存的話把這個文件夾刪了就好了。

      當然我們還可以更改緩存文件夾的位置,比如:

      1

      requests_cache.install_cache('demo_cache', backend='filesystem', use_temp=True)

      這里添加一個 use_temp 參數,緩存文件夾便會使用系統的臨時目錄,而不會在代碼區創建緩存文件夾。

      當然也可以這樣:

      1

      requests_cache.install_cache('demo_cache', backend='filesystem', use_cache_dir=True)

      這里添加一個 use_cache_dir 參數,緩存文件夾便會使用系統的專用緩存文件夾,而不會在代碼區創建緩存文件夾。

      另外除了文件系統,requests-cache 也支持其他的后端,比如 Redis、MongoDB、GridFS 甚至內存,但也需要對應的依賴庫支持,具體可以參見下表:

      比如使用 Redis 就可以改寫如下:

      1

      2

      backend = requests_cache.RedisCache(host='localhost', port=6379)

      requests_cache.install_cache('demo_cache', backend=backend)

      更多詳細配置可以參考官方文檔:https://requests-cache.readthedocs.io/en/stable/user_guide/backends.html#backends。

      Filter

      當然,我們有時候也想指定有些請求不緩存,比如只緩存 POST 請求,不緩存 GET 請求,那可以這樣來配置:

      1

      2

      3

      4

      一個神器,大幅提升爬取效率

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      import time

      import requests

      import requests_cache

      requests_cache.install_cache('demo_cache2', allowable_methods=['POST'])

      start = time.time()

      session = requests.Session()

      for i in range(10):

      session.get('http://httpbin.org/delay/1')

      print(f'Finished {i + 1} requests')

      end = time.time()

      print('Cost time for get', end - start)

      start = time.time()

      for i in range(10):

      session.post('http://httpbin.org/delay/1')

      print(f'Finished {i + 1} requests')

      end = time.time()

      print('Cost time for post', end - start)

      這里我們添加了一個 allowable_methods 指定了一個過濾器,只有 POST 請求會被緩存,GET 請求就不會。

      看下運行結果:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      Finished 1 requests

      Finished 2 requests

      Finished 3 requests

      Finished 4 requests

      Finished 5 requests

      Finished 6 requests

      Finished 7 requests

      Finished 8 requests

      Finished 9 requests

      Finished 10 requests

      Cost time for get 12.916549682617188

      Finished 1 requests

      Finished 2 requests

      Finished 3 requests

      Finished 4 requests

      Finished 5 requests

      Finished 6 requests

      Finished 7 requests

      Finished 8 requests

      Finished 9 requests

      Finished 10 requests

      Cost time for post 1.2473630905151367

      這時候就看到 GET 請求由于沒有緩存,就花了 12 多秒才結束,而 POST 由于使用了緩存,一秒多就結束了。

      另外我們還可以針對 Response Status Code 進行過濾,比如只有 200 會緩存,則可以這樣寫:

      1

      2

      3

      4

      5

      import time

      import requests

      import requests_cache

      requests_cache.install_cache('demo_cache2', allowable_codes=(200,))

      當然我們還可以匹配 URL,比如針對哪種 Pattern 的 URL 緩存多久,則可以這樣寫:

      1

      2

      3

      urls_expire_after = {'*.site_1.com': 30, 'site_2.com/static': -1}

      requests_cache.install_cache(

      'demo_cache2', urls_expire_after=urls_expire_after)

      這樣的話,site_1.com 的內容就會緩存 30 秒,site_2.com/static 的內容就永遠不會過期。

      當然,我們也可以自定義 Filter,具體可以參見:https://requests-cache.readthedocs.io/en/stable/user_guide/filtering.html#custom-cache-filtering。

      Cache Headers

      除了我們自定義緩存,requests-cache 還支持解析 HTTP Request / Response Headers 并根據 Headers 的內容來緩存。

      比如說,我們知道 HTTP 里面有個 Cache-Control 的 Request / Response Header,它可以指定瀏覽器要不要對本次請求進行緩存,那 requests-cache 怎么來支持呢?

      實例如下:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      import time

      import requests

      import requests_cache

      requests_cache.install_cache('demo_cache3')

      start = time.time()

      session = requests.Session()

      for i in range(10):

      session.get('http://httpbin.org/delay/1',

      headers={

      'Cache-Control': 'no-store'

      })

      print(f'Finished {i + 1} requests')

      end = time.time()

      print('Cost time for get', end - start)

      start = time.time()

      這里我們在 Request Headers 里面加上了 Cache-Control 為 no-store,這樣的話,即使我們聲明了緩存那也不會生效。

      當然 Response Headers 的解析也是支持的,我們可以這樣開啟:

      1

      requests_cache.install_cache('demo_cache3', cache_control=True)

      如果我們配置了這個參數,那么 expire_after 的配置就會被覆蓋而不會生效。

      更多的用法可以參見:https://requests-cache.readthedocs.io/en/stable/user_guide/headers.html#cache-headers。

      總結

      好了,到現在為止,一些基本配置、過期時間配置、后端配置、過濾器配置等基本常見的用法就介紹到這里啦,更多詳細的用法大家可以參考官方文檔:https://requests-cache.readthedocs.io/en/stable/user_guide.html。

      希望對大家有幫助。

      更多精彩內容,請關注我的公眾號「進擊的 Coder」和「崔慶才丨靜覓」。

      數據庫

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:PDCA循環——快速提升軟件質量的必備工具
      下一篇:大廠高質量代碼的評判標準
      相關文章
      亚洲午夜无码久久久久软件| 中文字幕 亚洲 有码 在线| 亚洲爆乳大丰满无码专区| 亚洲午夜成激人情在线影院| 亚洲毛片在线观看| 亚洲AV永久精品爱情岛论坛| 国产日韩亚洲大尺度高清| 国产精品亚洲视频| 亚洲精品国产精品国自产观看| 亚洲av高清在线观看一区二区| 国产精品亚洲а∨无码播放麻豆 | 亚洲av永久无码精品漫画| 亚洲精品国产精品乱码不99| 久久亚洲中文字幕精品一区| 国产黄色一级毛片亚洲黄片大全| 亚洲美女在线国产| 亚洲色偷偷偷鲁综合| 亚洲精品~无码抽插| 久久青青草原亚洲AV无码麻豆| 亚洲AV第一页国产精品| 91情国产l精品国产亚洲区| 337p日本欧洲亚洲大胆艺术| 亚洲欧洲精品一区二区三区| 亚洲av永久无码精品三区在线4| 亚洲综合色7777情网站777| 亚洲中文字幕无码爆乳app| 亚洲国产精品无码久久久秋霞1| 日韩国产精品亚洲а∨天堂免| 午夜亚洲国产精品福利| 亚洲精品网站在线观看不卡无广告| 区三区激情福利综合中文字幕在线一区亚洲视频1 | 亚洲中文字幕无码日韩| 亚洲妇熟XXXX妇色黄| 亚洲无线电影官网| 亚洲国产日韩在线成人蜜芽| 中文字幕乱码亚洲无线三区| 亚洲aⅴ天堂av天堂无码麻豆| 亚洲精品视频免费| 久久精品国产精品亚洲精品 | 国产成人A人亚洲精品无码| 亚洲一区二区在线免费观看|