Scrapy-redis分布式爬蟲+Docker快速部署
打算爬一個網(wǎng)站的數(shù)據(jù),量比較大,url
鏈接從0開始達到2億,剛開始用request
遞歸寫了個爬蟲,發(fā)現(xiàn)速度低的可憐,不算任何的錯誤,也只能達到.5秒一個請求,這速度實在不能忍,所以想著用分布式爬蟲,所以才有了這篇文章
開發(fā)環(huán)境+框架开呐、庫
開發(fā)環(huán)境:macOS High Sierra 10.13
Python3.5
開發(fā)工具:PyCharm
Python庫:pymysql豁跑、scrapy历极、scrapy-redis悄窃、requests、BeautifulSoup4花鹅、redis-py
運行環(huán)境:Centos7.4
Centos6.9
Docker
開始搭建環(huán)境
安裝Python3
Windows請自行查找教程安裝、Mac用戶系統(tǒng)自帶了Py2.6
,我建議升級到Python3
,用HomeBrew
安裝即可
Homebrew安裝命令
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
然后使用brew install python
安裝Python3
安裝成功后再終端輸入python3
查看是否安裝成功,如果輸入python
默認使用本機的py2
,當然也可以去配置文件設(shè)置個alias
將python
設(shè)置成啟動py3
如圖安裝成功
然后用
easy_install pip
安裝Python
的包管理工具pip
注意:有的人用的是Anaconda
作為py
環(huán)境,我剛開始也是用的Anaconda
,但是在install scrapy-redis
的時候發(fā)現(xiàn)無法導(dǎo)入,默認源都沒有這個庫,但我沒找到哪個源有,所以強制切換了本機環(huán)境為brew
的python3
,如果你能用conda install scrapy-redis
那就不用切換
切換環(huán)境:
在終端輸入which python3
將下面的路徑復(fù)制出來
打開
Finder
按Command + Shift + G
輸入~/
找到.bash_profile
文件如果沒有這個文件的,先看一下是否開啟了顯示隱藏文件功能,沒開始可以使用
Command + Shift + .
開啟,如果你的系統(tǒng)不是macOS Sierra
及之后的系統(tǒng),那么需要使用命令defaults write com.apple.finder AppleShowAllFiles -bool true
來開啟隱藏文件功能(需要強制重啟Finder
生效)
打開.bash_profile
文件
將
Anaconda
的環(huán)境變量注釋掉,將brew
安裝的寫入進去,路徑就是上面which python3
獲得的,最后bin
后面換成${PATH}
即可,然后輸入source ~/.bash_profile
使之立即生效,然后終端查看一下,是否已經(jīng)切換成功(剛開始路徑是anaconda
)開始安裝第三方庫
pip install requests scrapy scrapy-redis redis BeautifulSoup4
至此環(huán)境方面的東西已經(jīng)處理完
簡單原理介紹
這是在網(wǎng)上找了一張圖,很好的解釋了
scrapy-redis
原理,我簡單的描述下主要原理使用
redis
來維護一個url queue
,然后scrapy
爬蟲都連接這一個redis
獲取url
,且當爬蟲在redis
處拿走了一個url
后,redis
會將這個url
從queue
中清除,保證不會被2個爬蟲拿到同一個url
,即使可能2個爬蟲同時請求拿到同一個url
,在返回結(jié)果的時候redis
還會再做一次去重處理,所以這樣就能達到分布式效果,我們拿一臺主機做redis queue
,然后在其他主機上運行爬蟲.且scrapy-redis
會一直保持與redis
的連接,所以即使當redis queue
中沒有了url
,爬蟲會定時刷新請求,一旦當queue
中有新的url
后,爬蟲就立即開始繼續(xù)爬.
創(chuàng)建工程
打開終端隨便找個空目錄運行scrapy startproject projectname
來初始化一個scrapy
項目
目錄結(jié)構(gòu)如下
用Pycharm
打開(其他工具也可)
工程需要修改地方主要在于Spider
和settings
,其他的都和正常Scrapy
項目一樣
首先是Spider
,我們新建的一般都是繼承與scrapy.Spider
,要使用Scrapy-redis
需要繼承RedisCrawlSpider
,引入from scrapy_redis.spiders import RedisCrawlSpider
,且不再需要start_urls
,用redis_key
來取代
修改setting.py
,添加以下選項
如果需要將
item
結(jié)果返回到redis
需要使用scrapy_redis
提供的pipelines
,如下其余都按
Scrapy
正常項目編寫
主服務(wù)器環(huán)境搭建
主要就安裝redis
開啟外網(wǎng)訪問
我這里環(huán)境是Centos 6.9
1点骑、直接使用yum
安裝
yum install redis
2、啟動redis
systemctl start redis.service
3谍夭、設(shè)置開機啟動(非必要)
systemctl enable redis.service
4黑滴、設(shè)置redis
密碼(非必要)
打開文件/etc/redis.conf
,找到其中的# requirepass foobared
紧索,去掉前面的#袁辈,并把foobared
改成你的密碼
如果是本地安裝了redis
想測試的話,Mac上啟動redis
是使用redis-server
命令
然后新開個終端使用
redis-cli
連接服務(wù)器上也是一樣的使用
redis-cli
連接
注意:服務(wù)器上搭建的redis
外網(wǎng)應(yīng)該是無法訪問,需要修改配置文件,如果是Mac運行的話,配置文件路徑在啟動redis的時候會寫,上圖中有,如果是Centos
安裝的redis
,默認是在/etc/redis/redis.conf
下,如果沒有就在redis
的安裝目錄下
將下圖標記處的bind 127.0.0.1
加上#
,然后重啟一下redis
外網(wǎng)即可鏈接
測試連接
子服務(wù)器跑爬蟲
用其他服務(wù)器運行爬蟲(也可以用運行redis
的服務(wù)器)
通過ftp、scp
等方式將爬蟲上傳至服務(wù)器上
然后在子服務(wù)器上也安裝python3
和pip
命令依次是
yum install python34
yum install python34-setuptools
easy_install-3.4 pip
yum install -y python34-pip.noarch python34-devel.x86_64
最后一條不安裝的話用pip
會出現(xiàn)報錯
然后安裝一下第三方庫
pip install requests scrapy scrapy-redis redis BeautifulSoup4
最后運行爬蟲
scrapy runspider vmSpider.py
最后的參數(shù)是你的Spider
名字
如下圖,爬蟲啟動成功,等待url
待爬
測試
連接主服務(wù)器,往redis
中push一條url
返回子服務(wù)器可以看到,爬蟲已經(jīng)將我剛剛添加的那條
url
給請求了且根據(jù)我的提取規(guī)則把我要的數(shù)據(jù)也提取了出來并且也幫我把數(shù)據(jù)返回到了
redis
里而
redis
里的urls就又是空的了為了對比一下珠漂,我把爬蟲關(guān)掉晚缩,又添加了一條
url
可以看到現(xiàn)在
url
就在redis
里了綜上,我們的效果已經(jīng)達到了,可以在多臺服務(wù)器上跑爬蟲了,然后在主服務(wù)器上的
redis
里會把爬回的數(shù)據(jù)匯總
處理返回的數(shù)據(jù)
scapy-redis
把數(shù)據(jù)全部返回到了redis
里,我們不可能等它留在那,得想辦法提取出去,我這里是提取到MySQL
中,這個我的思路就是,直接在主服務(wù)器上跑個腳本去redis
里提取就行了
直接上代碼
import redis
import json
import pymysql
import logging
import time
# mysql
connection = pymysql.connect('127.0.0.1', 'vdata', 'root', 'vdata', 3306)
cursor = connection.cursor()
# redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379, decode_responses=True)
r = redis.Redis(connection_pool=pool)
r.pipeline()
# log
logging.basicConfig(level=logging.ERROR,
format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S',
filename='myapp.log',
filemode='a')
while True:
print('循環(huán)中')
k,v = r.blpop(['vm:items'])
result = json.loads(v)
title = result['title']
videoid = result['videoid']
author = result['author']
fav = result['favorite']
sql = 'insert into vindex(title, videoid, author,favorite)(select "%s","%s","%s","%s" from dual where not exists(select * from vindex where videoid="%s"))' % (
title, videoid, author, fav, videoid)
try:
n = cursor.execute(sql)
connection.commit()
if n == 0:
print('數(shù)據(jù)已存在...')
except Exception as err:
connection.rollback()
print('插入數(shù)據(jù)庫錯誤,數(shù)據(jù)庫回滾,錯誤信息:' + str(err))
logging.error(str(title) + '------insert Error:' + str(err))
代碼中關(guān)鍵部分是k,v = r.blpop(['vm:items'])
,這句會去redis里獲取items list中第一行的值,獲取到了就將redis
里的這個值刪除,然后我們存到數(shù)據(jù)庫里,當然我在數(shù)據(jù)庫sql
里也做了下重復(fù)數(shù)據(jù)不允許插入,然后我們在服務(wù)器上跑這個腳本就行了
流程就成了爬蟲從redis
獲取url
,然后返回請求的數(shù)據(jù)到redis
,腳本從redis
獲取數(shù)據(jù)到mysql
存儲.挺完美的,缺點也有,目前沒有做錯誤處理,如果url
拿走了沒請求成功就不知道了,下次添加個中間件,把失敗的url
也返回到redis
,然后根據(jù)狀態(tài)碼重新添加回queue
讓爬蟲繼續(xù)爬.
Docker
雖然看上去挺不錯了,但是發(fā)現(xiàn)個問題媳危,我到一臺服務(wù)器上部署的時候荞彼,就得走一遍安裝python``pip
第三方庫這樣的流程,雖然也不麻煩,但是感覺還是不夠省事,然后就發(fā)現(xiàn)了Docker
注意:Docker
要求Centos
內(nèi)核版本要高于3.1,而Centos6.9
默認是2.6,查看內(nèi)核命令uname -r
如果不是3.1的內(nèi)核裝不上
Docker
,辦法可以升級內(nèi)核或者升級Centos7
,如果只是升級內(nèi)核的話,Docker
是無法安裝到最新版本的,但我由于服務(wù)器里數(shù)據(jù)較多,就只選擇升級了內(nèi)核1、安裝elrepo yum 源(提供內(nèi)核更新待笑、硬件驅(qū)動等軟件源支持)
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://www.elrepo.org/elrepo-release-6-8.el6.elrepo.noarch.rpm
2卿泽、內(nèi)核升級
yum --enablerepo=elrepo-kernel -y install kernel-lt
3、修改引導(dǎo)文件
vi /etc/grub.conf
將
default設(shè)置成0``default=0
表示使用第一個內(nèi)核啟動4滋觉、重啟服務(wù)器
reboot
5签夭、查看內(nèi)核
uname -r
看看是否已經(jīng)是3.1以上了
安裝Docker
#安裝 Docker
yum -y install docker
#啟動 Docker 后臺服務(wù)
service docker start
#測試運行 hello-world
docker run hello-world
然后我們來讓Docker幫我們自動化,需要使用到Docker Compose
做編排服務(wù)
首先安裝一下Docker Compose
,這里可以用pip
安裝,但使用pip
安裝就違背我們的初衷了,還得先裝python
,所以我們用官方推薦的方式curl
sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
$ docker-compose --version
docker-compose version 1.22.0, build 1719ceb
先在我們的項目目錄下建立3個文件docker-compose.yml``Dockerfile``requirements.txt
第一個文件docker-compose.yml
version: '3'
services:
spider:
build: .
volumes:
- .:/code
- 代表使用V3版本的寫法
- services(v1版本沒services)
- 建立一個名為spider的services
- 用當前目錄下的
Dockerfile
構(gòu)建服務(wù) - 裝載路徑,將當前目錄下的文件裝載到容器的/code目錄下(會自動建立文件夾)
第二個文件Dockerfile
#Python3作為基礎(chǔ)映像
FROM python:3.5
#設(shè)置環(huán)境變量,使用which Python可以查看
ENV PATH=/usr/local/bin:$PATH
#添加當前目錄到容器里
ADD . /code
#設(shè)置容器工作路徑
WORKDIR /code
#安裝Python插件
RUN pip install -r requirements.txt
# #設(shè)置容器工作目錄
WORKDIR /code/vmoredis/vmoredis/spiders
# #運行命令
CMD /usr/local/bin/scrapy runspider vmSpider.py
這里要注意,先設(shè)置工作路徑為/code
是為了能運行pip
,運行完了再將路徑設(shè)置到Spider
下,才能訪問到Spider
文件,不然就提示找不到對應(yīng)的py
文件
第三個文件requirements.txt
BeautifulSoup4
scrapy
scrapy_redis
redis
pymysql
requests
就是我們需要安裝的python庫
上傳服務(wù)器測試
上傳文件到服務(wù)器,目錄如下
cd
到根目錄下,運行docker-compose up
注意:這里有個問題,就是我之前測試的時候運行了
docker-compose up
,后來我換了正式的文件,發(fā)現(xiàn)還是之前的報錯這個
test.py
是我測試時運行的,現(xiàn)在居然還是這個錯誤,我以為是緩存,在網(wǎng)上搜索了,說要加參數(shù)--no-recreate
啥的,最后去看了文檔才知道,改變了Dockerfile
是需要用build
命令重構(gòu)才行,意思先docker-compose build
再docker-compose up
就可以了如圖啟動成功,只需要安裝docker,然后這些依賴啥的自動就好了,省事,雖然要安裝
docker
,但是docker
可以開多個服務(wù)例如docker-compose scale spider=4
,也可以自己搭建倉庫注冊鏡像,就更省事了