# Python 基礎爬蟲
[![](https://img.shields.io/badge/dynamic/json?color=orange&label=總觀看人數&query=%24.viewcount&url=https://hackmd.io/@AndyChiang/StaticCrawler%2Finfo)]()
> [name=AndyChiang][time=Fri, Feb 5, 2021 9:59 AM][color=#00CDAF]
###### tags: `Python` `爬蟲`
## 安裝Beautiful Soup與requests
這兩個套件都是爬蟲好用(必備)的套件,使用起來簡單,適合用於靜態網站爬蟲(最常被爬的:PTT、Dcard、Yahoo等等)。
### Beautiful Soup
Beautiful Soup通常用來分析抓下來的HTML,使用pip安裝:
```
pip install beautifulsoup4
```
### Requests
Requests用於抓取HTML原始碼下來,一樣是使用pip安裝:
```
pip install requests
```
## 引用
大部分爬蟲引用這兩個模組就夠用了,少部分需要其他模組(os、json等等)
```
import requests
from bs4 import BeautifulSoup
```
## 抓取並分析網站
抓取網站使用到 requests.get() 函數,參數為我們想要爬蟲的網址(URL),範例為PTT的C_Chat版。
```
response = requests.get("https://www.ptt.cc/bbs/C_Chat/index.html")
```
抓下來的並不是我們想要的HTML,因此要經過 BeautifulSoup 轉化。
轉化後的HTML排版並不好看,`soup.prettify()` 可以美化排版。
```
response = requests.get("https://www.ptt.cc/bbs/C_Chat/index.html")
soup = BeautifulSoup(response.text, "html.parser")
print(soup.prettify())
```
## 搜尋節點
搜尋為爬蟲相當重要的一環,抓下來的資訊零零散散,搜索就是幫助我們找到想要的資訊。
### find()
搜尋第一個符合條件的HTML節點,參數為要搜尋的標籤名稱,回傳第一個符合條件的節點。
```
result = soup.find("h2")
```
### find_all()
搜尋所有符合條件的HTML節點,回傳符合條件的節點列表。
```
result = soup.find_all("h3")
```
參數可加入關鍵字指定屬性值,另外limit參數可以限制搜尋的數量。
```
result = soup.find_all("h3", itemprop="headline", limit=3)
```
回傳為一個符合條件的節點列表(list),當然也有索引值。
```
print(result[0]) # 印出第一個符合條件的節點
```
如果要同時搜尋多個HTML標籤,可以將標籤名稱打包成串列(list)後當成參數。
```
result = soup.find_all(["h3", "p"], limit=2)
```
如果要搜尋指定的CSS屬性值,則可以加入參數指定。
因為 `class` 是 Python 的保留字,所以要用 `class_`。
```
titles = soup.find_all("p", class_="summary", limit=3)
```
遇到這種情況,也可以用 `{ key : value }` 表示法。
```
titles = soup.find_all("p", {"class" : "summary"}, limit=3)
```
使用 string 參數可搜尋指定文字內容的節點。
```
soup.find_all("p", string="Hello World")
```
### select_one()
select()使用CSS選擇器的方式搜尋節點,類似jQuery的語法。
搜尋某節點底下的子節點,回傳第一個符合條件的子節點。
```
result.select_one("a")
```
### select()
搜尋某節點底下的子節點,回傳多個符合條件的子節點的列表(list),另外limit參數可以限制搜尋的數量。
```
result.select("a", limit=3)
```
所以通常是先find()到想爬的標籤,再select()往下找子節點。
如果要搜尋指定的CSS屬性值,則是用類似 Emmet 的語法。
比方說想搜尋 class=summary 的 h2 底下的 a 子節點,就要這樣搜尋:
```
links = soup.select("h2.summary a")
```
### find_parent() 或 find_parents()
從目前節點向上搜尋符合條件的父節點。find_parent()用於搜尋單個,find_parents()則用於搜尋多個。
比方說想搜尋 itemprop="url" 的 a 上層的 h2 父節點,就要這樣搜尋:
```
result = soup.find("a", itemprop="url")
parents = result.find_parents("h2")
```
### find_previous_sibling()
在同級的節點中,搜尋上一個節點。
比方說想搜尋 itemprop="headline" 的 h2 上一個的 a 節點,就要這樣搜尋:
```
result = soup.find("h2", itemprop="headline")
previous_node = result.find_previous_siblings("a")
```
### find_next_sibling()
在同級的節點中,搜尋下一個節點。
比方說想搜尋 itemprop="headline" 的 h2 下一個的 p 節點,就要這樣搜尋:
```
result = soup.find("h2", itemprop="headline")
next_node = result.find_next_siblings("p")
```
## 抓取資料
搜尋到想要的節點後,下一個重要步驟當然就是抓取資料下來嘛!
### get()
抓取節點屬性值,參數為屬性。
比方說想搜尋一個 a 的 href(URL連結),就要這樣搜尋:
```
title.select_one("a").get("href")
# 等同於...
title.select_one("a")["href"]
```
### getText()
抓取節點內部文字。
比方說想搜尋一個 a 的內部文字,就要這樣搜尋:
```
title.select_one("a").getText()
# 等同於...
title.select_one("a").text
# 等同於...
title.select_one("a").string
```
## 下載圖片
還在一張一張下載圖片嗎? 太落伍了! 試試看用爬蟲自動下載圖片吧~
1. 先搜尋到該圖片的節點,然後抓取圖片的來源網址(src),並且使用 `get()` 下載圖檔下來(注意,此時你的電腦中還不會出現圖片!)。
```
link = result.get("src")
img = requests.get(link)
```
2. 接著開一個新檔案,用到 open() 函數,參數為檔名和輸入方式(圖片為二進位輸入,所以是 wb),此範例因為只有一張圖,不然多張圖的情況檔名記得要換。
```
file = open("img1.jpg","wb")
```
3. 把剛下載的圖檔內容寫進新檔案中,然後關檔(務必要關!!),就完成啦!
```
file.write(img.content)
file.close()
```
4. 推薦要寫:建議是把圖片統一存在一個資料夾,要不然下載的圖片會跟你的檔案混在一起喔!
開檔前先檢查有沒有資料夾,如果沒有,就建立新資料夾。
```
if not os.path.exists("images"):
os.mkdir("images") # 建立資料夾
```
**完整程式碼:**
```
link = result.get("src") # 抓取圖片src
img = requests.get(link) # 下載圖片
if not os.path.exists("images"):
os.mkdir("images") # 建立資料夾
file = open("images\\img1.jpg","wb") # 開啟新檔
file.write(img.content) # 寫入內容
file.close() # 關檔
```
## 210209補充 - Requests套件(進階)
在前面,我們可能只會這樣寫:
```
# 引入 requests 模組
import requests
# 使用 GET 方式下載普通網頁
response = requests.get("https://www.ptt.cc/bbs/C_Chat/index.html")
soup = BeautifulSoup(response.text, "html.parser")
```
但現在跟你說,Request還有更多的功能。
### 狀態碼(status code)
參數 `status_code` 可以得到該網站的狀態碼。
```
import requests
response = requests.get("https://www.ptt.cc/bbs/C_Chat/index.html")
print(response.status_code)
# 200
```
順便提一下狀態碼(Status Code)是什麼,幾個常見的有:
* 200:一切順利,結果已經回傳。
* 301:伺服器將使用者重新定向(re-direct)到另一個位址,當網站更換網域名稱或更改 Routes 時可能會發生。
* 400:錯誤的語法請求。
* 401:未通過伺服器的身份驗證,當請求沒有一併發送正確憑證時會發生。
* 403:伺服器已經理解請求,但是拒絕執行它,意即與請求一併發送的憑證無效。
* 404:找不到目標。
更多狀態碼:[MDN - HTTP 狀態碼](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Status)
### GET 請求
#### 增加 URL 查詢參數
有些網站URL中會夾帶的關鍵字,使用 params 參數生成。
```
import requests
my_params = {
"key1": "value1",
"key2": "value2"
}
response = requests.get(
"https://www.ptt.cc/bbs/C_Chat/index.html", params=my_params)
print(response.url)
# https://www.ptt.cc/bbs/C_Chat/index.html?key1=value1&key2=value2
```
#### 加入 headers
使用 headers 參數,經常加入 user-agent 或 cookie 等關鍵字。
```
# 自訂表頭
my_headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36'
}
# 將自訂表頭加入 GET 請求中
response = requests.get(
"https://www.ptt.cc/bbs/C_Chat/index.html", headers = my_headers)
```
#### cookie
如果是要找網頁存了哪些 cookie,可使用 cookie 參數加上 cookie 名稱。
```
# 含有 cookie 的內容
response = requests.get("http://my.server.com/has/cookies")
# 取出 cookie
print(response.cookies['my_cookie_name'])
```
將自己設定的 cookie 放入 GET 的 headers 中。
```
# 設定 cookie
my_cookies = {
my_cookie_name: "content"
}
# 將 cookie 加入 GET 請求
r = requests.get("http://httpbin.org/cookies", cookies = my_cookies)
```
### POST 請求
前面都是用 GET,但 POST 也算滿常用到的函數,可以用於回傳資料或上傳檔案。
#### 回傳資料
```
# 資料
my_data = {'key1': 'value1', 'key2': 'value2'}
# 將資料加入 POST 請求中
r = requests.post('http://httpbin.org/post', data = my_data)
```
#### 上傳檔案
```
# 要上傳的檔案
my_files = {'my_filename': open('my_file.docx', 'rb')}
# 將檔案加入 POST 請求中
r = requests.post('http://httpbin.org/post', files = my_files)
```
* 參考網址:[Python 使用 requests 模組產生 HTTP 請求,下載網頁資料教學](https://blog.gtwang.org/programming/python-requests-module-tutorial/)
## 相關文章
* [Python 動態網頁爬蟲](/Cp1938RtSZ6yu7DHfJvUDQ)
## 參考網址
* [開發Python網頁爬蟲前需要知道的五個基本觀念](https://www.learncodewithmike.com/2020/10/python-web-scraping.html)
* [7個Python使用BeautifulSoup開發網頁爬蟲的實用技巧(我主要是看這個)](https://www.learncodewithmike.com/2020/02/python-beautifulsoup-web-scraper.html)
* [有效利用Python網頁爬蟲幫你自動化下載圖片](https://www.learncodewithmike.com/2020/09/download-images-using-python.html)
* [Python 使用 Beautiful Soup 抓取與解析網頁資料,開發網路爬蟲教學](https://blog.gtwang.org/programming/python-beautiful-soup-module-scrape-web-pages-tutorial/)
* [IT鐵人賽-Python爬蟲小人生](https://ithelp.ithome.com.tw/articles/10202121)