JAVA综合面试题:页面的抓取、解析、保存数据库和生成HTML的页面展示

写一个jsp页面,访问时显示从下面的页面提取出的销售商详细信息:价格、运费、经销
商基本信息等,并写到一个数据库表里。
http://www.amazon.com/gp/offer-listing/B0012J52OC/

数据抓取的问题,涉及到
1 用URLConnection 读取页面信息,用httpclient也行
2 用Pattern 解析页面并拿到你要的信息
3 显示数据
4 存入数据库

这个是一个综合的考试,涉及的知识面比较广。

1 我这里只给出关键的部分,使用java程序实现,而不是JSP的代码。移植工作请自行完成。
2 我使用自己的数据库连接,请替换为应用服务器提供的数据源为好
3 代码分三部分,数据库结构,POJO类和应用程序

一、数据库结构 AmazonGoods.sql 使用的是MySQL的数据库

  1. -- ----------------------------   
  2. -- Table structure for amazongoods   
  3. -- ----------------------------   
  4. CREATE TABLE `amazongoods` (   
  5.   `id` int(11) NOT NULL AUTO_INCREMENT,   
  6.   `price` decimal(10,0) NOT NULL,   
  7.   `shipping` decimal(10,0) NOT NULL,   
  8.   `Seller` text NOT NULL,   
  9.   PRIMARY KEY (`id`)   
  10. ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;  
-- ----------------------------
-- Table structure for amazongoods
-- ----------------------------
CREATE TABLE `amazongoods` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `price` decimal(10,0) NOT NULL,
  `shipping` decimal(10,0) NOT NULL,
  `Seller` text NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;



二、POJO类 AmazonGoods.java

  1. package com.laozizhu.test.amazon;   
  2.   
  3. import java.math.BigDecimal;   
  4.   
  5. /**  
  6.  * 某一行的商品数据  
  7.  *   
  8.  * @author 老紫竹的家(laozizhu.com)  
  9.  *   
  10.  */  
  11. class AmazonGoods {   
  12.   public long getId() {   
  13.     return id;   
  14.   }   
  15.   
  16.   public void setId(long id) {   
  17.     this.id = id;   
  18.   }   
  19.   
  20.   public BigDecimal getPrice() {   
  21.     return price;   
  22.   }   
  23.   
  24.   public void setPrice(BigDecimal price) {   
  25.     this.price = price;   
  26.   }   
  27.   
  28.   public BigDecimal getShipping() {   
  29.     return shipping;   
  30.   }   
  31.   
  32.   public void setShipping(BigDecimal shipping) {   
  33.     this.shipping = shipping;   
  34.   }   
  35.   
  36.   public String getSeller() {   
  37.     return seller;   
  38.   }   
  39.   
  40.   public void setSeller(String seller) {   
  41.     this.seller = seller;   
  42.   }   
  43.   
  44.   // 序列号,主键   
  45.   private long id;   
  46.   // 价格   
  47.   private BigDecimal price;   
  48.   // 运费   
  49.   private BigDecimal shipping;   
  50.   // 商家信息   
  51.   private String seller;   
  52. }  
package com.laozizhu.test.amazon;

import java.math.BigDecimal;

/**
 * 某一行的商品数据
 * 
 * @author 老紫竹的家(laozizhu.com)
 * 
 */
class AmazonGoods {
  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public BigDecimal getPrice() {
    return price;
  }

  public void setPrice(BigDecimal price) {
    this.price = price;
  }

  public BigDecimal getShipping() {
    return shipping;
  }

  public void setShipping(BigDecimal shipping) {
    this.shipping = shipping;
  }

  public String getSeller() {
    return seller;
  }

  public void setSeller(String seller) {
    this.seller = seller;
  }

  // 序列号,主键
  private long id;
  // 价格
  private BigDecimal price;
  // 运费
  private BigDecimal shipping;
  // 商家信息
  private String seller;
}


三、应用类

  1. package com.laozizhu.test.amazon;   
  2.   
  3. import java.math.BigDecimal;   
  4. import java.sql.Connection;   
  5. import java.sql.DriverManager;   
  6. import java.sql.PreparedStatement;   
  7. import java.util.ArrayList;   
  8. import java.util.List;   
  9. import java.util.Properties;   
  10. import java.util.regex.Matcher;   
  11. import java.util.regex.Pattern;   
  12.   
  13. import com.laozizhu.tools.PageService;   
  14.   
  15. public class AmazonFetch {   
  16.   
  17.   /**  
  18.    * @param args  
  19.    */  
  20.   public static void main(String[] args) {   
  21.     // 我这里需要设置代理,如果你能直接访问互联网,则无需这段代码了   
  22.     initProxy();   
  23.   
  24.     // 读取页面数据   
  25.     String str = PageService.getPage("http://www.amazon.com/gp/offer-listing/B0012J52OC/""ISO-8859-1");   
  26.     // 解析页面,拿到商品信息   
  27.     List<AmazonGoods> list = parse(str);   
  28.     // 生成HTML表格   
  29.     buildTable(list);   
  30.     // 存入数据库   
  31.     saveToMySQL(list);   
  32.   }   
  33.   
  34.   /**  
  35.    * 简单的代理服务器,无需密码认证  
  36.    */  
  37.   private static void initProxy() {   
  38.     Properties prop = System.getProperties();   
  39.     // prop.put("proxySet", "true");   
  40.     // 设置http访问要使用的代理服务器的地址   
  41.     prop.setProperty("http.proxyHost""10.60.8.20");   
  42.     // 设置http访问要使用的代理服务器的端口   
  43.     prop.setProperty("http.proxyPort""8080");   
  44.   }   
  45.   
  46.   // 注意,美元符号要转义   
  47.   // 因为报价都包含小数点,所以用数字+小数点+2位小数即可   
  48.   // 商家信息包含了对应的标签   
  49.   static Pattern pPrice = Pattern.compile(   
  50.       "<span class=\"price\">\\$([\\d]+\\.[\\d]{2})</span>.*?(<ul class=\"sellerInformation\">.+?</ul>)", Pattern.DOTALL);   
  51.   // 运费   
  52.   // <span class="price_shipping">+ $6.04</span>   
  53.   static Pattern pShipping = Pattern   
  54.       .compile("<span class=\"price_shipping\">\\+ \\$([\\d]+\\.[\\d]{2})</span>", Pattern.DOTALL);   
  55.   
  56.   /**  
  57.    * 解析页面,获得商品列表  
  58.    *   
  59.    * @param page  
  60.    *          页面  
  61.    * @return 商品列表  
  62.    */  
  63.   private static List<AmazonGoods> parse(String page) {   
  64.     // 首先,把商品分成多个字符串片段   
  65.     // 分割符就是表格里的内容了。这个得查看HTML源代码才能找到合适的   
  66.     String[] strs = page.split("<tbody class=\"result\">");   
  67.     // 构造结果   
  68.     // 默认长度为片段的长度,呵呵   
  69.     List<AmazonGoods> list = new ArrayList<AmazonGoods>(strs.length);   
  70.     AmazonGoods goods = null;   
  71.     // 循环解析每个商品片段   
  72.     for (String str : strs) {   
  73.       // 注意,不是每个商品都有运费,所以正则最好不要写一个   
  74.       // 当然,你愿意弄复杂了也行,我个人不推荐这么做   
  75.       Matcher m = pPrice.matcher(str);   
  76.       if (m.find()) {   
  77.         goods = new AmazonGoods();   
  78.         goods.setPrice(new BigDecimal(m.group(1)));   
  79.         // 这里面包含了HTML的信息,包括Javascript内容,不过比较难删除   
  80.         // 因为有些页面文字是用js显示的,还是保留的比较好   
  81.         goods.setSeller(m.group(2));   
  82.   
  83.         // 查找运费   
  84.         m = pShipping.matcher(str);   
  85.         if (m.find()) {   
  86.           goods.setShipping(new BigDecimal(m.group(1)));   
  87.         }   
  88.         // 将商品加入列表   
  89.         list.add(goods);   
  90.       } else {   
  91.         // 没有找到价格,则这部分不包含商品信息,无需继续   
  92.         continue;   
  93.       }   
  94.     }   
  95.     return list;   
  96.   }   
  97.   
  98.   private static String buildTable(List<AmazonGoods> list) {   
  99.     StringBuilder b = new StringBuilder("<table>");   
  100.     b.append("<tr><th>价格</th><th>运费</th><th>商家信息</th></tr>");   
  101.     for (AmazonGoods goods : list) {   
  102.       b.append("<tr><th>" + goods.getPrice() + "</th><th>" + goods.getShipping() + "</th><th>" + goods.getSeller()   
  103.           + "</th></tr>");   
  104.     }   
  105.     b.append("</table>");   
  106.     return b.toString();   
  107.   }   
  108.   
  109.   private static void saveToMySQL(List<AmazonGoods> list) {   
  110.     // 这里就用最原始的方法获得数据库连接了。   
  111.     // 数据库结构请参考AmazonGoods.sql   
  112.     // 使用test的数据库   
  113.     Connection con = null;   
  114.     PreparedStatement st = null;   
  115.     String url = "jdbc:mysql://localhost:3306/";   
  116.     String db = "test";   
  117.     String driver = "com.mysql.jdbc.Driver";   
  118.     String user = "test";   
  119.     String pass = "test";   
  120.     BigDecimal ZERO = new BigDecimal("0");   
  121.     try {   
  122.       Class.forName(driver);   
  123.       con = DriverManager.getConnection(url + db, user, pass);   
  124.       st = con.prepareStatement("insert into AmazonGoods (price,shipping,seller) values(?,?,?)");   
  125.       for (AmazonGoods goods : list) {   
  126.         st.setBigDecimal(1, goods.getPrice());   
  127.         st.setBigDecimal(2, goods.getShipping()==null?ZERO:goods.getShipping());   
  128.         st.setString(3, goods.getSeller());   
  129.         if (st.executeUpdate() <= 0) {   
  130.           throw new Exception("保存数据错误!");   
  131.         }   
  132.         st.clearParameters();   
  133.       }   
  134.   
  135.     } catch (Exception ex) {   
  136.       ex.printStackTrace();   
  137.     } finally {   
  138.       if (st != null) {   
  139.         try {   
  140.           st.close();   
  141.         } catch (Exception ex) {   
  142.         }   
  143.       }   
  144.       if (con != null) {   
  145.         try {   
  146.           con.close();   
  147.         } catch (Exception ex) {   
  148.         }   
  149.       }   
  150.     }   
  151.   }   
  152. }  
package com.laozizhu.test.amazon;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.laozizhu.tools.PageService;

public class AmazonFetch {

  /**
   * @param args
   */
  public static void main(String[] args) {
    // 我这里需要设置代理,如果你能直接访问互联网,则无需这段代码了
    initProxy();

    // 读取页面数据
    String str = PageService.getPage("http://www.amazon.com/gp/offer-listing/B0012J52OC/", "ISO-8859-1");
    // 解析页面,拿到商品信息
    List<AmazonGoods> list = parse(str);
    // 生成HTML表格
    buildTable(list);
    // 存入数据库
    saveToMySQL(list);
  }

  /**
   * 简单的代理服务器,无需密码认证
   */
  private static void initProxy() {
    Properties prop = System.getProperties();
    // prop.put("proxySet", "true");
    // 设置http访问要使用的代理服务器的地址
    prop.setProperty("http.proxyHost", "10.60.8.20");
    // 设置http访问要使用的代理服务器的端口
    prop.setProperty("http.proxyPort", "8080");
  }

  // 注意,美元符号要转义
  // 因为报价都包含小数点,所以用数字+小数点+2位小数即可
  // 商家信息包含了对应的标签
  static Pattern pPrice = Pattern.compile(
      "<span class=\"price\">\\$([\\d]+\\.[\\d]{2})</span>.*?(<ul class=\"sellerInformation\">.+?</ul>)", Pattern.DOTALL);
  // 运费
  // <span class="price_shipping">+ $6.04</span>
  static Pattern pShipping = Pattern
      .compile("<span class=\"price_shipping\">\\+ \\$([\\d]+\\.[\\d]{2})</span>", Pattern.DOTALL);

  /**
   * 解析页面,获得商品列表
   * 
   * @param page
   *          页面
   * @return 商品列表
   */
  private static List<AmazonGoods> parse(String page) {
    // 首先,把商品分成多个字符串片段
    // 分割符就是表格里的内容了。这个得查看HTML源代码才能找到合适的
    String[] strs = page.split("<tbody class=\"result\">");
    // 构造结果
    // 默认长度为片段的长度,呵呵
    List<AmazonGoods> list = new ArrayList<AmazonGoods>(strs.length);
    AmazonGoods goods = null;
    // 循环解析每个商品片段
    for (String str : strs) {
      // 注意,不是每个商品都有运费,所以正则最好不要写一个
      // 当然,你愿意弄复杂了也行,我个人不推荐这么做
      Matcher m = pPrice.matcher(str);
      if (m.find()) {
        goods = new AmazonGoods();
        goods.setPrice(new BigDecimal(m.group(1)));
        // 这里面包含了HTML的信息,包括Javascript内容,不过比较难删除
        // 因为有些页面文字是用js显示的,还是保留的比较好
        goods.setSeller(m.group(2));

        // 查找运费
        m = pShipping.matcher(str);
        if (m.find()) {
          goods.setShipping(new BigDecimal(m.group(1)));
        }
        // 将商品加入列表
        list.add(goods);
      } else {
        // 没有找到价格,则这部分不包含商品信息,无需继续
        continue;
      }
    }
    return list;
  }

  private static String buildTable(List<AmazonGoods> list) {
    StringBuilder b = new StringBuilder("<table>");
    b.append("<tr><th>价格</th><th>运费</th><th>商家信息</th></tr>");
    for (AmazonGoods goods : list) {
      b.append("<tr><th>" + goods.getPrice() + "</th><th>" + goods.getShipping() + "</th><th>" + goods.getSeller()
          + "</th></tr>");
    }
    b.append("</table>");
    return b.toString();
  }

  private static void saveToMySQL(List<AmazonGoods> list) {
    // 这里就用最原始的方法获得数据库连接了。
    // 数据库结构请参考AmazonGoods.sql
    // 使用test的数据库
    Connection con = null;
    PreparedStatement st = null;
    String url = "jdbc:mysql://localhost:3306/";
    String db = "test";
    String driver = "com.mysql.jdbc.Driver";
    String user = "test";
    String pass = "test";
    BigDecimal ZERO = new BigDecimal("0");
    try {
      Class.forName(driver);
      con = DriverManager.getConnection(url + db, user, pass);
      st = con.prepareStatement("insert into AmazonGoods (price,shipping,seller) values(?,?,?)");
      for (AmazonGoods goods : list) {
        st.setBigDecimal(1, goods.getPrice());
        st.setBigDecimal(2, goods.getShipping()==null?ZERO:goods.getShipping());
        st.setString(3, goods.getSeller());
        if (st.executeUpdate() <= 0) {
          throw new Exception("保存数据错误!");
        }
        st.clearParameters();
      }

    } catch (Exception ex) {
      ex.printStackTrace();
    } finally {
      if (st != null) {
        try {
          st.close();
        } catch (Exception ex) {
        }
      }
      if (con != null) {
        try {
          con.close();
        } catch (Exception ex) {
        }
      }
    }
  }
}



四、辅助类 PageService.java

  1. package com.laozizhu.tools;   
  2.   
  3. import java.io.BufferedReader;   
  4. import java.io.FileNotFoundException;   
  5. import java.io.InputStream;   
  6. import java.io.InputStreamReader;   
  7. import java.io.OutputStream;   
  8. import java.net.ConnectException;   
  9. import java.net.HttpURLConnection;   
  10. import java.net.URL;   
  11. import java.util.zip.GZIPInputStream;   
  12.   
  13. /**  
  14.  * 读取URL的文本工具  
  15.  *   
  16.  * @author 赵学庆 <A href="www.java2000.net" target=_blank>www.java2000.net</A>  
  17.  */  
  18. public class PageService {   
  19.   private static final String BR = "\r\n";   
  20.   
  21.   /**  
  22.    * 读取文本。默认使用UTF-8编码  
  23.    *   
  24.    * @param page  
  25.    *          页面的URL,比如 <A href="http://www.java2000.net" target=_blank>http://www.java2000.net</A>  
  26.    * @return 读取到的文本字符串  
  27.    */  
  28.   public static String getPage(String page) {   
  29.     return getPage(page, "UTF-8");   
  30.   }   
  31.   
  32.   /**  
  33.    * 读取文本  
  34.    *   
  35.    * @param page  
  36.    *          页面的URL,比如 <A href="http://www.java2000.net" target=_blank>http://www.java2000.net</A>  
  37.    * @param charset  
  38.    *          页面的编码  
  39.    * @return 读取到的文本字符串  
  40.    */  
  41.   public static String getPage(String page, String charset) {   
  42.     String str = null;   
  43.     int count = 3;   
  44.     do {   
  45.       str = _getPage(page, charset);   
  46.       if (str == null || str.length() == 0) {   
  47.         try {   
  48.           Thread.sleep(1000);   
  49.         } catch (InterruptedException e) {   
  50.           e.printStackTrace();   
  51.         }   
  52.       }   
  53.     } while (str == null && count-- > 0);   
  54.     return str;   
  55.   }   
  56.   
  57.   private static String _getPage(String page, String charset) {   
  58.     try {   
  59.       URL url = new URL(page);   
  60.       HttpURLConnection con = (HttpURLConnection) url.openConnection();   
  61.       // 增加了浏览器的类型,就用Firefox好了,也许   
  62.       con   
  63.           .setRequestProperty(   
  64.               "User-Agent",   
  65.               "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727)");   
  66.       int index = page.indexOf("/"10);   
  67.       con.setRequestProperty("Host", index == -1 ? page.substring(7) : page   
  68.           .substring(7, index));   
  69.       InputStream is = con.getInputStream();   
  70.       if (con.getContentEncoding() != null  
  71.           && con.getContentEncoding().equalsIgnoreCase("gzip")) {   
  72.         is = new GZIPInputStream(con.getInputStream());   
  73.       }   
  74.       BufferedReader reader = new BufferedReader(new InputStreamReader(is,   
  75.           charset));   
  76.       StringBuilder b = new StringBuilder();   
  77.       String line;   
  78.       while ((line = reader.readLine()) != null) {   
  79.         b.append(line);   
  80.         b.append(BR);   
  81.       }   
  82.       reader.close();   
  83.       return b.toString();   
  84.     } catch (FileNotFoundException ex) {   
  85.       System.out.println("NOT FOUND:" + page);   
  86.       return null;   
  87.     } catch (ConnectException ex) {   
  88.       System.out.println("Timeout:" + page);   
  89.       return null;   
  90.     } catch (Exception ex) {   
  91.       ex.printStackTrace();   
  92.       return null;   
  93.     }   
  94.   }   
  95.   
  96.   public static String postPage(String page, String msg) throws Exception {   
  97.     URL url = new URL(page);   
  98.     HttpURLConnection con = (HttpURLConnection) url.openConnection();   
  99.     con.setDoOutput(true); // POST方式   
  100.     con   
  101.         .setRequestProperty(   
  102.             "User-Agent",   
  103.             "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727)");   
  104.     int index = page.indexOf("/"10);   
  105.     con.setRequestProperty("Host", index == -1 ? page.substring(7) : page   
  106.         .substring(7, index));   
  107.     con.setRequestMethod("POST");   
  108.     con.addRequestProperty("Content-Type""application/x-www-form-urlencoded");   
  109.     OutputStream os = con.getOutputStream(); // 输出流,写数据   
  110.     os.write(msg.getBytes("UTF-8"));   
  111.     InputStream is = con.getInputStream();   
  112.     if (con.getContentEncoding() != null  
  113.         && con.getContentEncoding().equalsIgnoreCase("gzip")) {   
  114.       is = new GZIPInputStream(con.getInputStream());   
  115.     }   
  116.     BufferedReader reader = new BufferedReader(new InputStreamReader(is,   
  117.         "UTF-8")); // 读取结果   
  118.     StringBuilder b = new StringBuilder();   
  119.     String line;   
  120.     while ((line = reader.readLine()) != null) {   
  121.       b.append(line);   
  122.       b.append(BR);   
  123.     }   
  124.     os.close();   
  125.     reader.close();   
  126.     return b.toString();   
  127.   }   
  128. }  

你可能感兴趣的:(java,html,数据结构,mysql,面试)