唐詩分析項目設計文檔

一. 唐詩項目介紹

1.1項目背景

爬蟲技術一直處於風口浪尖,爬了不該爬的會引來一身官司,爬想爬取的數據又不會輕而易舉獲得,但是又想來玩玩爬蟲技術,怎麼辦呢?首選古詩文網,因爲據瞭解它是沒有設置任何反爬蟲機制的,數據都是公開合法的,這裏我就選擇了唐詩三百首來進行爬蟲,你們不好奇哪位唐代詩人寫的詩最多嗎?你們不好奇詩人們最喜歡用什麼詞去作詩嗎?如果你對唐詩也感興趣,那就跟我一起開啓這個奇妙的詩詞探險吧。

1.2項目需求

一.用柱狀圖來展示詩人們的姓名和詩詞數量,並按詩詞數量降序排序。
二.用詞雲來展示詞的使用頻度,使用最頻繁的詞應該一眼看出。

1.3項目設計

一.通過爬蟲機制把唐詩的數據爬取出來,對它進行解析,將每首詩的數據寫入到數據庫中。
二.通過發起請求來對數據進行分析篩選,最後渲染成可視化效果。
在這裏插入圖片描述

1.4項目工具選擇

這是一個Web項目,Java選擇用IDEA這個編譯器,選擇用maven來引進一些需要用到的第三方庫,通過tomcat這個Web應用服務器來執行。

二.唐詩數據爬取模塊

2.1技術選型環節

2.1.1爬蟲技術

我瞭解到的爬蟲技術棧有 HtmlUnit,HttpClient,這裏我選擇了HtmlUnit這個框架。

  • 原因:HttpClient是用來模擬HTTP請求的,用的是socket通信,通過get方法來提交請求,只能獲取html靜態頁面的源碼,如果頁面中有js部分,則不能獲取到js執行後的源碼。
    HtmlUnit是一款無界面的瀏覽器程序庫,它模擬用戶去操作瀏覽器,允許調用頁面,填寫表單,點擊鏈接等,還可以執行js,有很多的API用起來也非常方便。

2.1.2分詞技術

使用ansj_seg庫對古詩的標題和正文進行分詞,爲詞雲做準備。這個中文分詞器正確率高,不容易出做,分詞速度也快,效果也比較高。

2.2項目依賴環節

解析請求html頁面

<dependency>
            <groupId>net.sourceforge.htmlunit</groupId>
            <artifactId>htmlunit</artifactId>
            <version>2.36.0</version>
        </dependency>

分詞

<dependency>
            <groupId>org.ansj</groupId>
            <artifactId>ansj_seg</artifactId>
            <version>5.1.6</version>
        </dependency>

2.3預研環節

通過編寫一些Demo來熟悉這些技術的使用,看看它展示的效果是否滿意。

2.3.1解析列表頁Demo

WebClient client = new WebClient(BrowserVersion.CHROME);
        client.getOptions().setJavaScriptEnabled(false);
        client.getOptions().setCssEnabled(false);

        String baseurl = "https://so.gushiwen.org";
        String pathurl = "/gushi/tangshi.aspx";
        List<String> detailUrlList = new ArrayList<>();//所有古詩的詳情頁的url
        //列表頁的解析
        {
            String url = baseurl + pathurl;
            HtmlPage page = client.getPage(url);//獲取詩詞頁面
            List<HtmlElement> divs = page.getBody().getElementsByAttribute("div", "class", "typecont");
            for (HtmlElement div : divs) {
                List<HtmlElement> as = div.getElementsByTagName("a");
                for (HtmlElement a : as) {
                    String detailUrl = a.getAttribute("href");
                    detailUrlList.add(baseurl + detailUrl);
                }
            }
        }

2.3.2分詞Demo

public static void main(String[] args) {
        String sentence="中華人民共和國成立了!中國人民從此站起來了";
       List<Term> termlist=NlpAnalysis.parse(sentence).getTerms();
       for(Term term:termlist){
           System.out.println(term.getNatureStr()+":"+term.getRealName());
       }
    }

2.4具體開發環節

2.4.1數據庫的設計

數據庫要存取:自增主鍵id,標題,朝代,作者,正文,分詞,sha-256。

  • SHA-256是爲了防止下載重複詩詞,所以用每首詩的標題和正文去計算SHA-256值。
CREATE TABLE `tangpoetry` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `sha256` char(64) NOT NULL,
  `dynasty` varchar(10) NOT NULL,
  `title` varchar(100) NOT NULL,
  `author` varchar(10) NOT NULL,
  `content` text NOT NULL,
  `words` text NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `sha256` (`sha256`)
) ENGINE=InnoDB AUTO_INCREMENT=703 DEFAULT CHARSET=utf8mb4

2.4.2爬取,解析數據

  • 獲取列表頁解析出每首詩的url
  • 解析詳情頁得出要往數據庫中寫入的數據(標題,朝代,作者,正文,計算SHA-256,計算分詞(切記分詞要去除特殊字符,比如符號,null等)需要將分詞拼接成字符串格式,方便存儲)

2.4.3數據寫入到數據庫中

  • 通過JDBC建立數據庫連接,將標題,朝代等信息插入到數據庫的表中。

2.4.4驗證並優化—多線程

*通過sql語句查看320首詩詞數據是否成功寫入到數據庫中。如果信息無誤,則插入成功。

  • 單線程版本效率低,每次都是主線程去完成各項任務,在解析詳情頁的時候每次都是主線程,而總共有320首詩,就要挨個去解析320次,非常耗時。
  • 引入多線程版本:讓主線程去獲取列表頁,解析列表頁,得出每首詩的url,開啓多個線程,去完成列表頁解析工作,並將數據寫入到數據庫中。
    問題:1.webclient不是線程安全的,每個線程都得自己建一個webclient對象
    2.connection不是線程安全的,需要通過傳
    datasource這個參數去建立連接。
    3.MessageDigest這個計算SHA-256的也不是線程安全的。
    多線程每次都要創建320個子線程去執行任務,執行完再銷燬,顯然有點浪費資源。
  • 線程池版本:這裏用的是Executors.newFixedThreadPool(30)這個固定數目的線程池,把多線程要處理的那些任務去交給線程池裏面的線程去完成,這裏我設置的是30個核心線程。
    BUG: JVM結束是指所有非後臺線程執行結束後才關閉,而線程池一直在主線程中,根本不可能自己關閉,它會不斷地去執行任務,重複的死循環,永不停止。
    解決辦法: 只要等所有子線程都完成任務,就可以手動關閉線程池。
  • 如何判斷子線程任務是都都結束了呢?
    1.CAS原子類
private  static AtomicInteger successCount=new AtomicInteger(0);
    private  static AtomicInteger failureCount=new AtomicInteger(0);
    private static class Job implements  Runnable{
        private  void work() throws IOException, InterruptedException {
            Random random=new Random();
            int n=random.nextInt(5);
            if(n<2){
                throw  new IOException();
            }
            Thread.sleep(5);
        }
        @Override
        public void run() {
            try{
                work();
                //successCount++;
                successCount.getAndIncrement();

            }catch (IOException e){
                //failureCount++;
                failureCount.getAndIncrement();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
     for(int i=0;i<COUNT;i++){
    Thread thread=new Thread(new Job());
    thread.start();
      }
      while(successCount.get()+failureCount.get()!=COUNT){
          Thread.sleep(1000);
          System.out.println("任務還沒完成");
      }
        System.out.println("任務全部完成");
    }

2.CountDownLatch:開始會設定要等待的線程數,主線程阻塞,每執行完一個子線程就調用countDown(),此時計數器-1,直到計時器爲0時纔將主線程喚醒,不爲0 就一直await(),爲0說明子線程任務都執行完了,就可以關閉線程池了。


       CountDownLatch countDownLatch = new CountDownLatch(detailUrlList.size());//指定countDownLatch要等待的線程數
     for (String url : detailUrlList) {
            pool.execute(new Job(url,dataSource2,countDownLatch));
        }
        countDownLatch.await();//如果沒處理結束,就等待
        pool.shutdown();//最後關閉線程池
    }

2.5總結問題

  • 在創建數據庫的時候,要合理的設計每個字段的類型,不能盲目浪費資源。
  • 多線程設計,一定要考慮線程安全問題,不然出大錯。
  • 學會不斷地優化項目。

三.詩詞可視化分析模塊

3.1技術選型

3.1.1 echarts可視化

echarts是一個開源免費的javascript可視化庫,柱狀圖和詞雲都是來自於它。而且它是開源的,中文文檔,方便上手。

3.1.2 jquery的發起ajax請求

因爲echarts的數據是寫死的,所以使用jquery來發起ajax請求,用httpservlet來處理請求,返回一個json格式的字符串,等加載成功執行success方法去進行可視化,這樣數據就是從請求響應獲取的,不再是寫死的。

3.1.3 FastJson格式

返回Json格式的第三方庫有很多,比如Gson,FastJson等,我選擇FastJson主要是因爲寫起來簡單,是阿里巴巴維護的,我這個應用小,也估計碰不到它的BUG。

3.2導入依賴

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
   <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

3.3代碼開發

3.3.1 JDBC建立數據庫連接

這裏用的是餓漢的設計模式,來進行數據庫連接。

3.3.2 整理詩的數量

  • 繼承HttpServlet,通過doGet方法來處理ajax發起的http請求,用複合查詢sql語句就可以搞定,再將數據寫入到JSONArray中,返回一個Json格式的字符串。
SELECT author, count(*) AS cnt FROM tangpoetry GROUP BY author HAVING cnt >=? ORDER BY cnt DESC
  • 用@WebServlet(“rank.json”)來配置path路徑,就不需要去web.xml去配置servlet了。
  • 發起ajax請求,收到響應後去執行success裏面的方法,進行柱狀圖的渲染。
method: "get",  // 發起 ajax 請求時,使用什麼 http 方法
        url: "rank.json?condition=10",   // 請求哪個 url
        dataType: "json",   // 返回的數據當成什麼格式解析
        success: function (data) {  // 成功後,執行什麼方法

通過html頁面的script標籤找到js文件去執行。

3.3.3 整理分詞

  • 繼承HttpServlet,通過doGet方法處理請求,將分詞都先放入到list中,再用map來整理每個詞出現的次數,最後將Key和Value來放入到JSONArray中,返回一個json格式的字符串。
  • 同樣用@WebServlet("/words.json")

3.4驗證效果

在這裏插入圖片描述

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章