Python抓取網頁資料的終極辦法
假設你在網上搜索某個專案所需的原始資料,但壞訊息是資料存在於網頁中,並且沒有可用於獲取原始資料的API。
所以現在你必須浪費30分鐘寫指令碼來獲取資料(最後花費 2小時)。
這不難但是很浪費時間。
Pandas庫有一種內建的方法,可以從名為read_html()的html頁面中提取表格資料:
https://pandas.pydata.org/
importpandasaspd
tables = pd.read_html("https://apps.sandiego.gov/sdfiredispatch/")
print(tables[0])
就這麼簡單! Pandas可以在頁面上找到所有重要的html表,並將它們作為一個新的DataFrame物件返回。
https://pandas.pydata.org/pandas-docs/stable/dsintro.html#dataframe
輸入表格0行有列標題,並要求它將基於文字的日期轉換為時間物件:
importpandasaspd
calls_df, = pd.read_html("http://apps.sandiego.gov/sdfiredispatch/", header=0, parse_dates=["Call Date"])
print(calls_df)
得到:
CallDateCallTypeStreetCrossStreets Unit
2017-06-0217:27:58Medical HIGHLAND AV WIGHTMAN ST/UNIVERSITY AV E17
2017-06-0217:27:58Medical HIGHLAND AV WIGHTMAN ST/UNIVERSITY AV M34
2017-06-0217:23:51Medical EMERSON ST LOCUST ST/EVERGREEN ST E22
2017-06-0217:23:51Medical EMERSON ST LOCUST ST/EVERGREEN ST M47
2017-06-0217:23:15Medical MARAUDER WY BARONLN/FROBISHER ST E38
2017-06-0217:23:15Medical MARAUDER WY BARONLN/FROBISHER ST M41
是一行程式碼,資料不能作為json記錄可用。
importpandasaspd
calls_df, = pd.read_html("http://apps.sandiego.gov/sdfiredispatch/", header=0, parse_dates=["Call Date"])
print(calls_df.to_json(orient="records", date_format="iso"))
執行下面的程式碼你將得到一個漂亮的json輸出(即使有適當的ISO 8601日期格式):
[
{
"Call Date":"2017-06-02T17:34:00.000Z",
"Call Type":"Medical",
"Street":"ROSECRANS ST",
"Cross Streets":"HANCOCK ST/ALLEY",
"Unit":"M21"
},
{
"Call Date":"2017-06-02T17:34:00.000Z",
"Call Type":"Medical",
"Street":"ROSECRANS ST",
"Cross Streets":"HANCOCK ST/ALLEY",
"Unit":"T20"
},
{
"Call Date":"2017-06-02T17:30:34.000Z",
"Call Type":"Medical",
"Street":"SPORTS ARENA BL",
"Cross Streets":"CAM DEL RIO WEST/EAST DR",
"Unit":"E20"
}
// etc...
]
你甚至可以將資料儲存到CSV或XLS檔案中:
importpandasaspd
calls_df, = pd.read_html("http://apps.sandiego.gov/sdfiredispatch/", header=0, parse_dates=["Call Date"])
calls_df.to_csv("calls.csv", index=False)
執行並雙擊calls.csv在電子表格中開啟:
當然,Pandas還可以更簡單地對資料進行過濾,分類或處理:
>>> calls_df.describe()
CallDateCall Type Street Cross Streets Unit
count6969696469
unique292292760
top2017-06-0216:59:50Medical CHANNEL WY LA SALLE ST/WESTERN ST E1
freq566552
first2017-06-0216:36:46NaNNaNNaNNaN
last2017-06-0217:41:30NaNNaNNaNNaN
>>> calls_df.groupby("Call Type").count()
CallDateStreet Cross Streets Unit
Call Type
Medical66666166
Traffic Accident (L1)3333
>>> calls_df["Unit"].unique()
array(['E46','MR33','T40','E201','M6','E34','M34','E29','M30',
'M43','M21','T20','E20','M20','E26','M32','SQ55','E1',
'M26','BLS4','E17','E22','M47','E38','M41','E5','M19',
'E28','M1','E42','M42','E23','MR9','PD','LCCNOT','M52',
'E45','M12','E40','MR40','M45','T1','M23','E14','M2','E39',
'M25','E8','M17','E4','M22','M37','E7','M31','E9','M39',
'SQ56','E10','M44','M11'], dtype=object)