1. 程式人生 > 程式設計 >Java實現網路資料提取所需知識點

Java實現網路資料提取所需知識點

本篇對一些常用的java知識做一個整合,三大特性、IO操作、執行緒處理、類集處理,目的在於能用這些只是實現一個網頁爬蟲的功能。

Ⅰ 首先對於一個java開發的專案有一個整體性的瞭解認知,專案開發流程:

專案階段:

1)專案準備:

  a)根據開會得到會議紀要,瞭解客戶的需求情況

  b)需求分析(需求分析文件)

  c)資料庫設計和網站(產品)原型設計

  d)架構設計

2)專案開發

  a)專案組長(PM,PL)進行專案的時間規劃,並劃分好每個人的工作任務

  b)程式設計師主要完成專案程式碼編寫和詳細設計文件編寫。(使用者手冊)

3)測試

  a)單元測試

  b)整合測試

  c)壓力測試

  d)迴歸測試

4)上線實施

Ⅱ  三大特性(封裝、繼承、多型)

封裝

1、  封裝重點在於一個private關鍵字,目的是為了讓類中的屬性不能直接修改或取得。也就是說,建立一個類時,所有的屬性都必須通過private進行封裝。

      既然屬性被封裝了,那麼如果想設定或取得屬性,就必須編寫getter/setter方法。

      同時,類中在建立物件時,為了方便,一般建議編寫一些帶引數的構造方法。

      如果不編寫構造方法,程式會自動加入一個無參的構造,但是,如果自己聲明瞭構造方法,那麼必須就手工加入一個無參構造,為了讓其他的框架可以通過無引數構造來建立物件。

2、  如果資料庫中有一張表,則需要你能根據表編寫一個這樣的類。

      這種類被稱為:VO(Value Object)、TO(Transfer Object)、POJO(Plain Olds Java Object)、DTO(DaTa Object)等

class Person {
  private String name;
  private Integer age;
  public Person() {
  }
  public Person(String name,Integer age) {
    this.name = name;
    this.age = age;
  }
  public String getName() {
    return this.name;
  }
  public Integer getAge() {
    return age;
  }
  public void setAge(Integer age) {
    this.age = age;
  }
  public void setName(String name) {
    this.name = name;
  }
}

技巧:在eclipse中編寫封裝類,可以宣告變數後,按shift+Alt+s鍵出現Generate Getters and Setters提示建立getter和setter方法。

Java實現網路資料提取所需知識點

繼承關係

繼承所使用的關鍵字:extends,介面實現所使用的關鍵字是:implements。

Java開發中對於介面和抽象類區別主要是單繼承和多繼承。

真正開發中介面用的更多,幾乎不編寫抽象類。

一般都是以介面作為標準來進行宣告。

這部分我們要求能夠掌握介面的宣告和實現方法。

interface Animal {
  public void cry();
  public void run();
}
class Cat implements Animal {

  @Override
  public void cry() {
    System.out.println("miao");
  }
  @Override
  public void run() {
    System.out.println("貓爬樹");
  }
}

多型

其實就是在繼承的基礎上進行方法覆寫和子類轉型。

package org.liky.test;
public class InterfaceDemo {
  public static void main(String[] args) {
    Animal a1 = new Cat();
    Animal a2 = new Dog();
    a1.cry();
    a2.cry();
  }
}
interface Animal {
  public void cry();
  public void run();
}
class Cat implements Animal {
  @Override
  public void cry() {
    System.out.println("miao");
  }
  @Override
  public void run() {
    System.out.println("貓爬樹");
  }
}
class Dog implements Animal {
  @Override
  public void cry() {
    System.out.println("Wang");
  }
  @Override
  public void run() {
    System.out.println("狗游泳");
  }
}

單例設計模式

單例模式有以下特點:

  1、單例類只能有一個例項。

  2、單例類必須自己建立自己的唯一例項。

  3、單例類必須給所有其他物件提供這一例項。

package org.liky.test;

public class TestSingleton {
  public static void main(String[] args) {
    Singleton s1 = Singleton.getInstance();
    Singleton s2 = Singleton.getInstance();
    Singleton s3 = Singleton.getInstance(); 
        //其實只建立了一個物件
     System.out.println(s1 + " --> " + s2 + " --> " + s3);    
  }
}
class Singleton {
  private static final Singleton instance = new Singleton();
  private Singleton() {
  }
  public static Singleton getInstance() {
    return instance;
  }
}

Ⅲ  IO操作

檔案內容讀取:

File、FileReader、FileWriter、BufferedReader、BufferedWriter、Scanner、InputStreamReader

資料夾遍歷:

File

檔案複製操作

如果想操作檔案的內容(對內容進行寫出和讀取),需要使用到的就是IO流中的輸入輸出操作。

這種輸入和輸出的操作流有兩種:

1)字元流:主要操作文字檔案(編寫爬蟲操作時,肯定要使用字元流來完成)

a)讀:FileReader

b)寫:FileWriter

2)位元組流:所有檔案都可以使用這種流操作

a)讀:InputStream

b)寫:OutputStream

需要能夠通過我們這裡的FileReader和FileWriter配合檔案類:File,完成內容的讀取和寫出。

/**
 * IO流操作的演示類,用來演示文字檔案的寫出和讀取
 * 
 * @author Liky
 * 
 */
public class FileTest {
  public static void main(String[] args) {
    // 寫出內容
    // writeData(
    // "D:/test.txt",// "這是“吉林一號”視訊衛星 8月9日11時25分拍攝的 九寨溝縣視訊 顯示了九寨溝縣的地形地貌 縣城呈狹長分佈 周邊山體有明顯滑坡痕跡 視訊中還可見縣城道路大部分完好 有車輛通行 一架飛機飛過 地面與空中交通並未中斷 影象提供:長光衛星技術有限公司 技術支援:北京愛太空科技發展有限公司");
    System.out.println(readData("D:/test.txt"));
  }

  /**
   * 寫出資料
   * 
   * @param filePath
   *      檔案儲存的位置
   * @param data
   *      要儲存的檔案內容資料
   */
  public static void writeData(String filePath,String data) {
    // 先有一個檔案,來儲存要寫出的資料
    File file = new File(filePath);
    // 建立輸出流物件
    try {
      FileWriter writer = new FileWriter(file);
      // 開始完成內容的輸出
      writer.write(data);
      // 資源必須回收,也就是必須將流關閉
      writer.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  /**
   * 讀取資料
   * 
   * @param filePath
   *      要讀取的檔案所在的完整路徑
   * @return 讀取出來的文件內容
   */
  public static String readData(String filePath) {
    // 也要建立檔案物件
    File file = new File(filePath);
    // 建立讀取的輸入流物件
    try {
      FileReader reader = new FileReader(file);

      // 每次呼叫read可以讀取一個字元,
      // 按照int型別返回,返回的是字元的編碼,
      // 需要通過強制型別轉換,變為char型別
      // Java中對於String這個類一般不建議反覆修改,因為會佔用記憶體。
      StringBuilder builder = new StringBuilder();
      // 因為檔案中有很多的字元,因此需要迴圈來進行內容的讀取。
      // 就需要判斷是否還有字元進行讀取
      int value = -1;
      // 每次讀取時,如果讀到內容,則會返回 0 - 65535 的char型別字元
      // 如果沒有讀取到內容,則返回 -1 ,因此我們可以根據這個 -1 來判斷後面是否還有內容
      while ((value = reader.read()) != -1) {
        // 將讀取到的內容儲存下來
        char c = (char) value;
        // 把字元放入到StringBuilder裡
        builder.append(c);
      }
      // 沒有讀取到內容,說明迴圈結束,已經到了檔案的末尾
      return builder.toString();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
}

目前這樣編寫已經可以實現內容的輸入和輸出操作了。

但是還不支援換行操作,如果想換行,需要人工進行\r\n的編寫。

如果不想人工編寫換行,就可以使用以下兩個類來完成輸入。

PrintWriter(列印流)

BufferedWriter(緩衝流)

public static void writeData(String filePath,String... data) {
    // 先有一個檔案,來儲存要寫出的資料
    File file = new File(filePath);
    // 建立輸出流物件
    try {
      // FileWriter writer = new FileWriter(file);
      PrintWriter pw = new PrintWriter(file);
      // 開始完成內容的輸出
      for (String str : data) {
        pw.println(str);
      }
      // 資源必須回收,也就是必須將流關閉
      pw.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

使用時,注意我們這裡加入了可變引數來動態傳入多個字串(即String... data)。

當讀取資料時,如果我們使用普通的讀取方式,對於換行的處理不方便。

如果想按行讀取內容,可以使用BufferedReader,Scanner

Scanner是JDK1.5新的

BufferedReader是JDK1.0就有的,所以使用BufferedReader。

為什麼現在還使用BufferedReader,因為Scanner不支援編碼的轉換。

public static String readData(String filePath) {
    // 也要建立檔案物件
    File file = new File(filePath);
    // 建立讀取的輸入流物件
    try {
      FileReader reader = new FileReader(file);
      BufferedReader bw = new BufferedReader(reader);
      // 每次呼叫read可以讀取一個字元,
      // 按照int型別返回,返回的是字元的編碼,
      // 需要通過強制型別轉換,變為char型別
      // Java中對於String這個類一般不建議反覆修改,因為會佔用記憶體。
      StringBuilder builder = new StringBuilder();
      // 因為檔案中有很多的字元,因此需要迴圈來進行內容的讀取。
      // 就需要判斷是否還有字元進行讀取
      String line = null;
      // 每次讀取時,如果讀到內容,則會返回 0 - 65535 的char型別字元
      // 如果沒有讀取到內容,則返回 -1 ,因此我們可以根據這個 -1 來判斷後面是否還有內容
      while ((line = bw.readLine()) != null) {
        // 將讀取到的內容儲存下來
        // 把字元放入到StringBuilder裡
        builder.append(line);
        System.out.println(line);
      }
      // 沒有讀取到內容,說明迴圈結束,已經到了檔案的末尾
      return builder.toString();
    } catch (Exception e) {
      e.printStackTrace();
    }

    return null;
  }

例如,將一個D盤的test.txt的內容讀取出來,再寫出到E盤的test.txt中

public static void copyFile(String inputFile,String outputPath) {
    // 首先建立輸入和輸出的檔案
    File input = new File(inputFile);
    File output = new File(outputPath);
    // 建立輸入和輸出流
    try {
      BufferedReader br = new BufferedReader(new FileReader(input));
      PrintWriter pw = new PrintWriter(output);
      // 每次讀入一行,所以準備一個變數來接收
      String line = null;
      while ((line = br.readLine()) != null) {
        pw.println(line);
      }
      pw.close();
      br.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

資料夾迭代

這裡只需要用到一個File類,但需要用裡面的一些方法來判斷是檔案還是資料夾

isFile():是否是檔案

isDirectory():是否是資料夾

還需要通過遞迴操作,將目錄下的所有子目錄也進行迭代。

多執行緒處理

使用多執行緒的目的肯定是為了提升程式的效率。

因為在進行網路資料爬取時,一般都是同時爬取多個網頁的資料,而不是單個網頁,因此在專案開發中我們需要通過多執行緒,來讓程式同時完成多個操作。

多執行緒有兩種實現方式:

1)繼承Thread類

2)實現Runnable介面

使用多執行緒時,還有兩個必須注意的方法:

1)start()啟動執行緒

2)run()編寫執行緒執行的主體。

先來完成一個倒計時功能:

public class ThreadDemo {
  public static void main(String[] args) {
    // new MyThread().start();
    // new Thread() {
    // public void run() {
    // for (int i = 10; i >= 0; i--) {
    // System.out.println("剩餘時間:" + i);
    // try {
    // Thread.sleep(100);
    // } catch (InterruptedException e) {
    // e.printStackTrace();
    // }
    // }
    // }
    // }.start();
    // new Thread(new MyRunnable()).start();
  }
}

//繼承Thread類必須重寫run類
class MyThread extends Thread {
  @Override
  public void run() {
    for (int i = 10; i >= 0; i--) {
      System.out.println("剩餘時間:" + i);
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}
class MyRunnable implements Runnable {
  @Override
  public void run() {
    for (int i = 10; i >= 0; i--) {
      System.out.println("剩餘時間:" + i);
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

類集處理

List:允許重複,可以根據下標來取得資料,會按照放入的順序來儲存資料。

    ArrayList:以陣列的形式儲存,適合不經常變動,但經常查詢的資料集。

    LinkedList:以連結串列的形式儲存,適合經常變動的資料集,但是不經常查詢

public class ListDemo {
  public static void main(String[] args) {
    LinkedList<Integer> list1 = new LinkedList<>();
    for (int i = 0; i <= 100000; i++) {
      list1.add(i);
    }
    long start = System.currentTimeMillis();
    for (int i = 0; i <= 100000; i++) {
      list1.get(i);
    }
    long end = System.currentTimeMillis();
    System.out.println("ArrayList: " + (end - start) + " ms");
  }
}

Set:不允許重複,儲存順序看心情,沒辦法根據下標取得資料

    HashSet:雜湊排序(沒有順序)

    TreeSet:二叉樹排序,按照固定的規則來排序,TreeSet中的內容必須實現一個Comparable的介面,並且必須覆寫compareTo的方法,根據給定的規則來排序。

public class SetDemo {
  public static void main(String[] args) {
    Set<Person> set = new TreeSet<>();
    set.add(new Person("張三",12));
    set.add(new Person("李四",22));
    set.add(new Person("王五",42));
    set.add(new Person("王八",42));
    set.add(new Person("趙六",32));
    System.out.println(set);
  }
}

class Person implements Comparable<Person> {
  private String name;
  private Integer age;

  public Person() {
    super();
  }

  public Person(String name,Integer age) {
    super();
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Integer getAge() {
    return age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  @Override
  public String toString() {
    return "Person [name=" + name + ",age=" + age + "]";
  }
  @Override
  public int compareTo(Person o) {
    if (this.age > o.age) {
      return 1;
    } else if (this.age < o.age) {
      return -1;
    }
    if (this.name.equals(o.name)) {
      return 0;
    }
    return 1;
  }
}

Map:key-value形式,可以根據key取得value,key按照Set集合的模式來儲存。

    HashMap:雜湊

    TreeMap:有順序

對於Map集合,要求能夠迴圈迭代出裡面的所有資料。

所以必須掌握Map的迴圈方法。

public class MapDemo {
  public static void main(String[] args) {
    Map<String,Integer> map = new HashMap<>();
    map.put("方便麵",20);
    map.put("火腿腸",120);
    map.put("礦泉水",20);
    map.put("可樂",30);

    // Map集合如果想迭代必須先按照key來進行迭代,
    // 再根key查詢value
    Set<String> keySet = map.keySet();
    for (String key : keySet) {
      System.out.println(key + " ---> " + map.get(key));
    }
  }
}

小結:

本篇對於java封裝、繼承、多型三大特性,IO操作,執行緒管理,類集處理(List、Set、Map)進行了闡述以及程式碼實現。

到此,對於網頁資料的爬寫的知識準備的可以了,下一篇我會先對一個檔案進行資料爬取,然後再對網頁上的資料程式碼實現爬蟲功能

到此這篇關於Java實現網路資料提取所需知識點的文章就介紹到這了,更多相關Java實現網路資料提取內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!