如何导入数据
数据可能有各种格式,虽然常见的是HDFS
,但是因为在Python爬虫中数据库用的比较多的是MongoDB
,所以这里会重点说说如何用spark导入MongoDB
中的数据。
当然,首先你需要在自己电脑上安装spark环境,简单说下,在这里下载spark,同时需要配置好JAVA
,Scala
环境。
这里建议使用Jupyter notebook
,会比较方便,在环境变量中这样设置
PYSPARK_DRIVER_PYTHON=jupyter PYSPARK_DRIVER_PYTHON_OPTS=notebook ./bin/pyspark
如果你的环境中有多个Python版本,同样可以制定你想要使用的解释器,我这里是python36
,根据需求修改。
PYSPARK_PYTHON=/usr/bin/python36
启动命令
进入spark根目录,./bin/pyspark
这是最简单的启动命令,默认会打开Python的交互式解释器,但是由于我们上面有设置过,会打开Jupyter notebook
,接下来变成会方便很多。
先来看看最简单的例子:
>>> textFile = spark.read.text("README.md") >>> textFile.count() # Number of rows in this DataFrame 126 >>> textFile.first() # First row in this DataFrame Row(value=u'# Apache Spark') >>> linesWithSpark = textFile.filter(textFile.value.contains("Spark")) >>> textFile.filter(textFile.value.contains("Spark")).count() # How many lines contain "Spark"? 15
这里有我之前写过的例子,可以照着写一遍 basic_exercise
我们的启动方式是./bin/pyspark
,我们可以家后面加很多参数,比如说如若我们要连接MongoDB,就需要这样
完整的可以参考Spark Connector Python Guide
./bin/pyspark --conf "spark.mongodb.input.uri=mongodb://127.0.0.1/test.myCollection?readPreference=primaryPreferred" \ --conf "spark.mongodb.output.uri=mongodb://127.0.0.1/test.myCollection" \ --packages org.mongodb.spark:mongo-spark-connector_2.11:2.3.0
这里有两个uri
,分别是input
和output
,对应读取的数据库和写入的数据库,最后面的packages
相当于引入的包的名字,我一般喜欢在代码中定义。
读取/保存数据
这里我们可以增加参数option
,在这里设置想要读取的数据库地址,注意格式。
读取数据
df = spark.read.format("com.mongodb.spark.sql.DefaultSource").option("uri", "mongodb://127.0.0.1/people.contacts").load()
保存数据
people.write.format("com.mongodb.spark.sql.DefaultSource").mode("append").option("uri", "mongodb://127.0.0.1/people.contacts").option("database", "people").option("collection", "contacts").save()
简单对比下,option
还可以定义database
和collection
,这样就不需要在启动Spark时定义。
以上是官网推荐的连接方式,这里需要说的是另一种,如果我没有从命令行中启动,而是直接新建一个py文件,该如何操作?
搜索相关资料后,发现是这样
#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = 'zhangslob' import os from pyspark.sql import SparkSession # set PYSPARK_PYTHON to python36 os.environ['PYSPARK_PYTHON'] = '/usr/bin/python36' # load mongo data input_uri = "mongodb://127.0.0.1:spark.spark_test" output_uri = "mongodb://127.0.0.1:spark.spark_test" my_spark = SparkSession\ .builder\ .appName("MyApp")\ .config("spark.mongodb.input.uri", input_uri)\ .config("spark.mongodb.output.uri", output_uri)\ .config('spark.jars.packages','org.mongodb.spark:mongo-spark-connector_2.11:2.2.0')\ .getOrCreate() df = my_spark.read.format('com.mongodb.spark.sql.DefaultSource').load()
必须要增加默认设置('spark.jars.packages','org.mongodb.spark:mongo-spark-connector_2.11:2.2.0')
,否则会报错。
使用Pipeline优化数据
熟悉MongoDB查询的人肯定了解Pipeline, Aggregation Pipeline
比如说,数据库中有10个字段,但是我只想查询_id
字段,那就需要这样查询
db.getCollection('user').find({}, {_id:1})
,结果
/* 1 */ { "_id" : ObjectId("5b8ccf702ba46e51f0755592") } /* 2 */ { "_id" : ObjectId("5b8ccf712ba46e51f0755593") } /* 3 */ { "_id" : ObjectId("5b8ccf712ba46e51f0755594") }
同样的,在处理数据量很大的情况下,可以加上这样的查询条件来保证内存够用,在spark这样使用
pipeline = '[{"$project": {"_id": 1}}, {"$limit": 1}]' df = my_spark.read.format('com.mongodb.spark.sql.DefaultSource').option("pipeline", pipeline).load()
可以看到我这里不仅是只导入了_id
,而且会有限制,使用"$limit"
来限制导入数据的数量,但是这里有BUG,实际导入的数量并不止1跳,有兴趣的可以看看这里 $limit stage produces more documents than expected
但是这样也是有代价的,这里其实调用了mongo的查询方法,而mongo的查询速度本来就很慢,所以加上Pipeline后会导致导入数据慢,本地测试可以用。