Python網頁爬蟲在日常生活中的應用非常普遍,用來自動化蒐集所需的資料,這時候如果能夠提供使用者一個介面,即可以依據不同的需求來爬取相應的資料,提升服務的體驗及彈性。
舉例來說,在[Python+LINE Bot教學]建構具網頁爬蟲功能的LINE Bot機器人文章中,分享LINE與Python網頁爬蟲的整合應用,讓使用者只要透過LINE輸入地區,Python網頁爬蟲就會依據這樣的需求,即時爬取正在營業的五間最高人氣餐廳。
而本文想來和大家分享另一個應用,就是網頁與Python網頁爬蟲的結合,像Google一樣,在網頁提供城市關鍵字的查詢,來驅動Python網頁爬蟲取得KLOOK及KKday網站的一日遊票券資訊,並且顯示於畫面上。其中的開發流程包含:
- 安裝開發套件
- 建立Django專案
- 建立Django應用程式
- 建立Django應用程式網址
- 建立Python網頁爬蟲
- 建立Django樣板
- 開發關鍵字查詢功能
一、安裝開發套件
首先,利用以下指令來安裝開發過程中,會使用到的套件:
$ pip install django $ pip install requests $ pip install lxml $ pip install requests
其中,本文使用Django框架為例,來進行網頁開發,而在Python網頁爬蟲中使用requests套件來發送請求,lxml及BeautifulSoup套件則分別是用來解析回傳結果與爬取所需的資料。
二、建立Django專案
套件安裝完成後,在桌面新增django-trip-tickets資料夾,接著,開啟命令提示字元視窗,切換到django-trip-tickets資料夾路徑,並且利用以下指令來建立Django專案(trip):
$ django-admin startproject trip .
注意指令的最後需要加一個點(.),代表在目前的路徑下建立Django專案。而要執行Django專案,則可以使用以下指令:
$ python manage.py runserver
在瀏覽器輸入指令執行結果的網址「http://127.0.0.1:8000/」,即可看到以下畫面:
三、建立Django應用程式
接下來,利用以下指令建立Django應用程式(tickets):
$ python manage.py startapp tickets
這時候,本文以Visual Studio Code為例,開啟django-trip-tickets資料夾,可以看到目前的檔案結構為:
為了要讓專案主程式能夠認得應用程式,就需要開啟專案主程式下的settings.py檔案,在INSTALL_APPS的地方,加入應用程式的設定檔,如下範例:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'tickets.apps.TicketsConfig', ]
四、建立Django應用程式網址
Django應用程式建立完成後,就需要有一組網址,讓使用者可以透過瀏覽器來進行請求(Request)。
在Django應用程式(tickets)資料夾下,新增urls.py檔案,加入應用程式的網址,如下範例:
from django.urls import path from . import views urlpatterns = [ path('', views.index, name="Index") ]
接著,把這個網址設定檔(urls.py)註冊到專案主程式的urls.py檔案中,如下範例:
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('tickets/', include("tickets.urls")), ]
這時候,當Django專案主程式的網址(http://127.0.0.1:8000/)加上tickets時,就會去配對應用程式(tickets)下的urls.py檔案,如果是空白(http://127.0.0.1:8000/tickets/),則會導向它的設定值,也就是首頁。
五、建立Python網頁爬蟲
接著就要來開發Python網頁爬蟲來蒐集KLOOK及KKday網站的一日遊票券資訊,為了提升未來的擴充性及保有一致性的介面,將使用Python物件導向的多型(Polymorphism)概念來進行設計,如下圖:
在這樣的架構下,每個旅遊票券網站的爬蟲都一定擁有爬取(scrape)方法(Method),如果未來要在增加一個網站時,也可以透過增加類別(Class)的方式來擴充。
在Django應用程式(tickets)下,新增scrapers.py檔案,其中引用以下模組(Module):
from abc import ABC, abstractmethod from bs4 import BeautifulSoup import requests from datetime import datetime
接著,定義一個網站抽象類別,其中包含城市名稱(city_name)屬性及爬取(scrape)抽象方法,如下範例:
from abc import ABC, abstractmethod from bs4 import BeautifulSoup import requests from datetime import datetime # 票券網站抽象類別 class Website(ABC): def __init__(self, city_name): self.city_name = city_name # 城市名稱屬性 @abstractmethod def scrape(self): # 爬取票券抽象方法 pass
在上一篇[Python爬蟲教學]快速搞懂AJAX動態載入網頁的爬取秘訣文章中,已經分享了如何爬取KKday網站的一日遊票券資訊,這邊將延續使用其中的scrape方法(Method),為了在網頁上顯示較為美觀,所以稍微修改幾個欄位的顯示格式,如下範例:
from abc import ABC, abstractmethod from bs4 import BeautifulSoup import requests from datetime import datetime # 票券網站抽象類別 class Website(ABC): def __init__(self, city_name): self.city_name = city_name # 城市名稱屬性 @abstractmethod def scrape(self): # 爬取票券抽象方法 pass # KKday網站 class Kkday(Website): def scrape(self): result = [] # 回傳結果 if self.city_name: # 如果城市名稱非空值 # 取得傳入城市的所有一日遊票券 response = requests.get( f"https://www.kkday.com/zh-tw/product/ajax_productlist/?keyword={self.city_name}&cat=TAG_4_4&sort=pasc") # 資料 activities = response.json()["data"] for activity in activities: # 票券名稱 title = activity["name"] # 票券詳細內容連結 link = activity["url"] # 票券價格 price = f'NT$ {int(activity["price"]):,}' # 最早可使用日期 booking_date = datetime.strftime(datetime.strptime( activity["earliest_sale_date"], "%Y%m%d"), "%Y-%m-%d") # 評價 star = str(activity["rating_star"])[ 0:3] if activity["rating_star"] else "無" result.append( dict(title=title, link=link, price=price, booking_date=booking_date, star=star, source="https://cdn.kkday.com/m-web/assets/img/favicon.png")) return result
其中,第28行網址最後修改為價格由低到高排序。第42行票券價格增加「NT$」單位及千分位符號。第45行調整最早可使用日期的格式,先將爬取到的日期字串轉型為日期後,再自訂喜歡的格式。另外,由於不是每一種票券都有評價,所以增加條件判斷,當有評價時顯示3位數的字串,沒有的話則顯示「無」。最後,第54行的source(網站來源)讀者可以依照自己的需求使用字串或是像這裡使用KKday網站小圖示的網址。
而KLOOK網站爬蟲的部分,在首頁左上角「分類」的地方,選擇「一日遊&導賞團」,如下圖:
而要爬取這些票券的資訊,就需要瞭解其中的HTML原始碼。在票券標題的地方,點擊滑鼠右鍵,選擇「檢查」,在「Elements(元素)」頁籤下,就能夠HTML原始碼結構,如下圖:
接下來,就可以在scrapers.py檔案中,增加KLOOK網站的爬蟲類別(Class),並且發送請求(Request)到KLOOK網站及解析回傳結果,如下範例:
# KLOOK客路網站 class Klook(Website): def scrape(self): result = [] # 回傳結果 if self.city_name: # 如果城市名稱非空值 # 取得傳入城市的所有一日遊&導賞團票券 response = requests.get( f"https://www.klook.com/zh-TW/search/?keyword={self.city_name}&template_id=2&sort=price&start=1") soup = BeautifulSoup(response.text, "lxml")
在回傳結果中,利用BeautifulSoup模組(Module)的find_all()方法(Method),取得十個一日遊票券區塊,讀者可以依照需求自行調整,如下範例:
# KLOOK客路網站 class Klook(Website): def scrape(self): result = [] # 回傳結果 if self.city_name: # 如果城市名稱非空值 # 取得傳入城市的所有一日遊&導賞團票券 response = requests.get( f"https://www.klook.com/zh-TW/search/?keyword={self.city_name}&template_id=2&sort=price&start=1") soup = BeautifulSoup(response.text, "lxml") # 取得十個票券卡片(Card)元素 activities = soup.find_all( "div", {"class", "j_activity_item_link j_activity_item_click_action"}, limit=10)
這時候,就可以透過迴圈,讀取這十個票券區塊,爬取其中的資訊,並且打包為串列(List),進行回傳,如下範例:
# KLOOK客路網站 class Klook(Website): def scrape(self): result = [] # 回傳結果 if self.city_name: # 如果城市名稱非空值 # 取得傳入城市的所有一日遊&導賞團票券 response = requests.get( f"https://www.klook.com/zh-TW/search/?keyword={self.city_name}&template_id=2&sort=price&start=1") soup = BeautifulSoup(response.text, "lxml") # 取得十個票券卡片(Card)元素 activities = soup.find_all( "div", {"class", "j_activity_item_link j_activity_item_click_action"}, limit=10) for activity in activities: # 票券名稱 title = activity.find( "a", {"class": "title"}).getText().strip() # 票券詳細內容連結 link = "https://www.klook.com" + \ activity.find("a", {"class": "title"}).get("href") # 票券價格 price = activity.find( "span", {"class": "latest_price"}).getText().strip() # 最早可使用日期 booking_date = activity.find( "span", {"class": "g_right j_card_date"}).get("data-serverdate")[0:10] # 評價 star = activity.find("span", {"class": "t14 star_score"}).getText( ).strip() if activity.find("span", {"class": "t14 star_score"}) else "無" result.append( dict(title=title, link=link, price=price, booking_date=booking_date, star=star, source="https://cdn.klook.com/s/dist_web/assert/desktop/imgs/favicon-098cf2db20.png")) return result
以上範例中,由於「最早可使用日期」不需要時分秒,所以第35行利用BeautifulSoup模組(Module)的get()方法(Method)取得HTML元素的屬性值(最早可使用日期)後,只取前10個字串。
六、建立Django樣板
Python網頁爬蟲的部分開發完成後,接下來,就要建立一個網頁,提供使用者輸入城市關鍵字來進行查詢,以及顯示一日遊票券資訊的爬取結果。
在Django應用程式(tickets)下,新增templates(樣板)資料夾,並且,為了避免與其它應用程式(APP)的樣板命名相同,在templates(樣板)資料夾下,再新增一個應用程式(tickets)資料夾,來放置網頁(index.html),如下圖:
七、開發關鍵字查詢功能
開啟index.html檔案,建立查詢表單,如下範例:<form action="" method="Post"> {% csrf_token %} <input name="city_name" placeholder="請輸入城市名稱"> <input type="submit" value="查詢"> </form>
當使用者輸入城市名稱關鍵字(city_name)後,點擊查詢按鈕時,表單就會利用POST方法將值傳送到Django應用程式(tickets)的views.py檔案中。
所以,開啟views.py檔案,將傳送過來的城市名稱關鍵字(city_name),傳入Python網頁爬蟲的類別(Class)中,並且將爬取的結果打包成tickets物件回傳給網頁,如下範例:
from django.shortcuts import render from .scrapers import Klook, Kkday def index(request): klook = Klook(request.POST.get("city_name")) kkday = Kkday(request.POST.get("city_name")) context = { "tickets": klook.scrape() + kkday.scrape() } return render(request, "tickets/index.html", context)
有了tickets爬取結果的物件後,開啟index.html檔案,在查詢表單的下方,利用表格(Table),來顯示Python網頁爬蟲的爬取結果,如下範例:
<form action="" method="Post"> {% csrf_token %} <input name="city_name" placeholder="請輸入城市名稱"> <input type="submit" value="查詢"> </form> <table class="table"> <thead> <tr> <th>票券名稱</th> <th>價格</th> <th>最早可使用日期</th> <th>評價</th> <th>來源網站</th> </tr> </thead> <tbody> {% for ticket in tickets %} <tr> <td> <a href="{{ ticket.link }}" target="_blank">{{ ticket.title }}</a> </td> <td>{{ ticket.price }}</td> <td>{{ ticket.booking_date }}</td> <td>{{ ticket.star }}</td> <td><img src={{ ticket.source }} style="width:32px; heigh:32px;" /></td> </tr> {% endfor %} </tbody> </table>
由於第一次載入網頁時,並沒有輸入城市名稱關鍵字,所以在views.py檔案中的「request.POST.get("city_name")」為None,所以並不會有爬取結果,當有輸入城市名稱關鍵字進行查詢時,則會顯示KLOOK及KKday網站的一日遊票券資訊。
如果想要整合Bootstrap框架來美化index.html網頁,可以參考[Django教學6]Django Template(樣板)整合Bootstrap實戰教學文章或以下的GitHub網址。最後,利用以下指令來執行Django專案:
$ python manage.py runserver
在瀏覽器輸入Django應用程式網址(http://127.0.0.1:8000/tickets/),以查詢「花蓮」為例的執行結果:
八、小結
網頁與Python網頁爬蟲整合後,即可提供使用者透過關鍵字查詢的方式,來爬取所需的資料,提升資料蒐集的效率。大家也可以試試看本文的實作,開發一個簡易的網頁,來有效率的查詢Python網頁爬蟲所取得的資料吧~
- Python學習資源整理
- [Django教學1]3步驟快速安裝Django網站框架
- [Django教學2]建立Django應用程式(APP)
- [Django教學5]Django Template(樣板)開發快速上手
- [Django教學6]Django Template(樣板)整合Bootstrap實戰教學
- [Python爬蟲教學]快速搞懂AJAX動態載入網頁的爬取秘訣
- [Python爬蟲教學]有效利用Python網頁爬蟲幫你自動化下載圖片
- [Python爬蟲教學]7個降低Python網頁爬蟲被偵測封鎖的實用方法
- [Python爬蟲教學]非同步網頁爬蟲使用GRequests套件提升爬取效率的實作技巧
- [Python爬蟲教學]學會使用Selenium及BeautifulSoup套件爬取查詢式網頁
- [Python爬蟲教學]解析如何串接Google Sheet試算表寫入爬取的資料
- [Python爬蟲教學]活用openpyxl套件將爬取的資料寫入Excel檔案
- [Python爬蟲教學]教你如何部署Python網頁爬蟲至Heroku雲端平台
- [Python爬蟲教學]輕鬆學會Python網頁爬蟲與MySQL資料庫的整合方式
- [Python爬蟲教學]整合Python Selenium及BeautifulSoup實現動態網頁爬蟲
- [Python爬蟲教學]Python網頁爬蟲動態翻頁的實作技巧
- [Python爬蟲教學]3個建構Python動態網頁爬蟲重要的等待機制
- [Python爬蟲教學]7個Python使用BeautifulSoup開發網頁爬蟲的實用技巧
- [Python爬蟲教學]Python網頁爬蟲結合LINE Notify打造自動化訊息通知服務
- [Python+LINE Bot教學]建構具網頁爬蟲功能的LINE Bot機器人
留言
張貼留言