2015年11月12日 星期四

Nutch 等 Web Crawler 的著作權問題

筆者雖然了解 Nutch 這類 Web Crawler 的運作模式和 Google 差不多, 既然 Google 可以營運, 想當然爾的, Nutch 應該也是合法的囉! 但是沒搞清楚, 心裏仍然有疑慮. 於是上網查了我國的法律, 以下幾個智財局的鏈結供讀者參考.

看過上述文章, 可以了解著作權在網路世界仍受到相當嚴密的保護, 並不是一般上網族所想到的那麼自由. 不過在 網路相關著作權問題之說明 這一篇文章有提到一段搜尋引擎的著作權放寬限制的說明, 使用 Nutch 抓取我國網站的夥伴們可以放心了. 此段文章內容如下 :

參、「搜尋引擎」
(一)網路服務業者提供的「搜尋引擎」服務,是透過軟體搜尋,將網路上所有資料下載儲存到其伺服器中,再透過自動編輯功能,使網友可以很快地找到所需要的資訊,即提供使用者有關網路資訊之索引、參考或連結之搜尋或連結之服務,例如Google、百度等,此種搜尋服務之業者所為下載儲存檔案的行為,當然是重製。然而網路業者事先未必取得所儲存檔案的著作財產權人的授權,有些人難免會產生是否會有侵害著作權的疑慮。
(二)在國際社會著作權領域的實務上,認為「搜尋引擎」重製他人著作,雖未取得著作人的授權,但因其利用的目的,是為了使網路的傳輸更有效率,而且對所重製之資料並未產生「市場替代」之效果,可以認為是合理使用。國際組織「萬維網聯盟(W3C)」對於搜尋引擎的運作,列有詳細的技術規範,基本上,搜尋引擎祇要符合該聯盟的技術規範所作的重製,應該都不會被認定為侵權行為。
(三)網路的世界是無國界的,在網路的秩序和規範方面,我國也必須遵循國際間的標準,因此,「搜尋引擎」之重製行為,不致對著作潛在市場與現在價值產生負面影響,應可認定為合理使用。
(四)又此種搜尋服務提供者對其使用者侵害他人著作權之行為,只要該等 業者對所搜尋或連結之資訊涉有侵權不知情(倘若使用者所連結之網 站張貼目前上映中的院線片供網友觀賞或下載者,難謂為不知情), 以及未直接自使用者之侵權行為獲有財產上利益者,並配合著作權人 之通知立即取下涉嫌侵權之內容,則該網路服務提供者亦可主張免除 「民事責任」。

不過, 筆者認為仍應注意以下 2 個問題 :

  1. 如果搜尋引擎的蒐集資料網站是註冊在國外的, 那麼是要遵守該國或我國的法律?
  2. 如果蒐集資料網站有公佈使用者條款限制對網站資料的使用, 是否應該要遵守? 如果未遵守是否違反著作權法? 或其他法律?

2015年11月7日 星期六

認識 Solr ( 5.4 ) -- 基礎概念

這一篇文章主要是要讓未接觸過 Solr 的讀者, 了解 Solr 是怎樣的一個軟體, 它的用途是什麼? 並將它和資料庫做個比較, 因為資料庫是程式開發人員常接觸到的平台.
或許經由這樣的比較, 讀者會有一個比較清楚的印象. 筆者參考的文件是尚未 release 的 5.4 版.

1. Solr 這類的平台的用途是什麼? Language support and linguistics in Lucene, Solr and ElasticSearch, and the eco-system 這篇文章可以看出這類平台的用途在於提供多種語言的文章快速全文檢索, 並支援透過語意查詢, 而不只是文字的相似比對.

2. Solr 的兩種模式: Standalone Solr, SolrCloud Cluster. 請看下表的比較.

表 1 : Standalone Solr 和 SolrCloud Cluster 的比較

  Standalone Solr SolrCloud Cluster
Solr 的一般功能
fault tolerance
high availability
load balancing
sharding
scale out

SolrCloud 的安裝可參考 nutch 1.8 + solr 4.9.0 探討系列三 : SolrCloud 安裝 這篇文章.

3. Solr 與 relational database 的比較 :

表 2 : Solr 與 relational database 的比較

relational database Solr
relational database 是將資料正規化, 儲存在 database 裏的 table. 比如說公司收到多張訂單, 訂單裏有訂單編號, 客戶的名稱, 送貨地址, 訂單的明細等等.正規化後, 會將訂單明細存到訂單明細 table, 而將其他資料存到訂單 table, 2 個 table 之間用訂單編號做關聯, 而 1 筆訂單 table 的資料可能關連到 1 到多筆訂單明細的資料. 如果公司有將客戶的資料儲存到客戶的 table, 那訂單 table 可能用客戶代號跟客戶 table 做關聯. 而客戶 table 除了地址之外, 可能還有聯絡電話, 聯絡人等等資料. 當我們要查詢一張訂單, 就可以用這些關聯的欄位, 將整張訂單的資料取出來或只取其中一些欄位的資料. 我們也可以用 SQL 語法一次查詢多張訂單的資料. Solr 是全文檢索軟體. 我們可以想像一個活頁簿, 當我們將一張張食譜加入這個活頁簿, 會將新加入食譜的某一欄位或多個欄位的各個詞語加入到活頁簿的第一頁做索引, 這個索引很特別, 記錄著各個詞語分別發生在那些食譜. 想像我們在 google 輸入一個或多個關鍵字時, 它就會列出有這些關鍵字的文章.
資料庫的資料儲存架構由大至小為 database(-->schema or user)-->table-->column 不像 relational database 一個資料庫有多個 table, 一個 core(SolrCloud 改稱為 collection)會有各個欄位的定義, 請想像上述所說的, 一張張的食譜有同樣的料理名稱, 食材, 製作步驟等欄位, 而這些食譜(documents)就是一個 collecton. 一個 solr server 可以有許多 core(在 SolrCloud 稱為 collection), 可能有食譜的 core, 人才資源庫的 core, 而這些 core 不像 relational database 的 table 可以做 join, 它們是各自獨立的.
relational database 使用 SQL 語法中的資料定義語言(Data Definition Language, DDL) 來建立或修改 database, schema, user, table, index, stored procedure, function, trigger 等等. 而使用資料操縱語言(Data Manipulation Language, DML) 來新增, 刪除, 修改 table 的資料. 另資料庫軟體常提供指令來 import /export 資料庫的資料及 schema, 也提供 backup/restore 資料庫的指令. 也提供圖形化介面方便使用者做上述的所有操作. Solr schema(field 等)的定義儲存在 schema.xml 中, 除了在啟動 server 前修改, 啟動 server 後亦可透過 Schema API 讀取或修改 schema. Solr server 主要的 configuration file solrconfig.xml 儲存 server 的 configuration. 而 solr.xml, core.properties 定義 core 的 configuration. Solr 提供 Configuration APIs 修改 solrconfig.xml 的設定. Solr 提供很多方式將資料加入 index. Solr 亦提供 Client APIs(有多種程式語言版本) 來執行 query, index, delete, commit, and optimize. Solr 亦提供 Backup/Restore API, 也提供簡易的 Admin 介面, 但大多是讀取系統狀態, 訊息而無法進行管理. 上述種種 APIs 都用 http request 來與 server 溝通執行.
relational database 的 table 的 column 需指定 data type, user 可以定義自己的 data type, 且每一 column 可以設定預設值. 除了在 insert 或 update data 的同時可以對資料做處理再存入 table, 也可以在 table 設定 trigger, 在資料 insert, update, delete 的前後做一些這個 table or 其他 table 資料的處理. 上述機制中除了呼叫 server 本身提供的預設 function 以外, 也可以呼叫使用者自訂 stored procedure 或 function 來處理資料. Solr 的 field 需指明是哪一種 field type. field type 也可自己定義. 一般而言, field type 定義時, class 選為 solr.TextField 時會定義 analyzer. analyzer 是由一個 charFilter(非必要), 一個 tokenizer(必要), 一至多個 filter(非必要) 依序來組成, 每一個都是一個 Java Class 實作. tokenizer 用來將field 傳入的文章斷詞, 分成一個個詞語. filter 則用來對詞語做進一步的處理, 如篩選不要的文字, 將字母轉成小寫, 同意詞的轉換, 不同國家語言的特別處理等等. 讀者可以參考以下的說明, 進一步了解. Char Filters can add, change, or remove characters while preserving the original character offsets to support features like highlighting. The job of a tokenizer is to break up a stream of text into tokens, where each token is (usually) a sub-sequence of the characters in the text. An analyzer is aware of the field it is configured for, but a tokenizer is not. Tokenizers read from a character stream (a Reader) and produce a sequence of Token objects (a TokenStream). Like tokenizers, filters consume input and produce a stream of tokens. Filters also derive from org.apache.lucene.analysis.TokenStream. Unlike tokenizers, a filter's input is another TokenStream. The job of a filter is usually easier than that of a tokenizer since in most cases a filter looks at each token in the stream sequentially and decides whether to pass it along, replace it or discard it.
relational database 的 table column 可以定義為 primary key, 確保該欄位的資料不會重複, 也可多個 column 共同定義為 primary key, 則這些 column 組合的資料在 table 裏不會重複. Solr 每一個 core 可以定義一個field 為 unique key, 也可以不設 unique key. 但一般會設 unique key, 且 unique key 只能為單一 field. 讀者可參考以下說明:The uniqueKey element specifies which field is a unique identifier for documents. Although uniqueKey is not required, it is nearly always warranted by your application design. For example, uniqueKey should be used if you will ever update a document in the index. 大部分 data import 時建 index, 都會檢查有沒有設 unique key. 筆者試出使用 Data Import Handler 的方式可以去掉 unique key.

field 的定義

<field name="my_text" type="text_general" indexed="true" stored="true"/>

field Type 的定義 -- index, query 使用不同的 analyzer

<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
  <analyzer type="index">
    <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-FoldToASCII.txt"/>
    <tokenizer class="solr.StandardTokenizerFactory"/>
    <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
    <!-- in this example, we will only use synonyms at query time
    <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
    -->
    <filter class="solr.LowerCaseFilterFactory"/>
  </analyzer>
  <analyzer type="query">
    <tokenizer class="solr.StandardTokenizerFactory"/>
    <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
    <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
    <filter class="solr.LowerCaseFilterFactory"/>
  </analyzer>
</fieldType>

field Type 的定義 -- index, query 使用相同的 analyzer

<fieldType name="nametext" class="solr.TextField">
  <analyzer>
    <tokenizer class="solr.StandardTokenizerFactory"/>
    <filter class="solr.StandardFilterFactory"/>
    <filter class="solr.LowerCaseFilterFactory"/>
    <filter class="solr.StopFilterFactory"/>
    <filter class="solr.EnglishPorterFilterFactory"/>
  </analyzer>
</fieldType>

field Type 的定義 -- 使用一個 analyzer(Java class) 來完成 charFilter, tokenizer, filter 對文字的的處理.

<fieldType name="nametext" class="solr.TextField">
  <analyzer class="org.apache.lucene.analysis.WhitespaceAnalyzer"/>
</fieldType>