java實現簡單的網路爬蟲(爬取電影天堂電影資訊)
阿新 • • 發佈:2018-12-31
在最開始,我們要在網上下載所用到的jar包,應為這只是一個簡單的網路爬蟲所以很多包裡的內容沒有用到。
下面幾個包就可以了。並且要引入這些包。
主類Bigdata.java
import org.htmlparser.util.ParserException; public class Bigdata { public static void main(String[] args) throws ParserException { String url = "http://www.dytt8.net"; System.out.println("開始爬取網頁"); MyCrawler crawler = new MyCrawler(url); crawler.crawling(new String[] { url },url);
這個雷氏負責下載成為文字檔案的類:
這個類是解析類工具類import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Method; import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpMethodParams; public class DownLoadFile {//重新命名 /** * 根據 url 和網頁型別生成需要儲存的網頁的檔名 去除掉 url 中非檔名字元 */ public String getFileNameByUrl(String url, String contentType) { // remove http:// url = url.substring(7); // text/html型別 if (contentType.indexOf("html") != -1) {//字串中沒有html url = url.replaceAll("[\\?/:*|<>\"]", "_") + ".txt";//將正則表示式中的那些字元替換成— return url; } // 如application/pdf型別 else { return url.replaceAll("[\\?/:*|<>\"]", "_") + "." + contentType.substring(contentType.lastIndexOf("/") + 1); } } /** * 儲存網頁位元組陣列到本地檔案 filePath 為要儲存的檔案的相對地址 */ private void saveToLocal(byte[] data, String filePath,String url) {//儲存 try { DataOutputStream out = new DataOutputStream(new FileOutputStream( new File(filePath),true)); out.writeBytes(url+" "); for (int i = 0; i < data.length; i++){ out.write(data[i]); } out.writeBytes("\r\n"); out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } /* 下載 url 指向的網頁 */ public String downloadfile(String url,byte[] responseBody,String url1) {//下載 String filePath = null; /* 1.生成 HttpClinet 物件並設定引數 */ HttpClient httpClient = new HttpClient(); // 設定 Http 連線超時 5s httpClient.getHttpConnectionManager().getParams() .setConnectionTimeout(2000); /* 2.生成 GetMethod 物件並設定引數 */ GetMethod getMethod = new GetMethod(url); // 設定 get 請求超時 5s getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 2000); // 設定請求重試處理 getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler()); /* 3.執行 HTTP GET 請求 */ try { int statusCode = httpClient.executeMethod(getMethod); // 判斷訪問的狀態碼 if (statusCode != HttpStatus.SC_OK) { System.err.println("Method failed: " + getMethod.getStatusLine()); filePath = null; } /* 4.處理 HTTP 響應內容 */ // 根據網頁 url 生成儲存時的檔名 filePath = "d:\\spider\\"+url1 +".txt"; /*+ getFileNameByUrl(url, getMethod.getResponseHeader("Content-Type") .getValue());*/ saveToLocal(responseBody, filePath,url); } catch (HttpException e) { // 發生致命的異常,可能是協議不對或者返回的內容有問題 System.out.println("Please check your provided http address!"); e.printStackTrace(); } catch (IOException e) { // 發生網路異常 e.printStackTrace(); } finally { // 釋放連線 getMethod.releaseConnection(); } return filePath; } }
import java.awt.SystemColor; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.htmlparser.Node; import org.htmlparser.NodeFilter; import org.htmlparser.Parser; import org.htmlparser.Remark; import org.htmlparser.Tag; import org.htmlparser.Text; import org.htmlparser.filters.NodeClassFilter; import org.htmlparser.filters.OrFilter; import org.htmlparser.filters.TagNameFilter; import org.htmlparser.tags.LinkTag; import org.htmlparser.tags.ParagraphTag; import org.htmlparser.tags.TitleTag; import org.htmlparser.util.NodeList; import org.htmlparser.util.ParserException; import org.htmlparser.visitors.NodeVisitor; import testchartset.Testchartset; public class HtmlParserTool { // 獲取一個網站上的連結,filter 用來過濾連結 public static Set<String> extracLinks(String url, LinkFilter filter,String url2) { Set<String> links = new HashSet<String>(); try { Parser parser = new Parser(url); parser.setEncoding(Testchartset.dectedCode(url)); //parser.setEncoding("gb2312"); /*NodeFilter filtercode = new TagNameFilter("META"); NodeFilter filtercode1 = new TagNameFilter("meta"); OrFilter linkFilter1 = new OrFilter(filtercode, filtercode1); NodeList node = parser.extractAllNodesThatMatch(linkFilter1); Node matanode = (Node) node.elementAt(0); String code = matanode.getText(); int start1 = code.indexOf("charset="); code = code.substring(start1); //int end1 = code.indexOf(" "); //if (end1 == -1) //end1 = code.indexOf(">"); String encode = code.substring(8, code.length() - 1); System.out.println(encode); parser.setEncoding(encode);*/ // 過濾 <frame >標籤的 filter,用來提取 frame 標籤裡的 src 屬性所表示的連結 NodeFilter frameFilter = new NodeFilter() { public boolean accept(Node node) { if (node.getText().startsWith("frame src=")) { //System.out.println("true"); return true; } else { //System.out.println("flase"); return false; } } }; // OrFilter 來設定過濾 <a> 標籤,和 <frame> 標籤 OrFilter linkFilter = new OrFilter(new NodeClassFilter( LinkTag.class), frameFilter); // 得到所有經過過濾的標籤 NodeList list = parser.extractAllNodesThatMatch(linkFilter); for (int i = 0; i < list.size(); i++) { Node tag = list.elementAt(i); if (tag instanceof LinkTag)// <a> 標籤 { //System.out.println(tag.getText()); LinkTag link = (LinkTag) tag; String linkUrl = link.getLink();// url if(linkUrl.startsWith(url2)){ } else{ if(linkUrl.startsWith("ftp")){ linkUrl=null; }else{ linkUrl = url2+linkUrl; } } //System.out.println(linkUrl); //System.out.println(linkUrl); //if (filter.accept(linkUrl)) //System.out.println(linkUrl); if(linkUrl!=null) links.add(linkUrl); } else// <frame> 標籤 { // 提取 frame 裡 src 屬性的連結如 <frame src="test.html"/> String frame = tag.getText(); //System.out.println(frame); int start = frame.indexOf("src="); //System.out.println(start); frame = frame.substring(start); int end = frame.indexOf(" "); if (end == -1) end = frame.indexOf(">"); String frameUrl = frame.substring(5, end - 1); if (filter.accept(frameUrl)) links.add(frameUrl); } } } catch (ParserException e) { e.printStackTrace(); } return links; } public static byte[] paerserhtml(String url, LinkFilter filter){ //byte[] content = null; String context = null; Parser parser; Pattern p = Pattern.compile("\\s*|\t|\r|\n"); try { parser = new Parser(url); //parser.setEncoding("gb2312"); parser.setEncoding(Testchartset.dectedCode(url)); NodeFilter filter1 = new TagNameFilter ("title"); //NodeList nodes = parser.extractAllNodesThatMatch(filter1); NodeFilter filter2 = new TagNameFilter ("div"); OrFilter linkFilter = new OrFilter(filter1, filter2); NodeList nodes = parser.extractAllNodesThatMatch(linkFilter); if(nodes!=null) { for (int i = 0; i < nodes.size(); i++) { Node textnode = (Node) nodes.elementAt(i); String line = textnode.getText(); //System.out.println(line); String text = textnode.toPlainTextString(); //System.out.println(text); if(line.contains("Zoom")){ //System.out.println(line.replaceAll(" ", "").replaceAll("\n", "")); Matcher m = p.matcher(text); String t = m.replaceAll(""); context += t; context += "\n"; }/*else{ context += text; }*/ // if(textnode.getText() //context += textnode.toPlainTextString(); //context += textnode.getText(); } //System.out.println(context); } /*NodeFilter filter2 = new TagNameFilter ("div"); NodeFilter filter3 = new TagNameFilter ("img"); OrFilter linkFilter = new OrFilter(filter3, filter2); NodeList nodes_context = parser.extractAllNodesThatMatch(linkFilter); if(nodes_context!=null) { for (int i = 0; i < nodes_context.size(); i++) { Node textnode_context = (Node) nodes_context.elementAt(i); context += textnode_context.toPlainTextString(); context += textnode_context.getText(); } } else{ System.out.println("null"); }*/ } catch (ParserException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); }finally{ } if(context==null){ context = "頁面找不到了"; return context.getBytes(); }else{
public interface LinkFilter {
public boolean accept(String url);
}
return context.getBytes(); } } }
定義一個介面
public interface LinkFilter { public boolean accept(String url); }
佇列類
import java.util.HashSet; import java.util.PriorityQueue; import java.util.Set; import java.util.Queue; public class LinkQueue { // 已訪問的 url 集合 private static Set visitedUrl = new HashSet(); // 待訪問的 url 集合 private static Queue unVisitedUrl = new PriorityQueue(); // 獲得URL佇列 public static Queue getUnVisitedUrl() { return unVisitedUrl; } // 新增到訪問過的URL佇列中 public static void addVisitedUrl(String url) { visitedUrl.add(url); } // 移除訪問過的URL public static void removeVisitedUrl(String url) { visitedUrl.remove(url); } // 未訪問的URL出佇列 public static Object unVisitedUrlDeQueue() { return unVisitedUrl.poll(); } // 保證每個 url 只被訪問一次 public static void addUnvisitedUrl(String url) { if (url != null && !url.trim().equals("") && !visitedUrl.contains(url) && !unVisitedUrl.contains(url)) unVisitedUrl.add(url); } // 獲得已經訪問的URL數目 public static int getVisitedUrlNum() { return visitedUrl.size(); } // 判斷未訪問的URL佇列中是否為空 public static boolean unVisitedUrlsEmpty() { return unVisitedUrl.isEmpty(); } }
爬蟲類
import java.util.Set; import org.htmlparser.util.ParserException; public class MyCrawler { /** * 使用種子初始化 URL 佇列 * * @return * @param seeds * 種子URL */ private String url1 = null; private void initCrawlerWithSeeds(String[] seeds) { for (int i = 0; i < seeds.length; i++) LinkQueue.addUnvisitedUrl(seeds[i]); } public MyCrawler(String url){//建構函式 this.url1 = url.replaceAll("[\\?/:*|<>\"]", "_"); } /** * 抓取過程 * * @return * @param seeds * @throws ParserException */ public void crawling(String[] seeds,final String url2) throws ParserException { // 定義過濾器,提取以http://www.lietu.com開頭的連結 LinkFilter filter = new LinkFilter() { public boolean accept(String url) { if (url.startsWith(url2)) return true; else return false; } }; // 初始化 URL 佇列 initCrawlerWithSeeds(seeds); // 迴圈條件:待抓取的連結不空且抓取的網頁不多於1000 while (!LinkQueue.unVisitedUrlsEmpty() && LinkQueue.getVisitedUrlNum() <= 10000000) { // 隊頭URL出佇列 String visitUrl = (String) LinkQueue.unVisitedUrlDeQueue(); byte[] context = HtmlParserTool.paerserhtml(visitUrl, filter); System.out.println(LinkQueue.getVisitedUrlNum()); if (visitUrl == null) continue; DownLoadFile downLoader = new DownLoadFile(); // 下載網頁 //System.out.println(context.toString()); downLoader.downloadfile(visitUrl,context,url1); // 該 url 放入到已訪問的 URL 中 LinkQueue.addVisitedUrl(visitUrl); // 提取出下載網頁中的 URL Set<String> links = HtmlParserTool.extracLinks(visitUrl, filter,url2); // 新的未訪問的 URL 入隊 for (String link : links) { LinkQueue.addUnvisitedUrl(link); } } } }
下面這個類有點特殊也有不完善的地方,是一個解析網頁編碼的類獲得網頁到底是什麼編碼的,然後在告訴下載器解析器以什麼編碼解析下載中文。
應為電影天堂網站所有網頁都是gb2312編碼的所以我就為了成功率註釋掉了程式碼改為只返回gb2312編碼。以後改好了是可以適應任何一種編碼的。
import org.htmlparser.Node; import org.htmlparser.NodeFilter; import org.htmlparser.Parser; import org.htmlparser.filters.OrFilter; import org.htmlparser.filters.TagNameFilter; import org.htmlparser.tags.BodyTag; import org.htmlparser.tags.Html; import org.htmlparser.util.NodeIterator; import org.htmlparser.util.NodeList; public class Testchartset{ private static final String oriEncode = "UTF-8,utf-8,gb2312,gbk,iso-8859-1"; public static void main(String [] args){ String url="http://www.dytt8.net/"; String decode=dectedCode(url); System.out.println(decode); } /** * 檢測字元級 * @param url * @return */ /*public static String dectedEncode(String url) { String[] encodes = oriEncode.split(","); for (int i = 0; i < encodes.length; i++) { if (dectedCode(url, encodes[i])) { return encodes[i]; } } return null; }*/ public static String dectedCode(String url) { /*try { Parser parser = new Parser(url); //parser.setEncoding(encode); NodeFilter filtercode = new TagNameFilter("META"); NodeFilter filtercode1 = new TagNameFilter("meta"); OrFilter linkFilter = new OrFilter(filtercode, filtercode1); NodeList node = parser.extractAllNodesThatMatch(linkFilter); Node matanode = (Node) node.elementAt(0); String code = matanode.getText(); //System.out.println(code); int start = code.indexOf("charset="); code = code.substring(start); //System.out.println(code); int end = code.indexOf(" "); //System.out.println(end); if(end==-1){ end=code.length(); } String encode1 = code.substring(8, end-1); String encode2 = code.substring(9,end-1); //System.out.println(encode1+"chkc"+encode2); String[] encodes = oriEncode.split(","); if(encode1.equals(encodes[0])||encode2.equals(encodes[0])||encode1.equals(encodes[1])||encode2.equals(encodes[1])) return "utf-8"; if(encode1.equals(encodes[2])||encode2.equals(encodes[2])) return "gb2312"; if(encode1.equals(encodes[3])||encode2.equals(encodes[3])) return "gbk"; if(encode1.equals(encodes[4])||encode2.equals(encodes[4])) return "iso-8859-1"; } catch (Exception e) { }*/ return "gb2312"; } }
經過爬取了上萬個頁面這個程式停了下來
可以看到爬下來的內容我都是以網址開頭空格之後就是電影的各項內容。這樣爬取頁面也是為了在hadoop中做資料分析。我的確做了一個數據分析電影明星哪一個成就最高,
但是因為資料集的原因結果不是很理想。在之後的部落格中我可能會把分析步驟發出來。