網路爬蟲技術結合地理編碼與資料分析,能有效整合多個來源的餐廳資訊。本文示範如何使用 Python 的 BeautifulSoup 解析餐廳網頁,擷取名稱、地址、電話等詳細資料,並利用地理編碼函式計算餐廳距離。此外,文章也說明如何整合芝加哥市提供的餐廳衛生檢查 JSON 資料,進行資料清理、轉換與篩選,最終產出高品質餐廳清單。程式碼範例中使用 SimpleNamespace 簡化資料結構,並示範如何運用 haversine 函式計算距離,以及如何根據衛生檢查分數等條件進行篩選,提供實用的餐廳資料整合與分析方法。
第四章:高品質餐廳搜尋與資料整合
餐廳詳細資料解析
在前面的章節中,我們已經建立了基本的餐廳資料抓取與地理編碼功能。本章節將探討如何解析餐廳的詳細資料,並結合多個網路服務的資料進行整合分析。
解析餐廳詳細資料頁面
inspection_detail 函式負責解析特定餐廳的詳細資料頁面。首先,它透過 BeautifulSoup 物件導航至 <body> 標籤內的首個 <h2> 標籤,以取得餐廳名稱。接著,它會定位到 <body> 內的首個 <table> 標籤,並逐一解析表格中的每一行。
def inspection_detail(business):
soup = get_business_soup(business)
# 解析餐廳名稱
business.name = soup.body.h2.text.strip()
# 解析詳細資料表格
table = soup.body.table
for row in table.find_all("tr"):
cells = row.find_all("td")
name = cells[0].text.strip()
value = cells[1].text.strip()
# 使用字典翻譯標籤名稱為 Python 屬性名稱
setattr(business, vdh_detail_translate[name], value)
資料翻譯與屬性設定
為了將 HTML 表格中的標籤名稱轉換為更易讀的 Python 屬性名稱,我們使用了一個翻譯字典 vdh_detail_translate。這個字典將原始的標籤名稱對映到更具描述性的屬性名稱。
vdh_detail_translate = {
'Phone Number:': 'phone_number',
'Facility Type:': 'facility_type',
'# of Priority Foundation Items on Last Inspection:': 'priority_foundation_items',
'# of Priority Items on Last Inspection:': 'priority_items',
'# of Core Items on Last Inspection:': 'core_items',
'# of Critical Violations on Last Inspection:': 'critical_items',
'# of Non-Critical Violations on Last Inspection:': 'non_critical_items',
}
組合多個函式實作高品質餐廳搜尋
透過組合之前定義的函式,我們可以建立一個生成器函式 choice_iter,它能夠產出一系列包含餐廳詳細資料的名稱空間物件。
def choice_iter():
base = SimpleNamespace(address='333 Waterside Drive')
geocode_detail(base)
soup = get_food_list_by_name()
for row in food_row_iter(food_table_iter(soup)):
geocode_detail(row)
inspection_detail(row)
row.distance = haversine((row.latitude, row.longitude), (base.latitude, base.longitude))
yield row
篩選高品質餐廳
對於產出的餐廳資料,我們可以套用多個篩選條件,以排除距離過遠或衛生檢查不合格的餐廳。
for business in choice_iter():
if business.distance > 0.75: continue
if business.priority_foundation_items > 1: continue
if business.priority_items > 1: continue
if business.core_items > 1: continue
print(business)
清潔資料入口的應用
芝加哥市提供了乾淨的餐廳檢查資料入口,透過簡單的 URL 即可下載 JSON 格式的檢查資料。然而,由於資料量龐大(超過 83,000 筆檢查記錄),我們需要套用篩選條件以縮小資料範圍。
https://data.cityofchicago.org/api/views/4ijn-s7e5/rows.json?accessType=DOWNLOAD
篩選條件的應用
我們可以根據檢查日期等條件來篩選資料,以取得特定時間範圍內的檢查記錄。
此圖示詳細解說:
- 原始資料:代表從網路服務取得的初始資料。
- 地理編碼:將地址轉換為經緯度座標的過程。
- 餐廳詳細資料解析:解析餐廳的詳細檢查資料。
- 篩選高品質餐廳:根據設定的條件篩選出符合標準的餐廳。
- 輸出結果:最終輸出的高品質餐廳清單。
內容解密:
此圖示呈現了從原始資料到輸出結果的整個處理流程,每一步驟都至關重要,共同構成了完整的資料處理和分析過程。
資料檢索與處理:芝加哥餐廳檢查報告
本章節將介紹如何使用Socrata API取得芝加哥市的餐廳檢查報告,並將資料儲存為JSON格式以便後續處理。
取得芝加哥市餐廳檢查報告JSON資料
首先,我們需要定義一個函式來取得芝加哥市的餐廳檢查報告資料:
def get_chicago_json():
scheme_netloc_path = "https://data.cityofchicago.org/api/views/4ijn-s7e5/rows.json"
form = {
"accessType": "DOWNLOAD",
"$where": "inspection_date > '2014-01-01'",
}
query = urllib.parse.urlencode(form)
with urllib.request.urlopen(scheme_netloc_path + "?" + query) as data:
with open("chicago_data.json", "w") as output:
output.write(data.read().decode("utf-8"))
內容解密:
scheme_netloc_path變數包含了Socrata API的URL,其中4ijn-s7e5是資料集的內部識別碼,rows.json指定了我們想要的資料格式。form字典中包含了查詢引數,其中$where子句用於過濾資料,只取得檢查日期在2014年1月1日之後的報告。- 使用
urllib.request.urlopen()函式傳送請求,並將回應內容寫入chicago_data.json檔案中。
將JSON資料轉換為Python物件
接下來,我們需要定義一個函式來將JSON資料轉換為Python的 SimpleNamespace 物件:
def food_row_iter():
with open("chicago_data.json", encoding="UTF-8") as data_file:
inspections = json.load(data_file)
headings = [item['fieldName'] for item in inspections["meta"]["view"]["columns"]]
for row in inspections["data"]:
data = SimpleNamespace(**dict(zip(headings, row)))
yield data
內容解密:
- 使用
json.load()函式載入JSON資料。 - 從
inspections["meta"]["view"]["columns"]中取得欄位名稱,並建立headings列表。 - 使用
zip()函式將欄位名稱與資料配對,並建立SimpleNamespace物件。
資料清理與轉換
為了方便後續處理,我們需要對資料進行一些清理與轉換:
def parse_details(business):
business.latitude = float(business.latitude)
business.longitude = float(business.longitude)
if business.violations is None:
business.details = []
else:
business.details = [v.strip() for v in business.violations.split("|")]
return business
內容解密:
- 將經緯度資料轉換為浮點數。
- 將違規事項分割為清單。
結合不同資料來源
最後,我們可以結合上述函式,定義一個新的函式來迭代處理餐廳資料:
def choice_iter():
base = SimpleNamespace(address="3420 W GRACE ST", city="CHICAGO", state="IL", zip="60618", latitude=41.9503, longitude=-87.7138)
for row in food_row_iter():
try:
parse_details(row)
row.distance = haversine((row.latitude, row.longitude), (base.latitude, base.longitude))
yield row
except TypeError:
pass
內容解密:
- 定義了一個基準位置
base。 - 使用
haversine()函式計算每個餐廳與基準位置之間的距離。 - 使用
try-except區塊處理無效或遺失的經緯度資料。
更敏感的分析:提升間諜技能
在前幾章的間諜任務中,我們主要專注於大量資料的收集和處理。然而,總公司並不總是需要詳細的資料,有時他們需要的是總結和評估。這意味著我們需要計算資料的中心趨勢、摘要、趨勢和相關性,因此需要編寫更複雜的演算法。
資料的理解:架構和後設資料
資料是由額外的資料來描述的,我們通常稱之為後設資料。一個基本的資料可能是 6371,如果沒有一些後設資料,我們根本不知道這代表什麼。最起碼,後設資料需要包括測量單位(在這個例子中是公里)以及被測量的事物(地球的平均半徑)。
對於客觀性較低的資料,可能沒有單位,而是一系列可能的值。例如,對於餐廳來說,它可能是一個 A-B-C 評分或透過-失敗的結果。追蹤後設資料對於正確解釋實際資料至關重要。
架構問題
一組資料應該由某個基本實體的多個例項組成。在我們的例子中,這個實體是某家餐廳最近的衛生檢查結果。如果每個例項都有一個一致的屬性集合,我們就可以把這組屬性稱為這組資料的架構。
在某些情況下,資料並不一致。也許有多個架構,或者架構非常複雜,有各種選項和替代方案。如果有好的後設資料,它應該能夠解釋架構。
計算中心趨勢
我們將學習如何計算我們收集的資料的平均值、中位數和眾數。這需要使用 Python 的內建函式和可能的額外函式庫如 NumPy。
使用 Python 進行資料分析
import csv
import statistics
def read_data(filename):
with open(filename, 'r') as file:
reader = csv.DictReader(file)
data = [row for row in reader]
return data
def compute_mean(data, field):
values = [float(row[field]) for row in data]
return statistics.mean(values)
def compute_median(data, field):
values = [float(row[field]) for row in data]
return statistics.median(values)
def compute_mode(data, field):
values = [row[field] for row in data]
return statistics.mode(values)
# 示例用法
data = read_data('health_inspections.csv')
mean_value = compute_mean(data, 'inspection_score')
median_value = compute_median(data, 'inspection_score')
mode_value = compute_mode(data, 'risk_category')
print(f"Mean: {mean_value}, Median: {median_value}, Mode: {mode_value}")
內容解密:
- 我們首先匯入必要的模組,包括
csv用於讀取 CSV 檔案,以及statistics用於計算統計值。 read_data函式讀取 CSV 檔案並將其內容轉換為字典列表。compute_mean、compute_median和compute_mode函式分別計算指定欄位的平均值、中位數和眾數。- 這些函式透過列表推導式提取指定欄位的值,並使用
statistics模組中的相應函式進行計算。 - 示例用法展示瞭如何使用這些函式來分析衛生檢查資料。
未來方向
我們可能會被拉向包括自然語言檔案分析在內的方向。報告、演講、書籍和文章有時與基本事實和數字一樣重要。如果我們想處理文字和語言,我們需要使用自然語言工具包(NLTK)。
在下一章中,我們將透過使用更深入的統計技術來執行更敏感的資料分析。我們將計算平均值、眾數和中位數,並檢視資料項之間的相關性。這需要我們掌握更多高階的話題,例如使用 Python 產生器函式,以及設計更高階別的結構,如 Python 模組、函式庫和應用程式。