---
title: 電影查詢 Line Bot 筆記
tags: sirla::workshop, linebot
---
# 電影查詢 Line Bot 筆記
[TOC]
## 套件
先進到虛擬環境再安裝
```shell=
pipenv shell
```
接著開始安裝
```shell=
pipenv install requests
pipenv install BeautifulSoup4
pipenv install lxml
```
* requests:用來處理 HTTP 請求
* BeautifulSoup4、lxml:用來解析處理requests取得的數據
## 透過爬蟲取得開眼電影網的資料
目標是爬取開眼電影網的[本週新片](http://www.atmovies.com.tw/movie/new/)
```python=
import requests
from bs4 import BeautifulSoup
#GET請求
r = requests.get('http://www.atmovies.com.tw/movie/new/')
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text, 'lxml')
print(soup)
```
* ```r.encoding = 'utf-8'```:Python 中文亂碼是由於Python 在解析網頁時預設用 Unicode 去解析,而大多數網站是 utf-8 格式的,並且解析出來之後,python 也會以 Unicode 字元格式輸出,會與系統編碼格式不同,導致中文輸出亂碼。
### Step 1
目標是 ==抓取電影名稱並加上超連結==
```python=
import requests
from bs4 import BeautifulSoup
r = requests.get('http://www.atmovies.com.tw/movie/new/')
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text, 'lxml')
filmTitle = soup.select('div.filmTitle a')
print(filmTitle)
```
這裡可以看到輸出的結果會是
```python=
[<a href="/movie/fben51502397/">絕地戰警FOR LIFE Bad Boys for Life </a>,
<a href="/movie/fjen64916630/">不完美的正義 Just Mercy </a>,
<a href="/movie/fskr58999329/">青春催落去 Start-Up </a>
......
```
從圖片中可發現電影名稱是在 class 為 filmTitle 的 div 中的 a 裡面,因此用```soup.select('div.filmTitle a')```來取得電影名稱。
![](https://i.imgur.com/gQ50zFJ.png)
****
### Step 2
接著整理一下剛剛取得的資料
```python=
import requests
from bs4 import BeautifulSoup
r = requests.get('http://www.atmovies.com.tw/movie/new/')
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text, 'lxml')
filmTitle = soup.select('div.filmTitle a')
print(filmTitle[0].text)
print("http://www.atmovies.com.tw/" + filmTitle[0]['href'])
```
我們只要 filmTitle[0] 裡的文字(也就是純粹的電影名稱),所以使用```filmTitle[0].text```。
而電影的網址會是 http://www.atmovies.com.tw/movie/fskr58999329/ ,也就是 http://www.atmovies.com.tw/ 加上 href 的那串連結。因此我們以 ```http://www.atmovies.com.tw/" + filmTitle[0]['href']``` 來取得電影網址。
****
#### 補充說明 - href
在 html 的語法中,超連結的語法是這樣表示的:
```htmlmixed=
<a href="要連結的 URL 會放在這裡">
```
> Reference:[HTML a href 連結屬性](https://www.wibibi.com/info.php?tid=240)
****
### Step 3 - 加上數量限制
由於是要讓使用者在 line 裡面使用,因此一次給太多數量的電影或多或少會造成使用者的困擾,所以加上數量限制是必要的。
```python=
import requests
from bs4 import BeautifulSoup
r = requests.get('http://www.atmovies.com.tw/movie/new/')
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text, 'lxml')
filmTitle = soup.select('div.filmTitle a')
content = ""
for i, data in enumerate(filmTitle):
print(i) #可以印出 i 來看看
content += data.text + "\n" + "http://www.atmovies.com.tw/" + data['href'] + "\n\n"
print(content)
```
在這裡是使用 for 迴圈來限制列出的電影數量。
使用的是 enumerate() 函数來計算列出次數,並建立一個空字串 content 來存放電影名稱及其超連結,因為要換行的關係,所以加上了 ```"\n"``` 。
****
#### 補充說明 - enumerate()
平常我們在使用 for 迴圈時大概都會是這種寫法:
```python=
i = 0
num = ['one', 'two', 'three']
for element in num:
print(i, num[i])
i +=1
```
此時獲得的輸出結果會是:
```
0 one
1 two
2 three
```
這時候可以使用 enumerate() 函数更簡潔的達成此目標
```python=
num = ['one', 'two', 'three']
for i, element in enumerate(num):
print(i, element)
```
****
### Step 4 - 加入 line bot 內
```python=
import os
# ------------新增的request------------
from flask import Flask, request, abort
# ------------------------------------
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage,
)
# ------------新增的部分------------
import requests
from bs4 import BeautifulSoup
# ---------------------------------
app = Flask(__name__)
line_bot_api = LineBotApi(os.environ.get('CHANNEL_ACCESS_TOKEN'))
handler = WebhookHandler(os.environ.get('CHANNEL_SECRET'))
@app.route("/callback", methods=['POST'])
def callback():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
print("Invalid signature. Please check your channel access token/channel secret.")
abort(400)
return 'OK'
# ------------新增的部分------------
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
if event.message.text == '本周新片':
r = requests.get('http://www.atmovies.com.tw/movie/new/')
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text, 'lxml')
content = []
for i, data in enumerate(soup.select('div.filmTitle a')):
if i > 20:
break
content.append(data.text + '\n' + 'http://www.atmovies.com.tw' + data['href'])
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text='\n\n'.join(content))
)
# ---------------------------------
if __name__ == "__main__":
app.run()
# 若沒有這部分就要設定環境變數讓 FLASK_APP = app.py 之類的
```
在本地端測試無誤後,即可發佈到 heroku 上,就完成電影查詢 line bot 了。