[TOC]
## 1. 分布式爬蟲
使用第三方庫scrapy-redis,GitHub地址:https://github.com/rmax/scrapy-redis
1. Scrapy多個spider不能共享待爬取隊列Scrapy queue, 即Scrapy本身不支持爬蟲分布式。
### 1.1 架構

結構變化是在請求隊列方面,由redis統一管理請求隊列,協調多個spider服務。redis提供數據存儲(爬取到的數據和待爬取請求URL)、指紋去重、
分布式爬蟲與scrapy框架的改變主要是下邊:
1. 有請求過來了,有spider引擎交個Scheduler,Scheduler將請求交給redis進行驗證(指紋隊列),如果該URL沒有爬取,則redis將URL放回給Scheduler
2. redis與爬蟲相關的三個隊列
"yaoq:dupefilter" :去重指紋隊列
"yaoq:items" : 存儲爬取到的數據隊列
"yaoq:requests" : 待爬取
## 2. 實踐
* scrapy單機爬蟲時,我們主要自己編寫兩個spider類:CrawlSpider和Spider,在分布式中對應RedisCrawlSpider和RedisSpider
### 2.1 spider
1. 繼承RedisCrawlSpider
2. 沒有start_urls(在redis中給定,多個爬蟲端去隨機獲取)
~~~
pip3 install scrapy-redis
~~~
~~~
__author__ = 'dailin'
from scrapy_redis.spiders import RedisCrawlSpider
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapyredis.items import Yaoq
class YaoQ(RedisCrawlSpider):
name = 'yaoq'
allowed_domains = ['yaoq.net']
rules = (
# 只提取復合規則的頁面鏈接,不做分析,所以跟頁面但是沒有,follow是對網易深一層的爬取,false表示不提取連接,也不請求頁面上的連接
Rule(LinkExtractor(allow=r'www.yaoq.net/thread.*\.html'), callback='parse_item', follow=False),
Rule(LinkExtractor(allow=r'www.yaoq.net/forum-95-\d+\.html'), follow=True)
)
def parse_item(self, response):
try:
item = Yaoq()
# print(response.text)
author = response.xpath("//div[@class='pti']//div[@class='authi']/a[1]/text()").extract()[0]
authorLocation = response.xpath("//div[@class='pti']//div[@class='authi']/a[1]/@href").extract()[0]
pubDate = response.xpath("//div[@class='pti']//div[@class='authi']//em[1]/text()").extract()[0]
# 提取所有文本
content = \
response.xpath("//div[@class='pcb']//div[@class='t_fsz']/table[1]//tr")[0].xpath('string(.)').extract()[0]
contentData = content.replace("\r\n", "")
title = response.xpath("//span[@id='thread_subject']/text()").extract()[0]
print(author)
print(authorLocation)
print(pubDate)
print(contentData)
print(title)
item['title'] = title
item['pubDate'] = pubDate
item['author'] = author
item['authorLocation'] = authorLocation
item['content'] = contentData
item['id'] = str(uuid.uuid1())
yield item
except BaseException as e:
print(e)
~~~
### 2.2 settings
~~~
# -*- coding: utf-8 -*-
# 指定使用scrapy-redis的調度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 指定使用scrapy-redis的去重
DUPEFILTER_CLASS = 'scrapy_redis.dupefilters.RFPDupeFilter'
# 指定排序爬取地址時使用的隊列,
# 默認的 按優先級排序(Scrapy默認),由sorted set實現的一種非FIFO、LIFO方式。
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderPriorityQueue'
# 可選的 按先進先出排序(FIFO)
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderQueue'
# 可選的 按后進先出排序(LIFO)
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderStack'
# 在redis中保持scrapy-redis用到的各個隊列,從而允許暫停和暫停后恢復,也就是不清理redis queues、
# 中斷后可以繼續爬取
SCHEDULER_PERSIST = True
# 只在使用SpiderQueue或者SpiderStack是有效的參數,指定爬蟲關閉的最大間隔時間
# SCHEDULER_IDLE_BEFORE_CLOSE = 10
# 通過配置RedisPipeline將item寫入key為 spider.name : items 的redis的list中,供后面的分布式處理item
# 這個已經由 scrapy-redis 實現,不需要我們寫代碼
ITEM_PIPELINES = {
'example.pipelines.ExamplePipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400
}
# 指定redis數據庫的連接參數
# REDIS_PASS是我自己加上的redis連接密碼(默認不做)
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
#REDIS_PASS = 'redisP@ssw0rd'
# LOG等級
LOG_LEVEL = 'DEBUG'
#默認情況下,RFPDupeFilter只記錄第一個重復請求。將DUPEFILTER_DEBUG設置為True會記錄所有重復的請求。
DUPEFILTER_DEBUG =True
~~~
### 2.3 運行爬蟲
#### 2.3.1 運行爬蟲
~~~
scrapy runspider scrapyredis/spiders/YaoQ.py
~~~
> 此時爬蟲處于等待狀態(因為沒有給定start_urls)
#### 2.3.2 在redis中給定start_urls
1. 此時redis中,沒有任何key

2. 給定起始url(就是想隊列中放入URL,讓爬蟲去爬)
>1. 這個起始的URL在redis中對應的鍵名默認是`spidername:start_urls`的組合,例如spider名稱為
> yaoqyaoq,對應的鍵名為yaoq:start_urls。
> 2. 也可以在程序中顯示的指定(start_url名稱任意,但是按照規范來較好)
~~~
lpush yaoq:start_urls 'http://www.yaoq.net/forum-95-1.html'
~~~
> 此時爬蟲開始,其他爬取到的URL也會存儲到redis中

> start_urls被取走,這個隊列消失,此時redis中有三個和該spider相關的隊列

> 1) yaoq:dupefilter:去重隊列
> 2) yaoq:items: 數據
> 3) yaoq:requests :待爬取隊列
#### 2.3.3 處理爬取到的數據
從redis讀取爬取的數據,爬取到的數據默認鍵是 spidername:items ,例如yaoq:items
把數據存儲到mysql(一直阻塞地消費yaoq:items隊列)
~~~
# coding=utf-8
__author__ = 'dailin'
import json
import redis
import pymysql
def main():
# 指定redis數據庫信息
rediscli = redis.StrictRedis(host='192.168.56.130', port=6379, db=0)
# 指定mysql數據庫
mysqlcli = pymysql.connect(host='192.168.56.130', user='root', passwd='tuna',
db='crawl_data', port=3306, charset="utf8", use_unicode=True)
while True:
# FIFO模式為 blpop,LIFO模式為 brpop,獲取鍵值
source, data = rediscli.blpop(["yaoq:items"])
item = json.loads(data)
try:
# 使用cursor()方法獲取操作游標
cur = mysqlcli.cursor()
sql = "INSERT INTO yaoq (author, author_location, content, id, pub_date, title) VALUES (%s, %s, %s, %s, %s, %s)"
# 使用execute方法執行SQL INSERT語句
content = item['content']
print(content)
cur.execute(sql,(item['author'], item['authorLocation'], item['content'] ,item['id'], item['pubDate'], item['title']))
# 提交sql事務
mysqlcli.commit()
# 關閉本次操作
cur.close()
print("inserted %s" % item['title'])
except Exception as e:
print(e)
if __name__ == '__main__':
main()
~~~
- Docker
- 什么是docker
- Docker安裝、組件啟動
- docker網絡
- docker命令
- docker swarm
- dockerfile
- mesos
- 運維
- Linux
- Linux基礎
- Linux常用命令_1
- Linux常用命令_2
- ip命令
- 什么是Linux
- SELinux
- Linux GCC編譯警告:Clock skew detected. 錯誤解決辦法
- 文件描述符
- find
- 資源統計
- LVM
- Linux相關配置
- 服務自啟動
- 服務器安全
- 字符集
- shell腳本
- shell命令
- 實用腳本
- shell 數組
- 循環與判斷
- 系統級別進程開啟和停止
- 函數
- java調用shell腳本
- 發送郵件
- Linux網絡配置
- Ubuntu
- Ubuntu發送郵件
- 更換apt-get源
- centos
- 防火墻
- 虛擬機下配置網絡
- yum重新安裝
- 安裝mysql5.7
- 配置本地yum源
- 安裝telnet
- 忘記root密碼
- rsync+ crontab
- Zabbix
- Zabbix監控
- Zabbix安裝
- 自動報警
- 自動發現主機
- 監控MySQL
- 安裝PHP常見錯誤
- 基于nginx安裝zabbix
- 監控Tomcat
- 監控redis
- web監控
- 監控進程和端口號
- zabbix自定義監控
- 觸發器函數
- zabbix監控mysql主從同步狀態
- Jenkins
- 安裝Jenkins
- jenkins+svn+maven
- jenkins執行shell腳本
- 參數化構建
- maven區分環境打包
- jenkins使用注意事項
- nginx
- nginx認證功能
- ubuntu下編譯安裝Nginx
- 編譯安裝
- Nginx搭建本地yum源
- 文件共享
- Haproxy
- 初識Haproxy
- haproxy安裝
- haproxy配置
- virtualbox
- virtualbox 復制新的虛擬機
- ubuntu下vitrualbox安裝redhat
- centos配置雙網卡
- 配置存儲
- Windows
- Windows安裝curl
- VMware vSphere
- 磁盤管理
- 增加磁盤
- gitlab
- 安裝
- tomcat
- Squid
- bigdata
- FastDFS
- FastFDS基礎
- FastFDS安裝及簡單實用
- api介紹
- 數據存儲
- FastDFS防盜鏈
- python腳本
- ELK
- logstash
- 安裝使用
- kibana
- 安準配置
- elasticsearch
- elasticsearch基礎_1
- elasticsearch基礎_2
- 安裝
- 操作
- java api
- 中文分詞器
- term vector
- 并發控制
- 對text字段排序
- 倒排和正排索引
- 自定義分詞器
- 自定義dynamic策略
- 進階練習
- 共享鎖和排它鎖
- nested object
- 父子關系模型
- 高亮
- 搜索提示
- Redis
- redis部署
- redis基礎
- redis運維
- redis-cluster的使用
- redis哨兵
- redis腳本備份還原
- rabbitMQ
- rabbitMQ安裝使用
- rpc
- RocketMQ
- 架構概念
- 安裝
- 實例
- 好文引用
- 知乎
- ACK
- postgresql
- 存儲過程
- 編程語言
- 計算機網絡
- 基礎_01
- tcp/ip
- http轉https
- Let's Encrypt免費ssl證書(基于haproxy負載)
- what's the http?
- 網關
- 網絡IO
- http
- 無狀態網絡協議
- Python
- python基礎
- 基礎數據類型
- String
- List
- 遍歷
- Python基礎_01
- python基礎_02
- python基礎03
- python基礎_04
- python基礎_05
- 函數
- 網絡編程
- 系統編程
- 類
- Python正則表達式
- pymysql
- java調用python腳本
- python操作fastdfs
- 模塊導入和sys.path
- 編碼
- 安裝pip
- python進階
- python之setup.py構建工具
- 模塊動態導入
- 內置函數
- 內置變量
- path
- python模塊
- 內置模塊_01
- 內置模塊_02
- log模塊
- collections
- Twisted
- Twisted基礎
- 異步編程初探與reactor模式
- yield-inlineCallbacks
- 系統編程
- 爬蟲
- urllib
- xpath
- scrapy
- 爬蟲基礎
- 爬蟲種類
- 入門基礎
- Rules
- 反反爬蟲策略
- 模擬登陸
- problem
- 分布式爬蟲
- 快代理整站爬取
- 與es整合
- 爬取APP數據
- 爬蟲部署
- collection for ban of web
- crawlstyle
- API
- 多次請求
- 向調度器發送請求
- 源碼學習
- LinkExtractor源碼分析
- 構建工具-setup.py
- selenium
- 基礎01
- 與scrapy整合
- Django
- Django開發入門
- Django與MySQL
- java
- 設計模式
- 單例模式
- 工廠模式
- java基礎
- java位移
- java反射
- base64
- java內部類
- java高級
- 多線程
- springmvc-restful
- pfx數字證書
- 生成二維碼
- 項目中使用log4j
- 自定義注解
- java發送post請求
- Date時間操作
- spring
- 基礎
- spring事務控制
- springMVC
- 注解
- 參數綁定
- springmvc+spring+mybatis+dubbo
- MVC模型
- SpringBoot
- java配置入門
- SpringBoot基礎入門
- SpringBoot web
- 整合
- SpringBoot注解
- shiro權限控制
- CommandLineRunner
- mybatis
- 靜態資源
- SSM整合
- Aware
- Spring API使用
- Aware接口
- mybatis
- 入門
- mybatis屬性自動映射、掃描
- 問題
- @Param 注解在Mybatis中的使用 以及傳遞參數的三種方式
- mybatis-SQL
- 逆向生成dao、model層代碼
- 反向工程中Example的使用
- 自增id回顯
- SqlSessionDaoSupport
- invalid bound statement(not found)
- 脈絡
- beetl
- beetl是什么
- 與SpringBoot整合
- shiro
- 什么是shiro
- springboot+shrio+mybatis
- 攔截url
- 枚舉
- 圖片操作
- restful
- java項目中日志處理
- JSON
- 文件工具類
- KeyTool生成證書
- 兼容性問題
- 開發規范
- 工具類開發規范
- 壓縮圖片
- 異常處理
- web
- JavaScript
- 基礎語法
- 創建對象
- BOM
- window對象
- DOM
- 閉包
- form提交-文件上傳
- td中內容過長
- 問題1
- js高級
- js文件操作
- 函數_01
- session
- jQuery
- 函數01
- data()
- siblings
- index()與eq()
- select2
- 動態樣式
- bootstrap
- 表單驗證
- 表格
- MUI
- HTML
- iframe
- label標簽
- 規范編程
- layer
- sss
- 微信小程序
- 基礎知識
- 實踐
- 自定義組件
- 修改自定義組件的樣式
- 基礎概念
- appid
- 跳轉
- 小程序發送ajax
- 微信小程序上下拉刷新
- if
- 工具
- idea
- Git
- maven
- svn
- Netty
- 基礎概念
- Handler
- SimpleChannelInboundHandler 與 ChannelInboundHandler
- 網絡編程
- 網絡I/O
- database
- oracle
- 游標
- PLSQL Developer
- mysql
- MySQL基準測試
- mysql備份
- mysql主從不同步
- mysql安裝
- mysql函數大全
- SQL語句
- 修改配置
- 關鍵字
- 主從搭建
- centos下用rpm包安裝mysql
- 常用sql
- information_scheme數據庫
- 值得學的博客
- mysql學習
- 運維
- mysql權限
- 配置信息
- 好文mark
- jsp
- jsp EL表達式
- C
- test