完整代码在这: Here
- 以下有什么说的不对的地方也欢迎各位指出,互相学习.
准备工作
- Zookeeper环境(zookeeper-3.4.10) -> 具体安装下面讲 安装步骤
- Kafka环境(kafka-1.0.0) -> 具体安装下面讲 安装步骤
- 系统环境(Win10 x64)
- Python环境(Python 3.4.4)
环境依赖
- 环境
- Python3.4.4(Python2暂未测试,如果有测试过的给我提下issue)
- 依赖包
- Scrapy
- pykafka
- 安装方式:
windows: pip install requirements.txt
linux: pip3 install requiremnets.txt
复制代码
项目结构
- consumer --- pykafka的消费者模块(测试接收以及之后接收爬虫数据)
- producer --- pykafka的生产者模块(测试发送)
- scrapy_kafka --- Scrapy + pykafka的爬虫(爬的是我学校的官网的所有a标签链接)
需要注意的地方
- 爬虫部分我就不说了,我就挑特别的地方
- kafka需要bytes数据,所以在pipeline接收到数据之后一定要encode;encode里面的encoding和消费者的decode编码要一致.
- pipeline里面实现一个方法 close_spider(self, spider) 用来关闭producer;不然Scrapy会一直挂在producer那里不动.
- 我在pipeline里面判断了KAFKA_IP_PORT这个配置写的参数:
- 单机部署可以用list或者str表示.
- 伪分布或者全分布可以用list,或者用逗号隔开都可以.
Zookeeper安装
- 我大致说下Zookeeper的安装过程(以下都是单点测试, 伪分布和全分布还请各位完整学习完后再搭建)
- 下载zookeeper.下载地址
- 解压在conf下把zoo_sample.cfg 复制(或直接重命名)为zoo.cfg
- 在系统环境变量中创建ZOOKEEPER_HOME值为zookeeper的Home目录
- 在PATH中添加zookeeper的bin目录.
- 在cmd中运行 zkServer
Kafka安装
- 也一样是单点
- 下载kafka并解压.下载地址
- 进入解压后的文件夹, 在conf下修改server.properties文件中log.dirs指定log目录
- 配置系统环境变量KAFKA_HOME
- 在PATH中添加kafka bin/windows的目录(linux的不用理windows那个文件夹)
- 启动kafka: kafka-server-start <kafka目录下config里面的server.properties的路径>
- 关于kafka的测试
- 创建topic:
kafka-topics --create --topic newtest --partitions 1 --replication-factor 1 --zookeeper localhost:2181
复制代码
- 意思是:创建一个topic; 名字:newtest; 分区:1个; 副本数:1个; zookeeper的监听地址(由于是单点且若未修改zoo.cfg, 则zookeeper默认在2181端口上运行)
- 创建producer:
kafka-console-producer --broker-list localhost:9092 --topic newtest
复制代码
- 此时窗口会到达等待输入的状态,先别关闭也不着急输入。启动consumer先。
- 创建consumer
kafka-console-consumer --zookeeper localhost:2181 --topic newtest
复制代码
- 当consumer创建成功后,回到producer的窗口输入一些字符什么的,看看consumer有没有出现(注:中文有可能是乱码,但不影响测试)
- 其他操作就请各位去参见kafka的官方文档或者系统学习后看看命令行的使用。
核心代码
-
代码部分最好看仓库: 代码仓库地址
-
以下是pipeline的代码, 主要也就是这部分和Kafka进行数据交互
# -*- coding: utf-8 -*-
# Scrapy
from scrapy.conf import settings
# PyKafka
from pykafka import KafkaClient
class ScrapyKafkaPipeline(object):
def __init__(self):
# 判断下配置里面个给的是啥
# 1. 如果长度等于1, list只有一个数据, 如果是字符肯定大于1
# 2. 否则, 判断类型是否是list, 是的话用 逗号分隔
# 3. 否则就是一个字符串
kafka_ip_port = settings['KAFKA_IP_PORT']
if len(kafka_ip_port) == 1:
kafka_ip_port = kafka_ip_port[0]
else:
if isinstance(kafka_ip_port, list):
kafka_ip_port = ",".join(kafka_ip_port)
else:
kafka_ip_port = kafka_ip_port
# 初始化client
self._client = KafkaClient(hosts=kafka_ip_port)
# 初始化Producer 需要把topic name变成字节的形式
self._producer = \
self._client.topics[
settings['KAFKA_TOPIC_NAME'].encode(encoding="UTF-8")
].get_producer()
def process_item(self, item, spider):
"""
写数据到Kafka
:param item:
:param spider:
:return:
"""
if spider.name == "KafkaScrapy":
self._producer.produce(item['url'].encode(encoding="UTF-8"))
return item
def close_spider(self, spider):
"""
结束之后关闭Kafka
:return:
"""
if spider.name == "KafkaScrapy":
self._producer.stop()
复制代码
- demo里的消费者的代码核心功能, 主要就是接收信息输出到控制台