<style>
.markdown-body hr, .markdown-body table br, .slides [title='*'], .markdown-body .slideONLY, h1 br, .slides .slide, .slides summary h2 {display:none}
summary h1, summary h2 {display:inline;border-bottom:0!important}
.slides h1 br,.slides .slideONLY{display:inline!important}
.slides details p{font-size:66%!important}
.slides details a{background-color:white;padding:0 5px}
</style>
<!-- .slide: data-background="#00bd00" -->
<!--觀看次數-->
![](https://img.shields.io/badge/dynamic/json?color=aqua&query=%24.viewcount&label=%E8%A7%80%E7%9C%8B%E6%AC%A1%E6%95%B8&suffix=%E6%AC%A1&url=https%3A%2F%2Fhackmd.io%2FLSYcAikNTMuJOwZwcjfkxw%2Finfo '*')
###### [@NCHUIT/](/@NCHUIT '*')[Python 教學/](/@NCHUIT/py '*')
:::spoiler {state=open}<h1>LINE Notify</h1>
<div class='slideONLY'>
<i class="fa fa-fw fa-google"></i>meet.google.com/czn-pbav-zvr <i class='fa fa-fw fa-google'></i>colab.research.google.com
<i class="fa fa-book"></i> 網頁 md.nchuit.cc/line-notify/edit?view <i class="fa fa-tv"></i> 簡報 md.nchuit.cc/line-notify
</div>
[ToC]
:::
<!--
| <i class="fa fa-fw fa-wpforms"></i>報名/簽到防疫表單 | <i class="fa fa-fw fa-wpforms"></i>入社表單 | <i class="fa fa-fw fa-comments-o"></i>回饋單/貓貓磁扣調查 |
|:-:|:-:|:-:|
| [![](https://i.imgur.com/YjOk5L9.png =172x)](https://forms.gle/bghmKYxjc9v7m9WE8) | [![](https://i.imgur.com/sLgC1CR.jpg =172x)](https://reurl.cc/q1keqn) | [![](https://i.imgur.com/SZXAKmT.png =172x)](http://reurl.cc/VjYNGZ) |
-->
###### 本教學中部分辭彙由VJ親自翻譯及選用,可能與網絡上某些內容有出入,特此說明。
> [name=VJ(世路)]
----
## [爬蟲](https://zh.wikipedia.org/wiki/%E7%B6%B2%E8%B7%AF%E7%88%AC%E8%9F%B2)是什麼?
每個我們在網絡上看到的網頁,其實背後都有一台伺服器,藉由向伺服器發送 **請求(`request`)** 後運用程式分析伺服器的 **回應(`response`)** 的內容,繼而呈現給開發者或使用者想要得到的資訊,這一系列動作稱為 **[網頁抓取](https://zh.wikipedia.org/wiki/%E7%BD%91%E9%A1%B5%E6%8A%93%E5%8F%96)**,而爬蟲則是將其進一步自動化的系統。本頁面將教授大家如何進行網頁抓取,而爬蟲就有待各位再進一步的開發。
----
## 爬蟲需知
----
[HTTP](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE)
: **H**yper**T**ext **T**ransfer **P**rotocol(超文本傳輸協定),縮寫:HTTP,是全球資訊網絡數據通信的基礎。
[HTTPS](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%AE%89%E5%85%A8%E5%8D%8F%E8%AE%AE)
: **H**yper**T**ext **T**ransfer **P**rotocol **S**ecure,經由**實聯制**進行通訊,但利用SSL/TLS來加密封包,更安全。
----
[URL(網址)](https://zh.wikipedia.org/wiki/%E7%BB%9F%E4%B8%80%E8%B5%84%E6%BA%90%E5%AE%9A%E4%BD%8D%E7%AC%A6)
: **U**niform **R**esource **L**ocator(統一資源定位符),俗稱網頁位址,簡稱**網址**,是網際網路上標準的資源的位址(Address),如同在網路上的門牌。
[URI](https://zh.wikipedia.org/wiki/%E7%BB%9F%E4%B8%80%E8%B5%84%E6%BA%90%E6%A0%87%E5%BF%97%E7%AC%A6)
: **U**niform **R**esource **I**dentifier(統一資源標識符),用於標識某一網際網路資源名稱的字串。簡單來說,它就是[檔案路徑](https://zh.wikipedia.org/wiki/%E8%B7%AF%E5%BE%84_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6))。
這種標識格式讓我們可以對網路中(一般指全球資訊網)的資源通過特定的協定進行++互動操作++。URI 的最常見的形式就是 URL,經常指定為非正式的網址。
----
[![](https://i0.wp.com/www.design-hu.com/wp-content/uploads/2018/04/domain-1-768x418.png)](https://www.design-hu.com/web-news/domain.html)
URL的完整格式如下:
```
[協定]://[使用者:密碼@][伺服器位址][:埠號]/[檔案路徑][?請求參數][#元素ID]
```
###### 其中[使用者@]、[使用者:密碼@]、[:埠號]、[檔案路徑]、==[?請求參數]==、[#元素ID]都屬於選填項。
---
![](https://miro.medium.com/max/875/0*s2u2kEx-8rAhxGaW)
----
### 請求與回應
HTTP 的基本運作方式就像上圖一樣,我們開啟網頁、或在網頁上做特定的操作的時候,其實都是在向伺服器發送請求(`request`),而伺服器則會對應 `request` 給予我們回應(`response`)。
回應(`response`)的主要內容就是 ==HTML==。而爬蟲更是進一步的分析它。
###### [*從HTML到MarkDown(建議閱讀)*](/@NCHUIT/mdhtml)
----
### 請求 Request
HTTP 的 `request` 目前共有[八種方法](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE#%E8%AF%B7%E6%B1%82%E6%96%B9%E6%B3%95),其中最常用的就是 **`get` 跟 `post`**。
#### `get` & `post` 資料發送概述
+ get -- [headers](https://zh.wikipedia.org/wiki/HTTP%E5%A4%B4%E5%AD%97%E6%AE%B5), [cookies](https://zh.wikipedia.org/wiki/Cookie), [params (英)](https://en.wikipedia.org/wiki/Query_string)
+ post -- headers, cookies, [data (英)](https://en.wikipedia.org/wiki/POST_(HTTP)#Use_for_submitting_web_forms)
:::info
:bulb:提示: Windows 裡,在 Chrome 瀏覽器中按下 `F12` 或 `Ctrl+Shift+I` 可以在 `Network` 頁面檢視所有請求(`requests`)。
:::
----
### ►練習 -- `get` 範例
請求(`requests`) 中的大部分的 `get` 都是在開啟網頁的時候就送出了,這邊以開啟 Google 的主畫面跟進行搜索為例。
###### 試試看打開下面的網頁後按 `F12` 找出那個 `request`。(需要重整才有)
一般的 `get`
: https://www.google.com/search
帶 `params` 的 `get`
: https://www.google.com/search?q=hello
###### 至於為什麼要加 `search` 其實是因為 Google 伺服器有個專門為搜索提供 `get` 的頁面被命名為 `search`,但當 `get` 請求沒有 `params` 的時候,它會自動跳向另一個被命名為 `webhp` 的頁面。
----
[![](https://stickershop.line-scdn.net/stickershop/v1/sticker/166501722/android/sticker.png)](https://store.line.me/stickershop/product/7034336)
----
### 回應 Response
向伺服器發出請求後,會得到伺服器的回應(`response`),其中值得注意的是 `response` 中 `headers` 裡的 `Set-Cookie`,顧名思義,它就是會設下 cookie。
`cookies` 在瀏覽器中關閉網頁前除非你又送請求不然都會一直按時保留著。
`cookies` 記錄的資料往往是使用者資訊,所以需要隱藏,這也是為什麼頁面會顯示「登入中」的原因。
---
## Python 函式庫 [`requests`](https://requests.readthedocs.io/en/latest/)
###### 在 Python 中引用函式庫 `requests` 就可以用程式的方式來送出請求。
### 安裝([colab](https://colab.research.google.com/) 不用)
```
pip install requests
```
----
### 語法
#### get
```
requests.get(url[,headers,cookies,params,...])
```
#### post
```
requests.post(url[,headers,cookies,data,...])
```
`[]` : 選用
[更多方法的語法(英)](https://www.w3schools.com/python/module_requests.asp)
----
### 引用及使用
###### 送出 `get` 請求後輸出回應的 *HTML 元素*。
#### 一般的 `get`
```python=
import requests
網址 = 'https://www.google.com'
回應 = requests.get(網址)
#其實平常還要附上 headers 跟 cookies 送出
#但 Google 的這個頁面沒有限制。
print(回應.text) #印不完,這個等下會講
print(type(回應)) #看它的資料型態
print(vars(回應)) #看它的屬性
```
###### `get()` 回傳給我們的是函式庫 `requests` 定義好的一個被命名為 `Response` 的 [`class`](/@NCHUIT/py5/edit?view#%E8%A3%9C%E5%85%85%E4%B8%BB%E9%A1%8C-%E7%89%A9%E4%BB%B6)
----
#### 帶表單資料 `params` 的 `get`
```python=
import requests
網址 = 'https://www.google.com/search'
表單 = { 'q':'hello' } #dict
回應 = requests.get(url=網址, params=表單)
# 同 get https://www.google.com/search?q=hello
print(回應.text)
```
###### 由於 `requests` 函式庫寫的函式引數很多,而且除了網址以外都是選用的,通常我們都會直接用[關鍵字定義參數](/@NCHUIT/py4/edit?view#%E9%97%9C%E9%8D%B5%E5%AD%97%E5%8F%83%E6%95%B8)。
----
### 短一點?
```python=
from requests import get#
回應 = get('https://www.google.com')
print(回應.text)
```
###### 但它看起來像亂碼??
---
## <i class="fa fa-fw fa-html5"></i>HTML
<table><tr><td style='font-size:90%'><b>H</b>yper<b>T</b>ext <b>M</b>arkup <b>L</b>anguage (超文本標記語言),是一種用於建立網頁的標準<b>標記語言</b>。<br>瀏覽器可以讀取HTML檔案,並將其彩現成<b>視覺化</b>網頁。<a href='https://zh.wikipedia.org/wiki/HTML'>維基百科</a><h6>HTML描述了一個網站的結構語意隨著線索的呈現,使之成為一種標記語言而<em>非程式語言</em>。</h6></td><td><img src='https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/HTML.svg/800px-HTML.svg.png' style="display:inline"></td></tr></table>
* 最新版本: 5.2/ 5.3(工作草案); 2017年12月14日,4年前
* 初始版本: 1993年,28年前
----
### HTML 元素
[維基百科](https://zh.wikipedia.org/wiki/HTML%E5%85%83%E7%B4%A0)
HTML 中,一個 HTML 元素是整個 HTML 檔案的一個基本組成單元。
HTML 檔案採用採用==樹狀結構==安排 HTML 元素。
常見的 HTML 元素有**標題**、**段落**、**連結**、**列表**、**嵌入媒體**等等。
:::info
🕹快捷鍵: 在 <i class="fa fa-fw fa-chrome"></i>Chrome 或 <i class="fa fa-fw fa-internet-explorer"></i>Edge 瀏覽器中按下 <kbd>F12</kbd> 或 <kbd>Ctrl + Shift + I</kbd> 可以在 `Elements`(元素) 頁面檢視所有 HTML 元素。
:::
----
<img height='600' src='https://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/DOM-model.svg/800px-DOM-model.svg.png' style='background-color:gray'>
----
### 例子(標題)
段落 [連結](https://google.com) <span style="color:red">紅色</span>
+ 清單項目**甲**
+ 清單項目*乙*
1. 列表項目***甲***
2. 列表項目~~乙~~
![](https://i.imgur.com/HviOatP.png '圖片')
----
### <i class="fa fa-fw fa-gamepad"></i>動動手: 檢查方法1 - 選單>檢查
|<i class="fa fa-fw fa-chrome"></i>Chrome|<i class="fa fa-fw fa-internet-explorer"></i>Edge|
|:-:|:-:|
|<img style="display:inline" width="200" src="https://i.imgur.com/Utx18BP.png">|<img style="display:inline" width="200" src="https://i.imgur.com/5TQdPUX.png">|
:::info
🕹快捷鍵: 在 <i class="fa fa-fw fa-chrome"></i>Chrome 或 <i class="fa fa-fw fa-internet-explorer"></i>Edge 瀏覽器中按下 <kbd>F12</kbd> 或 <kbd>Ctrl + Shift + I</kbd> 可以在 `Elements`(元素) 頁面檢視所有 HTML 元素。
:::
----
### <i class="fa fa-fw fa-gamepad"></i>動動手: 檢查方法2 - 選取工具
|<i class="fa fa-fw fa-chrome"></i>Chrome|<i class="fa fa-fw fa-internet-explorer"></i>Edge|
|:-:|:-:|
|<img style="display:inline" width="200" src="https://i.imgur.com/u7MUrsr.png">|<img style="display:inline" width="200" src="https://i.imgur.com/JIuGjD4.png">|
:::info
🕹快捷鍵: 在 <i class="fa fa-fw fa-chrome"></i>Chrome 或 <i class="fa fa-fw fa-internet-explorer"></i>Edge 瀏覽器中按下 <kbd>Ctrl + Shift + C</kbd> 可以直接啟動選取模式。
:::
![](https://i.imgur.com/gLDmcZD.png)
----
### ►練習
試用讀寫檔將剛剛輸出的內容存成副檔名為 `.html` 檔案後打開。
```python
from requests import get
回應 = get('https://www.google.com')
print(回應.text)
with open('檔名.html', 'w') as 檔案:
檔案.write(回應.text)
```
###### colab 找檔案請打開側邊欄。
----
### ►練習
試將剛剛的 HTML 檔案打開後找到通往搜索++圖片頁面++的 <i class="fa fa-fw fa-link"></i>超連結 [`<a>`](https://www.w3schools.com/tags/att_a_href.asp) 元素。
![](https://i.imgur.com/ZYmaxwo.png) ![](https://i.imgur.com/9SkQ0Lj.png)
:::info
🕹快捷鍵: 在 <i class="fa fa-fw fa-chrome"></i>Chrome 或 <i class="fa fa-fw fa-internet-explorer"></i>Edge 瀏覽器中按下 <kbd>Ctrl + Shift + C</kbd> 可以直接啟動選取模式。
:::
---
## Python 函式庫 [`bs4`](https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/#id14)
###### 在 Python 中引用函式庫 `bs4` 就可以分析回應。
### 安裝(colab 不用)
```
pip install bs4
```
----
### 引用及使用
###### 送出 `get` 請求後輸出回應的文字。
```python=
import requests, bs4
回應 = requests.get('https://www.google.com')
解析 = bs4.BeautifulSoup(回應.text)
#print(回應.text)
print(解析.text)
```
----
### 短一點?
```python=
from requests import get
from bs4 import BeautifulSoup as bs#
解析 = bs(get('https://www.google.com').text)
print(解析.text)
```
----
### 查找元素
#### 選取全部符合條件的元素 [`find_all()`](https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/#find-all)
```python=
from requests import get
from bs4 import BeautifulSoup as bs
解析 = bs(get('https://www.google.com/').text)
查找結果 = 解析.find_all('a', string='圖片')
#print(解析.text)
print(查找結果) #[<a ...>圖片</a>] 回傳的是裝滿搜索結果的容器
print(type(查找結果)) #當作 list 就好(會取索引就行)
print(查找結果[0])
```
###### 查找 Google 主畫面內文是[圖片](https://www.google.com.tw/imghp?hl=zh-TW&tab=wi)的 *超連結文字* (`<a>...</a>`) 元素。<br>對於 Google 的這個頁面,輸出的結果只有 *通往搜索圖片頁面* 的 *超連結文字* 的一個元素。
----
#### 選取第一個符合條件的元素 [`find()`](https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/#find)
```python=
from requests import get
from bs4 import BeautifulSoup as bs
解析 = bs(get('https://www.google.com/').text)
查找結果 = 解析.find('a', string='圖片') #colab 請查 'Images'
print(查找結果) #<a ...>圖片</a> 注意沒有[]
print(type(查找結果)) #回傳的是 bs4 定義的元素
print(查找結果['href']) #取到網址
print(查找結果[0]) #這會報錯
```
----
#### <i class="fa fa-fw fa-css3"></i>CSS 選擇器 `.select()` `.select_one()`
CSS 本身不支援 string 內文搜索
+ `select('tag[attr="value"]')`
+ `select('#id[attr="value"]')`
+ `select('.class[attr="value"]')`
+ `select_one('tag[attr="value"]')`
範例待補
###### *[請參閱 BS Doc - CSS 選擇器 (簡)](https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/#id42)*<br>*[請參閱 w3schools - CSS 選擇器 (英)](https://www.w3schools.com/css/css_selectors.asp)*
---
## POST `data`
另外特別值得注意的是,`get` 的 `params` 資料是能在網址就被看到,而 `post` 的 `data` 資料則是隱藏的,主要作用應該就是要保護送出的**密碼**或其他私隱資料不被他人在網址看到。另外 `cookies` 也是隱藏的,至於為什麼等下會提到。
###### 我們這裡不做深入的介紹,知道這些資料的存在、會複製就好,有興趣再從上面點進去看。網頁抓取需要的是你比照一個網頁的某一個 `request` 中的 `headers` 和 `cookies`,以及資料 `params` 或 `data`,送出這些後達到你想要的效果,所以不完全清楚這些資料的組成也沒關西,只需要知道它們在送出請求時會需要被送出,也可以經由多次嘗試剔除對伺服器而言沒用的資料。
----
### 應用延伸 -- [LINE Notify API](https://notify-bot.line.me/doc/en/)
| methods/headers | 資料 |
| --- | --- |
| Method | POST |
| Content-Type | application/x-www-form-urlencoded<br>或<br>multipart/form-data |
| Authorization | Bearer ==[`access_token`<br>(點我註冊)](https://notify-bot.line.me/my/)== |
----
#### 請求表單資料 - 1
| 參數名稱 | 必要/<br>選用 | 資料型態 | 說明 |
| ------ | --------- | ------- | --- |
| message | 必要 | 字串 | 要發送的訊息,<br>最多 1000 字元 |
| notification<br>Disabled | 選用 | `bool` | 無聲訊息開關。<br>打開`true`,<br>預設關閉`false`。 |
----
#### 請求表單資料 - 2
| 參數名稱 | 必要/選用 | 資料型態 | 說明 |
| ------ | --------- | ------- | --- |
| image<br>Thumbnail | 選用 | 網址 | 略縮圖,最大 240×240px `JPEG` |
| image<br>Fullsize | 選用 | 網址 | 原圖,最大 2048×2048px `JPEG` |
| image<br>File | 選用 | 檔案 | 發送圖片的檔案。限`.png`或`.jpeg` |
----
#### 請求表單資料 - 3
| 參數名稱 | 必要/選用 | 資料型態 | 說明 |
| ------ | --------- | ------- | --- |
| stickerId | 選用 | `Number` | 貼圖 ID。要和<br>貼圖包 ID 一起<br>送上不然不讓發訊息。 |
| sticker<br>PackageId | 選用 | `Number` | 貼圖包 ID。<br>[Sticker List](https://developers.line.biz/zh-hant/docs/messaging-api/sticker-list/) <br>[貼圖商店](https://store.line.me/home/zh-Hant) |
----
#### 回應
| response<br>headers | 資料說明 |
|:------------- | ------- |
| status | 200: 請求成功<br>400: 請求失敗<br>401: 權杖錯誤<br>500: 伺服器錯誤<br>Other: 作業超時或終止 |
###### [HTTP 404](https://zh.wikipedia.org/wiki/HTTP_404): 找不到伺服器<br>Python 函式庫 `requests` 定義給 `Response` 的 `status` 被命名為 `status_code`
---
#### 發送訊息
[權杖點我註冊](https://notify-bot.line.me/my/)
```python=
from requests import post
#權杖(註冊時給的 access_token)
權杖 = 'TQBCWMSFFTos89Eq9FcvItQnGV4FYrh0p3NZczIXFks'
訊息 = 'Hello'
#request 資料準備
網址 = 'https://notify-api.line.me/api/notify'
標頭 = { 'Authorization': 'Bearer '+權杖 }
#標頭['Content-Type']='application/x-www-form-urlencoded'
表單 = { 'message': 訊息 }
回應 = post(網址, headers=標頭, data=表單)
print(回應)
print(回應.text)
print(回應.status_code)
```
----
#### 發送貼圖
```python=
from requests import post
權杖 = 'TQBCWMSFFTos89Eq9FcvItQnGV4FYrh0p3NZczIXFks'
訊息 = '貼圖圖!!!'
貼圖包ID, 貼圖ID = 8525, 16581290
網址 = 'https://notify-api.line.me/api/notify'
標頭 = { 'Authorization': 'Bearer '+權杖 }
表單 = { 'message': 訊息,
'stickerId': 貼圖ID,
'stickerPackageId': 貼圖包ID }
回應 = post(網址, headers=標頭, data=表單)
print(回應.text)
```
----
#### 發送圖片
###### 結合之前的[讀寫檔](/5rX9DydFTfq6InGcKu4VdA?view),我們可以讀檔來發送圖片。
```python=
from requests import post
權杖 = 'TQBCWMSFFTos89Eq9FcvItQnGV4FYrh0p3NZczIXFks'
訊息 = '圖片片!!!'
圖片路徑 = r'.png 或 .jpg 圖片路徑'
網址 = 'https://notify-api.line.me/api/notify'
標頭 = { 'Authorization': 'Bearer '+權杖 }
表單 = { 'message': 訊息 }
檔案 = { 'imageFile': open(圖片路徑, 'rb') }
回應 = post(網址, headers=標頭, data=表單, files=檔案)
檔案['imageFile'].close() #若後面操作太多建議關檔
print(回應.text)
```
----
#### 發送圖片2
```python=
from requests import post
權杖 = 'TQBCWMSFFTos89Eq9FcvItQnGV4FYrh0p3NZczIXFks'
訊息 = '網址圖片!'
原圖網址 = r'https://imgur.dcard.tw/i4AJxskh.jpg'
縮圖網址 = r'https://i.imgur.com/qk1Ugyy.png'
網址 = 'https://notify-api.line.me/api/notify'
標頭 = { 'Authorization': 'Bearer '+權杖 }
表單 = { 'message': 訊息,
'imageFullsize': 原圖網址,
'imageThumbnail': 縮圖網址 }
回應 = post(網址, headers=標頭, data=表單)
print(回應.text)
```
###### API 裡還有其他網址請求跟用法就自己看了吧
----
### ►練習
[![](https://stickershop.line-scdn.net/stickershop/v1/sticker/16581290/android/sticker.png)](https://developers.line.biz/zh-hant/docs/messaging-api/sticker-list/#package-8525 '8525/16581290') [![](https://i.imgur.com/EIRcjpQ.png)](https://line.me/R/ti/g/R6Llgs7Tcv) ![](https://imgur.dcard.tw/i4AJxskh.jpg =230x)
註冊權杖後,試將這邊的貼圖和梗圖一次發送給這邊的 LINE 測試群,好讓我知道你完成了。
記得開**無聲**,不然會很吵XD,取消註冊的通知的話就沒辦法。
![](https://i.imgur.com/qk1Ugyy.png)
----
#### 參考
```python=
from requests import get, post
權杖 = 'TQBCWMSFFTos89Eq9FcvItQnGV4FYrh0p3NZczIXFks'
訊息 = 'VJ完成任務!'
貼圖包ID, 貼圖ID = 8525, 16581290
圖片網址 = 'https://imgur.dcard.tw/i4AJxskh.jpg'
圖片路徑 = './img.jpg'
圖片get = get(圖片網址)
with open(圖片路徑, 'wb') as 檔案:
檔案.write(圖片get.content)
網址 = 'https://notify-api.line.me/api/notify'
標頭 = { 'Authorization': 'Bearer '+權杖 }
表單 = { 'notificationDisabled': True, #無聲
'message': 訊息,
'stickerId': 貼圖ID,
'stickerPackageId': 貼圖包ID }
檔案 = { 'imageFile': open(圖片路徑, 'rb') }
回應 = post(網址, headers=標頭, data=表單, files=檔案)
檔案['imageFile'].close()
print(回應.text)
```
---
### ►練習
試寫一支程式重複檢索[批批踢](https://www.ptt.cc/bbs/index.html)八卦板上面的數字,一有變化就發送 Line 通知變更後的人數給*自己*。
:::warning
:warning: 注意: 以免伺服器當機,請從 Python 內建函式庫的 `time` 裡 `import` [`sleep`](https://docs.python.org/3/library/time.html#time.sleep) 以在迴圈中每次請求後加入延時。
:::
```python
from time import sleep
sleep(5)
```
```
sleep(等候秒數:int)
```
----
### 分析網頁、選取元素
###### 在頁面中使用 <kbd>Ctrl+Shift+C</kbd> 選取元素,第一次建議像下面寫,可以幫助理解、和分析網頁。
```python=
from requests import get
from bs4 import BeautifulSoup as bs
解析 = bs(get('https://www.ptt.cc/bbs/index.html').text)
#print(x.text)
查詢1 = 解析.find(string='Gossiping')
print(查詢1) #'Gossiping'
查詢2 = 查詢1.find_next('span')
print(查詢2) #<span class="hl f6">*數字*</span>
查詢結果 = 查詢2.text
print(查詢結果) #*數字*
#縮寫
查詢結果 = 解析.find(string='Gossiping').find_next('span').text
```
----
### 重複檢索
```python=
from requests import get
from bs4 import BeautifulSoup as bs
from time import sleep
最後變動 = '初始'
while True:
解析 = bs(get('https://www.ptt.cc/bbs/index.html').text)
查詢結果 = 解析.find(string='Gossiping').find_next('span').text
if 查詢結果 != 最後變動:
訊息 = '批批踢八卦板 人數:'+最後變動+'->'+查詢結果
print(訊息) #; lineNotify(權杖, 訊息)
最後變動 = 查詢結果
sleep(3) #wait_sec()
#from IPython.display import clear_output
#clear_output(True)
```
----
#### 時間輸出&時差問題
##### 函式庫 `datetime` 的 `class datetime` 有提供 `today()` 函式,輸出會是本地時間。
###### 由於 colab 的伺服器、Google 公司在美國,有時差,需要另外處理。
```python
from datetime import datetime, timedelta
#在 colab 上這樣輸出就是目前台灣時間了
print(datetime.today() + timedelta(hours=8))
```
###### 函式庫 `datetime` 另有提供 `timedelta()` 函式用作 `datetime` 的加法計算。
###### *[colab - `datetimeTW.ipynb` by VJ](https://colab.research.google.com/drive/1USLgR0zywsoy1lwwQxkTfjZ3mRSF631c?usp=sharing)*
----
### 通知函式
###### 為方便大家上手,提供我寫的函式給大家。
```python
lineNotify(權杖,訊息[,響鈴,貼圖包ID,貼圖ID,圖片路徑])
```
```python=
def lineNotify( token:str, message:str, alarm=True,
packageId=0, stickerId=0, img=None ):
from datetime import datetime #by VJ
from requests import post
from json import loads
if((not token) or (not message)):
return print('沒有輸入訊息')
u = 'https://notify-api.line.me/api/notify'
h = { 'Authorization': 'Bearer '+token }
nowMsg = '['+str(datetime.today())+'] '+message
d = { 'message': nowMsg,
'notificationDisabled': not alarm }
if stickerId and packageId:
d['stickerId'] = stickerId
d['stickerPackageId'] = packageId
elif bool(stickerId) != bool(packageId): #XOR
return print('缺少貼圖 ID 或貼圖包 ID')
f = {'imageFile': open(img, 'rb')} if img else None
x = post(u, headers=h, data=d, files=f)
if(img): f['imageFile'].close()
if(x.status_code == 200):
result = '成功發送 LINE 通知: "'+message+'" '
if(img): result += '以及檔案: "'+img+'" '
if(stickerId) : result += '還有貼圖'
else: result = loads(x.text)['message']
print('['+str(datetime.today())+'] '+result)
return result
```
----
### 清除輸出
[![](https://stickershop.line-scdn.net/stickershop/v1/sticker/166501732/android/sticker.png)](https://store.line.me/stickershop/product/7034336)
----
###### 有時候重複紀錄無用的訊息會造成記憶體的浪費,這時候就需要清除輸出,騰出記憶體空間。
#### colab (.ipynb)
```python
from IPython.display import clear_output
clear_output(True)
```
#### PC
```python
import os
os.system('cls') #Windows
os.system('clear') #Linux / Mac
os.system('cls' if os.name=='nt' else 'clear') #通用
```
---
## Session
相當於你打開瀏覽器,會幫你存下瀏覽網站設定的 cookie
```python
from requests import Session
requests = Session() #可以讓你像沒有from的寫法一樣
# 印出瀏覽器的cookie,當然,你剛打開的瀏覽器怎麼可能會有
print("cookies BEFORE")
[print(k,":",v) for k,v in requests.cookies.items()]
# 送請求
回應 = requests.get('https://www.google.com/')
# 印出回應的標頭
print("headers")
[print(k,":",v) for k,v in 回應.headers.items()]
# 印出瀏覽器的cookie
print("cookies AFTER")
[print(k,":",v) for k,v in requests.cookies.items()]
```
----
### 清掉指定cookie
網域、路徑只能用`vars(cookies)`自己翻
![](https://i.imgur.com/bQqTC4h.png)
```python
requests.cookies.clear('.google.com','/','NID')
```
### 清空cookies
```python
requests.cookies.clear_session_cookies()
```
### 設定cookie
```python
requests.cookies.set('測試設定cookie名稱', '測試設定cookie值')
```
----
:::spoiler <i class="fa fa-fw fa-gamepad"></i>練習: 我滿18了啊
get帶cookies
```python
from requests
網址='https://www.ptt.cc/bbs/Gossiping/index.html'
回應 = requests.get(網址,cookies={'over18':'1'})
print()
```
Session
```python
from requests import Session
requests = Session()
requests.cookies.set('over18', '1')
網址='https://www.ptt.cc/bbs/Gossiping/index.html'
回應 = requests.get(網址)
```
:::
還是批批踢八卦版:
https://www.ptt.cc/bbs/Gossiping/index.html
![](https://i.imgur.com/rklGjis.png)
這真的只能自己分析出來
{"metaMigratedAt":"2023-06-16T18:46:12.935Z","metaMigratedFrom":"YAML","title":"LINE Notify - Python 爬蟲基礎教學","breaks":true,"description":"國立中興大學資訊科學研習社1091學期起的一次主題社課","image":"https://i.imgur.com/aYRt5aY.png","slideOptions":"{\"allottedMinutes\":1}","contributors":"[{\"id\":\"6d6e3ba2-6820-4c6f-9117-f09bccc7f7aa\",\"add\":24100,\"del\":22331},{\"id\":\"e86b6571-4dea-4aa4-ba20-ece559b0e015\",\"add\":28834,\"del\":10257}]"}