多线程网页抓取程序的分析与改进.doc_第1页
多线程网页抓取程序的分析与改进.doc_第2页
多线程网页抓取程序的分析与改进.doc_第3页
多线程网页抓取程序的分析与改进.doc_第4页
多线程网页抓取程序的分析与改进.doc_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

一、GetWeb类源代码分析通过对程序代码分析,绘制程序执行流程概要如下:详细功能流程图如下(为了便于理解程序执行过程,流程图中的变量标识与源代码中的变量名并不对应):u 详细功能流程图中变量标识与源代码中变量名的对应关系:流程图中的变量源程序中的变量数据类型(java)描述startURLstrHomePageString主页地址unProcessURLsarrUrlsArrayList未处理URL集合maxDeepwebDepthint最大爬虫深度DxxxURLdeepUrls中的value值int某URL的深度值threadNum创建抓取线程时的循环变量i int循环变量imaxThreadintThreadNumint最大线程数u GetWeb类的主要字段和方法的功能概述字段摘要java.util.HashtableallUrls 存储所有URL的编号java.util.ArrayListarrUrl 存储所有URL供建立索引java.util.ArrayListarrUrls存储未处理的URLjava.lang.Stringcharset字符集, 默认为GB2312java.util.HashtabledeepUrls 存储URL的爬虫深度java.lang.StringfPath储存网页文件的目录名, 默认为webintintThreadNum最大线程数, 默认为10intintWebIndex 网页文件对应下标, 从0开始java.lang.StringmyDomain 主页地址中的域名java.lang.Stringreport 网页抓取报告longstartTime 抓取开始时刻java.lang.StringstrHomePage主页地址, 即起始URLintwebDepth爬虫深度, 默认为2intwebFailed 抓取失败的URL计数intwebSuccessed 抓取成功的URL计数方法摘要voidaddReport(java.lang.String s)生成报告文件, 将内容s添加到报告文件中(多线程同步操作)voidaddWebFailed()抓取失败URL计数(多线程同步操作)voidaddWebSuccessed()抓取成功URL计数(多线程同步操作)java.lang.StringgetAUrl()从未处理URL集合中取一个URL, 并将这个URL从未处理URL集合中删除(多线程同步操作)java.lang.StringgetDomain()判断用户所提供URL是否为域名地址java.lang.IntegergetIntWebIndex()生成一个新的网页文件下标(多线程同步操作)java.lang.StringgetUrl()从所有URL集合中取一个URL, 并将这个URL从所有URL集合中删除(多线程同步操作)voidgetUrlByString(java.lang.String inputArgs, java.lang.String strUrl)提取网页文本中的URL链接(解析新的网页,提取其中含有的链接信息)voidgetWebByHomePage()由用户提供的域名站点开始,对所有链接页面进行抓取voidgetWebByUrl(java.lang.String strUrl, java.lang.String charset, java.lang.String fileIndex)抓取URL的网页文本并从中提取URL链接(对后续解析出的url进行抓取)程序运行时,根据用户输入的主页地址和最大爬虫深度调用getWebByHomePage方法,首先将主页URL添加到arrUrl和allUrls队列中,为其分配初始编号0,并设置爬虫深度为1 ;然后调用getWebByUrl方法抓取主页内容,将其存入网页文件中,同时从抓回的网页文本中提取链接(使用getUrlByString方法)存入arrUrls队列和arrUrl队列中。同时在allUrls队列中为每个URL分配一个网页编号,在deepUrls中添加并设置每个URL的爬虫深度(父链接爬虫深度+1,这里为1+1=2)。接下来,循环创建10个抓取线程开始网页抓取。网页抓取线程不断从arrUrls队列中取出链接,抓取其网页内容。接着判断网页爬虫深度若大于最大爬虫深度则继续从arrUrls中取新的URL进行抓取,否则,要从刚刚抓取到的网页文本中提取链接,存入arrUrls队列和arrUrl队列,在allUrls队列中为每个URL分配网页编号,在deepUrls中设置每个URL的爬虫深度。然后才从arrUrls中取新的URL进行抓取。值得强调的是,后续添加URL到arrUrls时,要判断这个URL是不是已经抓取过了,如果已经抓取过了就不再加入待抓取URL队列了。最终,当arrUrls队列中的URL全部被取完时,程序终止抓取,从arrUrl提取网页文件索引信息输出到文件fileindex.txt中。同时,在程序整个运行过程中,每一步的调试信息都输出在report.txt文件中。二、存在问题及改进方法通过对所给程序代码分析,结合真实的Internet网络环境,我们不难看出该程序仍有以下几点不足:1程序从网页文本中提取URL时,只考虑了完整格式的URL的提取,而忽略了相对地址和绝对地址的提取。这显然是不符合实际情况的。因为大多数网站的站内链接大多为相对地址或绝对地址,这种链接占Internet链接中的很大一部分。虽然同一个站点的页面爬虫的抓取深度有限,但忽略站内所有相对链接和绝对链接会失去很多页面信息,造成抓取页面不完整,这对后期网页索引等工作是很不利的。完整格式的URL示例:Store绝对地址的URL示例:iPhone相对地址的URL示例:Mac因此,我们可以在getUrlByString方法中添加相应的代码来对从a标签href域中提取出的链接信息进行分析处理,使之能够抓取到相对地址和绝对地址指向的页面。但在提取链接信息时,同时也会提取到一些诸如:JavaScript(JavaScript脚本)、mailto(电子邮箱地址)、#(本页面)之类的信息,对于这些信息我们需要丢弃。2在通过URL抓取网页过程中,程序没有考虑不同的HTTP连接状态,如:访问的URL暂时无法提供服务(HTTP状态码503:Service Unavailable)。这种情况下,原来的程序似乎无能为力。对此,我们可以在getWebByUrl方法中添加相应的代码来获取HTTP连接状态码,然后根据不同的状态,做出相应的处理。一般常见的状态码是:200(正常)、3XX(重定向)、403(禁止访问)、404(未找到页面)、503(服务暂不可用)。显然只有在状态码为200或3XX时,我们才需要抓取对应网页内容,其他情况均无需抓取。三、改进后程序流程如下是对程序改进部分的流程概要:1对相对地址和绝对地址的处理如下图左(因为不是匹配完整格式的网页,所以需要从匹配到的链接中去除JavaScript、mailto、#等无效链接)2处理HTTP连接的不同状态码如下图右注:其余部分代码功能与原程序基本相同,故未在下图中画出。四、改进后程序代码及运行结果说明GetWeb.javaimport java.io.File;import java.io.BufferedReader;import java.io.FileOutputStream;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.PrintWriter;import .HttpURLConnection;import .MalformedURLException;import .URL;import java.util.ArrayList;import java.util.regex.Matcher;import java.util.regex.Pattern;import java.util.Hashtable;/* * 网页抓取爬虫 * author unknown * 代码修改注释: * 1. 代码改动主要在getWebByUrl方法和getUrlByString方法中 * 2. 修改了getDomain方法使之变为通用的获取主机域名的方法 * 3. 添加了三个方法: * (1)getHomePage方法, 从指定URL中提取协议部分和主机部分(一般此部分即为站点主页地址) * (2)isRedirectStatusCode方法, 判断指定HTTP状态码是否为重定向状态码3XX * (3)toFullAddress方法, 将指定URL转换为完整格式的URL并返回 */public class GetWeb /* * 爬虫深度, 默认为2 */private int webDepth = 2;/* * 线程数,默认为10 */private int intThreadNum = 10;/* * 主页地址, 即起始URL */private String strHomePage = ;/* * 域名 */private String myDomain;/* * 储存网页文件的目录名, 默认为web */private String fPath = web;/* * 存储未处理URL */private ArrayList arrUrls = new ArrayList();/* * 存储所有URL供建立索引 */private ArrayList arrUrl = new ArrayList();/* * 存储所有URL的网页号 */private Hashtable allUrls = new Hashtable();/* * 存储所有URL的深度 */private Hashtable deepUrls = new Hashtable();/* * 网页对应文件下标, 从0开始 */private int intWebIndex = 0;/* * 字符集, 默认为GB2312 */private String charset = GB2312;/* * 抓取网页报告 */private String report = ;/* * 开始时刻 */private long startTime;/* * 抓取成功的URL计数 */private int webSuccessed = 0;/* * 抓取失败的URL计数 */private int webFailed = 0;/* * 指定起始URL实例化网页抓取类的对象 * param s 起始URL */public GetWeb(String s) this.strHomePage = s;/* * 指定起始URL和爬虫深度实例化网页抓取类的对象 * param s 起始URL * param i 爬虫深度 */public GetWeb(String s, int i) this.strHomePage = s;this.webDepth = i;/* * 抓取成功URL计数(多线程同步操作) */public synchronized void addWebSuccessed() webSuccessed+;/* * 抓取失败URL计数(多线程同步操作) */public synchronized void addWebFailed() webFailed+;/* * 生成报告文件, 将内容s添加到报告文件中(多线程同步操作) * param s 报告内容 */public synchronized void addReport(String s) try report += s;PrintWriter pwReport = new PrintWriter(new FileOutputStream(report.txt);pwReport.println(report);pwReport.close(); catch (Exception e) System.out.println(生成报告文件失败!);/* * 从未处理的URL集合中取一个URL, 并将这个URL从未处理URL集合中删除(多线程同步操作) * return 一个URL */public synchronized String getAUrl() String tmpAUrl = arrUrls.get(0);arrUrls.remove(0);return tmpAUrl;/* * 从所有URL集合中取一个URL, 并将这个URL从所有URL集合中删除(多线程同步操作) * return 一个URL */public synchronized String getUrl() String tmpUrl = arrUrl.get(0);arrUrl.remove(0);return tmpUrl;/* * 生成一个新的网页文件下标(多线程同步操作) * return 网页文件下标 */public synchronized Integer getIntWebIndex() intWebIndex+;return intWebIndex;public static void main(String args) if (args.length = 0 | args0.equals() / 如果输入的起始URL为空, 则提示未输入URL, 程序退出System.out.println(No input!);System.exit(1); else if (args.length = 1) / 如果只输入了起始URL, 则默认爬虫深度为2, 开始网页抓取GetWeb gw = new GetWeb(args0);gw.getWebByHomePage(); else / 如果输入了起始URL和爬虫深度, 则按指定的爬虫深度开始抓取网页GetWeb gw = new GetWeb(args0, Integer.parseInt(args1);gw.getWebByHomePage();/* * 由用户提供的域名站点开始,对所有链接页面进行抓取 */public void getWebByHomePage() / 设置起始时刻startTime = System.currentTimeMillis();/ 获取主页域名this.myDomain = getDomain(strHomePage);if (myDomain = null) / 主页域名为空则出错返回System.out.println(Wrong input!);/ System.exit(1);return;/ 输出相关调试信息, 将主页地址添加到初始URL集合中System.out.println(Homepage = + strHomePage);addReport(Homepage = + strHomePage + !n);System.out.println(Domain = + myDomain);addReport(Domain = + myDomain + !n);arrUrls.add(strHomePage);arrUrl.add(strHomePage);allUrls.put(strHomePage, 0);/ 设置初始URL的爬虫深度deepUrls.put(strHomePage, 1);/ 建立存放网页文件的目录File fDir = new File(fPath);if (!fDir.exists() fDir.mkdir();/ 开始抓取网页System.out.println(Start!);this.addReport(Start!n);String tmp = getAUrl(); / 取一个新的URL/ 对新URL所对应的网页进行抓取this.getWebByUrl(tmp, charset, allUrls.get(tmp) + );/ 建立多个线程, 从URL集合中取链接进行抓取for (int i = 0; i 0) urlStr = conn.getHeaderField(Location);urlStr = toFullAddress(urlStr, strUrl); / 处理相对地址url = new URL(urlStr);conn = (HttpURLConnection) url.openConnection();conn.setInstanceFollowRedirects(false);conn.setReadTimeout(30000);/ 设定timeout时间conn.connect();/ 连线status = conn.getResponseCode();isAccess = true;staStr = Page has redirect!;break;/* * modify end */ 网页文件的路径String filePath = fPath + /web + fileIndex + .htm;PrintWriter pw = null;/ 初始化网页文件及相关输入输出流FileOutputStream fos = new FileOutputStream(filePath);OutputStreamWriter writer = new OutputStreamWriter(fos);pw = new PrintWriter(writer);if (isAccess = true) / 如果URL访问正常, 则开始获取网页内容InputStream is = null;is = url.openStream(); / 获取URL的输入流BufferedReader bReader = new BufferedReader(new InputStreamReader(is);StringBuffer sb = new StringBuffer();String rLine = null;String tmp_rLine = null;/ 开始读取网页文本内容while (rLine = bReader.readLine() != null) tmp_rLine = rLine;int str_len = tmp_rLine.length();if (str_len 0) / 如果网页文本内容不为空sb.append(n + tmp_rLine); / 将网页文本存入字符串变量pw.println(tmp_rLine); / 将网页文本存入网页文件中pw.flush();if (deepUrls.get(strUrl) = 300 & status = 307) ? true : false;/* * 从URL中提取默认主页地址 * param url URL链接 * return URL中包含的主页地址 */public String getHomePage(String url) String homepage = url;for (int i = 0, j = 0; i homepage.length(); i+) if (homepage.charAt(i) = /) j+;if (j = 3) homepage = homepage.substring(0, i);break;return homepage;/* * 将一个相对URL地址转换为绝对地址 * param urlAddress 相对地址 * param baseUrl 基地址 * return 若是相对地址则返回对应的绝对地址, 否则不做修改 * throws MalformedURLException */public String toFullAddress(String urlAddress, String baseUrl)throws MalformedURLException String absAddress = urlAddress;/ 相对地址与绝对地址URL u = new URL(baseUrl);if (absAddress.indexOf(:/) = -1) / 处理绝对地if (absAddress.charAt(0) = /) absAddress = http:/ + u.getHost() + absAddress; else String file = u.getFile();/ 处理相对地址if (file.indexOf(/) = -1) absAddress = http:/ + u.getHost() + absAddress; else String path = file.substring(0, file.lastIndexOf(/) + 1);absAddress = http:/ + u.getHost() + path + absAddress;return absAddress;/* * 判断用户所提供URL是否为域名地址 * return 域名 */public String getDomain(String homePage) String reg = (?=http:/a-zA-Z0-90,100.0,1).s*?.(com|cn|net|org|biz|info|cc|tv);Pattern p = Ppile(reg, Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(homePage); / 使用指定正则表达式匹配主页地址boolean blnp = m.find();if (blnp = true) / 如果匹配成功则返回主页域名return m.group(0);return null;/* * 提取网页文本中的URL链接(解析新的网页,提取其中含有的链接信息) * param inputArgs URL对应的网页文本 * param strUrl 网页文本对应的URL * throws MalformedURLException */public void getUrlByString(String inputArgs, String strUrl)throws MalformedURLException String tmpStr = inputArgs;String regUrl = (?*;Pattern p = Ppile(regUrl, Pattern.CASE_INSENSITIVE);Matcher m = p.matcher(tmpStr);boolean blnp = m.find();while (blnp = true) /* * modify by yang 2011.12.22 */String link = m.group(0);/ 跳过链到本页面内链接if (link.charAt(0) = #) continue;/ 跳过mailto链接if (link.indexOf(mailto:) != -1) continue;/ 跳过JavaScript链接if (link.toLowerCase().indexOf(javascript) != -1) continue;/ 处理相对地址与绝对地址link = toFullAddress(link, strUrl);/ 从link链接中提取域名String tDomain = getDomain(getHomePage(link);/* * modify end */ 如果解析到的URL没有包含在所有URL中,且URL中的域名与主页域名一致/ 则将之加入URL集合, 并向报告文件中添加相关记录信息if (!allUrls.containsKey(link) & tDomain.equals(myDomain) / 输出调试信息, 同时将之记录到报告文件中System.out.println(Find a new url,depth:+ (deepUrls.get(strUrl) + 1) + + link);addReport(Find a new url,depth: + (deepUrls.get(strUrl) + 1)+ + link + n);/ 将URL添加到未处理URL集合中arrUrls.add(link);/ 将URL添加到所有URL集合中arrUrl.add(link);/ 为URL生成一个网页号allUrls.put(link, getI

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论