我是一個很喜歡尋寶的人,會常常看 FB 的地方二手版,看有沒有實用的東西可以撿便宜,除了 FB 以外, PTT 地方版也有大量的二手物品販售訊息,所以 PTT 台南版我也是常常逛。
不過,台南版規規定,所有的 3C 商品買賣都必須放於置底文的推文,所以這些資訊的顯示方式真的是爆難看。既然是個網頁工程師,有問題就應該自己動手解決,雖然畫面不好看我們無力解決,但我們至少能轉接個 API 出來,讓有能力的設計師可以有資料去製作好看的介面。
一樣是使用 PTT web 版來抓取資訊,台南版 3C 置底文的網址是 https://www.ptt.cc/bbs/Tainan/M.1464690165.A.D11.html ,看起來就是把底下推文的項目抓取出來就可以了。
接下來觀察看看每個 item 的 html dom,看起來只要抓每個 .push
就能抓到所有的推文項目,然後再抓取每個項目的發文者、推文內容、時間。
不過我們也觀察到,因為一個推文有其字數限制,常常會有人連續發兩個推文,既然如此,那這兩筆同樣的發送人應該要被歸類在同一個項目。
先來寫個 getItem function,收個參數 callback,屆時把抓取到的項目丟給 callback。接著我們做 request 台南版 3C 至底文的網址。
const request = require('request');
const cheerio = require('cheerio');
function getItems(callback){
request('https://www.ptt.cc/bbs/Tainan/M.1388172150.A.860.html', (err, res, body)=>{
var $ = cheerio.load(body, { decodeEntities: false })
var items = []
})
}
再來我們來抓取推文列表,抓到之後,我們 each 所抓到的內容,並將發文者、推文內容、時間整理出來。接著我們判斷抓取的發文者是不是等於上一筆的發文者,若相同的話,就把它併入上一筆,若不是的話,就 push 進去 items。
const request = require('request');
const cheerio = require('cheerio');
function getItems(callback){
request('https://www.ptt.cc/bbs/Tainan/M.1388172150.A.860.html', (err, res, body)=>{
var $ = cheerio.load(body, { decodeEntities: false })
var items = []
$('div.push').each((index, obj)=>{
var seller = $(obj).find('.push-userid').html();
var content = $(obj).find('.push-content').html().replace(': ', '');
var time = $(obj).find('.push-ipdatetime').html().replace('\n', '');
if (items.length && seller === items[items.length - 1].seller) {
items[items.length - 1].content = items[items.length - 1].content + content;
} else {
currentUser = seller;
items.push({
seller: seller,
content: content,
time: time,
});
}
})
callback(items)
})
}
接下來我們用 express 來做 web service,先暫時聽 3000 port,未來 deploy 再改 80,經過測試沒問題就大功告成了。
const express = require('express');
const app = express();
app.get('/', function (req, res) {
getItems((items)=>{
res.json(items)
});
}).listen(3000)
const request = require('request');
const cheerio = require('cheerio');
const express = require('express');
const app = express();
app.get('/', function (req, res) {
getItems((items)=>{
res.json(items)
});
}).listen(3000)
function getItems(callback){
request('https://www.ptt.cc/bbs/Tainan/M.1388172150.A.860.html', (err, res, body)=>{
var $ = cheerio.load(body, { decodeEntities: false })
var items = []
$('div.push').each((index, obj)=>{
var seller = $(obj).find('.push-userid').html();
var content = $(obj).find('.push-content').html().replace(': ', '');
var time = $(obj).find('.push-ipdatetime').html().replace('\n', '');
if (items.length && seller === items[items.length - 1].seller) {
items[items.length - 1].content = items[items.length - 1].content + content;
} else {
currentUser = seller;
items.push({
seller: seller,
content: content,
time: time,
});
}
})
callback(items)
})
}
爬蟲的應用不僅僅只是在抓資料,其實很多時候我們會是當個中介者的角色。資訊這個領域開放性的觀念很重要,唯有大家願意分享和貢獻,才有可能有更多的人站在你的肩膀上往前邁進。
我不是個設計師,我設計不了很友善的畫面,憑我一個人的力量是沒辦法解決這個目標的問題,但我能貢獻我的一份能力,串起解決問題過程當中的一環,那麼這就是一件有價值的事情。