我看電影都是挺臨時決定的,常常興致來了並且剛好有空就會去看電影,但是每次這種狀況的時候,我都必須去每間電影院翻找時刻表,看看我想看的電影最近的一場是在哪邊、哪個時候。
我們今天的挑戰就來爬電影的時刻表,針對我想看的那部電影,吐給我最接近的電影時刻和電影院。
我們這次會選用開眼電影網來取得我們所需要的資料,進入網站後,我們觀察到可以在右邊的「 影片上映戲院查詢」下拉選單選擇電影和城市,然後就會就會出現這部電影在這個城市的所有時間場次,很顯然的,這對我們的目標有大的幫助。
同時我們也來觀察一下所送出的 request,只有送出一個 get request,真是非常簡潔。接下來我們來觀察一下這個 request 的 url 所代表的意義,有兩個我們看不懂的參數在網址裡面,fsen72527336
、a06
。
回到入口頁我們選擇下拉的位置,看一下 input select 的內容,可以發現 fsen72527336
其實是代表電影代碼,而 a06
是代表城市代碼。
如此一來,我們可以很容易的就組合出某部電影在某個城市的所有時間查詢的網址,那我們就接下去研究吧。
要取得最接近的某部電影時間,那麼會拆解成以下三個步驟:
我們可以從首頁這邊取得電影 select 所有 option 的 key value,然後對輸入的電影名稱做 regex 來比對,就能夠取得 film_id
了。
取得 film_id
之後,我們用之前探訪所得到的 city_id
,把這兩個數值組合成 url,然後用 postman 測試看看,確認是可以取得所有電影時刻。
我們可以用下面的程式碼來取得所有電影時間資料,接著取出大於目前的時間,再 sort 一下時間順序,就能取得可用的電影時間列表了。測試看看確定是可以拿到時間表就沒問題了。
var d = new Date()
var hour = d.getHours()
var minute = d.getMinutes()
$('#filmShowtimeBlock li')
.filter((index, obj)=>{
return $(obj).prop('classList').length == 0
})
.filter((index, obj)=>{
var time = $(obj).text().split(':')
return time[0]*60+time[1]*1 > hour*60+minute*1
})
.map((index, obj)=>{
return{
time: $(obj).text(),
theater: $(obj).closest('ul').find('.theaterTitle').text()
}
})
.get()
.sort((a,b)=>{
var a = a.time.split(':')
var b = b.time.split(':')
return (a[0]*60+a[1]*1 > b[0]*60+b[1]*1)? 1:-1
})
我們先攥寫一個 function,收兩個參數,接收我們查詢電影的關鍵字,然後將查詢到的 filmId 傳給 callback。
function getFilmId(movie, callback) {
request('http://www.atmovies.com.tw/movie/', (err, res, body) => {
var $ = cheerio.load(body)
var re = new RegExp(movie,"i");
var filmId = $('select[name=film_id] option').filter((index, obj) => {
return re.test($(obj).text())
}).val()
callback(filmId)
})
}
接下來我們攥寫 function 收取 filmId 和 cidtId,然後取得這部電影在這個城市的所有場次,然後在對抓取出來的場次做運算和排序。
function getMovieTimes(filmId, cityId, callback) {
request(`http://www.atmovies.com.tw/showtime/${filmId}/${cityId}/`, (err, res, body) => {
var d = new Date()
var hour = d.getHours()
var minute = d.getMinutes()
var $ = cheerio.load(body)
var times =
$('#filmShowtimeBlock li')
.filter((index, obj) => {
var time = $(obj).text().split(':')
return time[0] * 60 + time[1] * 1 > hour * 60 + minute * 1
})
.map((index, obj) => {
return {
time: $(obj).text(),
theater: $(obj).closest('ul').find('.theaterTitle').text()
}
})
.get()
.sort((a, b) => {
var a = a.time.split(':')
var b = b.time.split(':')
return (a[0] * 60 + a[1] * 1 > b[0] * 60 + b[1] * 1) ? 1 : -1
})
callback(times)
})
}
我們先抓取 filmId 然後再取抓取場次時間,這樣就完成了。
getFilmId('star', (filmId) => {
getMovieTimes(filmId, 'a06', (times) => {
console.log(times);
})
})
const request = require('request');
const cheerio = require('cheerio');
getFilmId('star', (filmId) => {
getMovieTimes(filmId, 'a06', (times) => {
console.log(times);
})
})
function getFilmId(movie, callback) {
request('http://www.atmovies.com.tw/movie/', (err, res, body) => {
var $ = cheerio.load(body)
var re = new RegExp(movie, "i");
var filmId = $('select[name=film_id] option').filter((index, obj) => {
return re.test($(obj).text())
}).val()
callback(filmId)
})
}
function getMovieTimes(filmId, cityId, callback) {
request(`http://www.atmovies.com.tw/showtime/${filmId}/${cityId}/`, (err, res, body) => {
var d = new Date()
var hour = d.getHours()
var minute = d.getMinutes()
var $ = cheerio.load(body)
var times =
$('#filmShowtimeBlock li')
.filter((index, obj) => {
var time = $(obj).text().split(':')
return time[0] * 60 + time[1] * 1 > hour * 60 + minute * 1
})
.map((index, obj) => {
return {
time: $(obj).text(),
theater: $(obj).closest('ul').find('.theaterTitle').text()
}
})
.get()
.sort((a, b) => {
var a = a.time.split(':')
var b = b.time.split(':')
return (a[0] * 60 + a[1] * 1 > b[0] * 60 + b[1] * 1) ? 1 : -1
})
callback(times)
})
}
我們可以把 web service 裝上去,然後用 get query string 來收欲查詢的電影,這樣就能快速的部署一個電影場次查詢 api。
其實這次的主題可以發現,這樣的功能有八成的資料是別人提供的,但可惜的就是他是一種網頁 service,而我們透過爬蟲就能將他們的服務轉成是 open api,那就能夠大幅杜的提升應用層面。