實現網絡數據提取你需要哪些java知識


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

Ⅰ  首先對於一個java開發的項目有一個整體性的了解認知,項目開發流程:

項目階段:

1) 項目准備:

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

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

  c) 數據庫設計和網站(產品)原型設計

  d) 架構設計

2) 項目開發

  a) 項目組長(PMPL)進行項目的時間規划,並划分好每個人的工作任務

  b) 程序員主要完成項目代碼編寫和詳細設計文檔編寫。(用戶手冊)

3) 測試

  a) 單元測試

  b) 集成測試

  c) 壓力測試

  d) 回歸測試

4) 上線實施

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

封裝

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

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

      同時,類中在創建對象時,為了方便,一般建議編寫一些帶參數的構造方法。

      如果不編寫構造方法,程序會自動加入一個無參的構造,但是,如果自己聲明了構造方法,那么必須就手工加入一個無參構造,為了讓其他的框架可以通過無參數構造來創建對象。

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

      這種類被稱為:VOValue Object)、TOTransfer Object)、POJOPlain Olds Java Object)、DTODaTa Object

 1 class Person {
 2     private String name;
 3     private Integer age;
 4     public Person() {
 5     }
 6     public Person(String name, Integer age) {
 7         this.name = name;
 8         this.age = age;
 9     }
10     public String getName() {
11         return this.name;
12     }
13     public Integer getAge() {
14         return age;
15     }
16     public void setAge(Integer age) {
17         this.age = age;
18     }
19     public void setName(String name) {
20         this.name = name;
21     }
22 }

技巧:在eclipse中編寫封裝類,可以聲明變量后,按shift+Alt+s鍵出現Generate  Getters  and  Setters提示創建getter和setter方法。

繼承關系

繼承所使用的關鍵字:extends,接口實現所使用的關鍵字是:implements

Java開發中對於接口和抽象類區別主要是單繼承和多繼承

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

一般都是以接口作為標准來進行聲明。

這部分我們要求能夠掌握接口的聲明和實現方法。

 1 interface Animal {
 2     public void cry();
 3     public void run();
 4 }
 5 class Cat implements Animal {
 6 
 7     @Override
 8     public void cry() {
 9         System.out.println("miao");
10     }
11     @Override
12     public void run() {
13         System.out.println("貓爬樹");
14     }
15 }

多態

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

 1 package org.liky.test;
 2 public class InterfaceDemo {
 3     public static void main(String[] args) {
 4         Animal a1 = new Cat();  5 Animal a2 = new Dog();  6         a1.cry();
 7         a2.cry();        
 8     }
 9 }
10 interface Animal {
11     public void cry();
12     public void run();
13 }
14 class Cat implements Animal {
15     @Override
16     public void cry() {
17         System.out.println("miao");
18     }
19     @Override
20     public void run() {
21         System.out.println("貓爬樹");
22     }
23 }
24 class Dog implements Animal {
25     @Override
26     public void cry() {
27         System.out.println("Wang");
28     }
29     @Override
30     public void run() {
31         System.out.println("狗游泳");
32     }
33 }

單例設計模式

單例模式有以下特點:

  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操作

文件內容讀取:

FileFileReaderFileWriterBufferedReaderBufferedWriterScannerInputStreamReader

文件夾遍歷:

File 

文件復制操作

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

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

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

a) 讀:FileReader

b) 寫:FileWriter

2) 字節流:所有文件都可以使用這種流操作

a) 讀:InputStream

b) 寫:OutputStream

 

需要能夠通過我們這里的FileReaderFileWriter配合文件類: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(緩沖流)

 

 1     public static void writeData(String filePath, String... data) {
 2         // 先有一個文件,來保存要寫出的數據
 3         File file = new File(filePath);
 4         // 建立輸出流對象
 5         try {
 6             // FileWriter writer = new FileWriter(file);
 7             PrintWriter pw = new PrintWriter(file);
 8             // 開始完成內容的輸出
 9             for (String str : data) {
10                 pw.println(str);
11             }
12             // 資源必須回收,也就是必須將流關閉
13             pw.close();
14         } catch (IOException e) {
15             e.printStackTrace();
16         }
17     }

 

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

 

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

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

ScannerJDK1.5新的

BufferedReaderJDK1.0就有的,所以使用BufferedReader

為什么現在還使用BufferedReader,因為Scanner不支持編碼的轉換

 

 1     public static String readData(String filePath) {
 2         // 也要建立文件對象
 3         File file = new File(filePath);
 4         // 建立讀取的輸入流對象
 5         try {
 6             FileReader reader = new FileReader(file);
 7             BufferedReader bw = new BufferedReader(reader);
 8             // 每次調用read可以讀取一個字符,
 9             // 按照int類型返回,返回的是字符的編碼,
10             // 需要通過強制類型轉換,變為char類型
11             // Java中對於String這個類一般不建議反復修改,因為會占用內存。
12             StringBuilder builder = new StringBuilder();
13             // 因為文件中有很多的字符,因此需要循環來進行內容的讀取。
14             // 就需要判斷是否還有字符進行讀取
15             String line = null;
16             // 每次讀取時,如果讀到內容,則會返回 0 - 65535 的char類型字符
17             // 如果沒有讀取到內容,則返回 -1 ,因此我們可以根據這個 -1 來判斷后面是否還有內容
18             while ((line = bw.readLine()) != null) {
19                 // 將讀取到的內容保存下來
20                 // 把字符放入到StringBuilder里
21                 builder.append(line);
22                 System.out.println(line);
23             }
24             // 沒有讀取到內容,說明循環結束,已經到了文件的末尾
25             return builder.toString();
26         } catch (Exception e) {
27             e.printStackTrace();
28         }
29 
30         return null;
31     }

 

 

 

 

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

 

 1     public static void copyFile(String inputFile, String outputPath) {
 2         // 首先建立輸入和輸出的文件
 3         File input = new File(inputFile);
 4         File output = new File(outputPath);
 5         // 建立輸入和輸出流
 6         try {
 7             BufferedReader br = new BufferedReader(new FileReader(input));
 8             PrintWriter pw = new PrintWriter(output);
 9             // 每次讀入一行,所以准備一個變量來接收
10             String line = null;
11             while ((line = br.readLine()) != null) {
12                 pw.println(line);
13             }
14             pw.close();
15             br.close();
16         } catch (Exception e) {
17             e.printStackTrace();
18         }
19     }

 

文件夾迭代

這里只需要用到一個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:以鏈表的形式存儲,適合經常變動的數據集,但是不經常查詢

 

 1 public class ListDemo {
 2     public static void main(String[] args) {
 3         LinkedList<Integer> list1 = new LinkedList<>();
 4         for (int i = 0; i <= 100000; i++) {
 5             list1.add(i);
 6         }
 7         long start = System.currentTimeMillis();
 8         for (int i = 0; i <= 100000; i++) {
 9             list1.get(i);
10         }
11         long end = System.currentTimeMillis();
12         System.out.println("ArrayList: " + (end - start) + " ms");
13     }
14 }

 

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

    HashSet:散列排序(沒有順序)

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

 

 1 public class SetDemo {
 2     public static void main(String[] args) {
 3         Set<Person> set = new TreeSet<>();
 4         set.add(new Person("張三", 12));
 5         set.add(new Person("李四", 22));
 6         set.add(new Person("王五", 42));
 7         set.add(new Person("王八", 42));
 8         set.add(new Person("趙六", 32));
 9         System.out.println(set);
10     }
11 }
12 
13 class Person implements Comparable<Person> {
14     private String name;
15     private Integer age;
16 
17     public Person() {
18         super();
19     }
20 
21     public Person(String name, Integer age) {
22         super();
23         this.name = name;
24         this.age = age;
25     }
26 
27     public String getName() {
28         return name;
29     }
30 
31     public void setName(String name) {
32         this.name = name;
33     }
34 
35     public Integer getAge() {
36         return age;
37     }
38 
39     public void setAge(Integer age) {
40         this.age = age;
41     }
42 
43     @Override
44     public String toString() {
45         return "Person [name=" + name + ", age=" + age + "]";
46     }
47     @Override
48     public int compareTo(Person o) {
49         if (this.age > o.age) {
50             return 1;
51         } else if (this.age < o.age) {
52             return -1;
53         }
54         if (this.name.equals(o.name)) {
55             return 0;
56         }
57         return 1;
58     }
59 }

 

Mapkey-value形式,可以根據key取得valuekey按照Set集合的模式來保存。

    HashMap:散列

    TreeMap:有順序

對於Map集合,要求能夠循環迭代出里面的所有數據。

所以必須掌握Map的循環方法。

 

 1 public class MapDemo {
 2     public static void main(String[] args) {
 3         Map<String, Integer> map = new HashMap<>();
 4         map.put("方便面", 20);
 5         map.put("火腿腸", 120);
 6         map.put("礦泉水", 20);
 7         map.put("可樂", 30);
 8 
 9         // Map集合如果想迭代必須先按照key來進行迭代,
10         // 再根key查找value
11         Set<String> keySet = map.keySet();
12         for (String key : keySet) {
13             System.out.println(key + " ---> " + map.get(key));
14         }
15     }
16 }

 

小結:

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

到此,對於網頁數據的爬寫的知識准備的可以了,下一篇我會先對一個文件進行數據爬取,然后再對網頁上的數據代碼實現爬蟲功能。

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM