--- 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 了。