基于Hadoop和Python的用户网站浏览分析实践指南
2026/9/9 11:09:37 网站建设 项目流程

前几天有个做运营的朋友问我,能不能帮他分析一下网站的用户浏览行为,看看用户到底对哪些页面感兴趣、一天里什么时候访问最多。聊到最后他补了一句:日志一天好几个G,Excel打不开。这就引出了我刚完成的一个项目——python基于Hadoop的用户网站浏览分析。整个链路从日志采集、HDFS存储、MapReduce清洗统计,到Hive多维分析和Python可视化,流程完整,数据量撑到几G甚至几十G都能扛住。不管是做课程设计、毕业设计,还是公司内部的数据分析起步,都有现成的参考价值。这篇文章我就把设计思路和实现细节拆开讲,踩过的坑也都一并整理出来。

1. 项目需求拆解:用户浏览分析到底在分析什么

1.1 目标是读懂用户行为,不是堆报表

很多初学者一上来就想着“用大数据框架搞个网站分析系统”,结果做出来的东西就是打印几个PV、UV数,本质和SQL count没区别。用户网站浏览分析的核心价值,是把日志里那些看似杂乱的访问记录,还原成一条条真实的用户行为路径:一个人从哪个页面进来、在哪些页面上停留、点了什么链接、最后从哪个页面离开。拆成可落地的分析指标,大致可以分成四类。

第一类是基础流量指标,包括PV(页面浏览量)、UV(独立访客数)、人均访问页数。这些指标是运营最常看的,用来判断整体流量盘子的大小。第二类是热门内容排行,哪些URL被访问得最多、哪些栏目受欢迎,直接指导内容推荐和首页布局。第三类是时段分析,凌晨访问的是哪些页面、工作日上午和晚上有什么差异,这决定广告投放和服务器扩容的时间点。第四类是路径分析,用户从首页跳到详情页再到搜索页,这个顺序能暴露产品设计的漏洞。

第四类路径分析是最难做也最有价值的,但受限于日志格式和隐私规则,通常只能做简化版,比如统计特定页面的前序来源和后继去向。你可以在Hive里把同一用户的访问记录按时间排序,用LAG函数取上一条页面来算跳转来源,这种方法我后面会详细讲。

1.2 为什么选Hadoop加Python,而不是MySQL或Spark

选型之前要先估计数据规模。普通个人网站一天的Nginx日志可能在几百MB到几GB,一个月积累下来就是几十GB到几百GB。这种量级MySQL不是不能处理,但要提前设计分表、索引,做报表查询时还是容易把慢查询打满。Hadoop的HDFS天然适合存储大日志文件,MapReduce和Hive能通过并行计算把扫描几GB文件的效率提起来,这正是选它作为分析底座的原因。

Python在这个体系里的角色很灵活:一是写数据清洗脚本,处理正则解析、字段抽取、异常过滤;二是作为MapReduce的脚本语言,通过Hadoop Streaming提交分布式任务;三是做下游可视化,把Hive和MapReduce的结果读出来画成图表。Python不直接替代Hadoop,而是充当“粘合剂”和“分析终端”,这样项目既体现大数据框架的分布式能力,又保留了Python在数据处理和可视化上的生态优势。

至于为什么不选Spark,核心是项目定位问题。单机日志分析的实时性要求不高,MapReduce足够完成任务,而且Hadoop本身是课程设计和很多企业内部项目的常用基础。如果想在后续扩展实时计算,再在同一个集群上引入Spark Streaming也不迟,初始阶段不必把技术栈堆得太重,能把一个闭环跑通比什么都重要。

2. 整体架构设计与模块划分

2.1 从Nginx日志到图表的完整数据流

这个项目的整体数据流可以概括为五个阶段。阶段一是日志采集,假设Nginx日志文件按天切分,用Flume或直接写Shell脚本定时将日志文件上传到HDFS原始目录。阶段二是数据清洗,用Python脚本解析每行日志,提取IP、访问时间、请求URL、状态码、流量大小、来源页、User-Agent,过滤掉静态资源请求和爬虫流量,输出以Tab分隔的干净文本。阶段三是上传HDFS,清洗后的结果落盘到HDFS的分析目录,为MapReduce或Hive建表做准备。

阶段四是核心计算,这里有两条并行路径:一条是用Hadoop Streaming跑Python写的Mapper和Reducer,完成PV、UV这类需要精确去重的统计;另一条是在Hive里建外部表,用SQL做多维分析,查询用户时段分布、热门URL、来源路径。阶段五是结果展示,把最终统计结果从HDFS或Hive导出成CSV,用Python的Pandas、Matplotlib生成图表,也可以用Flask搭一个简易Web页面,让运营同事自助查看。

这样设计的优势在于每个模块可以独立替换。比如日志源从Nginx换成Apache,只需要改解析脚本;Hive统计太重,可以换成Impala或Spark SQL;可视化不喜欢Matplotlib,可以换成ECharts前端页面。模块解耦对后续扩展非常重要,这也是我第一次搭这个项目时最深的体会。

2.2 Python在架构中的连接作用

从表面看,Python只在清洗和可视化阶段出现,但实际上它把整个链路串起来了。清洗之后,Python脚本可以调用hdfs dfs -put命令上传数据;MapReduce阶段,hadoop-streaming.jar直接执行Python脚本作为Mapper和Reducer;分析阶段,用hive -e "SQL语句"导出结果,Python又可以通过subprocess调用命令行读取输出。我还习惯写一个run_pipeline.py,依次调用清洗、上传、提交MapReduce、执行Hive查询、导出结果、绘图,实现一键运行。

这种“胶水式”设计的代价是增加了一层脚本调度,但对项目来说非常实用。尤其是课程设计或毕业设计,老师在验收时更看重整条链路的完整性和合理性,而不是某一个算法多复杂。用Python把所有阶段串联成一个可复现的流程,比每次手动敲十个命令行要专业得多。

这里再补充一个细节:选择Python版本时最好统一使用Python 3。Hadoop Streaming对Python版本没有硬性限制,但系统自带的Python 2早就停止维护,很多依赖库也不再支持,直接用Python 3写Mapper最稳妥。如果集群上没装Python 3,用Anaconda或pyenv装到当前用户目录即可,不需要全局权限。

3. 环境准备:Hadoop集群与Python环境搭建实操

3.1 Hadoop伪分布式搭建的几个关键坑

我先说结论:做课程设计或小规模测试,没必要一开始就上三台服务器集群,一台机器用伪分布式模式跑通整条链路完全足够。伪分布式和真实集群的区别只在于不同守护进程跑在同一台机器上,对HDFS、MapReduce、Hive的操作方式完全一样。等流程跑通了,再横向扩展成多节点集群,成本低不少。

搭建Hadoop时最常踩的坑有三个。第一个是JDK版本不匹配,Hadoop 3.x要求JDK 8或11,装错JDK版本会出现莫名其妙的UnsupportedClassVersionError。第二个是SSH免密登录没配好,启动脚本需要ssh到localhost,如果不做免密会不停要求输密码,直接导致DataNode起不来。第三个是格式化NameNode时临时目录冲突,如果你改过core-site.xml里的hadoop.tmp.dir,老的临时文件还残留,格式化就会失败。这个坑几乎每个新手都会遇到。

解决格式化失败的标准做法分三步:先停掉所有Hadoop进程,再把hadoop.tmp.dir指向的目录删掉重建,最后重新执行hdfs namenode -format。这样做会清掉HDFS上的所有数据,所以只适用于还没放业务数据的测试环境。实际项目中,格式化前一定要确认是否有需要保留的HDFS文件,必要时先做快照或导出。

伪分布式启动完成后,用jps命令检查进程:如果能看到NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager这五个进程,说明Hadoop本体已经正常。我自己还会额外跑一遍hdfs dfs -ls /做一个冒烟测试,确认客户端能正常连接HDFS。

3.2 Linux系统安装Python和必要依赖库

在Linux服务器上配Python环境,我建议优先用Miniconda或者虚拟环境,直接用系统路径容易污染全局环境。以CentOS为例,安装Miniconda后创建项目环境:

wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh source ~/.bashrc conda create -n web_log python=3.8 -y conda activate web_log

项目里需要安装的Python库不复杂,主要就是Pandas、Matplotlib、NumPy,做网页展示的话再加一个Flask。用pip安装即可:

pip install pandas matplotlib numpy flask

有个细节需要注意,如果你计划用Hadoop Streaming跑Python脚本,脚本里用的解释器路径必须写对。一般会在脚本第一行写#!/usr/bin/env python3,然后给脚本加执行权限:chmod +x mapper.py reducer.py。如果集群上的Python 3不是默认python3命令,你就需要在脚本里写绝对路径,或者在-files参数里把虚拟环境一起打包传上去。这个过程比较容易出问题,在后面的Streaming实战里我会单独说明。

4. 数据采集与预处理:从原始日志到结构化数据

4.1 日志格式解析:先搞懂每一列是什么

我做这个项目时用的是最常见的Nginx默认格式,一条完整日志长这样:

192.168.1.23 - - [10/Oct/2024:13:55:36 +0800] "GET /article/1024 HTTP/1.1" 200 5312 "https://www.example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"

这段日志里包含的信息按顺序是:客户端IP、两个-占位符、访问时间、请求行、状态码、返回字节数、来源页面、User-Agent。写解析脚本前一定要先用head -n 5 access.log看一下自己的日志格式,不要直接照抄网上的正则,因为不同网站的log_format配置千差万别。

我惯用的解析正则如下,你直接保存成parse_log.py

import re import sys log_pattern = re.compile( r'(?P<ip>\S+) \S+ \S+ ' r'\[(?P<time>[^\]]+)\] ' r'"(?P<request>[^"]+)" ' r'(?P<status>\d{3}) ' r'(?P<size>\d+) ' r'"(?P<referer>[^"]*)" ' r'"(?P<ua>[^"]*)"' ) for line in sys.stdin: line = line.strip() if not line: continue match = log_pattern.search(line) if not match: continue fields = match.groupdict() # 过滤静态资源 request = fields['request'] if any(ext in request for ext in ['.js', '.css', '.png', '.jpg', '.ico', '.gif', '.svg', '.woff']): continue # 过滤非200响应 if fields['status'] != '200': continue # 输出为Tab分隔字段:ip, time, request, status, size, referer, ua print(f"{fields['ip']}\t{fields['time']}\t{fields['request']}\t{fields['status']}\t{fields['size']}\t{fields['referer']}\t{fields['ua']}")

这段脚本里我默认过滤掉了静态资源和非200响应。静态资源请求会严重拉高PV,比如一个页面引用了十张图片,按原始日志统计PV会把真实页面浏览数放大十几倍。非200响应则说明用户请求失败,不应该算作有效浏览。这两个过滤规则是日志分析项目的通用基础,不是可选项。

4.2 清洗规则与实用细节

除了过滤静态资源和错误码,清洗阶段还需要处理三类脏数据:爬虫流量、Referer为空但不代表没有来源、内网IP和健康检查请求。爬虫判断可以简单通过User-Agent关键字识别,比如包含spiderbotslurppython-requests的流量直接丢掉;如果不想误杀搜索引擎SEO流量,可以把白名单做细一点,但对课程项目来说,粗暴过滤反而更能体现“数据质量意识”。

内网IP和健康检查也是常见干扰项,公司内部员工的访问、K8s探针、负载均衡器发出的请求会混在日志里,导致UV虚高。我在项目里维护了一个IP黑名单,把127.0.0.110.*192.168.*段统一过滤掉。这个规则可以根据实际环境调整。

清洗完成后的数据格式统一为Tab分隔的7列字段。这里特别提一个坑:不要把Referer和UA里的空格换成下划线,也不要试图用逗号分隔,因为这两个字段本身可能包含逗号。Hive的LazySimpleSerDe默认按Tab分隔,所以Tab分隔是最稳妥的。如果后续用Pandas读取Hive结果,也最好保持Tab分隔,读取时指定sep='\t'即可。

清洗脚本的执行方式建议用管道,直接把整个日志文件夹里的数据流式处理:

cat /data/logs/2024-10-10.log | python3 parse_log.py > clean_log.tsv

数据量大的时候不要一次性读入内存,一行行处理是最稳定的做法。清洗完成后,用wc -l对比原始行数和输出行数,过滤比例过高或过低都说明解析正则出了问题,需要回头检查日志格式。

4.3 把清洗结果上传到HDFS

上传前先在HDFS上建好目录结构,我习惯按日期分目录,方便后续做增量分析:

hdfs dfs -mkdir -p /user/hadoop/cleaned/20241010 hdfs dfs -put clean_log.tsv /user/hadoop/cleaned/20241010/

上传完成后用hdfs dfs -ls确认文件大小。如果你用的是伪分布式模式,默认副本数为1,只要能看到文件就说明HDFS写入成功。文件在HDFS上会自动切成多个Block,存入DataNode。这一步看似简单,但它是连接离线计算和数据存储的桥梁,很多新手会在这里漏掉。

5. 核心分析实现:MapReduce统计与Hive查询

5.1 用Hadoop Streaming写Python版MapReduce

先说清一个概念:Hadoop Streaming是Hadoop自带的一个工具,它允许我们用任意语言编写Mapper和Reducer,关键逻辑是把数据按行输入stdin,处理后再按行写入stdout。每个Mapper接收一行文本,处理后输出key\tvalue;系统会按key自动分组排序,然后交给Reducer。这里的分组排序是MapReduce框架最核心的能力,也是它比普通Python脚本快的原因。

下面写一个统计每个URL页面PV的Mapper,代码保存在url_pv_mapper.py

#!/usr/bin/env python3 import sys for line in sys.stdin: line = line.strip() if not line: continue fields = line.split('\t') if len(fields) < 3: continue # 第三个字段是request,格式类似:GET /article/1024 HTTP/1.1 request = fields[2] parts = request.split(' ') if len(parts) < 2: continue url = parts[1] print(f"{url}\t1")

Reducer很简单,按key累加:

#!/usr/bin/env python3 import sys current_url = None current_count = 0 for line in sys.stdin: line = line.strip() if not line: continue url, count_str = line.split('\t', 1) try: count = int(count_str) except ValueError: continue if current_url == url: current_count += count else: if current_url: print(f"{current_url}\t{current_count}") current_url = url current_count = count if current_url == url: print(f"{current_url}\t{current_count}")

提交到Hadoop集群执行的命令如下:

hadoop jar $HADOOP_HOME/share/hadoop/tools/lib/hadoop-streaming-*.jar \ -files url_pv_mapper.py,url_pv_reducer.py \ -mapper "python3 url_pv_mapper.py" \ -reducer "python3 url_pv_reducer.py" \ -input /user/hadoop/cleaned/20241010/* \ -output /user/hadoop/output/url_pv_20241010

这里有一个非常常见的错误:忘记在-files参数里把脚本传上去。如果直接用-mapper "python3 ./url_pv_mapper.py",脚本必须在每个NodeManager节点上存在,而单单放在提交任务的本机是不够的。使用-files可以让脚本随任务分发到所有执行的节点,这就是分布式环境下的正确做法。

另外注意,执行MapReduce后输出目录不能已存在,否则Job会直接失败。每次重新运行前都要先把旧的输出目录删掉,或者换一个新的输出目录。这个坑在初学阶段几乎每次都踩。

5.2 在Hive里做主流程分析

MapReduce适合完成单一维度的精确统计,但做多维度分析还是Hive的SQL更高效。我通常在Hive中创建一个外部表,直接指向清洗后的HDFS目录。外部表的好处是删除表不会删除HDFS文件,后续数据更新只需要上传文件,表里的数据自动可见。

建表语句如下:

CREATE EXTERNAL TABLE IF NOT EXISTS web_log_analysis ( ip STRING, visit_time STRING, request STRING, status INT, size INT, referer STRING, ua STRING ) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE LOCATION '/user/hadoop/cleaned/20241010';

建表之后做几个核心分析指标,这里我给出三个常用查询。第一个是小时维度的PV趋势:

SELECT hour(visit_time) AS hour, count(*) AS pv FROM web_log_analysis GROUP BY hour(visit_time) ORDER BY hour;

注意visit_time字段里带有中括号和时区信息,如果直接用hour()函数,Hive可能解析失败。所以清洗阶段最好把时间字段格式化成标准格式,比如2024-10-10 13:55:36,这样后面所有时间函数都能正常用。要在清洗脚本里提前处理好,不要拖到查数时再改。

第二个是热门URL排行:

SELECT request, count(*) AS pv, count(DISTINCT ip) AS uv FROM web_log_analysis GROUP BY request ORDER BY pv DESC LIMIT 20;

第三个是用户访问路径中的来源页排行。用LAG窗口函数可以取到每个用户按时间排序后的上一条访问URL,再统计来源分布:

SELECT referer, count(*) AS ref_cnt FROM ( SELECT ip, visit_time, request, LAG(request) OVER (PARTITION BY ip ORDER BY visit_time) AS referer FROM web_log_analysis ) t WHERE referer IS NOT NULL GROUP BY referer ORDER BY ref_cnt DESC LIMIT 20;

这个SQL里我通过PARTITION BY ip来定义同一个用户,ORDER BY visit_time定义访问顺序,LAG取上一条URLL。实际项目中如果存在代理IP导致用户被误识别,还需要加Cookie维度,这里简化处理。

5.3 计算结果导出到本地

Hive支持直接通过INSERT OVERWRITE DIRECTORY把查询结果写到HDFS目录,然后再用hdfs dfs -get拉到本地。我常用的一条命令是这样:

INSERT OVERWRITE DIRECTORY '/user/hadoop/output/hour_pv' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' SELECT hour(visit_time), count(*) FROM web_log_analysis GROUP BY hour(visit_time);

然后本地执行:

hdfs dfs -getmerge /user/hadoop/output/hour_pv ./hour_pv.tsv

-getmerge会把目录里的多个文件合并成一个文件再下载,非常适合给Pandas读取。Pandas读取时用pd.read_csv('hour_pv.tsv', sep='\t', header=None, names=['hour', 'pv']),就可以进入绘图环节了。

6. 可视化展示与结果解读

6.1 用Pandas加Matplotlib快速出图

可视化的目标不是花哨,而是让业务方一眼看懂规律。我习惯用Matplotlib直接出静态图,再放到Flask页面里展示。以小时PV趋势为例,读取完CSV后这样画图:

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv('hour_pv.tsv', sep='\t', header=None, names=['hour', 'pv']) df = df.sort_values('hour') plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文乱码 plt.rcParams['axes.unicode_minus'] = False plt.figure(figsize=(12, 6)) plt.plot(df['hour'], df['pv'], marker='o') plt.xlabel('小时') plt.ylabel('PV数') plt.title('全天小时级PV趋势') plt.xticks(df['hour']) plt.grid(True) plt.savefig('hour_pv.png', dpi=120)

中文乱码是Windows和Linux上的老问题。Linux服务器通常需要先把中文字体安装好,再用font_manager指定字体文件,否则图里的中文会变成方块。我在服务器上直接放了一个simhei.ttf到项目fonts目录,然后用matplotlib.font_manager.FontProperties(fname='fonts/simhei.ttf')加载,效果比改全局配置更可控。

6.2 图表选择要和业务问题对应

小时PV趋势适合折线图,热门URL排行适合横向条形图,来源页分布适合饼图或桑基图。但不要让图表堆满整个页面,我的习惯是只保留三张核心图:小时趋势图、热门页面top10条形图、核心页面来源占比图。这三张图对应运营每天要看的流量健康度、内容热度、转化入口三个问题。

我把最终结果用Flask包了一个很简单的页面,路由/analysis渲染一张HTML,在img标签里嵌入Matplotlib生成的PNG图片。Flask代码不多,核心就几行:

from flask import Flask, render_template app = Flask(__name__) @app.route('/analysis') def analysis(): return render_template('analysis.html', chart1='static/hour_pv.png', chart2='static/top_url.png')

整个项目做完之后,后端调度脚本和数据展示页面分开,数据每日更新后就重新生成图表,页面无感知刷新。如果后续要支持用户交互筛选日期,再把静态PNG换成ECharts或Plotly Dashboard即可,分析框架不用改动。

7. 常见问题与排错经验实录

7.1 NameNode格式化失败及Hadoop启动异常

格式化失败这个坑,我在3.1一节里提到了一部分,这里给出完整的排查清单。先执行hdfs namenode -format,如果看到Storage directory ... already exists错误,基本可以断定是临时目录残留。解决办法是先清空hadoop.tmp.dir对应的目录,再重新格式化。如果格式化成功但DataNode启动后立即退出,多半是集群ID不一致,需要把HDFS数据目录清空后所有节点一起重新初始化。

启动进程后还要检查日志。Hadoop的日志通常在$HADOOP_HOME/logs/目录,看到java.io.IOException: File does not exist这种错误时,不要慌,先看是文件名对应的目录没建,还是权限不对。我遇到过好几次hdfs dfs -mkdir -p时漏掉父目录权限,导致后续上传文件失败。

7.2 Streaming任务提交后一直卡住或报错

卡住最常见的原因是YARN资源不足。伪分布式模式下ResourceManager默认允许的最大内存很小,如果同时跑多个Map任务,新提交的任务会一直处于ACCEPTED状态等待资源。解决办法是在yarn-site.xml中调大yarn.nodemanager.resource.memory-mb,同时调整mapreduce.map.memory.mbmapreduce.reduce.memory.mb。如果是机器内存本来就小,可以降低并发,比如设置mapreduce.job.maps=4

另外,Streaming任务如果输出目录已经存在会直接报org.apache.hadoop.mapred.FileAlreadyExistsException。我一般会在提交命令前加一行hdfs dfs -rm -r /user/hadoop/output/url_pv_20241010,省得反复删目录。

7.3 Python脚本在Streaming里经常出现ModuleNotFoundError

这个问题要分两种情况看。如果Mapper和Reducer用了第三方库,比如Pandas或NumPy,而NodeManager默认的Python环境没有这些库,任务就会报找到模块。解决办法有两个:一是尽量让Mapper和Reducer只使用Python标准库,不依赖第三方库,这是最轻量的做法;二是如果确实要用,可以使用-archives参数把虚拟环境压缩包分发到集群节点的指定目录,并在-mapper命令里指定虚拟环境里的Python路径。第二种方式配置复杂,课程项目通常不用走这一步,我建议课题项目尽量在Mapper里避免第三方依赖,把常用逻辑用标准库手写。

7.4 Hive查询结果中文乱码或时区偏移

Hive表存储的是原始字符串,中文乱码一般发生在用Pandas读取结果时。这通常是字符集编码不一致,建议清洗阶段就把所有非ASCII字符用原样存储,读取时统一指定encoding='utf-8'。如果是从HDFS拉取的文件,先用file命令确认编码格式,再用对应编码读取。

时区偏移则更隐蔽。Nginx日志里的时间是[10/Oct/2024:13:55:36 +0800],清洗后变成字符串2024-10-10 13:55:36,这是本地时间。如果后续用from_unixtime之类的函数转换,Hive默认时区是UTC,会导致时间差8小时。解决办法是在建表查询前执行SET time zone='Asia/Shanghai',或者在清洗阶段直接去掉时区后缀,只保留日期时间字符串,统一按本地时间解析。

7.5 数据倾斜和性能优化

按URL分组统计时,如果某几个极热门页面占了大量数据,MapReduce和Hive都会出现数据倾斜:单个Reducer要处理的数据量远大于其他Reducer,任务卡在这个Reducer上。应对方法各有不同。MapReduce场景下,可以给key加一个随机后缀进行两阶段聚合;Hive场景下,可以开启数据倾斜优化参数:SET hive.groupby.skewindata=true。实践下来,Hive的这个参数在绝大多数场景下都能明显缓解倾斜问题。

对于大幅提升查询效率的技巧,我这里还要提两点。一是把清洗后的数据转成ORC或Parquet列式存储格式,底层用Snappy压缩,查询扫描的数据量会大幅缩减。二是分区表设计,按日期分区,查询时只需要读当天数据。如果你做的是长期项目,这两点建议尽早落地。

8. 最后一点个人体会

这个项目做完,我最大的感受是“链路通”比“算法深”更重要。很多人在用Hadoop和Python做用户浏览分析时,容易陷入某个单点技术里出不来,比如研究半天SQL优化,却忽略了日志解析阶段的正则错误。整条流水线跑通之后,每一步的效率瓶颈一目了然,再去针对性优化就顺理成章。我个人的习惯是,在写任何清洗和分析脚本前,先抽出100行日志做本地调试,确认每一步输出都正确后再挂到全量数据上跑,这个习惯帮我省下了无数重跑任务的时间。后面如果再扩展,可以考虑引入Kafka做实时日志采集,用Spark Streaming替换离线MapReduce,但底层的分析思路和表结构设计不需要大改。希望这篇文章能帮你把一个网站浏览分析项目完整落地,有问题可以直接在评论区聊。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询