从数据抓取到应用分析


本例主要是为了学习大数据的相关知识,整个学习过程包含了:

  1. nutch抓取网上数据;
  2. Hbase数据筛取;
  3. 中文分词及mapreduce数据统计分析。

首先需要安装nutch2和hbase. nutch2只能通过源码编译安装,指定hbase为默认存放抓取数据的地方。因此,建议先安装hbase, 再编译安装nutch。同时,hbase可以存在hdfs上,方便空间扩展和mapreduce计算。所以,建议首先安装hadoop2环境。如果仅仅是为了测试用,可以只安装single mode. 环境安装顺序为.

  1. Hadoop -- 网上有很多安装攻略,不列举;
  2. Hbase -- 按照官网的quick start安装配置即可;
  3. Nutch2 -- 官网也有编译方法,编译过程很长,一定要耐心一点。一般除了仓库连接有问题基本不会遇到什么问题。

接下来就是用nutch爬取网站数据。nutch2的爬取方式有两种,一种是deploy的方式,一种是local的方式,我们测试就用local的方式。注意,nutch只能爬取静态页面的结果。如果想爬取服务器数据或者动态数据得另寻它法。先切换到$NUTCH_HOME/runtime/local/下面。以爬取新浪股票的网页为例。首先需要添加种子链接;

$ mkdir -p urls
$ echo http://finance.sina.com.cn/stock/ > urls/seed.txt

然后需要配置过滤规则,一般是在local/conf/regex-urlfilter.txt下面。主要看这几项内容

# skip file: ftp: and mailto: urls 这个是跳过file/ftp/mailto等链接
-^(file|ftp|mailto):

# skip image and other suffixes we can't yet parse
# for a more extensive coverage use the urlfilter-suffix plugin 这个是跳过资源文件和js
-\.(gif|GIF|jpg|JPG|png|PNG|ico|ICO|css|CSS|sit|SIT|eps|EPS|wmf|WMF|zip|ZIP|ppt|PPT|mpg|MPG|xls|XLS|gz|GZ|rpm|RPM|tgz|TGZ|mov|MOV|exe|EXE|jpeg|JPEG|bmp|BMP|js|JS)$

# skip URLs containing certain characters as probable queries, etc. 这个是跳过所有含有以下符号的链接
-[?*!@=]

# skip URLs with slash-delimited segment that repeats 3+ times, to break loops
-.*(/[^/]+)/[^/]+\1/[^/]+\1/

# accept anything else 这个是加上所有你希望添加的链接
+.

总之,‘-’就是丢弃链接,‘+’就是添加链接,可以根据自己情况添加filter.

接下来就是启动hadoop/hbase

$ start-all.sh
$ cd $HBASE_HOME
$ bin/start-hbase.sh

然后切换到nutch2/runtime/local下面,nutch抓取数据需要存放到hbase上面,因此还需要拷贝hbase下的jar包到local/lib下面。

$ bin/crawl urls stock 3

crawl就开始抓取工作了, 抓取的文件默认放到了hbase里面(nutch编译时决定的)。urls是种子目录,stock是id,用来命名hbase的表。3就是迭代次数。抓取时间较长。

抓取结束后可进入hbase shell里查看。

$ bin/hbase shell
$ : list

用list可以看到hbase里面多了个“stock_webpage”的表。接下来看看这个表的结构。
可以用scan tablename查看,但是内容太多了,抓不到重点。其实nutch抓取网页后会对网页进行分析,我们来看看这个文档。

$ more $NUTHC_HOME/runtime/local/gora_hbase_mapping.xml

<gora-orm>
    
    <table name="webpage">
        <family name="p" maxVersions="1"/>
        <family name="f" maxVersions="1"/>
        <family name="s" maxVersions="1"/>
        <family name="il" maxVersions="1"/>
        <family name="ol" maxVersions="1"/>
        <family name="h" maxVersions="1"/>
        <family name="mtdt" maxVersions="1"/>
        <family name="mk" maxVersions="1"/>
    </table>
    <class table="webpage" keyClass="java.lang.String" name="org.apache.nutch.storage.WebPage">
        
        <!-- fetch fields                                       -->
        <field name="baseUrl" family="f" qualifier="bas"/>
        <field name="status" family="f" qualifier="st"/>
        <field name="prevFetchTime" family="f" qualifier="pts"/>
        <field name="fetchTime" family="f" qualifier="ts"/>
        <field name="fetchInterval" family="f" qualifier="fi"/>
        <field name="retriesSinceFetch" family="f" qualifier="rsf"/>
        <field name="reprUrl" family="f" qualifier="rpr"/>
        <field name="content" family="f" qualifier="cnt"/>
        <field name="contentType" family="f" qualifier="typ"/>
        <field name="protocolStatus" family="f" qualifier="prot"/>
        <field name="modifiedTime" family="f" qualifier="mod"/>
        <field name="prevModifiedTime" family="f" qualifier="pmod"/>
        <field name="batchId" family="f" qualifier="bid"/>

        <!-- parse fields                                       -->
        <field name="title" family="p" qualifier="t"/>
        <field name="text" family="p" qualifier="c"/>
        <field name="parseStatus" family="p" qualifier="st"/>
        <field name="signature" family="p" qualifier="sig"/>
        <field name="prevSignature" family="p" qualifier="psig"/>
        
        <!-- score fields                                       -->
        <field name="score" family="s" qualifier="s"/>
        <field name="headers" family="h"/>
        <field name="inlinks" family="il"/>
        <field name="outlinks" family="ol"/>
        <field name="metadata" family="mtdt"/>
        <field name="markers" family="mk"/>
    </class>
    
    <table name="host">
      <family name="mtdt" maxVersions="1"/>
      <family name="il" maxVersions="1"/>
      <family name="ol" maxVersions="1"/>
    </table>
    
    <class table="host" keyClass="java.lang.String" name="org.apache.nutch.storage.Host">
      <field name="metadata" family="mtdt"/>
      <field name="inlinks" family="il"/>
      <field name="outlinks" family="ol"/>
    </class>
    
</gora-orm>

hbase存取数据的方式是rowkey:family:colum:value. 什么意思呢?就是一个rowkey有多个family,一个family有多个column,每个column有对应的value. 再看看这个文件。
table name为webpage,加上id前缀就是‘stock_webpage‘. 它有好多family, p/f/s...
fetch_filed就是抓取网页是存放的信息. 这些filed name都属于'f'这个family;
parse_filed就是分析后存放的信息。这些filed name都属于’p‘这个family.

所以,如果我们想查看http://finance.sina.com.cn/stock/ 这个网页下的文本信息只需要这样查看。

http://finance.sina.com.cn/stock p:c:value

什么意思呢,即rowkey为这个链接,family为'p', column为'c'的value. 当然,这个是有接口可以调用的。后续会提及。

那我们在分析数据的时候就可以根据这个存放规则去找到相应的文本信息了。关于分析的部分,我首先把rowkey里面包含finance的文本信息提取出来放到本地。可以放到hdfs,也可以直接放到另一个hbase表。

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.TextOutputFormat;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
  
import org.apache.hadoop.hbase.client.Result;  
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.filter.SubstringComparator;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;  
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;  
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;  
 


public class HBaseToHdfs {
    public static void main(String[] args) throws Exception {
        Configuration conf = HBaseConfiguration.create();
        Job job = Job.getInstance(conf, HBaseToHdfs.class.getSimpleName());
        job.setJarByClass(HBaseToHdfs.class);
         
        job.setMapperClass(HBaseToHdfsMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);
         
        job.setNumReduceTasks(0);
         Scan scan = new Scan();
        //过滤hbase里面所有含有finance的rowkey
         Filter filter3 = new RowFilter(CompareFilter.CompareOp.EQUAL,
                 new SubstringComparator("finance"));
         scan.setFilter(filter3);

        TableMapReduceUtil.initTableMapperJob("stock_webpage", scan,HBaseToHdfsMapper.class ,Text.class, Text.class, job);
         
        job.setOutputValueClass(TextOutputFormat.class);
        FileOutputFormat.setOutputPath(job, new Path("/stock"));
         
        job.waitForCompletion(true);
    }
     
     
    public static class HBaseToHdfsMapper extends TableMapper<Text, Text> {
        private Text outKey = new Text();
        private Text outValue = new Text();
        @Override
        protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException {
            //key在这里就是hbase的rowkey
            byte[] name = null;

            try {
                //得到family为'p',column为'c'的值
                name = value.getColumnLatestCell(Bytes.toBytes("p"), Bytes.toBytes("c")).getValue();
            } catch (Exception e) {}

            outKey.set(key.get());
            String temp = (name==null || name.length==0)?"NULL":new String(name);
            System.out.println(temp);
            outValue.set(temp);
            context.write(outKey, outValue);
        }
 
    }
}

现在我的数据放到了workspace/HBaseToHdfs/stock里面,接下来放到hdfs上并作mapreduce. 简单的做个中文分词并计算wordcount。

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.ByteArrayInputStream;

import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;

public class ChineseCount {
    
      public static class TokenizerMapper 
           extends Mapper<Object, Text, Text, IntWritable>{
        
        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text();
          
        public void map(Object key, Text value, Context context
                        ) throws IOException, InterruptedException {
            
            byte[] bt = value.getBytes();
            InputStream ip = new ByteArrayInputStream(bt);
            Reader read = new InputStreamReader(ip);
            //map之前先做分词
            IKSegmenter iks = new IKSegmenter(read,true);
            Lexeme t;
            while ((t = iks.next()) != null)
            {
                word.set(t.getLexemeText());
                context.write(word, one);
            }
        }
      }
  
  public static class IntSumReducer 
       extends Reducer<Text,IntWritable,Text,IntWritable> {
    private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable<IntWritable> values, 
                       Context context
                       ) throws IOException, InterruptedException {
      int sum = 0;
      for (IntWritable val : values) {
        sum += val.get();
      }
      result.set(sum);
      context.write(key, result);
    }
  }

  public static void main(String[] args) throws Exception {
    Configuration conf = new Configuration();
    conf.set("fs.default.name", "hdfs://localhost:9000");
    
    String[] args1 = new String[] { "/input", "/output" };
    String[] otherArgs = new GenericOptionsParser(conf, args1).getRemainingArgs();
    if (otherArgs.length != 2) {
      System.err.println("Usage: wordcount <in> <out>");
      System.exit(2);
    }
    Job job = new Job(conf, "word count");
    job.setJarByClass(ChineseCount.class);
    job.setMapperClass(TokenizerMapper.class);
    job.setCombinerClass(IntSumReducer.class);
    job.setReducerClass(IntSumReducer.class);
    job.setOutputKeyClass(Text.class);
    job.setOutputValueClass(IntWritable.class);
    FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
    FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
    System.exit(job.waitForCompletion(true) ? 0 : 1);
  }
}

好了,大功告成,可以去hdfs上的/ouput里面查看分词结果,后续的分析待追加。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • HBase那些事 @(大数据工程学院)[HBase, Hadoop, 优化, HadoopChen, hbase]...
    分痴阅读 3,879评论 3 17
  • 入门指南 1. 简介 Quickstart会让你启动和运行一个单节点单机HBase。 2. 快速启动 – 单点HB...
    和心数据阅读 4,323评论 1 41
  • 1 周末和简洁一起去游泳,她说上个男朋友不了了之了,她自己都不晓得哪里出了状况,反正就是被祝福分手了。 简洁,职场...
    清儿姑娘阅读 474评论 0 3
  • 我喝醉的时候 膨胀而麻木的身体 像给未来打的一个补丁
    c5de959d631b阅读 244评论 0 3
  • 她站在阳台边上 给大叔讲一通电话 无意间瞥见别家穿着裤衩在阳台的人 对面的大黄狗摇着尾巴追着女主人 说着说着 风吹...
    毛毛静丫阅读 179评论 0 0