摘要:本文討論國家圖書館O(簡(jiǎn)稱:國圖)PAC檢索結(jié)果的頁面 MARC數(shù)據(jù)的收集的程序思路,文中的代碼使用C#語言,基于.net框架實(shí)現(xiàn)。
關(guān)鍵詞:下載國圖CNMARCC#shy;shy;編程
在日常的編目工作中,筆者經(jīng)常會(huì)使用國圖的OPAC,因?yàn)閲鴪D的CNMARC數(shù)據(jù)權(quán)威,收藏的圖書種類很齊全,更重要的是任何人都可以免費(fèi)使用它的檢索服務(wù)。圖書館采編室很多時(shí)候需要為一批數(shù)量可觀的圖書提前準(zhǔn)備MARC數(shù)據(jù),以為后續(xù)的編目工作做好準(zhǔn)備,在經(jīng)費(fèi)不足而沒有購買收費(fèi)MARC數(shù)據(jù)源的情況下,充分利用國圖OPAC的檢索服務(wù)進(jìn)行數(shù)據(jù)的收集并重新組織成標(biāo)準(zhǔn)MARC數(shù)據(jù)也許是最佳的數(shù)據(jù)準(zhǔn)備方式。筆者在國圖OPAC檢索結(jié)果的CNMARC數(shù)據(jù)的重新組裝一文中詳細(xì)討論了CNMARC記錄的重新組裝的思路,本文將主要討論從國圖檢索結(jié)果中下載收集MARC數(shù)據(jù)的思路。這里主要考慮解決以下三個(gè)關(guān)鍵的技術(shù)問題。一是如何實(shí)現(xiàn)最簡(jiǎn)化操作方式,也就是說在實(shí)際操作中不必頻繁地進(jìn)行鼠標(biāo)或鍵盤操作。這是因?yàn)榫退闶呛?jiǎn)單的復(fù)制粘貼操作,重復(fù)數(shù)百甚至上千次將是非常繁瑣的事情。二是通過怎樣的邏輯實(shí)現(xiàn)直接的結(jié)果數(shù)據(jù)的訪問,也就是說從用戶的角度來看是怎樣才能省略中間的頁面以及結(jié)果中不重要的內(nèi)容。三是對(duì)得到的結(jié)果數(shù)據(jù)如何進(jìn)行處理以及存儲(chǔ)。下面詳細(xì)討論。
一、最簡(jiǎn)化操作
這也許是最容易解決的技術(shù)問題。為了簡(jiǎn)化程序的設(shè)計(jì),可以選擇ISBN為唯一的檢索關(guān)鍵字,在這個(gè)前提下,構(gòu)思并設(shè)計(jì)出一個(gè)ISBN列表文件的導(dǎo)入功能,實(shí)現(xiàn)所有圖書ISBN的一次性導(dǎo)入,每次檢索前通過點(diǎn)擊某個(gè)按鈕讓程序讀入并顯示下一個(gè)ISBN記錄,直到所有ISBN都處理完畢。ISBN列表文件是一個(gè)簡(jiǎn)單的txt文本文件,文件里每一ISBN占據(jù)一行。這樣的實(shí)現(xiàn)方式可使效率進(jìn)一步得到提高,因?yàn)槭∪チ嗣看味家腎SBN輸入操作。但是同時(shí),程序也應(yīng)該提供單個(gè)ISBN輸入的方式,這樣就能保證輸入的靈活性。
二、結(jié)果數(shù)據(jù)的訪問
通過實(shí)際的瀏覽器操作,比如檢索ISBN “9787111338017”,選擇“中文文獻(xiàn)”,選擇ISBN字段,確定后瀏覽器導(dǎo)航到結(jié)果頁面。觀察結(jié)果頁面的URL,可以發(fā)現(xiàn)兩個(gè)關(guān)鍵串:“2R4Q2UBHG9HPDYS2NEL21626DS9AT67SBQ13JGSIFTA3YAMEIE-04983”和“request=9787111338017”。只需確定這兩個(gè)串就可以確定整個(gè)URL,因?yàn)槠渌际枪潭ǖ摹8M(jìn)一步地說,對(duì)于前面的加密串,重要的是要確定“-”字符前面的前綴部分。因此,只需保證獲取的加密串的前綴正確就可以構(gòu)造出整個(gè)結(jié)果頁面的URL了。為了獲取html頁面的特定內(nèi)容,需要定義GetPageContent和FindOneMatch兩個(gè)靜態(tài)方法?;谄紤],下面僅列出FindOneMatch的代碼:
public static string FindOneMatch(string pageContent, string pat, string groupName){
string res = \"\";
Match m = Regex.Match(pageContent, pat, RegexOptions.IgnoreCase);
if (m.Success){
res = m.Groups[groupName].ToString();
}
return res;
}
GetPageContent方法的簽名為:string GetPage Content(string url, Encoding e),該方法接受URL和一個(gè)Encoding對(duì)象,訪問指定的URL,以指定的編碼返回頁面內(nèi)容。FindOneMatch方法接受頁面內(nèi)容,模式和分組名作為參數(shù),對(duì)內(nèi)容執(zhí)行一次正則查找,返回匹配的捕獲內(nèi)容。為了獲取上述的加密串前綴,只需對(duì) “http://opac.nlc.gov.cn/F/”的頁面內(nèi)容執(zhí)行一次查找即可。其代碼如下:
string pageContent = GetPageContent(\"http://opac.nlc.gov.cn/F\", Encoding.UTF8);
string pat = \"
string resultURL=\"http://opac.nlc.gov.cn/F/\" + token +
\"?func=find-bfind_code=ISBrequest=\" +
isbn + fixedPart;
其中的fixedPart為上述討論中結(jié)果頁面URL中的其他的不變的部分。
結(jié)果頁面訪問的思路已經(jīng)確定,接下來是獲取MARC內(nèi)容,這里指的不是標(biāo)準(zhǔn)MARC數(shù)據(jù),而是以HTML格式存在的“字段名和指示符—字段內(nèi)容”的數(shù)據(jù)。觀察結(jié)果頁面的HTML源代碼,可以發(fā)現(xiàn)MARC格式以及其他格式的數(shù)據(jù)顯示是通過用js函數(shù)shiftfmt(obj, name)響應(yīng)點(diǎn)擊事件應(yīng)用AJAX技術(shù)局部更新 details2 節(jié)點(diǎn),并且可以在該函數(shù)內(nèi)發(fā)現(xiàn)數(shù)據(jù)內(nèi)容的url的構(gòu)造方法如下:
var url=\"http://opac.nlc.gov.cn:80/F/...?func=full-set-set_bodyset_number=\"+g(\"set_number\").value+\"set_entry=\"+g(\"set_entry\").value+\"format=\"+name;
var host=location.href.replace(/(\\/F\\/.*)/I,'/');
url=url.replace(/http:\\/\\/.*?\\//,host);
上面代碼中的省略號(hào)是指前述討論的加密串,而下面兩句代碼是用當(dāng)前url的主機(jī)部分替換構(gòu)造的url的主機(jī)部分。因此,只需確定set_number和set_entry以及format的值就可以構(gòu)造整個(gè)url了。而通過實(shí)際觀察發(fā)現(xiàn)format的值固定為001,只需要找到set_number和set_entry就可以了。以下是獲取MARC內(nèi)容的代碼:
string pageContent = GetPageContent(resultURL, Encoding.UTF8);
string pat = @\"\";
string set_number = FindOneMatch(pageContent, pat, \"set_number'\");
pat = @\"\";
string set_entry = FindOneMatch(pageContent, pat, \"set_entry\");
string format = \"001\";
string marcURL = \"http://opac.nlc.gov.cn/F/\" + token +
\"?func=full-set-set_bodyset_number=\" + set_number + \"set_entry=\" +
set_entry + \"format=\" + format;
string marcHtmlContent=GetPageContent(marcURL, Encoding.UTF8);
三、對(duì)獲取的MARC內(nèi)容的處理
獲取到的MARC內(nèi)容是以以下形式存在的:
</tr>
同時(shí)里面還有可能存在一些HTML特殊字符,如“#39;”,其對(duì)應(yīng)字符為“'”。因此,在提取實(shí)際內(nèi)容前,還需要將HTML特殊字符轉(zhuǎn)換成相應(yīng)的字符,但是暫不處理“#60;”和“#62;”(對(duì)應(yīng)字符分別為“<”和“>”)以及“nbsp;”,因?yàn)樵谔崛〉恼齽t模式串里還要區(qū)分HTML標(biāo)記符號(hào)以及后面對(duì)“nbsp;” 區(qū)分進(jìn)行處理。為了保存“字段名和指示符—字段內(nèi)容”形式的數(shù)據(jù),需要定義一個(gè)NameContent結(jié)構(gòu):
public struct NameContent{
public readonly string NCKey;//存儲(chǔ)字段名和指示符
public readonly string NCValue;//存儲(chǔ)字段內(nèi)容
public NameContent (string ncKey, string ncValue){
NCKey = ncKey;
NCValue = ncValue;
}
}
下面是提取內(nèi)容的方法:
public static List
FindFields(string pageContent, Dictionary
string convertedPC = ConvertSpecialHtmlChs(pageContent,htmlSpecialChars);
List
string pat = @\"
MatchCollection ms = Regex.Matches(convertedPC, pat, RegexOptions.IgnoreCase);
for (int i = 0; i < ms.Count; i++){
string sfk = ms[i].Groups[\"FieldName\"].ToString().Replace(\""\",\"\").Trim();
string sfv;
if (sfk.ToUpper() != \"LDR\")
sfv = ms[i].Groups[\"FieldContent\"].ToString().Replace(\""\", \"\").Trim();
else
sfv = ms[i].Groups[\"FieldContent\"].ToString().Replace(\""\", \" \");
sfv = sfv.Replace(\"gt;\",\">\");
sfv = sfv.Replace(\"#62;\", \">\");
sfv = sfv.Replace(\"lt;\",\"<\");
sfv = sfv.Replace(\"#60;\", \"<\");
fields.Add(new NameContent (sfk, sfv));
}
return fields;
}
代碼中的方法string ConvertSpecialHtmlChs(string pageContent, Dictionary
Dictionary
/*下面省略號(hào)中的代碼為向htmlSpecialChars加入html特殊字符對(duì),比如可以從文件讀入*/
…
List
string marcStr = GetMarcRecord(listNC);//生成MARC記錄
上述代碼中,方法GetMarcRecord(List
四、結(jié)語
本文詳細(xì)討論了下載收集國圖OPAC頁面的MARC數(shù)據(jù)的程序思路,但很多細(xì)節(jié)還需要在實(shí)際代碼里完善,如異常處理、多線程的采用等。此外,出于篇幅的考慮也沒有詳細(xì)討論ConvertSpecialHtmlChs等方法,但在主體思路清晰的情況下,這些方法的實(shí)現(xiàn)并不困難。
參考文獻(xiàn):
國家圖書館.新版中國機(jī)讀目錄格式使用手冊(cè)[M].北京:北京圖書館出版社,2004.
(作者單位:廣東建設(shè)職業(yè)技術(shù)學(xué)院)