Professional Documents
Culture Documents
1 準備工作
在我們開始應用前,由於我們接下來會常常使用到第三方函式庫,那請你一定要了解 Python
環境該如何安裝函式庫。以下如果有使用輔助工具,都以 PyCharm 為主
1.1 必要安裝
請在開始前準備好下方兩個軟體
以下幫大家快速帶過安裝方式,如果有的讀者就可以跳過下面兩個小節
圖: 3.4.3 官網版本
以下是開發環境的參考安裝步驟
https://www.jetbrains.com/pycharm/ 官方網站下載 community 版本
接著安裝步驟如下
1 準備工作 3
圖: 這裡要記得做出選擇
選好以後啟動程式
1 準備工作 4
圖: 選完佈景主題 skip
1.2 虛擬環境
在開始前,我們要探討一下『虛擬環境』和『非虛擬環境』,因為這會影響我們安裝函式庫的
方式,PyCharm 在開啟 Python 專案的時候預設會使用『虛擬環境』,簡單來說『虛擬環境』就是
『為每一個專案準備一個乾淨的初始環境』,先用下面這張圖表示一下不用虛擬環境的情況
1 準備工作 5
圖: 不用虛擬環境的情況
你會發現有以下重大優缺點
1. 優點: 安裝過的函式庫不用重複安裝,開多少個『專案』都可以吃得到
2. 缺點: 當你要告訴別人如何架設環境的時候,我們會直接對你的 Python 翻譯器產生一份『需
求文件』
,這份需求文件會記錄所有安裝過的函式庫,問題你給別人的專案根本只用到其中幾
個
1 準備工作 6
圖: 使用虛擬環境的情況
那優缺點剛好反過來
1. 優點: 每個專案都是『乾淨』的,
『需求文件』也是
2. 缺點: 每個專案都必須重新安裝函式庫
,所以 PyCharm 預設
那由於我們在真正開發的時候,對於『乾淨』的需求通常是『最高優先』
,如果你想偷懶,不想使用,可以使用下面的方式調整 (讀者自行決定是否使
也會使用『虛擬環境』
用虛擬環境)
1 準備工作 7
圖: 不用虛擬環境步驟 1
圖: 不用虛擬環境步驟 2
1 準備工作 8
1.3 安裝函式庫方式
1.3.1 方式 1. 使用圖形介面
圖: Project Interpreter
接著點擊下面的 + 來搜尋函式庫
1 準備工作 9
圖: 選下面的 +
圖: 選擇 install package
最後會出現綠色的成功,就是安裝完畢了
1 準備工作 11
圖: 成功安裝
1.3.2 方式 2. 使用命令
圖: 下的命令
1 準備工作 12
圖: 成功安裝
簡單來說下的指令就是
圖: 完整步驟
1 準備工作 13
有時候我們在做一個複雜程式的時候,很難一次寫到完,或者是你想要把你所有的實驗步驟
寫成一份文件,這時候 Jupyter Notebook 就會是你最佳的利器,他可以讓你一個步驟一個步驟執
行程式,本書的程式也是由 Jupyter Notebook 撰寫,那這邊教學一下如何用 PyCharm 跟 Jupyter
Notebook 連動,請先利用上面的方式安裝 jupyter 函式庫,再接著下面的圖操作
圖: 開啟 jupyter 伺服器步驟
圖: 絕對要記得的兩個重點
2 PANDAS 基本使用 15
2 Pandas 基本使用
2.1 介紹
不管要抓取什麼樣的資料,把你辛苦的成果儲存下來絕對是最重要的一件事,所以在抓取前,
我們要先教你如何處理表格還有除存成最萬用的 CSV 格式!
2.2 用途
2.3 安裝方法
1. 使用 PyCharm: PyCharm -> Settings -> Project -> Project Interpreter -> + -> (搜索)pandas
-> Install Packages
2. 使用命令列: cd 到你安裝 Python 的資料夾 -> 輸入 python -m pip install pandas
2.4 官方文件
http://pandas.pydata.org/pandas-docs/stable/
2.5 可以處理的表格形式
http://pandas.pydata.org/pandas-docs/stable/io.html
2 PANDAS 基本使用 16
圖: 可以處理的表格形式
2.6 目標
2.7 資料集位置
1. 需要登入才能下載
2. 只取裡面的 ted_main.csv 來做分析
你一定要記得這兩個稱呼,看官方文件的時候才有辦法看得懂!
2.10 讀取表格操作
1. 必要參數: 檔案位置
2. encoding 選用參數 (有預設值): 讀取使用編碼
一個 DataFrame
2.11 DataFrame 大小
[程式]: df.shape
2.12 表格行篩選
篩選行的時候,我們就像在操作字典一樣,對你的 DataFrame 加上 [ ]
2.12.1 單行操作
[程式]: df["comments"]
[輸出]: 0 4553
1 265
2 124
3 200
4 593
…
2545 17
2546 6
2547 10
2548 32
2549 8
Name: comments, Length: 2550, dtype: int64
2.12.2 多行操作
2.13 表格列篩選
1. 篩選列的時候,我們使用的是.loc(少用, 如果有自己創造列標籤才用得上),iloc(常用, 使用
pandas 幫你創的 0 開始的列標籤)
2. 使用 iloc 的時候會得到一個像是 list 的資料,接著就可以使用類似 list 的操作來操作
3. .iloc -> [“第一筆資料”, “第二筆資料”, “第三筆資料”, …, “最後一筆資料”]
2.13.1 單列篩選
[程式]: df.iloc[0]
2.13.2 多列篩選
[5 rows x 17 columns]
2.13.3 頭幾列篩選
[程式]: # 頭五列
df.head(5)
[5 rows x 17 columns]
2.13.4 尾幾列篩選
使用 tail 函式來做尾部的篩選
[程式]: # 尾五列
df.tail(5)
[5 rows x 17 columns]
2 PANDAS 基本使用 21
2.14 表格行+列篩選
2.15 列過濾
1. 過濾操作是把符合我們期待的列留下來,不符合期待的列丟掉的一個操作
2. 核心概念是做一個跟我們的資料筆數一樣大的布林 list,對到 True 的資料留下,對到 False
的資料丟掉
3. (特別) 這時候一樣是對你的 DataFrame 加上 [ ] , 把布林 list 丟進你的 [ ] 裡
[3 rows x 17 columns]
[2 rows x 17 columns]
[輸出]: 0 True
1 False
2 False
3 False
4 False
…
2545 False
2546 False
2547 False
2548 False
2549 False
Name: description, Length: 2550, dtype: bool
[程式]: # 帶入 DataFrame
df[bool_filter]
[6 rows x 17 columns]
2.16 儲存表格
1. 必要參數: 檔案位置
2. 選用參數 encoding(有預設值): 讀取使用編碼
3. 選用參數 index(有預設值 True): 要不要把 pandas 幫你產生的列編號寫進檔案, True: 寫,
False: 不寫, 通常我會選 False
[程式]: # 把剛剛儲存的東西讀出來給你看看
pd.read_csv("filter.csv", encoding = "utf-8")
[6 rows x 17 columns]
2.17 刪除行
title
0 Do schools kill …
1 Averting the cli…
2 Simplicity sells
3 Greening the ghetto
4 The best stats y…
… …
2545 What we're missi…
2546 The most Martian…
2547 What intelligent…
2548 A black man goes…
2549 How a video game…
2.18 轉換類別
[輸出]: 0 1140825600
1 1140825600
2 1140739200
3 1140912000
4 1140566400
…
2545 1496707200
2546 1492992000
2547 1492992000
2548 1499472000
2549 1492992000
Name: film_date, Length: 2550, dtype: int64
2 PANDAS 基本使用 25
[程式]: # 我們先對一個格子做一次看看
from datetime import datetime
import pytz
print("原始:", df["film_date"][0])
print("轉換 (當地時間):", datetime.fromtimestamp(df["film_date"][0]))
print("轉換 (標準時間):", datetime.utcfromtimestamp(df["film_date"][0]))
原始: 1140825600
轉換 (當地時間): 2006-02-25 08:00:00
轉換 (標準時間): 2006-02-25 00:00:00
apply 只要把你定義的流程名字丟進去,就會自動對每一個格子做一次
[程式]: # 設定回去原表格
df["film_date(datetime)"] = df["film_date"].apply(timeflow)
df[ ["film_date", "film_date(datetime)"] ]
2.19 進階列過濾
[輸出]: 0 True
1 False
2 False
3 False
4 False
…
2545 False
2546 False
2547 False
2548 False
2549 False
Name: tags, Length: 2550, dtype: bool
2 PANDAS 基本使用 27
[輸出]: 0 False
1 False
2 False
3 False
4 True
…
2545 False
2546 False
2547 False
2548 False
2549 False
Name: tags, Length: 2550, dtype: bool
3 Google 動圖下載
我們馬上進入正題,來我們的第一個練習吧!我們這個練習最重要的目的是先讓你理解一下網
路的概念,接下來的爬蟲就會對你非常簡單,另外也會教你如何透過網路爬蟲大量的抓取圖片!
3.1 什麼是網路
我心中的網路就如同下圖的樣子,你的電腦就彷彿是一個人,伺服器就像是一個服務台,人會
不斷地向服務台詢問問題,也會得到對應的答案
圖: 網路的樣子
那只剩最後一個要釐清的東西了!什麼是『問題』呢?
『問題』就是所謂的一個『網址』
!也就
是說,我們只要懂得我們平常使用的網址,我們就可以懂大半的網路世界了!
3 GOOGLE 動圖下載 30
3.2 網址解析
我們先用最簡單的網址來做個解析
https://www.google.com.tw/search?q=apple&oq=apple&aqs=chrome.
.69i57j69i61j69i65l3j69i60.1283j0j4&sourceid=chrome&ie=UTF-8
上面這個網址是我在 Google 搜尋 Apple 這字眼得到的網址
如果你仔細看,這網址其實可以被分成四個部分
❤ https: 這部分是我們在跟服務台溝通使用的語言,順帶一提,https 的 s 代表 secure,代表
傳輸過程會經過『加密成密碼』的動作,才可以預防別人在傳輸線中進行偷聽的動作
❤ www.google.com.tw: 到第一個/前,你可能看到兩種形式,一種就是我們現在看到的,我們
叫做 Domain Name(域名),另外一種是由四個數字構成的 IP,先說 IP,IP 就是你的電腦在網路世
界的位址,而 Domain Name 背後一定會對到一個 IP,我喜歡稱 Domain Name 為地標,以例子來
說就是: Domain Name(小巨蛋) = IP(臺北市松山區南京東路四段 2 號) 的關係
❤ search: 到? 前則是我們的第三個部分,我喜歡稱他為向『服務台』遞出的『問題本身』
,我
們這裡遞出的問題就是一個叫做『search』的問題
❤ q=apple&op=apple: 第四個部分則是在? 後的一連串東西,事實上這是由許多組組成的,你
可以用 & 符號把他們分開,我們仔細看一組 q=apple,你應該也發現了,這其實就是 search 這個
問題所需要的一些必要資訊,每組必要資訊都以附加資訊名 = 附加資訊值的方式存在
3.3 網路抓取兩步驟
3.3.1 步驟 1: 查看原始碼
圖: 點選好手氣
3 GOOGLE 動圖下載 31
我們的目標是抓取https://www.google.com/doodles的所有動圖
請先幫我在空白的地方點選『查看原始碼』
圖: 點選查看原始碼
你應該會看到這樣的圖
圖: 跳出的頁面
那為什麼我們要查看『原始碼』呢?
其實這才是最重要的問題,這裡我告訴你答案!!
!一定要好好記得唷
『原始碼是你跟服務台搭訕的第一個問題 (網址列) 的答案』
『第一個問題的答案』『第一個問題的答案』『第一個問題的答案』很重要所以說三次!
接下來我們要做什麼呢?
查看我們想要的答案有沒有在『第一個問題的答案』裡,如果有的話,我們等等就讓程式也詢
問你『網址列上的問題』
3 GOOGLE 動圖下載 32
圖: 只有六張動圖在上面
3.3.2 步驟 2: 找出真正問題
在步驟 1 的時候,我們已經知道藉由『第一個問題』,也就是網址列,是沒有我們想要的答案
的,事實上,這在現代的網頁挺常發生的,第一個問題只拿得到一點點東西,必須補上後續的問題
才會拿到更多東西!那問題怎麼觀察後續的問題呢?請跟著下圖打開我們 Chrome 的 Network 工具
3 GOOGLE 動圖下載 33
圖: 更多工具-開發人員工具
圖: Network
接著由於我們初學乍到,我們稍微做點弊,上方的分類是根據問題以及答案的不同分成不同類,
我們先將它固定在 XHR
圖: 先固定成 XHR
接著點擊一則來看看,並且觀察右邊的 Header 部分
3 GOOGLE 動圖下載 35
圖: 觀察右邊的 Header
我想聰慧的你應該已經發現些什麼了吧?這網址大有蹊蹺,因為『年份』和『月份』歷歷在目,
那接下來該如何做呢?旁邊還有兩個可以按的,一個叫做『response』(回應),一個叫做『preview』
(漂亮化的回應),我們按按看 preview
圖: Network Preview
3.3.3 此網站的結論
3.4 開始寫程式
3.4.1 JSON 回應
我們看到已經得到回應了!但是我們要看回應是哪種形態來用對應的函式庫處理,我們看看剛
剛 response 的部分
圖: 回應的樣式
圖: 再看一次 preview
birthday-4646625505968128-l.png
圖片標題: 米莉安特拉利 85 歲冥誕
圖片網址: https://www.google.com/logos/doodles/2018/miriam-tlalis-85th-
birthday-5099732341882880-l.png
圖片標題: 2018 年父親節 (愛沙尼亞、挪威)
圖片網址: https://www.google.com/logos/doodles/2018/fathers-day-2018-estonia-
norway-5766574735622144-l.png
圖片標題: 伊莉莎李昂那多扎馬菲拉斯 131 歲冥誕
圖片網址: https://www.google.com/logos/doodles/2018/elisa-leonida-zamfirescus-131st-
birthday-6181104577937408-l.png
圖片標題: 紀念阿曼達克羅維
圖片網址: https://www.google.com/logos/doodles/2018/celebrating-amanda-
crowe-5837715529531392-l.png
圖片標題: 2018 年柬埔寨獨立紀念日
圖片網址: https://www.google.com/logos/doodles/2018/cambodia-independence-
day-2018-5069486645313536-l.png
圖片標題: 2018 年美國大選
圖片網址: https://www.google.com/logos/doodles/2018/united-states-
elections-2018-5184312390451200.6-l.png
圖片標題: 麥可德托羅斯 82 歲冥誕
圖片網址: https://www.google.com/logos/doodles/2018/michael-dertouzos-82nd-
birthday-5050461148151808-l.png
圖片標題: 2018 年巴拿馬獨立紀念日
圖片網址: https://www.google.com/logos/doodles/2018/panama-independence-
day-2018-5325969073111040-law.gif
圖片標題: 2018 年亡靈節
圖片網址: https://www.google.com/logos/doodles/2018/day-of-the-
dead-2018-5705827255058432-law.gif
圖片標題: 約瑟夫蒂勒爾 160 歲冥誕
圖片網址: https://www.google.com/logos/doodles/2018/joseph-burr-tyrrells-160th-
birthday-6697443532996608-l.png
3.4.2 下載圖片
doodles = json.load(response)
for d in doodles:
url = "https:" + d["url"]
title = d["title"]
# 你可以把網址用/切出, 會發現我們可以得到一個 list, 最後一個東西就是我們要的檔案名字
# print(url.split("/"))
# 這裡必須自行建立你的資料夾在你的 Project 底下
fpath = "doodles/" + url.split("/")[-1]
urlretrieve(url, fpath)
我們看看儲存的結果
3.4.3 結論
3.5 整年份下載
更進階的,我們除了搞定整年份以外,順便把創造資料夾的工作讓程式來處理
# 創造資料夾
import os
# 在建立前必須先檢查資料夾有沒有存在
# 不存在才可以創建
if not os.path.exists(" 資料夾名"):
os.mkdir(" 資料夾名")
處理月份: https://www.google.com/doodles/json/2018/1?hl=zh_TW
處理月份: https://www.google.com/doodles/json/2018/2?hl=zh_TW
處理月份: https://www.google.com/doodles/json/2018/3?hl=zh_TW
處理月份: https://www.google.com/doodles/json/2018/4?hl=zh_TW
處理月份: https://www.google.com/doodles/json/2018/5?hl=zh_TW
處理月份: https://www.google.com/doodles/json/2018/6?hl=zh_TW
處理月份: https://www.google.com/doodles/json/2018/7?hl=zh_TW
處理月份: https://www.google.com/doodles/json/2018/8?hl=zh_TW
處理月份: https://www.google.com/doodles/json/2018/9?hl=zh_TW
處理月份: https://www.google.com/doodles/json/2018/10?hl=zh_TW
處理月份: https://www.google.com/doodles/json/2018/11?hl=zh_TW
3 GOOGLE 動圖下載 42
處理月份: https://www.google.com/doodles/json/2018/12?hl=zh_TW
你可以看到所有的圖片依照月份好好的儲存了,而且只是簡單的加了幾行程式碼而已!試試看
吧!
圖: 完整的圖片儲存
3.6.1 安裝函式庫
請安裝
# 3 個月就好!! 不然圖太大了
for m in range(1, 3):
url = "https://www.google.com/doodles/json/2018/" + str(m) + "?hl=zh_TW"
response = urlopen(url)
doodles = json.load(response)
for d in doodles:
url = "https:" + d["url"]
# 把所有要儲存的 url 儲存起來
urllist.append(url)
print("總共圖片數:", len(urllist))
# 把大圖準備成 寬 * 高 個小圖
# 我想要每列五個圖
width = 5
# 算出來應該會是多少個列
height = int(len(urllist) / width) + 1
# 順便調整整個大圖大小, 我用 20 英吋 * 30 英吋
plt.figure(figsize=(20, 30))
# enumerate 可以回傳 (index, 資料) 這樣的 tuple
for (index, url) in enumerate(urllist):
plt.subplot(height, width, index + 1)
response = urlopen(url)
# 利用 PIL 讀取圖片
img = Image.open(response)
# 不需要座標軸
plt.axis("off")
# 把小圖畫出來
plt.imshow(img)
總共圖片數: 69
3 GOOGLE 動圖下載 44
4 TABELOG 餐廳收集 45
4 Tabelog 餐廳收集
4.1 HTML 解析
在開始前,我們要先教你什麼是網頁的表示形式,請你記住下面的這個比喻,首先我們先講個
故事:
『有一座城堡,城堡裡有公主和王子,公主養了一隻狗!』
要怎麼讓這故事有一個固定的表示形式呢?我們想到一個聰明的方法,把這故事表示的有『結
構性』,也就是把『擁有』的概念表示出來
4.1.1 元素
結構性的第一個概念,把一個一個物體用包含的形式表示出來,我喜歡叫他是一個一個『盒
子』,就像惡作劇禮物一樣,打開一個盒子還有另外一個盒子,我們用來表示一個盒子的開始,代
表盒子的結束,順帶一提,比較文言的說法是『元素』
<城 堡>
<公 主>
<狗>
</ 狗 >
</ 公 主 >
<王 子>
</ 王 子 >
</ 城 堡 >
特殊元素 請你一定要記住幾個特殊的盒子品牌,因為我們在抓取的時候特別常用,瀏覽器看到這
些品牌的時候會特別處理
❤ img: 圖片
4 TABELOG 餐廳收集 46
❤ video: 影片
❤ a: 超連結
4.1.2 屬性
如果你今天有兩個公主,我們的一大問題就是我們分不出哪個公主是哪個!譬如說今天有兩個
超連結,你並沒有不一樣的地方來讓他們可以把他們不同的連結顯示出來,也是一個大問題,所以
我們會在盒子的一開始的地方把這不一樣的部分加進去,這個加進去不一樣的地方,我喜歡叫他做
『盒子的特徵』,比較文言的說法是『屬性』!
</ 元 素 >
特殊屬性 這裡有些『特殊特徵』
,這些『特殊特徵』一定要配上『特殊盒子』才會起作用,我們一
起來看看有哪些
❤ img 和 video 配上 src 屬性:
❤ a 配上 href 屬性:
通用屬性 除了一定要跟特定元素配合的特殊特徵以外,還有兩個所有人都可以使用的『通用特
徵』,這兩個特徵一開始存在的目的其實是為了在網頁開發的時候可以透過這些不同點選到我要的
元素,而我們在教電腦爬過網頁的時候也可以利用這兩個特徵來定位我想要的盒子!這兩個特徵分
別為 class(職業) 和 id(身分證),要注意一點,兩個職業以上的時候我們會用『空白鍵』分開職業!
4.1.3 內容
我們有了『盒子』
,有了『特徵』
,但是我們始終沒有把東西秀給使用者看,所以我們還需要最
後一個東西,我喜歡叫他做『紙條』,因為就好像是你打開這個惡作劇禮物的時候,在某些盒子裡
面看到了紙條提示,『紙條』就會直接秀在頁面上讓使用者看到,比較文言的稱呼叫做『內容』
4.2 網站分析
4.2.1 目標網頁
圖: 目標網頁
4 TABELOG 餐廳收集 48
圖: 來到目標網址的流程
4.2.2 步驟 1: 打開原始碼
別忘了我們之前說的唷!網路就是一個『遊客中心』
,你應該跟『服務台』(server) 遞出你想問
的問題,拿到答案!而『原始碼』就是第一個問題,也就是『網址列』的答案
圖: 右鍵點選空白的地方查看原始碼
你會發現跟上一個練習不一樣的是,我們的原始碼裡面就包含了我們想要的答案,而且從第一
則到最後一則全部都在上面
4 TABELOG 餐廳收集 49
圖: 發現想要的答案
4.2.3 此網站的結論
於是,我們應該可以跳過第二個步驟了!因為我們發現『原始碼』就包含著我們需要的東西,
而想要得到『原始碼』,只要用『網址列』拿到答案即可!
4.3 開始寫程式
這個程式我們打算教會你如何處理網頁形式的回應,請記得先安裝對應函式庫唷!
4.3.1 必要函式庫
4.3.2 送出問題
請把我們上們分析完的結論,『問題就是網址列』真正的送出去,並且得到答案以後,轉換成
我們可以處理的『盒子』形式
/Users/Elwing/Library/Python/3.6/lib/python/site-packages/bs4/__init__.py:181:
UserWarning: No parser was explicitly specified, so I'm using the best available HTML
parser for this system ("lxml"). This usually isn't a problem, but if you run this
code on another system, or in a different virtual environment, it may use a different
parser and behave differently.
The code that caused this warning is on line 193 of the file
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/runpy.py. To get rid
of this warning, change code that looks like this:
BeautifulSoup([your markup])
to this:
markup_type=markup_type))
4.3.3 找元素
接著你最需要學會的事情就是尋找包含你想要的答案的『盒子』(元素),在 BeautifulSoup 最
基本的兩個操作就是:
❤ find: 找第一個符合條件的元素
❤ find_all: 找所有符合條件的元素,回傳答案是一個 List
在找尋元素的時候,你可以使用下面的兩個『特徵』幫忙做篩選,也就是上面我們提到的『class』
和『id』
接著你要使用另外一個工具來幫你定位盒子,請見下圖
4 TABELOG 餐廳收集 51
圖: 定位的步驟
response = urlopen("https://tabelog.com/tw/tokyo/rstLst/?SrtT=rt")
html = BeautifulSoup(response)
# 使用 find_all, 因為要找多個
# 這裡我通常喜歡單用一個職業找, 比較不容易因為網頁的改變而必須重寫程式
restaurants = html.find_all("li", class_="list-rst")
# 我只印第一家餐廳給你看
print(restaurants[0])
4.3.4 再繼續找元素
找到你想要的元素以後,再繼續深入把你想要的元素萃取出來,不過這裡要注意一下,我們
find_all 得到的是一個列表,而不是元素,所以你必須用 for…in 走過一個一個元素,再繼續 find
圖: 找我們要的部分,我想要英文名字以及他連到的 blog
response = urlopen("https://tabelog.com/tw/tokyo/rstLst/?SrtT=rt")
html = BeautifulSoup(response)
restaurants = html.find_all("li", class_="list-rst")
# 走過每一家餐廳, 對那個餐廳盒子再繼續下去找
for r in restaurants:
# 只找第一個 match, 所以使用 find
en = r.find("a", class_="list-rst__name-main")
print(en)
href="https://tabelog.com/tw/tokyo/A1316/A131601/13041029/"
target="_blank">Torishiki</a>
4.3.5 萃取目標
你會發現我們通常有兩個目標會想萃取,第一個是『紙條』(內容),對於上面的例子來說就是
英文店名,第二個是『特殊特徵』,對於上面的例子就是 href 特徵,語法分別如下
# 萃取特殊特徵, 跟字典一樣使用 []
元素 [特殊特徵]
response = urlopen("https://tabelog.com/tw/tokyo/rstLst/?SrtT=rt")
html = BeautifulSoup(response)
restaurants = html.find_all("li", class_="list-rst")
for r in restaurants:
en = r.find("a", class_="list-rst__name-main")
# 萃取紙條和特殊特徵
print(en.text, en["href"])
Matsukawa https://tabelog.com/tw/tokyo/A1307/A130701/13124391/
Saito https://tabelog.com/tw/tokyo/A1308/A130802/13015251/
Shinohara https://tabelog.com/tw/tokyo/A1301/A130101/13200949/
Sugita https://tabelog.com/tw/tokyo/A1302/A130204/13018162/
Quintessence https://tabelog.com/tw/tokyo/A1314/A131405/13159567/
Kyoaji https://tabelog.com/tw/tokyo/A1301/A130103/13002887/
Restaurant l'equateur https://tabelog.com/tw/tokyo/A1307/A130702/13121866/
SUGALABO https://tabelog.com/tw/tokyo/A1307/A130704/13182678/
PELLEGRINO https://tabelog.com/tw/tokyo/A1303/A130302/13072775/
Hashiguchi https://tabelog.com/tw/tokyo/A1308/A130801/13134517/
L'Effervescence https://tabelog.com/tw/tokyo/A1306/A130602/13116356/
Furuta https://tabelog.com/tw/tokyo/A1313/A131301/13176780/
CHIUnE https://tabelog.com/tw/tokyo/A1301/A130101/13203796/
Tomura https://tabelog.com/tw/tokyo/A1308/A130802/13005027/
Choyo https://tabelog.com/tw/tokyo/A1301/A130103/13172920/
Ajiman https://tabelog.com/tw/tokyo/A1307/A130701/13001332/
SATO Buriand https://tabelog.com/tw/tokyo/A1319/A131905/13159782/
4 TABELOG 餐廳收集 56
Kohaku https://tabelog.com/tw/tokyo/A1309/A130905/13049130/
Mitani https://tabelog.com/tw/tokyo/A1309/A130902/13042204/
Torishiki https://tabelog.com/tw/tokyo/A1316/A131601/13041029/
4.3.6 再繼續找元素
在接下來的日文名字比較簡單,但是比較難的是評分的部分,評分難的是三個評分的元素長得
一模一樣
圖: 三個分數的盒子都長得一樣
response = urlopen("https://tabelog.com/tw/tokyo/rstLst/?SrtT=rt")
html = BeautifulSoup(response)
restaurants = html.find_all("li", class_="list-rst")
for r in restaurants:
en = r.find("a", class_="list-rst__name-main")
# 抓取日本名, 這邊請讀者試一下去找元素
ja = r.find("small", class_="list-rst__name-ja")
# 抓取所有的分數先
scores = r.find_all("b", class_="c-rating__val")
# 利用 [0] 取得第一個 (綜合評分) [1] 取得第二個 (晚間評分) [2] 取得第三個 (午間評分)
print(scores[0].text, scores[1].text, scores[2].text, ja.text, en.text, en["href"])
4.4 所有的頁面下載
我們在上面已經學會了一個頁面的下載,我們現在試試看整個頁面的下載
圖: 觀察網址的規律
圖: 在跑到死的時候會出現的錯誤
# 從第一頁開始
# page = 1
# 我因為講義篇幅關係,從 59 頁開始
page = 59
while True:
# 利用增加的數字製作網址
url = "https://tabelog.com/tw/tokyo/rstLst/" + str(page) + "/?SrtT=rt"
print("[正在處理]", url)
# 增加處理機制
try:
response = urlopen(url)
# 這裡你要看一下上面是發生什麼錯誤
except HTTPError:
print("[完成] 應該是來到最後一頁了")
break
html = BeautifulSoup(response)
restaurants = html.find_all("li", class_="list-rst")
for r in restaurants:
en = r.find("a", class_="list-rst__name-main")
ja = r.find("small", class_="list-rst__name-ja")
scores = r.find_all("b", class_="c-rating__val")
4 TABELOG 餐廳收集 59
[正在處理] https://tabelog.com/tw/tokyo/rstLst/59/?SrtT=rt
3.66 3.66 - 酒膳 蔵四季 Shuzenkurashiki
https://tabelog.com/tw/tokyo/A1321/A132102/13125348/
3.66 3.85 3.58 ラ ヴィータ LA VITA https://tabelog.com/tw/tokyo/A1309/A130903/13012197/
3.66 3.66 - そば工房 玉江 Sobakouboutamae
https://tabelog.com/tw/tokyo/A1323/A132301/13047621/
3.66 3.66 - 富士屋本店 日本橋浜町 Fujiyahontennihombashihamachou
https://tabelog.com/tw/tokyo/A1302/A130204/13193413/
3.66 3.66 - 鯛良 六本木店 Taira https://tabelog.com/tw/tokyo/A1307/A130701/13108409/
3.66 3.66 - BAR 五 Bago https://tabelog.com/tw/tokyo/A1303/A130302/13093230/
3.66 3.66 3.63 レストラン セン Restaurant Sen
https://tabelog.com/tw/tokyo/A1309/A130904/13117224/
3.66 3.66 - 楽食ふじた rakushokufujita https://tabelog.com/tw/tokyo/A1317/A131701/13166724/
3.66 3.65 3.63 レストラン 代官山小川軒 Resutorandaikanyamaogawaken
https://tabelog.com/tw/tokyo/A1303/A130303/13001763/
3.66 3.60 3.63 すずき Suzuki https://tabelog.com/tw/tokyo/A1316/A131604/13122982/
3.66 3.66 - カラペティ バトゥバ QUAND L'APPETIT VA TOUT VA
https://tabelog.com/tw/tokyo/A1307/A130702/13097828/
3.66 3.60 3.59 green glass guri-ngurasu
https://tabelog.com/tw/tokyo/A1321/A132104/13196314/
3.66 3.66 - はせ川 Hasegawa https://tabelog.com/tw/tokyo/A1312/A131201/13143482/
3.66 3.66 3.50 重よし Shigeyoshi https://tabelog.com/tw/tokyo/A1306/A130601/13001153/
3.66 3.66 - ロウホウトイ Rouhoutoi https://tabelog.com/tw/tokyo/A1307/A130703/13203446/
3.66 3.66 - すし龍尚 Sushishouryuu https://tabelog.com/tw/tokyo/A1316/A131602/13172896/
3.66 3.56 3.58 ソンブルイユ SOMBREUIL https://tabelog.com/tw/tokyo/A1309/A130905/13211031/
3.66 3.64 3.52 Libre 白金高輪店 Libre https://tabelog.com/tw/tokyo/A1316/A131602/13222761/
3.66 3.66 3.58 割烹 山路 Kappouyamaji https://tabelog.com/tw/tokyo/A1301/A130103/13195108/
3.66 3.66 3.08 きんとき Kintoki https://tabelog.com/tw/tokyo/A1320/A132002/13044590/
[正在處理] https://tabelog.com/tw/tokyo/rstLst/60/?SrtT=rt
3.66 3.64 3.52 の弥七 Noyashichi https://tabelog.com/tw/tokyo/A1309/A130903/13209784/
3.66 3.75 3.39 calme Calme https://tabelog.com/tw/tokyo/A1317/A131705/13197166/
3.66 3.66 3.46 ファイヤーホール 4000 麻布十番店 Faiyahoruyonsen
https://tabelog.com/tw/tokyo/A1307/A130702/13220650/
3.66 3.66 3.06 銀屋 Ginya https://tabelog.com/tw/tokyo/A1316/A131602/13153190/
3.66 3.66 - 和食 ひまわり Washokuhimawari
https://tabelog.com/tw/tokyo/A1301/A130103/13114242/
3.66 3.66 - 江戸前 鮨 服部 edomaesushihattori
https://tabelog.com/tw/tokyo/A1307/A130701/13098338/
3.66 3.66 - あらいかわ Araikawa https://tabelog.com/tw/tokyo/A1307/A130702/13218481/
3.66 3.66 - バーホシ イーリス Bahoshiirisu
https://tabelog.com/tw/tokyo/A1301/A130101/13200352/
3.66 3.66 3.06 くすのき Kusunoki https://tabelog.com/tw/tokyo/A1304/A130401/13223239/
4 TABELOG 餐廳收集 60
4.5 加上儲存
# 先 import
import pandas as pd
# 準備空的 DataFrame, 先固定住 columns
df = pd.DataFrame(columns=["a", "b"])
# 準備一個 Series, index 的欄位要跟 columns 對到
s = pd.Series([資料 1, 資料 2], index=["a", "b"])
# 因為 Series 沒有橫列的標籤, 所以加進去的時候一定要 ignore_index=True
df = df.append(s, ignore_index=True)
# 儲存成 csv, 不過列編號的數字不用存, 所以我把 index=False
df.to_csv(" 檔名.csv", encoding="utf-8", index=False)
import warnings
warnings.filterwarnings('ignore')
# 先 import
import pandas as pd
# 準備空的 DataFrame
df = pd.DataFrame(columns=["綜合評分", "晚間評分", "午間評分", "日文店名", "英文店名", "Blog"])
page = 1
while True:
url = "https://tabelog.com/tw/tokyo/rstLst/" + str(page) + "/?SrtT=rt"
try:
response = urlopen(url)
except HTTPError:
break
html = BeautifulSoup(response)
restaurants = html.find_all("li", class_="list-rst")
for r in restaurants:
en = r.find("a", class_="list-rst__name-main")
ja = r.find("small", class_="list-rst__name-ja")
scores = r.find_all("b", class_="c-rating__val")
# 準備 Series 和 append 進 DataFrame
# 這邊由於會超過頁面, 所以我把 list 換行一下
s = pd.Series([scores[0].text, scores[1].text,
scores[2].text, ja.text, en.text, en["href"]],
index=["綜合評分", "晚間評分", "午間評分", "日文店名", "英文店名", "Blog"])
df = df.append(s, ignore_index=True)
page = page + 1
# 輸出 csv
df.to_csv("tabelog.csv", encoding="utf-8", index=False)
圖: 儲存的 CSV
按照下面的步驟做,不過記得在關掉的時候不要儲存回去,如果要再執行程式前也記得把開起來的
檔案關掉 (Excel 會把檔案鎖住)
4.6 結語
恭喜你完成了第一個網頁的練習,我們後續還要再用各種例子來讓大家學習網頁的知識!
5 GOOGLE 小姐姐 64
5 Google 小姐姐
5.1 介紹
圖: Google 小姐住處
那別忘記了,網路是『動態』的,隨著你的動作,可能會有更多的『問題』(網址) 被送給伺服
器,所以請你把『更多工具』-『開發人員工具』- 『Network』一樣開起來,你會發現,隨著你按下
廣播的按鍵的時候,有一個問題悄悄被問出去
5 GOOGLE 小姐姐 65
圖: 按下廣播的時候被問出的問題
當你把這個問題放在旁邊的網頁送出的時候,應該會震驚的發現,你單獨得到了 mp3,而且下
載不過是一個按鍵的事
圖: 可以下載了
圖: 第二次慢速版網址
這 是 第 一 次 的 網 址: https://translate.google.com.tw/translate_tts?ie=
UTF-8&q=%E4%BD%A0%E5%A5%BD&tl=zh-TW&total=1&idx=0&textlen=2&tk=612044.
1040128&client=t
這 是 第 二 次 的 網 址: https://translate.google.com.tw/translate_tts?ie=
UTF-8&q=%E4%BD%A0%E5%A5%BD&tl=zh-TW&total=1&idx=0&textlen=2&tk=612044.
1040128&client=t&ttsspeed=0.24
你可以清楚地看到 ttsspeed 就會是念的速度,那我們可不可以自己修改這速度呢?何不你自
己試試看下面的這個我修改過的網址呢!?
https://translate.google.com.tw/translate_tts?ie=UTF-8&q=%E4%BD%A0%
E5%A5%BD&tl=zh-TW&total=1&idx=0&textlen=2&tk=612044.1040128&client=t&
ttsspeed=0.1
是不是又更慢了!如果忘記的讀者請翻到 Doodles 的章節,好好的複習『網址構成』!學會網
址的構成讓你可以輕鬆自如地想要什麼,就拿什麼!
5.2 開始寫程式
5.2.1 選擇網址
由於上面的網址很多參數我們並不知道怎麼設定,這裡提供一個特別,老師私藏的網址當作我
們送出的網址
https://translate.google.com/translate_tts?ie=UTF-8&tl=en&client=
tw-ob&q=hello
其實就是上面網址的不同版本 (client 不同),這個網址基本上問號後面的每一個資料我們都知
道要填什麼
❤ ie: 編碼,固定式 utf-8
5 GOOGLE 小姐姐 67
5.2.2 先來試試看一小段文字
2. 使用第三方函式庫 requests,建議使用,會讓整個過程變得比較簡單
第二種方式如下
# 第一種儲存方式
f = open(" 檔名", "wb")
f.write(audio)
f.close()
5 GOOGLE 小姐姐 68
# 第二種儲存方式
with open(" 檔名", "wb") as f:
f.write(audio)
圖: 儲存的 mp3
圖: 我準備的文章
5.2.3 調整速度
base = "https://translate.google.com/translate_tts?ie=UTF-8&tl=zh-TW&client=tw-ob&q="
# 我設定在外面方便調整, 當然讀者也可以做成個函數, 方便帶入不同數值
speed = 0.1
# 加入 ttsspeed 的網址參數, 由於 speed 是數字, 一定要轉換成字串才能串連
url = base + article + "&ttsspeed=" + str(speed)
response = requests.get(url)
audio = response.content
with open("test.mp3", "wb") as f:
f.write(audio)
5.3 大量文章轉換
import glob
# 會回傳一個所有符合格式的檔名 list
# 格式可以用 * 來表示任意字元
flist = glob.glob(" 檔名格式")
# 創造資料夾
import os
# 在建立前必須先檢查資料夾有沒有存在
# 不存在才可以創建
if not os.path.exists(" 資料夾名"):
os.mkdir(" 資料夾名")
flist = glob.glob("./input/*.txt")
for fname in flist:
# 替換成 fname
with open(fname, "r", encoding="utf-8") as f:
print("[處理中]:", fname)
article = f.read()
base = "https://translate.google.com/translate_tts?ie=UTF-8&tl=zh-TW&client=tw-ob&q="
url = base + article
response = requests.get(url)
audio = response.content
# 準備存檔的名字, 把.txt 替換成.mp3 即可
savename = fname.replace(".txt", ".mp3")
# 這裡比較特別, 我把 input 替換成 output, 但只替換掉最左邊的 input, 帶入選用參數 (1)
5 GOOGLE 小姐姐 71
[處理中]: ./input/chou.txt
[儲存中]: ./output/chou.mp3
[處理中]: ./input/lin.txt
[儲存中]: ./output/lin.mp3
圖: 儲存的結果
5.4 進階 - 長文章
這裡最後補充一下如果太長的文章該如何做,如果你直接讀取長度太長的文章,會出現錯誤,
不過在 requests 模組,我們要多學一個功能,叫做 raise_for_status(),不像 urlopen 可以直接回傳
錯誤,你必須在使用後才會吐出錯誤,我準備了一個 b.txt,這裡就是更長的文章了
5 GOOGLE 小姐姐 72
圖: 我準備的長篇文章
你 可 以 試 試 看 下 面 的 程 式, 你 會 發 現 錯 誤 就 出 來 了, 那 你 當 然 也 可 以 以 使 用 re-
sponse.status_code 或者 try…except 去處理
這裡我們收到一個叫做 404 的錯誤,404 的錯誤的意思是 Not Found,
『資源不存在』的意思,
這裡 Google 回 404 的意思就是告訴你,這太長了,這麼長的資源我們無法翻譯,無法提供給你這
個資源!
[處理句子]: Later that day, when the Princess was sitting at the table, something was
heard coming up the marble stairs.
[處理句子]: Splish, splosh, splish splosh!
[處理句子]: The sound came nearer and nearer, and a voice cried, "Let me in, youngest
daughter of the King."
[處理句子]: The Princess jumped up to see who had called her.
[處理句子]: Now when she caught sight of the frog, she turned very pale.
[處理句子]: "What does a frog want with you?"
[處理句子]: demanded the King, looking rather surprised.
[處理句子]: The Princess hung her head.
5 GOOGLE 小姐姐 74
[處理句子]: "When I was sitting by the fountain my golden ball fell into the water.
[處理句子]: This frog fetched it back for me, because I cried so much."
[處理句子]: The Princess started to cry again.
[處理句子]: "I promised to love him and let him eat from my golden plate, drink from my
golden cup, and sleep on my golden bed."
[處理句子]: The King looked at the frog and thought for a while before he spoke.
[處理句子]: "Then you must keep your promise, my daughter."
6 PTT 文章抓取 75
6 PTT 文章抓取
6.1 目標
先說明一下我們這個練習的目標
❤ 抓取 PTT Movie 版〸頁文章
❤ 把成果存成一個 CSV 檔案
❤ (進階) 抓取有年齡限定的 Gossiping 版
6.2 想要教給你的東西
❤ 403 的解決
❤ 較為簡陋網頁的抓取
❤ Cookie 的使用
6.3 網站分析
圖: 目標 (Movie 版首頁)
6.3.1 步驟 1: 打開原始碼
一樣點擊右鍵打開原始碼,這裡應該非常明顯的可以看到我們想要的東西就在原始碼裡!
圖: 原始碼
6.3.2 此網站的結論
6.4 開始寫程式
圖: 403 的 Error
6.4.2 Header
Header 就是所謂網址的信封,那該如何填寫這個信封呢?我們藉由下面的例子一起看一下!
6.4.3 確認 Header
圖: Network
事實上你會發現,大部分網站檢查的最基本就是『user-agent』這欄位有無填寫,而且最基本
的檢查就是『有寫就好』!
r = Request(url)
r.add_header("User-Agent", "Mozilla/5.0")
response = urlopen(r)
html = BeautifulSoup(response)
# 讀者可以把這行註解拿掉, 看看完整的 html
# html
6 PTT 文章抓取 79
6.4.6 抓取一篇文章
由簡單而難,我們先挑一篇文章試試看
我挑選的是 https://www.ptt.cc/bbs/movie/M.1540692666.A.E90.html
這裡最難的是,你會發現 main-content 混著『看板』,『時間』,『ID』導致我們很難抓取
圖: 混雜在一起導致難以抓取
# 丟掉前可以先保留起我們需要的資訊
values = content.find_all("span", class_="article-meta-value")
print("作者:", values[0].text)
print("看板:", values[1].text)
print("文章標題:", values[2].text)
print("發文時間:", values[3].text)
# 扣掉 推文 的部分
# 這裡我順便統計一下這篇文到底是幾分, 推: +1, 噓: -1
# 至於推文我就不留下了, 想留下的讀者可以自行試試看
pushes = content.find_all("div", class_="push")
# 準備總體推噓分數
score = 0
for single_push in pushes:
# 你仔細看的話, 會發現前面的推噓有個共同的職業 push-tag
pushtag = single_push.find("span", class_="push-tag").text
if "推" in pushtag:
score = score + 1
elif "噓" in pushtag:
score = score - 1
single_push.extract()
print("推噓分數:", score)
# 最後你的 main-content 就是一個乾淨的, 把 text 取出就可以了
print(content.text)
推噓分數: 84
我本身小時候還滿容易暈車想吐
常常吐的到處都是
會想吐是因為汽油味很重
長大後雖然不會吐了
但只要聞到汽油味還是很不舒服
到現在都很討厭坐車
現在常常看到有人說看電影會暈會想吐
老實說我無法體會
因為從以前我以為會想吐都只是汽油味
很多人玩戰慄時空想吐
看一屍到底也想吐 看超狂亨利也想吐
到底還有哪些是會讓人想吐的電影?
我還不曾看電影看到吐過
很想體會一下
-----
Sent from JPTT on my Asus ASUS_Z00UD.
--
� 發信站: 批踢踢實業坊 (ptt.cc), 來自: 180.204.208.157
� 文章網址: https://www.ptt.cc/bbs/movie/M.1540692666.A.E90.html
恭喜你已經完成一篇文章的抓取了,我們下面再來進階一點,先教你一下如何爬過需要『超過
18 歲』認證的頁面和完整的看板抓取!
6 PTT 文章抓取 82
6.5 需要認證的版面
6.5.1 目標
圖: 被重新導向到新的網頁
6 PTT 文章抓取 83
6.5.2 Cookie
圖: 查看 cookie
你可以用下面的程式碼來為你的問題加上 cookie
# 使用 requests 模組較為方便
import requests
jar = requests.cookies.RequestsCookieJar()
# 你可以維護一個糖果罐, 把不同網頁的 cookie 設定進來
jar.set("over18", "1", domain="www.ptt.cc")
# 多帶入 cookies 參數
response = requests.get(" 網頁", cookies=jar)
url = "https://www.ptt.cc/bbs/Gossiping/M.1542451587.A.A0A.html"
6 PTT 文章抓取 84
jar = requests.cookies.RequestsCookieJar()
# 你可以維護一個糖果罐, 把不同網頁的 cookie 設定進來
jar.set("over18", "1", domain="www.ptt.cc")
# 多帶入 cookies 參數
response = requests.get(url, cookies=jar).text
# 接下來的就跟上面一樣!
html = BeautifulSoup(response)
content = html.find("div", id="main-content")
print("推噓分數:", score)
print(content.text)
如題
6 PTT 文章抓取 85
現在幾乎每天都有競選晚會
然後每個候選人都在比自己的場有沒有爆場
問題來了
這種競選晚會
到底有什麼好玩的?????
要在現場待三四個小時
然後候選人來的時間可能不到二十分鐘
最後散場
搞得整個人很累
想問一下這種競選晚會
好玩在哪??
??
-----
Sent from JPTT on my iPhone
--
� 發信站: 批踢踢實業坊 (ptt.cc), 來自: 27.52.44.142
� 文章網址: https://www.ptt.cc/bbs/Gossiping/M.1542451587.A.A0A.html
6.6 完整程式
6.6.1 定義單頁函式
由於頁面的抓取是每次都必須的,我會建議定義成一個獨立的函式,版面會變得較為整齊,而
且可以重複利用!但要注意的是,剛剛我們是直接印出來,這裡如果定義成函式就必須改成回傳值!
# 先做最基礎的判斷, 非公告和版規我回傳答案
if not "公告" in title and not "版規" in title:
response = requests.get(article_url, cookies=jar).text
html = BeautifulSoup(response)
content = html.find("div", id="main-content")
# 準備我們要回傳的字典
result = {}
values = content.find_all("span", class_="article-meta-value")
# 先把文章資訊記錄在字典裡
result["author"] = values[0].text
result["board"] = values[1].text
result["title"] = values[2].text
result["time"] = values[3].text
接著把剛剛定義的函式加入到我們的每頁的抓取中!
jar = requests.cookies.RequestsCookieJar()
jar.set("over18", "1", domain="www.ptt.cc")
# 從 movie 版首頁開始
url = "https://www.ptt.cc/bbs/movie/index.html"
# 準備要記錄的表格
df = pd.DataFrame(columns=["作者", "看板", "標題", "時間", "分數", "內容"])
# 走過五頁, range(5) 會幫你產生一個類似 [0, 1, 2, 3, 4] 的 list
for times in range(5):
response = requests.get(url, cookies=jar).text
html = BeautifulSoup(response)
# 得到每一篇文章的區域
articles = html.find_all("div", class_="r-ent")
# 走過每一篇文章
for single_article in articles:
# 得到 title 的超連結元素 <a>
title_area = single_article.find("div", class_="title").find("a")
# 如果有 title 才繼續 (被刪除的文章會沒有 title)
if title_area:
# 得到 title 的文字
title = title_area.contents[0]
# 得到 title 的超連結屬性 href
article_url = "https://www.ptt.cc" + title_area["href"]
# 使用我們剛剛定義的函式
result = get_page_meta(article_url)
# 檢查是不是回傳 None(公告和版規會回傳 None)
if result:
data = [result["author"], result["board"], result["title"],
result["time"], result["score"], result["content"]]
s = pd.Series(data, index=["作者", "看板", "標題", "時間", "分數", "內容"])
df = df.append(s, ignore_index=True)
# 往下一頁前進, string 參數可以找裡面文字符合我們帶入字串的元素
url = "https://www.ptt.cc" + html.find("a", text=re.compile(r"上頁"))["href"]
df.to_csv("ptt.csv", index=False, encoding="utf-8")
6.6.2 檢視成果
圖: 我們抓取出來的成果
7 狂新聞下載 89
7 狂新聞下載
7.1 介紹
『POST』
這個主題除了要教你如何處理新聞,順便還要教你一個很重要的網路附帶資訊的方式: ,
最後還要教會你一點簡單的視覺化!
7.2 複習
還記得我們討論過網址的四個部分吧?
https://www.google.com.tw/search?q=apple&oq=apple&aqs=chrome.
.69i57j69i61j69i65l3j69i60.1283j0j4&sourceid=chrome&ie=UTF-8
上面這個網址是我在 Google 搜尋 Apple 這字眼得到的網址
❤ https: 使用語言
❤ www.google.com.tw: 服務台地址
❤ search: 問題
❤ q=apple&op=apple: 附加資訊
但你有沒有想過,會不會有一些資訊是不能放在檯面上的?譬如如果是一個登入的問題,我們
可以把我們的『帳號』和『密碼』放在網址上嗎?很明顯是不行的!但是也不能不傳,不然對面的
服務台也不知道你到底是不是一個正確的使用者!那怎麼辦呢?簡單,傳個『秘密紙條』過去就可
以了!
7.3 網址抓取兩步驟
圖: 目標頁面
7.3.1 步驟 1: 查看原始碼
查看原始碼的時候,我們可以看到第一頁的條目都在上面,圖我稍微減少了一些額外的東西,
只留取新聞和更多的部分
圖: 右鍵 - 查看原始碼
但問題是第二頁以後呢?在你按下『更多』的按鈕的時候,你會發現,並沒有重整頁面,所以
我在步驟 1 得到的結論就是,我沒辦法通過『網址列』來拿到所有每一頁的新聞,我只能拿到第一
頁的新聞!
7.3.2 步驟 2: 找到真正的問題
圖: 開發人員工具 - Network
圖: Header 的最下面
沒錯,最後一個步驟,就是觀察附加資訊的方式:
❤ GET: 所有資訊都在網址上,最常出現,我們之前的例子都屬於 GET 例如 Google 查詢
圖: GET 的方式
'create_time': '2018-11-19',
'id': 13986,
'thumbnail': '//crazypic.ck101.com/b/0/b053d749c173b97b161d161f93147461.jpg',
'title': '挑戰在台中的大學售出百罐蜂蜜檸檬!正妹的反應出乎意料?!',
'view': 319},
{'author_name': '專打腦殘',
'crazy_rating': 4,
'create_time': '2018-11-19',
'id': 13984,
'thumbnail': '//crazypic.ck101.com/e/c/ec7761a377831ba55554c72f8ad2335d.jpg',
'title': '選前奧步再現!寧願繳 50 萬罰金也要觸法提民調',
'view': 458},
{'author_name': '專打腦殘',
'crazy_rating': 5,
'create_time': '2018-11-19',
'id': 13983,
'thumbnail': '//crazypic.ck101.com/2/4/2434200761856884aca8d2e62477ed55.jpg',
'title': '生為中國人的悲哀!主場馬拉松被硬塞國旗失冠軍',
'view': 543},
{'author_name': '專打腦殘',
'crazy_rating': 3,
'create_time': '2018-11-17',
'id': 13979,
'thumbnail': '//crazypic.ck101.com/0/8/08fdb95f17edf7ec659000f28a3aa003.jpg',
'title': '國民黨輔選韓國瑜 惡意攻擊陳菊「肥滋滋大母豬」',
'view': 594},
{'author_name': 'ssaa794613',
'crazy_rating': -1,
'create_time': '2018-11-17',
'id': 13978,
'thumbnail': '//s3.imgs.cc/img/ZC6kdml.png',
'title': '批評別人的競選經費反遭自己被打臉???',
'view': 416},
{'author_name': 'ssaa794613',
'crazy_rating': 0,
'create_time': '2018-11-17',
'id': 13976,
'thumbnail': '//crazypic.ck101.com/8/7/8781cf156f83c05925aa85daa557772d.jpg',
'title': '候選人政見太狂 單身網友嗨爆',
'view': 603},
{'author_name': 'ssaa794613',
'crazy_rating': 0,
'create_time': '2018-11-17',
'id': 13975,
'thumbnail': '//crazypic.ck101.com/d/9/d942bfb0d6f05554903a976add123a24.jpg',
7 狂新聞下載 94
7.3.4 得到新聞內容
圖: JSON 內容
我們已經正確儲存成一個表格了,一起來看看這個表格
圖: 一頁的儲存
7.3.5 多頁儲存和文字雲
我們順便來做個多頁的儲存和一個有趣的資料視覺化:『文字雲』吧,文字雲就是把重要或者
多數的文字用比較大的字型凸顯,這也是現在人蠻喜歡使用的一種視覺方式,畢竟對於出現幾次之
類的計數,一般的時候我們並沒有那麼在意,我們比較在意的反而會是對於這個主題,哪些東西會
是主要的重點!
首先我們先把 jieba 函式庫安裝好,並且直接利用 urlretrieve 把他的較大字典讀取來完成比較
精準的分詞 (如果對於 jieba 不熟可以翻閱我的基本語法一章或者參閱 github 上說明文件)
import jieba
# 讀入額外的字典
jieba.load_userdict("bigdict.txt")
1. 把詞分開
2. 加上空白組合回去
以一個例子來說,『我喜歡你』是正常的中文,『我喜歡你』是把中文處理的像是英文
請安裝 wordcloud 函式庫 (如果是 32-bit 的 Python 請使用 whl 安裝) 和 Pillow 函式庫 (直接
安裝即可),並且準備一個有輪廓的圖來做文字雲,我準備的圖在這裡 https://drive.google.
com/open?id=1VtHL7e5wv2FBxrkayodGZNn4ag2LTeem
7 狂新聞下載 97
圖: 我準備的文字雲圖
記得圖片要純白背景,有時候無法產生是因為背景並不是真正的純白!最後由於 Python
函式庫大部分沒有原生支援中文字型,所以請讀者到 https://noto-website-2.storage.
googleapis.com/pkgs/NotoSansCJKtc-hinted.zip 下載 Google 提供的中文字型,並且
選擇一個你喜歡的粗細放入同一層資料夾,我選擇的是 Bold 的粗細
import jieba
import matplotlib.pyplot as plt
%matplotlib inline
mask_path = "map.jpg"
# 先把我們要做文字雲的圖片開起來
mask = np.array(Image.open(mask_path))
# 準備 WordCloud
# 由於原生不支援中文字型, 請下載後選擇粗細放入同一層資料夾
# https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKtc-hinted.zip
# max_words: 構成雲的最大詞數, 順帶一提, wordcloud 處理中文的時候會只剩兩個字以上組成的詞
# mask: 剛剛準備的圖
# collocations: 要不要把兩個詞組合成一個有意義短語, 通常用於英文, 中文不需要
wc = WordCloud(font_path = "./NotoSansCJKtc-Bold.otf",
background_color="white", max_words=5000,
mask=mask, collocations=False)
wc.generate(fulltext)
Out[10]:
7 狂新聞下載 99
你可以看到最近狂新聞最火紅的幾個單字,『台大』,『台灣』,『館長』等等,順便你也發現其
實有些詞語是切得不準確的,例如:
『柯文哲』這個詞沒有被切出來,如果你需要更精確的分詞,請
7 狂新聞下載 100
7.3.6 結語
8 Youtube 下載
8.1 介紹
這章我們要教你的主要事情,不是影片下載,影片的下載只是次要的事,我們要教你的是如何
處理更複雜的 POST(e.g. 登入) 以及如何在爬蟲的時候維持著登入狀態!
8.2 目標
圖: 儲存到播放清單的方式
圖: Youtube 登入的導向
我們之所以把這個章節放在最後,是因為這招是『最後的手段』!如果依照之前的方式可以找
到真正的問題,我會推薦你使用之前的方式 (原始碼和 Network),但如果遇到下面兩個情況:
要讓 Python 操縱瀏覽器,你需要一些工具,我們先來看個完整工具圖
圖: 完整工具圖
要完成這個自動操縱,我們需要下面三個工具:
所以整個流程就是:
『特定程式語言函式庫』操縱『通用的自動操縱介面』再去操縱『瀏覽器』
8.5 開始操縱
首 先 先 從 http://chromedriver.chromium.org/downloads 下 載 最 新 版
chromedriver,建議可以順便將 Chrome 更新 (右上角三個點 - 說明),並且解壓縮後放入專
案裡
8 YOUTUBE 下載 104
圖: 下載對應作業系統並且放入同一層資料夾
按下下一步的密碼頁面
8 YOUTUBE 下載 106
# click 就是點擊
driver.find_element_by_id("identifierNext").click()
# 這裡要注意一下, 因為事實上頁面不是馬上就能得到
# 我們要連等待這個動作都要模擬, 於是我通常會在這裡等待 3 秒, 讓頁面完整載入
# 要記得要 import time 這內建函式庫 (不用安裝)
time.sleep(3)
# 輸入密碼
driver.find_element_by_class_name("whsOnd").send_keys("改成你的 Google 密碼")
driver.find_element_by_id("passwordNext").click()
# 這裡我多等了一下, 等 5 秒登入並且 youtube 載入
time.sleep(5)
# 這裡我們拿到我們之前跟各位介紹過的 cookies, 要做什麼用呢? 稍微保密一下
# cookies: 瀏覽網站自動送出的附加資訊
cookies_list = driver.get_cookies()
# 順手把瀏覽器關掉
driver.close()
你應該可以看到以下的畫面
圖: 自動被喚起的 Chrome
你可以看到上面有『受到自動軟體控制』的畫面!那為何我們停止了呢?說好的抓播放清單呢?
8 YOUTUBE 下載 108
其實到這裡就可以了,事實上為何我們可以在每個頁面都保持著登入呢?我們並不是每看一個頁面
都會輸入登入訊息啊,為何同一個網址可以讓每個人保持著不同的登入狀態呢?沒錯,事實上,你
的登入訊息是有寄送給伺服器的,不過是記載在 cookie 裡,所以你只要把 cookie 記錄下來,在以
後使用網址的時候把 cookie 一併送出就可以了,如果忘記的同學可以回頭看一下 ptt 那章節的『八
,這裡之所以只使用 selenium 處理登入是因為後續如果還要靠點擊的來處理剩下的也太
卦版登入』
複雜了,所以我們改使用 requests 模組!
圖: 接下來的尋找
直接開始寫程式吧!
for l in playlists:
# 找到 a
title = l.find("a", "vm-video-title-text")
# 拿出 href 屬性 (超連結網址)
url = "https://www.youtube.com" + l.find("a", "vm-video-title-text")["href"]
# 把 title 和 url 準備成一個字典, 並且加入我們的 urllist 裡
urllist.append({"title":title.text, "url":url})
print(urllist)
8.5.2 Playlist 下載
圖: 我們下載的影片
你可以看到我們完美的下載完了
8.6 結語