# 網路爬蟲 Vincent55 楊竣鴻 Note: 2023-07-11 09:20 - 11:20 2023-07-11 14:50 - 16:50 上課前先解釋如何使用這份簡報,可以使用編輯模式看 Note 跟範例 --- ## 這堂課你會學/操作到 - 網頁基本原理 - 爬蟲常用套件 - Requests - BeautifulSoup - Selenium - API --- # 網頁基本原理與複習 - HTTP request/response - HTTP methods - HTML、JavaScript、CSS Note: 在進到爬蟲之前,先介紹什麼是 HTTP,包含了 HTTP methods 跟 Requests 與 Response,再來會介紹網頁上的三個主要的元素 HTML CSS JavaScript ---- ## HTTP requests ![](https://hackmd.io/_uploads/H1YDd5srn.png) ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview Note: 可以看到這張圖的左邊代表了我們目前看到的介面就是瀏覽器,也就是客戶端,其實我們的一些資源是向伺服器去做請求的,可以看到像是 Image 是向 Web Sever 發出 http get 的請求, Video 則是向 Video Server 發出 http get 的請求 ---- ## HTTP methods http://dev.vincent55.tw/ | methods | 功能 | 參數 | | -------- | -------- | -------- | | GET | 取得資料 | `?key1=value1&key2=value2` | | POST | 上傳資料 | key1=value1&key2=value2 | | DELETE | 刪除資料 | | | PATCH | 更新資料 | | Note: 剛有提到 HTTP GET,GET 是一種 HTTP Methods,主要是在取得資料的時候使用的 Method,同時在請求時也可以放參數 可以發現在使用 GET 請求放置參數的時候是直接放在網址後方,這樣就不太安全,例如在登入的時候填寫帳號密碼,若有中間人或有人站在你後面,那很容易被看到。 POST 請求常會用在表單發送的時候,他會將參數放在 Requests body 內。 ---- ## HTTP response code https://http.cat/ Note: 我們發出了一個 HTTP requests 後,伺服器就會回一個 response,那這時候我們可以透過 response status code 來看回應的狀態。 1XX – 連接正在進行中。 2XX – 請求成功完成,伺服器給了瀏覽器預期的響應。 3XX – 這個請求被收到了,但是需要重新定向。 4XX – 請求已經發出,但頁面無效 - 這是網站一方的錯誤,通常在頁面不存在的情況下出現。 5XX – 客戶端的請求是有效的,但伺服器未能完成請求。 ---- ## 聽說 F12 可以駭入小恐龍 (1/3) - 瀏覽器開發人員工具 ![](https://hackmd.io/_uploads/H1a0gniHh.png) Note: 了解 HTTP 基本使用方式後,我們可以使用大多數瀏覽器內建的開發者工具來做測試 ---- ## 聽說 F12 可以駭入小恐龍 (2/3) ![](https://hackmd.io/_uploads/H1gcpqGLh.png) Note: 可以開 https://csie.ncku.camp/ 來演示 元素面板 Elements : 可以看到當前網頁的 DOM 物件、HTML、CSS 並能修改。 控制面板 Console : 目前網頁訊息,可以與 JavaScript 進行命令互動。 來源面板 Source : 可設定目前 JavaScript 的斷點,並測試運行。若網頁是架在本機,能直接存檔。 網路面板 Network : 檢測目前網頁由外向內、由內向外的網路流量,可以看到詳細這些流量的內容(requests header、post data ...) 性能面板 Performance : 紀錄網頁生命週期中發生的事件,用來調整網頁的性能。 內存面板 Memory : 用於檢測當前內存的使用狀態。 應用面板 Storage : 檢測載入資源。 e.g. Local Storage 、 cookies 、 Cache ---- ## 聽說 F12 可以駭入小恐龍 (3/3) `chrome://dino/` - 在 Console 打入 - `Runner.instance_.setSpeed(1000000)` Note: 剛才有提到 Console 可以與網頁的 JavaScript 做互動,因此我們能透過與小恐龍的的互動來調整速度 從這個例子我們能看到跑在網頁的 JavaScript 是可以修改的,因此網頁設計者常會將 JavaScript [混淆](https://obfuscator.io/) 讓人難以查看真正行為 ---- ## 網頁三要素 - HTML - 骨骼 - CSS - 皮膚 - JavaScript - 大腦 Note: 將網頁比做人體,HTML CSS JavaScript 分別對應了骨骼 皮膚 大腦 HTML 提供了我們網頁的基本架構,比如我們需要一段文字跟一個按鈕 有了基本架構後,CSS 可以美化這個頁面,讓文字的自行修改,與按鈕的大小 以上都只是網頁外表的部分,我們需要 JavaScript 來提供邏輯,建立出可以與使用者互動的介面。 ---- ## HTML(1/2) - 超文本標記語言 - Document Object Model (DOM) ![](https://hackmd.io/_uploads/B1s_DdtK3.png) Note: HTML 是一個標記語言,可以看到途中的下方, html head title 各是一個 tag,由一個角括號與文字表示 瀏覽器透過 http requests 取得 HTML 後,會將其轉換為 DOM,並渲染到畫面上,DOM 是一個樹狀的架構,每個標籤都是一個節點,可以用來表示我們的 HTML 可以看到我們最上層的標籤是 html,接下來有 head 跟 body,head 代表了我們網頁的基本資訊, body 代表了網頁的內容。 可以看到有些標籤裡面有一些屬性 class id 等,能讓 CSS 跟 JavaScript 鎖定到元素 ---- ## HTML(2/2) - Document Object Model (DOM) - 節點之間的關係 ![](https://hackmd.io/_uploads/SkwDnjjBh.png) Note: 我們有了 DOM 後,猶豫他是一個樹狀結構,因此我們能簡單的在節點與節點之間移動 ---- ## CSS - CSS 通常會寫在 HTML 的 head 或者獨立出來一個 CSS file - CSS Selector - CSS 用來鎖定元素的 [規範](https://developer.mozilla.org/zh-TW/docs/Web/CSS/CSS_Selectors) ```HTML= <head> <link rel="stylesheet" type="text/css" href="mycss.css"> <style> body{ background:#fff; color:#777; } h1{ font-weight:bold; font-style:italic; font-family:sans-serif; color:green; } a { color: #0077cc; text-decoration: none; } .container { border: 2px solid #ccc; padding: 20px; border-radius: 10px; } </style> </head> ``` Note: CSS 可以用來美化我們的介面,CSS 通常會寫在 HTML 的 head 用 style 標籤包起來,或者獨立一個 css file 再透過引入的方式使用。 在爬蟲這堂課裡面,CSS 可以只需要了解 CSS 選擇器,在 CSS 裡面是透過 CSS 選擇器來鎖定元素的去替換屬性,那這個特性我們可以來利用在爬蟲的時候定位元素。 ---- ## JavaScript - 語言特性 - 單執行緒 - 非同步 - https://developer.mozilla.org/zh-TW/docs/Learn/JavaScript/Asynchronous/Introducing - http://latentflip.com/loupe/ - 動態弱型別語言 - https://openhome.cc/zh-tw/javascript/basics/wat/ Note: 單執行緒: 一次只做一件事,一次只占用一個執行緒 非同步: 在執行非同步函式的時候不會等待回傳值占用執行緒,透過事件迴圈 (Event loop)實現 動態語言: 變數的型態不在定義階段時決定 弱型別: 變數在與不同型態的變數互動時,可以被隱式轉換為另一個類型 ---- ## 挑戰 1 - 開發人員工具練習 https://jupiter.challenges.picoctf.org/problem/9670/ - 找到完整的 flag - 共有三個部分 Hint: 使用開發人員工具,檢查 HTML CSS JavaScript --- # 爬蟲常用套件 ---- ## 爬蟲是什麼 - 一種依照特定規則抓取網路資訊的程式 - 股票爬蟲 - PTT 爬蟲 - 搜尋引擎 Note: 剛才我們操作的都是透過瀏覽器向伺服器發送 HTTP 請求,但其實不只瀏覽器可以做到,可以透過程式來向伺服器發送 HTTP 請求 網路爬蟲就只是一種透過特定的規則去抓取網路資訊一個程式 ---- ## 爬蟲是怎麼運作的 - 取得網路資訊 - 網頁原始碼、API - 解析資訊 - 解析工具們 - 開發人員工具、眼睛... - 資料清洗工具們 - BeautifulSoup、regex... - 後續使用 - 存起來 - 傳給自己 - Telegram、Line... Note: 網路爬蟲是怎麼從零到有產生的 ---- ## Requests (1/4) > Requests is a simple, yet elegant, HTTP library. - 用 Python 向網站發 HTTP 請求 - <img src="https://warehouse-camo.ingress.cmh1.psfhosted.org/98d69ff5a75b6a9a8cbb54b7ce9c126f9d857f79/68747470733a2f2f706570792e746563682f62616467652f72657175657374732f6d6f6e7468" alt="Downloads"> Note: request 是一個 Python 的函式庫,讓我們可以透過簡單的語法向伺服器發出 HTTP 請求 ---- ## Requests (2/4) - 安裝 - `pip install requests` ```python= import requests #先將欲發出 GET 請求的網址先存在 url url = 'https://example.com/' #對 url 發出 GET 請求,並將 Response 物件存在 res res = requests.get(url) print(type(res), res) #Output: <class 'requests.models.Response'> <Response [200]> ``` Note: 這邊是一個範例 ---- ## Requests (3/4) - Response 物件的使用方式 - res.status_code : 該 HTTP 狀態碼 - res.text : 回應物件的字串(str)型態 - res.json() : 將回應物件透過 JSON decoder 回傳 JSON 格式。 - ... - [官方文件](https://requests.readthedocs.io/en/latest/user/quickstart/#response-content) Note: 取得 Response 物件後,我們可以有許多使用的方法,可以使用 status_code 取得 HTTP response code,或者使用 text 取得字串型態的回傳,我們接下來會很常使用這個,以及 .json() 可以幫助我們解析回傳值為 json 格式 ---- ## Requests (4/4) ```python= import requests #先將欲發出 GET 請求的網址先存在 url url = 'https://example.com/' #對 url 發出 GET 請求,並將 Response 物件存在 res res = requests.get(url) print(res.text) ''' <!doctype html> <html> <head> <title>Example Domain</title> <meta charset="utf-8" /> <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <style type="text/css"> body { background-color: #f0f0f2; margin: 0; padding: 0; font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; } div { width: 600px; margin: 5em auto; padding: 2em; background-color: #fdfdff; border-radius: 0.5em; box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02); } a:link, a:visited { color: #38488f; text-decoration: none; } @media (max-width: 700px) { div { margin: 0 auto; width: auto; } } </style> </head> <body> <div> <h1>Example Domain</h1> <p>This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.</p> <p><a href="https://www.iana.org/domains/example">More information...</a></p> </div> </body> </html> ''' ``` ---- ## 挑戰 2 - 取得官網原始碼 `https://csie.ncku.camp/` ---- ## API 政府資料開放平台 https://data.gov.tw/ Note: 把網站比做一個餐廳,前端就像一個外場,就是現在各位看的的頁面,而後端就是一個餐廳的後台,用來處理外場的需求,API 就是一個菜單,供客人知道該怎麼取得對應資料或服務,之間傳遞資料的格式現今最常見的就是 JSON 政府資料開放平台讓我們可以透過 API 的方式取得政府資訊。 ---- ## JSON - 一種輕量級資料交換格式 - 物件、陣列、數值、字串、布林值、空值 ``` { "firstName": "John", "lastName": "Smith", "sex": "male", "age": 25, "ismarry": false, "child": null, "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" }, "phoneNumber": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ] } ``` Note: JSON 是一種輕量級資料交換格式,資料型態只有這邊列出的六種,其中物件可以把它想成是 Python 的 dict,也是一種 key-value 的架構,陣列則是能儲存多個值。 JSON 被廣泛利用在前端與後端的資料交換,與設定檔格式,像是 minecraft 就是使用 JSON 作為設定檔格式,甚至有些人會選擇將他當作資料庫來使用,因此很多語言都有內建 JSON 的函式庫 ---- ## JSON - Python 標準庫裡面有 json 工具 - `import json` - https://docs.python.org/zh-tw/3/library/json.html - Requests 裡面也有內建 - `response.json()` Note: 我們在 Python 裡面可以透過 import 的方式來引入 json,以及剛才提到的 Requests 的 Response 物件也有內建 JSON 解析器 ---- ## JSON 檔案操作 - 檔案名稱 - `xxx.json` - JSON 儲存到 `.json` 檔案 ``` with open('result.json', 'w', encoding='utf-8') as f: json.dump(res, f, indent=2, sort_keys=True, ensure_ascii=False) ``` - 從 `.json` 檔案取得 JSON ``` with open('result.json', 'r') as result_fd: result_file = json.load(result_fd) print(result_file) ``` Note: 我們可以透過存檔讀檔的方式將 json 作為一種資料庫來儲存資料。 ---- ## JSON ```python= import requests import json #先將欲發出 GET 請求的網址先存在 url url = 'http://dev.vincent55.tw/json' #對 url 發出 GET 請求,並將 Response 物件透過 json 解析後存在 res res = requests.get(url).json() print(type(res), res) print(res['slideshow']['slides'][1]['title']) # 將回傳值存於 result.json with open('result.json', 'w', encoding='utf-8') as f: json.dump(res, f, indent=2, sort_keys=True, ensure_ascii=False) ''' <class 'dict'> {'slideshow': {'author': 'Yours Truly', 'date': 'date of publication', 'slides': [{'title': 'Wake up to WonderWidgets!', 'type': 'all'}, {'items': ['Why <em>WonderWidgets</em> are great', 'Who <em>buys</em> WonderWidgets'], 'title': 'Overview', 'type': 'all'}], 'title': 'Sample Slide Show'}} Overview ''' ``` ---- ## 挑戰 3 - 取得貓貓圖片 https://api.thecatapi.com/v1/images/search - 可以輸入一個數字,取得該數量的貓貓圖片,並儲存到 `result.json` 裡面 - 進階挑戰 - 如果有重複執行該程式,`result.json` 不會被覆蓋 - Hint: 可以善用讀檔與寫檔 ---- ## BeautifulSoup (1/4) - 從 HTML 或 XML 檔案中解析資料 - 也可拿來修復未閉合標籤等錯誤的文件 Note: 若沒有 API 可供使用, 我們就需要自行去解析原始碼內容,BeautifulSoup 是一套可以解析 HTML 的工具,提供了很多好用的選取器取得想要的資料。 我們可以透過 requests 的 Response 物件的 text 取得字串型態的回傳值,再來我們可以透過 BeautifulSoup 去解析內容。 ---- ## BeautifulSoup (2/4) - 安裝 - `pip install beautifulsoup4` ```python= from bs4 import BeautifulSoup html_text = """ <html><head></head><body><h1>Hello, World!</h1></body></html> """ soup = BeautifulSoup(html_text, "html.parser") print(soup.prettify()) ``` ```text <html> <head> </head> <body> <h1> Hello, World! </h1> </body> </html> ``` Note: html_text 是一個 HTML 字串的範例,透過 BeautifulSoup 解析,之後使用 prettify 查看解析的狀態。 ---- ## Beautifulsoup (3/4) - soup.find() : 根據條件回傳"第一個"符合的元素 - `soup.find('p', id='myid', class_='myclass')` - soup.find_all() : 根據條件回傳"所有"符合的元素,由串列表示。 - soup.select_one() : 透過 CSS Selector "第一個"符合的元素 - soup.select() : 透過 CSS Selector "所有"符合的元素,由串列表示。 Note: BeautifulSoup 提供了一些選取器 ---- ## Beautifulsoup (4/4) ```python= from bs4 import BeautifulSoup import requests # 將 resp.text 也就是 HTML 資料定義到 BeautifulSoup 物件內,並用 html.parser 解析 HTML 內容 soup = BeautifulSoup(requests.get(url).text, "html.parser") # 輸出網頁的 title print(soup.title.getText()) #輸出第一個尋找到的 <li> 元素的文字 print(soup.li.getText()) #輸出第一個尋找到的 <li> 元素的文字(相同效果) print(soup.find('li').getText()) #尋找全部 <li> 元素的文字 lis = soup.find_all('li') for li in lis: print(li.getText()) ``` ---- ## 案例 - ptt 棒球版爬蟲 (1/3) https://www.ptt.cc/bbs/Baseball/index.html - class="r-ent" ![](https://hackmd.io/_uploads/Byjn9KnH2.png) Note: 觀察一下頁面可以發現各個文章有個共通點就是都有 r-ent 這個 class ,因此我們能透過 find_all 的方式取得所有文章 ---- ## 案例 - ptt 棒球版爬蟲 (2/3) ```python= from bs4 import BeautifulSoup import requests url = "https://www.ptt.cc/bbs/Baseball/index.html" r = requests.get(url) soup = BeautifulSoup(r.text, "html.parser") for article in soup.find_all(class_="r-ent"): print(article) ``` Note: 取得所有文章後,檢查我們需要元素的 class ---- ## 案例 - ptt 棒球版爬蟲 (3/3) ```python= from bs4 import BeautifulSoup import requests import json url = "https://www.ptt.cc/bbs/Baseball/index.html" r = requests.get(url) soup = BeautifulSoup(r.text, "html.parser") results = [] for article in soup.find_all(class_="r-ent"): results.append({ "title":article.find(class_="title").text.strip(), "author": article.find(class_="author").text.strip(), "link": article.find(class_="title").a['href'] }) # 將回傳值存於 result.json with open('result.json', 'w', encoding='utf-8') as f: json.dump(results, f, indent=2, sort_keys=True, ensure_ascii=False) ``` ---- ## 挑戰 4 - ptt 棒球版爬蟲(自動換頁) https://www.ptt.cc/bbs/Baseball/index.html Hint: 可將目前的爬蟲寫成一個 function,並且每次都用爬蟲去取得上一頁的連結 Note: 如果學員提早完成了,可以請他將結果以 JSON 儲存 --- # Selenium - 動態載入網頁資料 - 模擬使用者在瀏覽器的行為 - 點擊按鈕、輸入帳號密碼、捲動捲軸 Note: 我們剛才學的都是使用 HTTP request 取得我們要的資源,但有些頁面是透過 JavaScript 去動態產生資料的,這時候我們就需要另外一種爬蟲的方式 例如 Selenium 是可以模擬瀏覽器的工具,就像我們在瀏覽一個頁面一樣,能動態的渲染頁面 ---- ## Selenium - 安裝 - `pip install selenium webdriver-manager` Note: 為了讓Selenium可以自動化控制瀏覽器,我們必須先安裝瀏覽器的驅動程式 ---- ## Selenium ```python= from selenium import webdriver from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager import time driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install())) time.sleep(10) driver.close() ``` Note: ChromeDriverManager 可以自動下載符合環境的瀏覽器驅動程式,讓 Selenium 能夠使用該驅動程式來啟動瀏覽器 ---- ## Selenium ```python= from selenium import webdriver from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By import time driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install())) driver.get("http://www.python.org") #前往該網頁 elem = driver.find_element(By.NAME, "q") #找到特定元素 elem.clear() #將該欄位清空 elem.send_keys("nckucsie") #模擬使用者打入字串 elem.send_keys(Keys.RETURN) #模擬試用者 Enter time.sleep(5) #sleep 5 秒查看結果 driver.close() #將 driver 關閉 ``` Note: demo 這段程式,確保學員能夠跑起來 ---- ## 鎖定元素 - https://selenium-python.readthedocs.io/locating-elements.html#locating-elements Note: Selenium 提供了很多方法來選取到想要的元素 回到上一頁說明鎖定元素的過程 ---- ## 案例 - 取得頁面所有文章 https://forum.gamer.com.tw/ ![](https://hackmd.io/_uploads/rJHO6BvK2.png) Note: ```python= from selenium import webdriver from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By import json driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install())) #前往該網頁 driver.get("https://forum.gamer.com.tw/") #找到特定元素 forum_lists = driver.find_elements(By.CLASS_NAME , "forum_list") result = [] #遍歷每個文章 for forum in forum_lists: #取得各文章的內容 result.append({ "title": forum.find_element(By.CLASS_NAME, "forum_list_title").text, "url": forum.find_element(By.TAG_NAME, "a").get_attribute("href") }) #儲存到 json file with open('result.json', 'w', encoding='utf-8') as f: json.dump(res, f, indent=2, sort_keys=True, ensure_ascii=False) #將 driver 關閉 driver.close() ``` Note: 除了 find_element,也帶到 get_attribute 取得連結 href ---- ## 鎖定到元素後可以幹嘛 - https://selenium-python.readthedocs.io/navigating.html ---- ## 案例 - 捲動頁面 - 在 driver 執行 JavaScript `driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")` https://forum.gamer.com.tw/ Note: ```python= from selenium import webdriver from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By import time driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install())) #前往該網頁 driver.get("https://forum.gamer.com.tw/") #捲動頁面到最下端 driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") #sleep 五秒,觀察變化 time.sleep(5) #將 driver 關閉 driver.close() ``` ---- ## 挑戰 5 - 巴哈姆特哈拉版爬蟲(自動捲動) https://forum.gamer.com.tw/ Hint: 結合我們剛的兩個案例,先自動捲動後再取得文章 - 進階挑戰 - 在每次取得文章後,都進行捲動一次,並限制共會捲動幾次,且保證不會有文章重複 --- ## 補充 ---- ### 更多爬蟲技巧 - 異步爬蟲 - 分析經驗 ---- ### 反爬蟲 - headers - cookies - proxy ---- ### 專案上的建議 - 在開始動手前,先分析網站,搞不好能發現隱藏 API - 不到最後關頭不使用模擬瀏覽器 --- ## 謝謝各位 https://vincent55.tw/contacts/ ---- ### 如果一個 Package 沒有 \_\_init__.py - Namespace package - https://python3-cookbook-personal.readthedocs.io/zh_CN/latest/c10/p05_separate_directories_import_by_namespace.html ---- ## more pip - Wheel ( .whl ) - ![](https://hackmd.io/_uploads/r1NEtZqHh.png) - 安裝包 - 本質上是一個 zip 檔 - 一種預編譯發行版的格式 - ready-to-install format - `pip install <wheel file>.whl` Note: 更小、安裝更快 解釋為何需要 compile 分享案例,在一家診所,需要自動讀取健保卡資料填入 X 光機控制軟體,作業系統是 windows XP,Python3 最高支援版本為 python3.4.4,沒有辦法連到外網,無法使用 pip 來下載 pypi 的 package,因此只好用 USD 將 wheel file 放到機器上用 pip install 下載 ---- ## pipenv - pip + virtualenv - Pipfile - Pipfile.lock ---- ## JavaScript 補充 - JavaScript 能拿來寫後端? - Node.js ? JavaScript ? - 為什麼要把 JavaScript 移植到後端? > Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. V8 engine: https://chromium.googlesource.com/v8/v8 Note: 以 V8 為核心,加上一系列 C/C++ 的套件,成功的讓 Server 端也可以執行 JavaScript JavaScript 自帶非同步,且語言特性適合用來接收高併發的需求 ----
{"description":"Vincent 楊竣鴻 / 想要有效地利用 Python 套件 / Package / poetry / crawling / crawler / Requests / BeautifulSoup4 / BeautifulSoup / 2023-05-27 13:00","slideOptions":"{\"backgroundTransition\":\"none\",\"parallaxBackgroundSize\":\"1920px 1080px\",\"parallaxBackgroundHorizontal\":0,\"parallaxBackgroundVertical\":0}","title":"Python 網路爬蟲選修課程 - 成大資工資訊營","contributors":"[{\"id\":\"1e882b7f-b741-4bc1-9480-d5e91f0d2819\",\"add\":20115,\"del\":2310}]"}
    1492 views