手动反爬虫,禁止转载: 原博地址 https://blog.csdn.net/lys_828/article/details/121296019(CSDN博主:Be_melting)
知识梳理不易,请尊重劳动成果,文章仅发布在CSDN网站上,在其他网站看到该博文均属于未经作者授权的恶意爬取信息
经过上面一系列的操作,现在在我们需要进行数据分析中非常重要的一个环节,即实体提取。
- 在进行深入数据分析前,我们需要把原子数据记录按照属性进行分组和聚合。
- 原子数据记录就是最小层面的数据,所有高级报表都是基于原子数据进行分析和加工汇聚的产物。
- 数据集中包含的实体有:飞机、航班和航空公司
7.1 获取飞机具体航班数量并保存到数据库
接下来主要对飞机进行实体提取,获取机尾编号并将数据存到数据库中。新建一个example02.py文件,然后前置准备环境不变,代码如下,获取部分字段。
from SparkReady import start_spark
import time
spark = start_spark('C04S02',12,'18g')
on_time_dataframe = spark.read.parquet('../data/on_time.parquet')
on_time_dataframe.registerTempTable("on_time_performance")
flights = spark.sql("""
select Carrier,FlightDate,FlightNum,Origin,Dest,TailNum from on_time_performance
""")
进行数据展示时,可以直接进行flights.show()或者使将数据转化为rdd,然后进行take()获取指定的数据,两种方式的代码如下。
flights.show(1)
flights2 = on_time_dataframe.rdd.map(
lambda x:
{
'Carrier': x.Carrier,
'FlightDate': x.FlightDate,
'FlightNum': x.FlightNum,
'Origin': x.Origin,
'Dest': x.Dest,
'TailNum': x.TailNum
}
)
print(flights2.first())
通过rdd转化后的数据类型可以进一步进行分组操作,比如可以统计每个飞机的航班数量,代码设计如下。
flights_per_airplane = flights2 \
.map(lambda x :(x['TailNum'],[x])) \
.reduceByKey(lambda a,b : a+b) \
.map(lambda x : { 'TailNum' : x[0],
'Flights' : sorted(x[1],key=lambda x : (x['FlightNum'],x['FlightDate'],x['Origin'] , x['Dest']))})
print(flights_per_airplane.first())
程序运行结束后,输出内容如下。
通过观察可以发现Flights键对应的值是列表套字典,使用SQL数据库是没有办法进行处理,此时就体现出NoSQL的优势了,直接可以存储此类型数据,保存的代码如下。
import pymongo_spark
pymongo_spark.activate()
flights_per_airplane.saveToMongoDB('mongodb://localhost:27017/example.flights_per_airplane')
程序运行完毕后,刷新Compass页面,后出现flights_per_airplane表,输出如下。
7.2 利用Flask进行数据展示
将刚刚处理过后的数据进行展示,在Compass软件上,当我们进行单条记录的查看时候会很卡顿,想要查看到里面具体的信息时候很不方便,因此可以进一步将数据表现在网页上面,创建7.0版本的on_time07.py文件。在最后添加如下内容。
@app.route("/airplane/flights/<tail_number>")
def flights_per_airplane(tail_number):
flights = client.example.flights_per_airplane.find_one({'TailNum' : tail_number})
return render_template('flights_per_airplane.html',flights=flights,tail_number=tail_number)
由于指定的是flights_per_airplane.html渲染文件,所以需要设置里面的内容,具体的代码如下。
{% extends "index.html" %}
{% block body2 %}
<div>
<p class="lead">机尾编号为 {{tail_number}} 的航班情况</p>
<table class="table table-condensed table-striped">
<thead>
<th>航空公司</th>
<th>飞行时间</th>
<th>航班编号</th>
<th>出发地</th>
<th>目的地</th>
</thead>
<tbody>
{% for flight in flights['Flights'] %}
<tr>
<td>{{flight['Carrier']}}</td>
<td>{{flight['FlightDate']}}</td>
<td><a href="/on_time?Carrier={{flight['Carrier']}}&FlightDate={{flight['FlightDate']}}&FlightNum={{flight['FlightNum']}}">{{flight['FlightNum']}}</a></td>
<td>{{flight['Origin']}}</td>
<td>{{flight['Dest']}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
两个文件的代码进行修改,保存后运行,然后再浏览器的网址栏输入:http://127.0.0.1:5000/airplane/flights/N35236,输出的结果如下。
其中航班编号一列是蓝色的数值,属于添加了超链接,可以鼠标点击,会跳转到具体该航班的信息页面,比如点击第一行航班编号为1034的数据,输出结果如下。
7.3 数据查找与索引添加
如果直接在Compass软件中进行数据的查找,要查询到一条数据就需要把当前表中的数据全部遍历一遍。
考虑到查询的速度,可以添加要查询字段的索引,比如这里查询的是TailNum,就可以设置一下这个字段的索引,然后再次进行查询,搜索结果如下。(查询的速度会大幅度提升)
7.4 利用爬虫获取补充数据
通过飞机的机尾编号可以查询飞机的详细信息,可以借助美国官方网站Aircraft Registration (faa.gov)进行查询。
具体输入的地方是在页面的中间,如下。
比如输入N35236然后回车,就会跳转到对应的飞机详细页面。
通过以上的查询就可以获得一个飞机的详细数据,从而补充数据库。具体实现的思路就先从数据库中获取所有飞机的唯一机尾编号,然后单独保存,接着利用爬虫程序,以此读取文件中的数据,输入到网站中的对应框中,点击Go按钮,跳转到详情页面,从而实现数据的爬取。
(1)第一步,获取所有航班的机尾编号。创建一个新的文件为example03.py,可以连接刚刚保存到Mongo DB中的数据,也可以直接读取原数据,这里采用获取原数据的方式,因为比较简单快捷,具体的代码操作如下。
from util.SparkReady import start_spark
import time
spark = start_spark('C04S03',14,'12g')
on_time_dataframe = spark.read.parquet('../data/on_time.parquet')
on_time_dataframe.registerTempTable("on_time_dataframe")
total_flights_by_month = spark.sql ("""
select count(distinct(TailNum)) from on_time_dataframe
""")
total_flights_by_month.show(5)
输出结果如下。(注意由于是计算位置唯一值,SQL语句进行count会自动过滤掉空值)
如果不采用SparkSQL进行数据的操作,而是采用rdd的方式处理,就需要额外的注意空值和空字符串的数据,代码操作及输出结果如下。
(2)将数据保存到文件。选择保存的是json文件,需要将数据转化为DF数据类型,然后由于是Spark会自动根据使用的处理器数量进行分区,因此为了方便获取文件中的数据,这里指定重新分区合并所有数据到一个文件中,然后以覆盖写的方式进行数据的写入。
records = unique_tail_numbers.map(lambda x : {'TailNum' : x}).toDF()
records.repartition(1).write.mode("overwrite").json("../data/tail_numbers.json")
整个example03.py文件运行后,会在data文件夹下生成目标数据,打开文件后,内容如下。
(3)爬虫获取相应的数据。这里可以使用商业的爬虫软件,也可以自己通过selenium进行程序设计爬取数据,最终可以获取到对应的飞机信息,数据文件结果如下。
(4)将爬虫数据于原数据进行组合。创建一个新的文件为example04.py,然后进行数据的合并操作,具体代码如下。
from SparkReady import start_spark
spark = start_spark('C04S04',12,'12g')
faa_tail_number_inquiry = spark.read.json('../data/faa_tail_number_inquiry.jsonl')
faa_tail_number_inquiry.show()
print(faa_tail_number_inquiry.count())
输出的结果为:3988(由于是爬虫获取的数据,可能网站上确实没有对应结尾编号的信息,也可能程序爬取的过程中出现了问题,所以最终的数据量肯定是低于原表的数据量)
然后再读取原表中的数据,进行左连接(以原表为依据),代码如下。
print ("***"*80)
unique_tail_numbers = spark.read.json('../data/tail_numbers.jsonl')
tail_num_plus_inquiry = unique_tail_numbers.join(
faa_tail_number_inquiry,faa_tail_number_inquiry.TailNum == unique_tail_numbers.TailNum,
'left_outer'
)
tail_num_plus_inquiry.show(5)
print (tail_num_plus_inquiry.count())
输出的结果如下。(数据的有效匹配率:3988/4898=81.42%)