貼近生活的上網自動化
requests
selenium
林承諺
我從小就有一個夢想 : 開遊戲外掛
我獨自摸索了好幾年,沒想過是要用到【網路爬蟲】這個領域
或許這聽起來很陌生 很困難
但這讓我實現了一個從小的夢想
或召喚隊輔學長姐悶
QR CODE
每天都重複?
好無聊 !
AUTO
讓電腦自動完成日常瑣事
定義
用程式模擬人類在網路上的行為
是怎麼做的?
抓取資料 (GET)
傳送資料 (POST)
常用的有3種模式
自動點擊
就像你上網找歌單
網路爬蟲
你啟動它
輸出
進入kkbox
找到資料
記下來
進入kkbox
找到資料
記下來
抓取資料GET範例
想找歌單? 按一下就好
進入kkbox
找到資料
記下來
而且程式不會分心滑到其他地方
傳送資料POST範例
就像你填春遊報名表
進入表單
填寫資料
送出
網路爬蟲
你啟動它
輸出ok
進入表單
準備資料
送到伺服器
想去春遊? 按一下就好
而且程式可以一秒送出很多表單
進入表單
準備資料
送到伺服器
用途:
看得到的網頁,理論上都可以「爬」
語法 | 優點 | 套件 |
---|---|---|
Python | 易學,初學者首選 | requests BeautifulSoup Selenium |
JavaScript | 模擬網頁行為,能抓取動態資料 | axios, cheerio |
Java | 穩定,適合大量處理 | Jsoup, Selenium |
C# | 整合Windows系統 | HtmlAgilityPack |
Python ✅
入門最快
只要語言能發 request、能處理 HTML,就能寫爬蟲
但
網路爬蟲三大套件
直接發送請求
整理接收結果
模擬真實使用者
類型 | 用途 | 特色 |
---|---|---|
requests + BeautifulSoup | 靜態網頁 | 直接抓下整個網頁 |
Selenium | 動態網頁 | 模擬人類操作,可取得包含動態數值 |
API爬蟲 | 伺服器端口 | 官方資料接口、回傳指定資料 |
後面都會再詳細解釋
pip install requests
# PRESENTING CODE
# PRESENTING CODE
import requests
# PRESENTING CODE
url = "https://ckcsc.net/"
data = {"key": "example"}
response = requests.get(url)
response = requests.post(url, json=data)
有兩種?
GET? POST?
# PRESENTING CODE
屬性 | 用途 | 範例 |
---|---|---|
requests.get | 向伺服器【請求】資料 | 瀏覽網頁 |
request.post | 向伺服器【發送】資料 | 提交表單 |
透過瀏覽器互動
平時上網
取得網頁的原始碼
發送取得網頁的請求
回復請求資料
網路爬蟲
# PRESENTING CODE
import requests
a = requests.get("https://ckcsc.net")
print(a)
賦值
使用模組
使用GET
要取得的網址
執行看看?
# PRESENTING CODE
import requests
a = requests.get("https://ckcsc.net")
print(a)
輸出的是200,怎麼不是整個網站?
<Response [200]>
直接print,預設會顯示 HTTP 回應
【狀態碼】
數字 | 英文顯示 | 中文意義 |
---|---|---|
200 | OK | 請求成功 |
404 | Not Found | 請求的資源不存在 |
500 | Internal Server Error | 伺服器內部發生錯誤 |
快速得知請求的成功與否
我知道這裡寫得有夠簡略
# PRESENTING CODE
import requests
a = requests.get("https://ckcsc.net")
print(a.text)
讓它輸出網站的整個內容
輸出【文字屬性】
輸出結果
看的出來成電社網大部分都是JavaScript動態生成
# PRESENTING CODE
import requests
a = requests.get("https://tw.yahoo.com/")
print(a.text)
換成爬取雅虎奇摩的首頁試試看
輸出結果
Edge: Too Many Requests
<Response [429]>
.text
Why?
沒有出現網站而是出現錯誤?
常見的手段:
✔️偽裝成正常的瀏覽器
✔️不要告訴他你是Python
❗新增 User-Agent
常見的請求 Header,用於識別發送請求的應用程式
不告訴你
我是Python,請求網頁
就像是你的電腦的身分證,伺服器會接收到
給你html
我是Mozilla/5.0 (Windows NT 10.0; Win64; x64),請求網頁
1.
2.
檢查
network
3.
4.
重新整理
隨便點一樣請求
5.
往下滑,會找到User-Agent
# PRESENTING CODE
import requests
url = "https://tw.yahoo.com/"
Useragent = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
response = requests.get(url, headers=Useragent)
print(response.text)
實作User-Agent
將User-Agent以字典形式儲存並附在括號中,參數名稱header後面
# PRESENTING CODE
輸出結果
整個網頁
# PRESENTING CODE
import requests
url = "https://httpbin.org/delay/5"
Useragent = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
response = requests.get(url, headers=Useragent)
print(response.text)
實測看看這個網站
拖了好久才輸出
實測 !
# PRESENTING CODE
import requests
url = "https://httpbin.org/delay/5"
Useragent = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
response = requests.get(url, headers=Useragent)
print(response.text)
設定延遲5秒才輸出
✔️讓程式適可而止,適時放棄
❗新增 timeout
✔️像是抓取跑很慢的網站
# PRESENTING CODE
response = requests.get(url, timeout=5)
基本語法:
設定等待不超過5秒
# PRESENTING CODE
import requests
url = "https://httpbin.org/delay/5"
try:
response = requests.get(url, timeout=3)
print(f"發送成功")
print(response.text)
except:
print("伺服器在3秒內沒有回應")
實際演練:
伺服器在3秒內沒有回應
在嘗試3秒後就會放棄
如果沒成功執行則報錯
只能在指定秒數內執行完成,否則就會報錯
抓取台北市政府官網,要使用User-Agent並在嘗試最多3秒後放棄
# PRESENTING CODE
import requests
url = "https://www.gov.taipei/"
Useragent = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
try:
response = requests.get(url, headers=Useragent,timeout=3)
print(f"請求成功")
print(response.text)
except:
print("伺服器在3秒內沒有回應")
伺服器在3秒內沒有回應
網站有更進階的反爬蟲或是當機了😱
但目前已經可以取得大部分的網站
cookies
proxiex
郵差
( API )
寄信者
( 使用者 )
面試官
( 伺服器 )
寄信
傳送
傳送
送回
✅填地址和郵遞區號
✅填面試官要的個資
❌知道怎麼送信
❌知道面試官怎麼審核
乖乖在家就會送回來 不用懂
你只需要知道✅
你不需要知❌
底層系統細節: 哪種程式語言、資料庫結構、演算法等。
系統基礎架構: 部署在哪裡、使用了哪些伺服器、網路拓撲結構等
版本細節:只要 API 的介面保持向後兼容,我們通常不需要深入了解目標系統的具體版本號和內部更新細節。
輕鬆易懂
抓取空氣品質指標
不使用API
使用API
使用他「專門提供做這件事的連結」 ,取得整理好的資料
https://data.moenv.gov.tw/api/v2/aqx_p_432?api_key=9b651a1b-0732-418e-b4e9-e784417cadef&limit=1000&sort=ImportDate%20desc&format=JSON"
# PRESENTING CODE
import requests
url = "https://data.moenv.gov.tw/api/v2/aqx_p_432?api_key=9b651a1b-0732-418e-b4e9-e784417cadef&limit=1000&sort=ImportDate%20desc&format=JSON"
Useragent = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
response = requests.get(url, headers=Useragent)
print(response.text)
輸出:
# PRESENTING CODE
我是樹林人
❓Python看不懂
✔️轉換成Python字典格式
❗使用.JSON解碼
語法:
response = (requests.get(url, headers=Useragent)).json()
在回傳後的資料加上.json()
注意事項:只有格式為json時才會運作,否則會報錯
# PRESENTING CODE
import requests
url = "https://data.moenv.gov.tw/api/v2/aqx_p_432?api_key=9b651a1b-0732-418e-b4e9-e784417cadef&limit=1000&sort=ImportDate%20desc&format=JSON"
Useragent = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
response = (requests.get(url, headers=Useragent)).json()
print(response)
輸出:
✅輸出Python字典格式
❓轉換成字典之後,如何取得其中的數值?
✔️直接取得字典內的數值
❗使用KEY處理巢狀結構
ex.取得pm10=多少?
# PRESENTING CODE
data = {
'status': 'success',
'result': {
'user': {
'id': 123,
'profile': {
'name': 'Alice',
'age': 30,
'address': {
'city': 'Taipei',
'zipcode': '100'
}
},
'hobbies': ['reading', 'coding']
}
}
}
user_id = data['result']['user']['id']
print(user_id)
基本語法
依序取得鍵中的鑑中的值
data['result'] -> {'user': ...}
data['result']['user'] -> {'id': 123, 'profile': ...}
data['result']['user']['id'] -> 123
# PRESENTING CODE
import requests
import json
url = "https://data.moenv.gov.tw/api/v2/aqx_p_432?api_key=9b651a1b-0732-418e-b4e9-e784417cadef&limit=1000&sort=ImportDate%20desc&format=JSON"
Useragent = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
response = requests.get(url, headers=Useragent)
data = response.json()
for record in data['records']:
if record['sitename'] == '屏東':
print(record['pm10'])
30
❓想取得那種資料怎麼辦
✔️自己找&直接get讓他回傳
ex.網頁遊戲的個人資料頁面
1.
2.
檢查
network
3.點擊會取得資料的物件
ex.按下頭像會跳出資料
4.你會看到一個.json,點進去
API在此
這是使用post,會在下個章節說明
5.
回傳的內容
此時就可以透過解析巢狀結構來獲取想要的資料
抓取產銷履歷Tracecode為00991927504943的產品名稱(ProductName)
# PRESENTING CODE
(貼標) 福山萵苣油麥菜-大陸妹
用for迴圈遍歷,如果Tracecode為00991927504943,就印出ProductName【(貼標) 福山萵苣油麥菜-大陸妹】
屬性 | 用途 | 範例 |
---|---|---|
requests.get | 向伺服器【請求】資料 | 瀏覽網頁 |
request.post | 向伺服器【發送】資料 | 提交表單 |
剛剛get ➡️ 他提交給我們
現在post ➡️ 我提交給他們
透過瀏覽器互動
平時上網
資料傳送
回復結果
發送我們的資料
回復【我收到了】
網路爬蟲
通常用於:
# PRESENTING CODE
url = "https://XXX.com/api/sign_in"
response = requests.post(url, data=data)
基本語法
用POST
要發送的資料
要發送到哪個網址?哪個網址會接收帳密?
傳到首頁 Yahoo奇摩 ?
❌
✔️傳到接收帳密的API
不同API處理不同收到的資料
你要先【找到發送到哪】
而每個位置有不同的網址
處理登入
處理攻擊
處理好友
ex.登入要發送到
https://www.XXXXX.com/api/sign_in
修改個資要發送到
https://www.XXXXX.com/api/user_settings
前面寫過,使相同方式尋找API 並 post出去
1.點擊會發送資料的物件
ex.按下登入會送出密碼
POST帳密出去
API在此
payload 發送出去的資料(帳密)
2.查看帳密發送到哪了
response 伺服器回復(是否登入成功)
payload ➡️ 發送出去的資料
3.查看發送了甚麼
response 伺服器回復(是否登入成功)
ok表示成功
可知發送出去的是一個json格式
# PRESENTING CODE
import requests
url = "https://www.pagamo.org/api/sign_in"
data = {
'account': 'wallace999999',
'encrypted': True,
'password': 'SQ3GcclkDIlmMulkfdpnUxSO1FssO0RBLPfrmFT5x2DuteYMSRr2luPaYJywYXRpLclcOckosmHWURjTYdIHMYFPTtZ3VT6WHNz+9p4hpf94NAAtKIic6tQn0uuI2B2PBGCMg88ZJ77CB8yqGumt/T5+ZbaueHHn42OWzCE1h6Xo8fSsRZnrQMXRkUwLbRojZ6FTa2yTcGMwblOia7ohULM/sDvOYXTXX8oMCSChvbUcXcpszmgegdnKj7KRmkOLuEYX3LcZjY1c34B70R8HYhBsWZnLjdxHC7f70OfcKplnWOPHpOPIy8+SMATJoBF4Eq1/jvPduaf8W4PfucXKzA=='
}
response = requests.post(url, data=data)
print(response.text)
要發送的網址
發送的資料
發送到【接收帳密的端口】
語法範例 : 登入
# PRESENTING CODE
系統回復: 會自轉化成Python字典
剛剛的網頁回復:
✔️資料相同
範例:遊戲外掛
自動開PaGamO的【假期模式】
事實上,只是POST到API兩個小數字而已
事實上,只是POST到API兩個小數字而已
✔️相同效果
# PRESENTING CODE
import requests
info_url = 'https://www.pagamo.org/users/personal_information.json'
s = requests.Session()
def open_hoilday():
#登入
login_url = 'https://www.pagamo.org/api/sign_in'
login_data = {'account': "wallace999999", 'encrypted': True, 'password': "SQ3GcclkDIlmMulkfdpnUxSO1FssO0RBLPfrmFT5x2DuteYMSRr2luPaYJywYXRpLclcOckosmHWURjTYdIHMYFPTtZ3VT6WHNz+9p4hpf94NAAtKIic6tQn0uuI2B2PBGCMg88ZJ77CB8yqGumt/T5+ZbaueHHn42OWzCE1h6Xo8fSsRZnrQMXRkUwLbRojZ6FTa2yTcGMwblOia7ohULM/sDvOYXTXX8oMCSChvbUcXcpszmgegdnKj7KRmkOLuEYX3LcZjY1c34B70R8HYhBsWZnLjdxHC7f70OfcKplnWOPHpOPIy8+SMATJoBF4Eq1/jvPduaf8W4PfucXKzA=="}
print('正在登入...')
login_resp = s.post(login_url, data=login_data).json()
gc_id = login_resp['data']['gtm_info']['gc_id']
print(gc_id)
headers = { "Origin": "https://www.pagamo.org", "Referer": f"https://www.pagamo.org/map?course_code=TMGQOQS7" } # 將數據以JSON格式傳遞
# 發送POST請求
response = s.post("https://www.pagamo.org/gamecharacters/holiday_mode", json={'gc_check': gc_id, 'key': gc_id}, headers=headers)
print(response.text)
open_hoilday()
範例程式碼:
登入
POST到holiday(假期模式)
對https://httpbin.org/post發送一組帳號密碼,觀察他的回應
提示: 可以透過前面簡報範例進行修改
# PRESENTING CODE
import requests
data = {'username': 'wallace999999', 'password': '12345678'}
response = requests.post('https://httpbin.org/post', data=data)
print(response.text)
{
"args": {},
"data": "",
"files": {},
"form": {
"password": "12345678",
"username": "wallace999999"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Content-Length": "40",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.32.3",
"X-Amzn-Trace-Id": "Root=1-6807af2e-736618625aab73be70d21e72"
},
"json": null,
"origin": "111.249.209.22",
"url": "https://httpbin.org/post"
}
會把你發送的東西傳回來,代表他有收到
{
"args": {},
"data": "",
"files": {},
"form": {
"password": "12345678",
"username": "wallace999999"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Content-Length": "40",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.32.3",
"X-Amzn-Trace-Id": "Root=1-6807af2e-736618625aab73be70d21e72"
},
"json": null,
"origin": "111.249.209.22",
"url": "https://httpbin.org/post"
}
會把你發送的東西傳回來,代表他有收到
蝦?什麼網站會做這種蠢事情?把你傳回來?
https://httpbin.org
但至少你現在學會POST了
✔️有
❗httpbin.org
由 Python 的 Flask 框架建構
主要用途:讓開發者測試 HTTP 請求與回應
測試不同 HTTP 請求:
GET, POST, PUT, DELETE, PATCH, OPTIONS
檢視回傳的資料格式 / headers
模擬伺服器回應,例如:
模擬延遲 (/delay)
模擬錯誤 (/status/404)
回傳指定的 JSON (/json)
⬅️剛剛出現的
⬅️測試是否會崩潰
⬅️前面出現的 抓取個資
模擬各種端口
各種類型爬蟲都可以試
放在公開的網路平台上,讓你自己也可以使用
提供啟動方法:
路上有交通規則
爬蟲也有行為規範
存放於網站的根目錄下
ex.
https://www.xxxxx.com/robots.txt
因為他只是一份文件,不具強制力,惡意爬蟲可能會無視
語法簡介
#
符號/
開頭*
語法簡介
禁止所有爬蟲存取整個網站:
禁止特定爬蟲存取特定目錄:
允許特定爬蟲存取特定目錄:
User-agent: *
Disallow: /
User-agent: BadBot
Disallow: /secret/
User-agent: GoodBot
Allow: /public/
Disallow: /
語法簡介
User-agent:
指定適用於哪些爬蟲Disallow:
禁止爬蟲存取的路徑或目錄Allow:
允許爬蟲存取的路徑或目錄User-agent: GoodBot
Allow: /public/
Disallow: /
ex.
特別允許某一個
# PRESENTING CODE
抓取Yahoo網站的robots.txt,分析那些不被允許抓取
# PRESENTING CODE
不被允許:
Disallow: /p/
Disallow: /r/
Disallow: /bin/
Disallow: /includes/
Disallow: /blank.html
Disallow: /_td_api
Disallow: /_td-hl
Disallow: /_tdpp_api
Disallow: /_remote
Disallow: /_multiremote
Disallow: /_tdhl_api
Disallow: /_td_remote
Disallow: /_tdpp_remote
Disallow: /sdarla
Disallow: /digest
Disallow: /tdv2_fp
Disallow: /tdv2_mtls_fp
Disallow: /*?bcmt=*
Disallow: /tw_ms
Disallow: /nel_ms/
可見他不允許我們抓取許多頁面
我很想教但是快沒時間了
用來自動化操作瀏覽器
End.