###### tags: `Tech`
# Basic Web Scrawler With Javascript
Github Repository : https://github.com/UnderSam/Xinpei_Library_Scrawler
## Require skill
1. Javascript
2. HTML DOM
3. Jquery
4. promise
5. curiosity
## Packages
1. Cheerio
2. Request
## What
嘗試做一個爬蟲程式來搜尋[新北市立圖書館](http://webpac.tphcc.gov.tw/toread/opac/search?)的藏書資訊系統,並且能夠根據搜尋到的書去做更進一步的查詢。
## How
首先我們要先分析這個網站是怎麼樣去處理查詢系統的,先找到查詢的首頁,並且觀察網址,網址如下:
`http://webpac.tphcc.gov.tw/toread/opac/search?`
然後我們搜尋一本書,在記錄其網址,會變成如下:
```
http://webpac.tphcc.gov.tw/toread/opac/search?q=app&max=&view=CONTENT&location=
```
顯然的,這個網頁搜尋是用GET來傳輸request,還不懂GET、POST這些的可以參考[這個網址](https://www.wibibi.com/info.php?tid=235),也可以打開開發者視窗(F12)來找到NetWork,並且找到你的url,找到會如下(這邊用app為關鍵字做搜尋)

再往下一點可以看到QueryString這個參數,因為GET跟POST都可以傳遞參數,只是差在會不會放在URL上面,所以兩個在傳參還是會把PARAMS丟到PAYLOAD裡面,這邊可以觀察有幾個參數是server需要的 :

想要理解參數可以幹嘛的,因為是GET傳輸所以你可以直接去改url,例如把location改成1就是變成顯示第二頁,改掉max就是改預設最大顯示數量。
現在萬事俱備,開始架構我們的蒐書系統。
## Construct our Scrawler
假設今天要搜尋的是有關app的書,那我們肯定要先有個書單,把書單傳給client後再由client選一本書來回傳資訊,可是我們這個url只能搜尋到書單阿!怎麼辦呢?這時候就可以去看書單的html有沒有什麼端倪 :
1. 先找到一本書,我們以愛的APP為範本 :

2. 對著裡面看到的任何一個地方右鍵並且 [檢查]
3. 我們會得到如下的html原始碼 :

竟然有附上這本書的URL,那事情就好辦多了,我們只要先取得書單,並且儲存起來等待client需要某本書再去搜尋就好了。既然現在有架構了,就來撰寫程式吧 !
## Programming
### BookList
首先去回顧之前書單上面的原始碼,可以在上面找到標題、圖片連結、作者、藏書、連結。再來我們打開一個檔案取名為book.js,並引入幾個package
```javascript=
var request = require("request");
var cheerio = require("cheerio");
var rp = require("request-promise");
```
首先先把剛剛搜尋後的URL
```
http://webpac.tphcc.gov.tw/toread/opac/search?q=app&max=&view=CONTENT&location=
```
去掉搜尋串得到
```
http://webpac.tphcc.gov.tw/toread/opac/search?
```
從request-promise的document中可以得到,用法是先宣告一個options,裡面先放入url,qs,header...等資訊,塞入後會變成這樣
```javascript=
var options = {
uri: 'http://webpac.tphcc.gov.tw/toread/opac/search?',
qs: {
q:searchkey,
max:"",
view:"CONTENT",
location:"0",
},
timeout:5000,
headers: {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
"Accept-Language":"en-US,en;q=0.9",
"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Connection":"keep-alive"
},
json: true, // Automatically parses the JSON string in the response
transform: function(body){
return cheerio.load(body,{decodeEntities: false});
}
};
```
uri為我們的搜尋basis-url,而qs就是queryString,我們要從之前的那張圖片照著格式放進來,timeout為過幾秒後reject,而header為向server表明user-agent的身分(不然有些會擋掉無user-agent的爬蟲),最後由transform將response直接轉成cheerio的格式。
接下來就專注在把拿到的html弄成我們要的資訊,我們繼續觀察原始碼

每一本書都用class(is_img)來隔開,那要取得每個is_img底下的資料我們可以使用jquery的filter,如下
```javascript=
$(".is_img").filter(function(){
/...
code here
.../
})
```
那抓到了每本書的Parent後,我們現在就要來找我們要的資訊放在哪個Children,繼續打開母TAG後會得到

這邊直接幫大家整理 :
1. 標題 : 放在reslt_item_head底下
2. 作者 : 放在crs_author底下
3. 連結 : 放在reslt_item_head底下的a的href屬性
4. 圖片 : img tag的src屬性
5. 館藏 : 這邊看到第一本書的館藏在id['MyPageLink_4']底下,可是寫完去跑又發現其他本書都沒有資料,這時候打開其他本的也一起比較

原來後面還有_+number,這邊我們就用迴圈一起來處理,第n本書就append n-1到id後面去抓資料
現在tag也有了,我們直接來寫剩下的邏輯部分 :
```javascript=
const pr = rp(options)
.then(function($){
clearInterval(interval);
var title;
var author;
var json={};
var cn = -1;
var cnn="";
$(".is_img").filter(function(){
var data_title = $(this).find(".reslt_item_head").text().trim();
var data_author = $(this).find(".crs_author").text().trim();
var links = $(this).find(".reslt_item_head>a").attr("href");
if(cn >= 0) cnn="_"+cn;
cn++;
var data_count = $(this).find("#MyPageLink_4"+cnn).text().trim();
data_count = data_count.replace(" 本館藏 可借閱", "");
var image = $(this).find("img").attr("src");
if(image == '/toread/images/macheteicons/book.gif')
{
image = "http://webpac.tphcc.gov.tw/toread/images/macheteicons/book.gif";
}
data_title = data_title.replace("/","");
json.title = data_title;
json.img = image;
json.author = data_author;
json.xinpei_lib = data_count;
json.link = "http://webpac.tphcc.gov.tw"+links;
console.log(json);
counter++;
})
})
.catch(reason =>{
console.log(reason);
})
```
最後,再return我們的request-promise
```javascript=
return Promise.all([pr]).then(()=>{
console.log(counter); //show books count
})
```
全部兜在一起之後會變成
```javascript=
function Xinpei_url(searchkey){
console.log("start xinpei url")
var counter = 0;
var options = {
uri: 'http://webpac.tphcc.gov.tw/toread/opac/search?',
qs: {
q:searchkey,
max:"2",
view:"CONTENT",
location:"0",
},
timeout:5000,
headers: {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
"Accept-Language":"en-US,en;q=0.9",
"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Connection":"keep-alive"
},
json: true, // Automatically parses the JSON string in the response
transform: function(body){
return cheerio.load(body,{decodeEntities: false});
}
};
var start = Date.now();
const pr = rp(options)
.then(function($){
clearInterval(interval);
var title;
var author;
var json={};
var cn = -1;
var cnn="";
$(".is_img").filter(function(){
var data_title = $(this).find(".reslt_item_head").text().trim();
var data_author = $(this).find(".crs_author").text().trim();
var links = $(this).find(".reslt_item_head>a").attr("href");
if(cn >= 0) cnn="_"+cn;
cn++;
var data_count = $(this).find("#MyPageLink_4"+cnn).text().trim();
data_count = data_count.replace(" 本館藏 可借閱", "");
var image = $(this).find("img").attr("src");
if(image == '/toread/images/macheteicons/book.gif')
{
image = "http://webpac.tphcc.gov.tw/toread/images/macheteicons/book.gif";
}
data_title = data_title.replace("/","");
json.title = data_title;
json.img = image;
json.author = data_author;
json.xinpei_lib = data_count;
json.link = "http://webpac.tphcc.gov.tw"+links;
console.log(json);
counter++;
})
})
.catch(reason =>{
console.log(reason);
})
var interval = setInterval(() => {
console.log("Waiting:",(Date.now() - start) / 1000);
}, 1000)
return Promise.all([pr]).then(()=>{
console.log(counter);
})
}
```
接下來執行這個function,並且帶入app這個關鍵字,會跑出結果 :
```javascript=
start xinpei url
Waiting: 1.007
Waiting: 2.007
{ title: '愛的APP ',
img: '/toread/covers.svc?h=140&rid=574942&t=m&w=99',
author: '法蘭柯 ((Frankel, Laurie)); 陳佳琳',
xinpei_lib: '4',
link: 'http://webpac.tphcc.gov.tw/toread/opac/bibliographic_view/574942?location=0&mps=50&ob=desc&q=app&sb=relevance&start=0&view=CONTENT' }
{ title: 'Apples',
img: 'http://static.findbook.tw/image/book/9780521752398/large',
author: '',
xinpei_lib: '1',
link: 'http://webpac.tphcc.gov.tw/toread/opac/bibliographic_view/408225?location=0&mps=50&ob=desc&q=app&sb=relevance&start=0&view=CONTENT' }
{ title: 'APP之繼承 : 以APP開發創業的故事 ',
img: '/toread/covers.svc?h=140&rid=702097&t=m&w=99',
author: '林廣維',
xinpei_lib: '8',
link: 'http://webpac.tphcc.gov.tw/toread/opac/bibliographic_view/702097?location=0&mps=50&ob=desc&q=app&sb=relevance&start=0&view=CONTENT' }
{ title: 'The appeal',
img: 'http://static.findbook.tw/image/book/9780440243816/large',
author: 'Mississippi; Wall Street (New York, N.Y.); Grisham,, John',
xinpei_lib: '1',
link: 'http://webpac.tphcc.gov.tw/toread/opac/bibliographic_view/403400?location=0&mps=50&ob=desc&q=app&sb=relevance&start=0&view=CONTENT' }
.
.
.
.
.
.
{ title: 'L\'appart : the delights and disasters of making my Paris home ',
img: 'http://static.findbook.tw/image/book/9780804188388/large',
author: 'Lebovitz, David.; Lebovitz, David; Paris (France); Paris (France)',
xinpei_lib: '3',
link: 'http://webpac.tphcc.gov.tw/toread/opac/bibliographic_view/772016?location=0&mps=50&ob=desc&q=app&sb=relevance&start=0&view=CONTENT' }
```
之後有了這個JSON後,我們就可以弄個美美的介面給使用者選擇,並且根據他想要的書去幫他爬囉 !
各自書本的資料爬蟲我也會一起放上Github,邏輯是一樣的,大家可以試試看喔><
參考資料 :
1. [document of request-promise](https://github.com/request/request-promise)