AndyChiangFri, Feb 5, 2021 9:59 AM
Python
爬蟲
這兩個套件都是爬蟲好用(必備)的套件,使用起來簡單,適合用於靜態網站爬蟲(最常被爬的:PTT、Dcard、Yahoo等等)。
Beautiful Soup通常用來分析抓下來的HTML,使用pip安裝:
pip install beautifulsoup4
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())
搜尋為爬蟲相當重要的一環,抓下來的資訊零零散散,搜索就是幫助我們找到想要的資訊。
搜尋第一個符合條件的HTML節點,參數為要搜尋的標籤名稱,回傳第一個符合條件的節點。
result = soup.find("h2")
搜尋所有符合條件的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()使用CSS選擇器的方式搜尋節點,類似jQuery的語法。
搜尋某節點底下的子節點,回傳第一個符合條件的子節點。
result.select_one("a")
搜尋某節點底下的子節點,回傳多個符合條件的子節點的列表(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()則用於搜尋多個。
比方說想搜尋 itemprop="url" 的 a 上層的 h2 父節點,就要這樣搜尋:
result = soup.find("a", itemprop="url")
parents = result.find_parents("h2")
在同級的節點中,搜尋上一個節點。
比方說想搜尋 itemprop="headline" 的 h2 上一個的 a 節點,就要這樣搜尋:
result = soup.find("h2", itemprop="headline")
previous_node = result.find_previous_siblings("a")
在同級的節點中,搜尋下一個節點。
比方說想搜尋 itemprop="headline" 的 h2 下一個的 p 節點,就要這樣搜尋:
result = soup.find("h2", itemprop="headline")
next_node = result.find_next_siblings("p")
搜尋到想要的節點後,下一個重要步驟當然就是抓取資料下來嘛!
抓取節點屬性值,參數為屬性。
比方說想搜尋一個 a 的 href(URL連結),就要這樣搜尋:
title.select_one("a").get("href")
# 等同於...
title.select_one("a")["href"]
抓取節點內部文字。
比方說想搜尋一個 a 的內部文字,就要這樣搜尋:
title.select_one("a").getText()
# 等同於...
title.select_one("a").text
# 等同於...
title.select_one("a").string
還在一張一張下載圖片嗎? 太落伍了! 試試看用爬蟲自動下載圖片吧~
get()
下載圖檔下來(注意,此時你的電腦中還不會出現圖片!)。link = result.get("src")
img = requests.get(link)
file = open("img1.jpg","wb")
file.write(img.content)
file.close()
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() # 關檔
在前面,我們可能只會這樣寫:
# 引入 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
可以得到該網站的狀態碼。
import requests
response = requests.get("https://www.ptt.cc/bbs/C_Chat/index.html")
print(response.status_code)
# 200
順便提一下狀態碼(Status Code)是什麼,幾個常見的有:
更多狀態碼:MDN - HTTP 狀態碼
有些網站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 參數,經常加入 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 的內容
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)
前面都是用 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)