ToB企服应用市场:ToB评测及商务社交产业平台

标题: 基于Hadoop框架实现的对历年四级单词的词频分析(入门级Hadoop项目) [打印本页]

作者: 忿忿的泥巴坨    时间: 2024-5-19 07:40
标题: 基于Hadoop框架实现的对历年四级单词的词频分析(入门级Hadoop项目)
前情提要:飞物作者屡次四级考试未能通过,进而恼羞成怒,制作了基于Hadoop实现的对历年四级单词的词频分析项目,盼望督促自己尽快通过四级(然而并没有什么卵用)

项目需求:Pycharm、IDEA、Linux、Hadoop运行情况、Hive、beeline、八爪鱼采集器
数据来源:https://zhenti.burningvocabulary.cn/cet4


“如果你想要数据,就得自己来拿,这规矩你早就懂得”  ——某V姓男子
一、 数据采集

1.从目标网站上获取所需要的网址

用来获取数据的网站是一个由主界面指向各个题目页面的分支结构,所以需要使用Python爬虫从主界面获取每一个题目页面的网址
  1. # 从该四级真题主网站上获取各个具体题目页面的链接网址
  2. import re
  3. import requests
  4. # 防止爬虫被拦截
  5. header = {
  6.     'User-Agent': 'User-Agent:  Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36'
  7. }
  8. httpsGet = requests.get("https://zhenti.burningvocabulary.cn/cet4", headers=header)  # 爬取整个页面
  9. httpsTxt = open(r"E:\ShuJu\SiJiDanCi\AllWWW.txt", 'w', encoding='utf-8')  # 创建储存网站信息的txt文件(真正的英语大佬都在使用拼音来命名文件夹)
  10. httpsTxt.write(httpsGet.text)  # 将爬取信息存入txt
  11. httpsTxt.close()  # 关闭并保存文件
  12. a = open(r"E:\ShuJu\SiJiDanCi\AllWWW.txt", encoding='utf-8').read()  # a为主网页的信息
  13. httpsAns = re.findall(
  14.     r'<a  target="_blank" href="https://www.cnblogs.com/(.*?)">', a,  # 正则表达式查找各页面网址
  15.     re.S)
  16. httpsTxt.close()  # 关闭存有主网站信息的txt
  17. print(httpsAns)  # 输出正则表达式的查找结果
  18. with open(r"E:\ShuJu\SiJiDanCi\cet4WWW.txt", 'w', encoding="UTF8") as f:  # 将结果存入文件
  19.     for item in httpsAns:
  20.         f.write(str(item) + "\t")
  21. f.close()
复制代码
获得了每一期题目的页面地址


2.使用八爪鱼采集器获取题目数据

(飞物作者用自己的爬虫爬了一天都没能拿到数据,只好借助外力)


点击新建,自定义任务,把需要爬取的网址全部复制粘贴已往,保存设置,然后在页面中点击你需要的文本,然后点击采集按钮就可以启动采集了

采集完成后获得一个存有历年所有英语题目的csv格式文件shuju.csv
之后对该csv文件进行数据处理
二、数据处理

使用Python将存有所有英语题目中的无用数据剔除,获得仅存有所有单词的文本数据
  1. # 将csv格式的文件转换为txt格式
  2. import pandas as pd
  3. df = pd.read_csv(r"E:\ShuJu\SiJiDanCi\shuju.csv", index_col=0)
  4. print(df)
  5. df.to_csv(r"E:\ShuJu\SiJiDanCi\shuju.txt", sep='\t', index=False)
复制代码
  1. #清洗无用数据
  2. import re
  3. with open(r"E:\ShuJu\SiJiDanCi\shuju.txt", "r", encoding="UTF8") as f:  # 读入原始数据
  4.     data = f.read()
  5. f.close()
  6. str = re.sub(r"[^a-zA-Z]+", " ", data)  # 将非字母型的字符全部替换为空格
  7. str = str.lower()  # 将大写字母替换为小写字母
  8. print(str)  # 将数据处理情况显示在控制台,查看效果
  9. with open(r"E:\ShuJu\SiJiDanCi\sijiShaitext.txt", 'w', encoding="UTF8") as f:  # 将数据写出
  10.     f.write(str)
  11. f.close()
复制代码
终极效果长这个模样

三、Hadoop计算


作者在这里使用三台Linux假造机搭建的完全分布式Hadoop集群来进行计算
1.词频统计

起首是大家耳熟能详的wordcount计数 : map和reduce利用
打开IDEA,复制粘贴代码然后导包
Driver
  1. public class WordCountDriver {
  2.     public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
  3.         // 1 获取配置信息以及获取job对象
  4.         Configuration conf = new Configuration();
  5.         Job job = Job.getInstance(conf);
  6.         // 2 关联本Driver程序的jar
  7.         job.setJarByClass(WordCountDriver.class);
  8.         // 3 关联Mapper和Reducer的jar
  9.         job.setMapperClass(WordCountMapper.class);
  10.         job.setReducerClass(WordCountReducer.class);
  11.         // 4 设置Mapper输出的kv类型
  12.         job.setMapOutputKeyClass(Text.class);
  13.         job.setMapOutputValueClass(IntWritable.class);
  14.         // 5 设置最终输出kv类型
  15.         job.setOutputKeyClass(Text.class);
  16.         job.setOutputValueClass(IntWritable.class);
  17.         // 6 设置输入和输出路径
  18.         FileInputFormat.setInputPaths(job, new Path("E:\\ShuJu\\SiJiDanCi\\sijiShaitext.txt"));
  19.         FileOutputFormat.setOutputPath(job, new Path("E:\\ShuJu\\SiJiDanCi\\sijiOUT.txt"));
  20.         // 7 提交job
  21.         boolean result = job.waitForCompletion(true);
  22.         System.exit(result ? 0 : 1);
  23.     }
  24. }
复制代码
Mapper
  1. public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
  2.     Text outK = new Text();
  3.     IntWritable outV = new IntWritable(1);
  4.     @Override
  5.     protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
  6.         //map被循环遍历,读取文件中的每一行字符串,重写map方法
  7.         //1、获取一行存入字符串line
  8.         String line = value.toString();
  9.         //2、切割存入名为words的字符串数组
  10.         String[] words = line.split(" ");//以空格为分界线拆分单词
  11.         //3、循环写出,读取words中的各个字符串
  12.         for (String word : words) {//从words数组中读出的字符串word
  13.             //封装outK
  14.             outK.set(word);
  15.             //写出
  16.             context.write(outK, outV);
  17.         }
  18.     }
  19. }
复制代码
Reducer
  1. public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
  2.     int sum;
  3.     IntWritable value = new IntWritable();
  4.     @Override
  5.     protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
  6.         // 1 累加求和
  7.         sum = 0;
  8.         for (IntWritable count : values) {
  9.             sum += count.get();//数据类型切换为int
  10.         }
  11.         // 2 输出
  12.         value.set(sum);
  13.         context.write(key, value);
  14.     }
  15. }
复制代码
这样就得到了初步的词频统计结果,默认是按照字母表顺序来排列的
接下来进行一个排序,可以更直观的看出单词的出现情况
(作为一个懒人肯定要挑出现次数高的单词来背)
2.按照单词出现次数递减排序

FlowBean
  1. public class FlowBean implements WritableComparable<FlowBean> {
  2.     private long num; //每个单词的个数
  3.     //提供无参构造
  4.     public FlowBean() {
  5.     }
  6.     public long getnum() {
  7.         return num;
  8.     }
  9.     public void setnum(long num) {
  10.         this.num = num;
  11.     }
  12.     @Override
  13.     public void write(DataOutput out) throws IOException {
  14.         out.writeLong(this.num);
  15.     }
  16.     @Override
  17.     public void readFields(DataInput in) throws IOException {
  18.         this.num = in.readLong();
  19.     }
  20.     @Override
  21.     public String toString() {
  22.         return String.valueOf(num);
  23.     }
  24.     @Override
  25.     public int compareTo(FlowBean o) { //按照单词个数进行递减排序
  26.         if (this.num > o.num) {
  27.             return -1;
  28.         } else if (this.num < o.num) {
  29.             return 1;
  30.         } else {
  31.             return 0;
  32.         }
  33.     }
  34. }
复制代码
Driver
  1. public class FlowDriver {
  2.     public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
  3.         //1 获取job对象
  4.         Configuration conf = new Configuration();
  5.         Job job = Job.getInstance(conf);
  6.         //2 关联本Driver类
  7.         job.setJarByClass(FlowDriver.class);
  8.         //3 关联Mapper和Reducer
  9.         job.setMapperClass(FlowMapper.class);
  10.         job.setReducerClass(FlowReducer.class);
  11.         //4 设置Map端输出数据的KV类型
  12.         job.setMapOutputKeyClass(FlowBean.class);
  13.         job.setMapOutputValueClass(Text.class);
  14.         //5 设置程序最终输出的KV类型
  15.         job.setOutputKeyClass(Text.class);
  16.         job.setOutputValueClass(FlowBean.class);
  17.         //6 设置输入输出路径
  18.         FileInputFormat.setInputPaths(job, new Path("E:\\ShuJu\\SiJiDanCi\\sijiOUT.txt\\part-r-00000"));
  19.         FileOutputFormat.setOutputPath(job, new Path("E:\\ShuJu\\SiJiDanCi\\sijiOUTCompare"));
  20.         //7 提交Job
  21.         boolean b = job.waitForCompletion(true);
  22.         System.exit(b ? 0 : 1);
  23.     }
  24. }
复制代码
Mapper
  1. public class FlowMapper extends Mapper<LongWritable, Text, FlowBean, Text> {
  2.     private FlowBean outK = new FlowBean();
  3.     private Text outV = new Text();
  4.     @Override
  5.     protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
  6.         String line = value.toString();
  7.         String[] split = line.split("\t");
  8.         try {
  9.             outK.setnum(Long.parseLong(split[1].trim()));
  10.         } catch (ArrayIndexOutOfBoundsException e) {
  11.             outK.setnum(Long.parseLong(split[0].trim()));
  12.         }
  13.         outV.set(split[0]);
  14.         //4 写出outK outV
  15.         context.write(outK, outV);
  16.     }
  17. }
复制代码
Reducer
  1. public class FlowReducer extends Reducer<FlowBean, Text, Text, FlowBean> {
  2.     @Override
  3.     protected void reduce(FlowBean key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
  4.         for (Text value : values) {
  5.             context.write(value, key);
  6.         }
  7.     }
  8. }
复制代码
这样就得到了我们想要的数据

四、Hive存储

接下来将数据存入Hive表格便于查询
起首把上面处理好的单词文件上传至HDFS中的/test文件夹中
然后在HDFS上创建一个表来存入数据,位置在/test/WordsData
使用Linux上的beeline客户端连接Hive,输入建表语句,word列存单词,num列存对应出现次数
  1. CREATE EXTERNAL TABLE Words
  2. (
  3.   word  STRING,
  4.   num   INT
  5. )
  6. ROW FORMAT DELIMITED              
  7. FIELDS TERMINATED BY '\t'
  8. LINES TERMINATED BY '\n'
  9. STORED AS TEXTFILE            
  10. LOCATION '/test/WordsData';
复制代码
将文本文件中的单词数据写入Hive表格
  1. LOAD DATA INPATH '/test/part-r-00000' OVERWRITE INTO TABLE Words;
复制代码
接下来就可以使用SQL语句愉快的查询各种单词的数据了
  1. select * from Words where num>100;
复制代码


想必这些操尴尬刁难大家来说简直有手就行
点我跳转 Ciallo~(∠・ω< )⌒★


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4