Vincent550102
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- type: slide tags: presentation title: 網路爬蟲 - 成大資工資訊營 description: Vincent / 楊竣鴻 / crawling / crawler / Requests / BeautifulSoup4 / BeautifulSoup / Selenium slideOptions: backgroundTransition: none parallaxBackgroundSize: '1920px 1080px' parallaxBackgroundHorizontal: 0 parallaxBackgroundVertical: 0 # parallaxBackgroundImage: https://fastly.picsum.photos/id/735/200/300.jpg --- # 網路爬蟲 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 自帶非同步,且語言特性適合用來接收高併發的需求 ----

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully