1 of 92

Python 爬蟲實戰

楊証琨, 楊鎮銘

中央研究院資訊科學研究所資料洞察實驗室

2 of 92

Outline

  • 靜態網頁以外的爬蟲
    • 圖片爬蟲
    • 檔案爬蟲
    • 網站爬蟲
  • 現實世界的爬蟲
    • 現代網站爬蟲衍生的問題
    • 動態網頁爬蟲

2

3 of 92

圖片爬蟲 - 圖片的 tag?

  • 打開瀏覽器的開發者工具
  • 選擇 inspect 工具
  • 滑鼠移到你想選擇的圖片

3

4 of 92

圖片爬蟲 - 與文字爬蟲的差異

  • 文字爬蟲
    • 送 request 到目標網頁
    • 透過 Beautifulsoup 取得目標文字 tag
    • 透過 .text 可以直接拿到文字資訊
  • 圖片爬蟲透過解析網頁只能拿到圖片位置資訊
    • 送 request 到目標網頁
    • 透過 Beautifulsoup 取得目標圖片 tag
    • 透過 src 屬性拿到圖片位置
    • 再送 request 到圖片真正的位置
    • 取得圖片並儲存

4

5 of 92

爬文字的過程 - 取得頁面資訊

  • 爬文字的過程

5

request

request

Client

Server

.html

.html

https://gushi.tw/hu-shih-memorial-hall/

6 of 92

爬文字的過程 - 解析頁面並取得文字

  • 爬文字的過程

Beautifulsoup( )

尋找第 4 個 <p> tag

透過 .text 取得文字資訊

6

Client

.html

7 of 92

爬圖片的過程 - 取得頁面資訊

7

request

request

Client

Server

.html

.html

https://gushi.tw/hu-shih-memorial-hall/

8 of 92

爬圖片的過程 - 取得位置資訊

  • 爬圖片的過程

Beautifulsoup( )

尋找第 1 個 <img> tag

透過 src 取得圖片位置資訊

8

Client

.html

9 of 92

爬圖片的過程 - 取得圖片

  • 爬圖片的過程

9

request

request

Client

Server

img

img

http://gushi.tw/wp-content/uploads/2016/08/logo.png

10 of 92

爬圖片的過程 - 下載圖片

from urllib.request import urlretrieve

# 透過 urlretrieve 下載圖片

# url: 你要下載的圖片位置

# file: 你要儲存的文件名稱

urlretrieve(url, file)

10

11 of 92

爬圖片的過程 - 偽裝成瀏覽器發送請求

  • 部份網站會判斷你是否為爬蟲程式
  • 加上身份識別偽裝成瀏覽器送出請求

11

request

request

Client

Server

12 of 92

身份識別 User-Agent

  • 敘述瀏覽器使用的系統, 平台, 版本等資訊的字串
  • 瀏覽器開發者工具 > Network

12

13 of 92

身份識別 User-Agent

  • 敘述瀏覽器使用的系統, 平台, 版本等資訊的字串
  • 瀏覽器開發者工具 > Network > 重新整理網頁

13

14 of 92

身份識別 User-Agent

  • 重新整理網頁之後選擇對網頁送出的 request

14

15 of 92

身份識別 User-Agent

  • 檢查 Headers 欄位中的 Request Headers

15

16 of 92

爬圖片的過程 - 偽裝成瀏覽器發送請求

  • 部份網站會判斷你是否為爬蟲程式
  • 偽裝成瀏覽器發送請求預防被檔

from urllib.request import build_opener

from urllib.request import install_opener

opener = build_opener()

opener.addheaders = [('User-Agent', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36')]

install_opener(opener)

16

17 of 92

練習 00: 下載圖片 (5 ~ 8 mins)

目標程式:00_download_image.py

目標網站:https://gushi.tw/hu-shih-memorial-hall/

目標:下載該頁面的第一個圖片

  • 取得圖片的位置
  • 透過開發者工具找到 User-Agent 的值
  • 偽裝成瀏覽器送出請求
  • 下載圖片

17

18 of 92

Bonus: 檢查圖片下載百分比

  • 透過 urlretrieve 的 callback 實作
  • callback 代表做完功能 A 時要做的功能 B

from urllib.request import urlretrieve

# 設計 callback

def check_percentage(chunk, size, remote):

percentage = 100.0 * chunk * size / remote

if percentage > 100.0:

percentage = 100.0

print('Download...{:.2f}%'.format(percentage))

urlretrieve(url, 'logo.png', check_percentage)

18

19 of 92

圖片存檔 - 副檔名的重要性

  • 副檔名只是讓電腦知道要用甚麼方式讀取的提示
  • 副檔名錯誤就無法正確開啟檔案
  • 直接更改副檔名並不等於轉檔

19

test.docx

test.xlsx

用 Office Word 開啟的文件

用 Office Excel 開啟的文件

無法開啟文件

rename

20 of 92

站外資源

20

21 of 92

眼見不為憑

  • http://i.imgur.com/q6fMyz9.jpg

21

22 of 92

眼見不為憑

  • http://i.imgur.com/q6fMyz9.png

22

23 of 92

眼見不為憑

  • http://i.imgur.com/q6fMyz9.gif

23

24 of 92

獲取真實的圖片格式

import requests

from bs4 import BeautifulSoup

from PIL import Image

url = 'http://i.imgur.com/q6fMyz9.jpg'

response = requests.get(url, stream=True)

# 讓 PIL.Image 讀進圖片幫我們了解圖片格式

image = Image.open(response.raw)

print(image.format) # JPEG

24

25 of 92

圖片存檔 - 原始檔名與正確的副檔名

  • 透過圖片 URL 提取檔名
  • 透過檢查過後的圖片格式當作副檔名

# url = 'http://i.imgur.com/q6fMyz9.jpg'

filename = url.split('/')[-1] # q6fMyz9.jpg

filename = filename.split('.')[0] # q6fMyz9

ext = image.format.lower() # JPEG -> jpeg

download_filename = '{}.{}'.format(filename, ext) # q6fMyz9.jpeg

25

26 of 92

練習 01: 判斷格式並下載圖片 (5 ~ 8 mins)

目標程式:01_download_image_and_check_format.py

目標:下載圖片並以正確格式儲存

  • 透過 stream 的方式送出請求
  • 透過 PIL.Image 檢查圖片格式
  • 透過字串處理取得適合的檔案名稱
  • 下載圖片

url = 'http://imgur.com/rqCqA.png'

url = 'http://imgur.com/rqCqA.jpg'

url = 'http://imgur.com/rqCqA.gif'

26

27 of 92

練習 01: 延伸思考

  • 是否可以不透過 stream 的方式來判斷圖片?
    • 透過 BytesIO 將圖片轉成 PIL.Image 需要的格式
  • 是否可以只傳一次 request 就做判斷格式與存檔
    • bonus_one_requests.py
    • 直接使用 PIL.Image 存檔而不透過 urlretrieve

27

28 of 92

Outline

  • 靜態網頁以外的爬蟲
    • 圖片爬蟲
    • 檔案爬蟲
    • 網站爬蟲
  • 現實世界的爬蟲
    • 現代網站爬蟲衍生的問題
    • 動態網頁爬蟲

28

29 of 92

檔案爬蟲 - 超連結檔案

  • 透過開發者工具查看檔案的 tag

29

30 of 92

檔案爬蟲 - 超連結的本體

  • 外表看似圖片,但其實本體是超連結

30

<a href=”...”>

</a>

<img src=”...”>

31 of 92

檔案爬蟲 - 定位節點

  • 尋找所有 a tag 再用 regular expression 過濾 href
  • 尋找所有裏面包含 img tag 的 a tag
  • 尋找相同圖片而且上層是 a tag 的 img tag

31

src = 'http://140.112.115.12/exam/sites/all/modules/filefield/icons/application-pdf.png'

<a href=”...”>

</a>

<img src=”...”>

32 of 92

檔案爬蟲 - 定位節點

  • 尋找相同圖片而且上層是 a tag 的 img tag

# 透過 regular expression 找到相同圖片的 img tag

images = soup.find_all('img',

{'src': re.compile('application-pdf\.png')})

for image in images:

# 透過 parent 函數尋訪 img tag 的上一層 tag

print(image.parent['href'])

32

33 of 92

href 的絕對路徑與相對路徑

  • 透過給予檔案位置 (URL) 讓網頁可以參考並顯示�e.g. <a href='...'>, <img src='...'>
  • 敘述檔案位置的方式分為

33

34 of 92

檔案爬蟲 - 相對位置

  • URL 代表檔案在網路上的位置
  • 無法直接對相對路徑送 requests

34

請問可以外送十杯咖啡嗎?

可以阿,請問要送到哪裡?

門口進來左轉的樓梯到三樓右手邊的櫃台

… 是奧客嗎

35 of 92

檔案爬蟲 - 相對位置

  • URL 代表檔案在網路上的位置
  • 無法直接對相對路徑送 requests

url = '/exam/sites/all/modules/pubdlcnt/pubdlcnt.php?file=http://140.112.115.12/exam/sites/default/files/exam/graduate/106g/106_graduate_4.pdf&nid=5814'

response = requests.get(image_url)

35

36 of 92

檔案爬蟲 - 將相對路徑轉為絕對路徑

  • 必須把相對路徑轉為絕對路徑才能送出 request
  • URL 有一定的格式,很難單純做字串處理轉換
  • 透過參考用的絕對路徑就可以轉換相對路徑

36

http://exam.lib.ntu.edu.tw/graduate

/exam/sites/all/modules/pubdlcnt/pubdlcnt.php

絕對路徑

相對路徑

http://exam.lib.ntu.edu.tw/exam/sites/all/modules/pubdlcnt/pubdlcnt.php

組合

37 of 92

檔案爬蟲 - 取得絕對位置

  • 透過 urllib.parse.urljoin 取得絕對位置
  • 當前網頁的 URL 最適合拿來做參考用的絕對路徑

from urllib.parse import urljoin

print(urljoin(絕對路徑, 相對路徑))

37

38 of 92

檔案爬蟲 - 解析 URL

  • urljoin 其實是透過 urllib.parse.urlparse 將兩組 URL 拆解成數個片段再去組合出新的絕對路徑

38

絕對路徑

相對路徑

39 of 92

練習 2-1: 檔案下載與 URL 轉換 (5 ~ 8 mins)

目標程式:02_1_observe_urljoin.py

目標:觀察不同的情況透過 urljoin 的結果

  • 開頭有無斜線與兩條斜線的差別
  • 不斷回到上一層的結果

print(urljoin(response.url, '105g/'))

print(urljoin(response.url, '/105g/'))

print(urljoin(response.url, '//facebook.com'))

print(urljoin(response.url, '../'))

print(urljoin(response.url, '../../'))

39

40 of 92

練習 2-2: 檔案下載與 URL 轉換 (5 ~ 8 mins)

目標程式:02_2_download_history_exam.py

目標網站:http://140.112.115.12/exam/graduate

目標:下載頁面上的所有考古題 (25 份)

  • 定位超連結的 tag
  • 將超連結的相對路徑轉換成絕對路徑
  • 字串處理取得要下載的檔案名稱
  • 下載檔案

40

41 of 92

Outline

  • 靜態網頁以外的爬蟲
    • 圖片爬蟲
    • 檔案爬蟲
    • 網站爬蟲
  • 現實世界的爬蟲
    • 現代網站爬蟲衍生的問題
    • 動態網頁爬蟲

41

42 of 92

網站結構

  • 網頁是一份 HTML 檔案
  • 網站是一堆網頁以階層式的方式組成的集合

42

root

articles

imgs

js

43 of 92

網站瀏覽行為

  • 網站存放在遠端電腦中,我們稱該電腦為主機
  • 網站瀏覽即是查看目標主機的不同檔案而已

43

44 of 92

網站瀏覽行為

  • 網站存放在遠端電腦中,我們稱該電腦為主機
  • 網站瀏覽即是查看目標主機的不同檔案而已

44

root

articles

imgs

js

Home

45 of 92

網站瀏覽行為

  • 網站存放在遠端電腦中,我們稱該電腦為主機
  • 網站瀏覽即是查看目標主機的不同檔案而已

45

root

articles

imgs

js

主題特輯

46 of 92

遍歷網站 - 從網頁連結到其他網頁

  • 透過開發者工具查看
  • 超連結即是 <a> tag

46

Home

主題特輯

檢查 href 路徑之後送 request�(urljoin, requests)

取得主題特輯頁面

47 of 92

透過迴圈尋訪網站

  • 透過迴圈對所有網址超連結都送出 request

47

root

index1

index2

index3

48 of 92

透過迴圈尋訪網站

  • 並不是所有網頁的超連結都會出現在首頁
  • 只做一次迴圈無法發現其他網頁裡的超連結

48

root

index1

index2

index3

hidden

49 of 92

遍歷網站

  • 看過網站所有超連結 = 看過所有網頁所有超連結
  • 紀錄所有需要送 requests 的超連結,直到送過所有超連結

[ index2, index3 ]

[ index3 ]

[ hidden ]

[ ]

49

root

index1

index2

index3

hidden

50 of 92

遍歷網站 - 直到看過所有連結

  • 宣告一個 list 儲存即將要送 request 的網址
  • 決定送 request 的中止條件

# 儲存即將要送 request 的網址

wait_list = ['https://afuntw.github.io/demo-crawling/demo-page/ex1/index1.html']

# 當 wait list 裡還有網址的時候...

while wait_list != []:

# 送 request 的流程

50

51 of 92

遍歷網站 - 更新 wait_list 清單

  • 從 wait_list 中取出網址
  • 從 wait_list 中刪除已經取出的網址
  • 從 wait_list 中放入新的網址

# 從 wait_list 中取出第一個網址並更新

url = wait_list.pop(0)

# 從 wait_list 中放入新的網址

wait_list.append(new_url)

51

52 of 92

練習 03: 取得真正的所有標題 (5~8 mins)

目標程式:03_crawling_demo1_hidden.py

目標網站:https://goo.gl/VnwhCJ

目標:透過超連結不斷爬取多個網頁的 h1 tag

  • 將需要送 request 的超連結存入等待清單
  • 不斷的拿等待清單的超連結送 request

52

53 of 92

現實中網站的超連結設計

  • 網頁間可以互相超連結
  • 導覽列

53

54 of 92

遍歷網站的迴圈問題

wait list

  • About
  • Contact

wait list

  • Contact

wait list

  • Contact
  • Home
  • Contact
  1. wait list 有重複 URL
  2. 打算對 Home 重複存取

54

55 of 92

解決遍歷網站的迴圈問題

  • 建立一個已經送過 request 的清單 viewed_list
  • wait_list 裏面也不能有重複的 URL

viewed_list = []

# 將送過 request 的網址存入 viewed_list

viewed_list.append(url)

# 檢查新網址沒有出現在 wait_list 與 viewed_list

if new_url not in wait_list and new_url not in viewed_list:

wait_list.append(new_url)

55

56 of 92

練習 04: 避免迴圈問題 (5~8 mins)

目標程式:04_crawling_demo2_no_infinite.py

目標網站:https://goo.gl/WfYqY5

目標:避免無窮迴圈的爬取網站的 h1 tag

  • 將需要送 request 的超連結存入等待清單
  • 紀錄送過的 request

56

root

index1

index2

index3

hidden

57 of 92

Summary

  • 圖片爬蟲 & 檔案爬蟲
    • 如果下載檔案被拒絕可以嘗試加上 User-Agent
    • 必要時下載圖片要檢查格式
    • 送 request 之前記得確認路徑是否為絕對路徑
  • 網站爬蟲
    • 超連結網頁裡的超連結網頁也要送 request
    • 紀錄存取過的網頁

57

58 of 92

Outline

  • 靜態網頁以外的爬蟲
    • 圖片爬蟲
    • 檔案爬蟲
    • 網站爬蟲
  • 現實世界的爬蟲
    • 現代網站爬蟲衍生的問題
    • 動態網頁爬蟲

58

59 of 92

href 不全然是你想要的網址

  • <a href=”...”> 並非定義一串網址,而是超連結
  • urljoin 回傳的值並非全部都可以送 request

59

60 of 92

你對 href 夠了解嗎?

  • 對 anchor 送出 request 會拿到同樣的頁面
  • 網頁一般只會用 HTTP 或是 HTTPS 協定
  • 無法對程式碼送出 request

60

href 可能的值

敘述

範例

absolute URL

絕對路徑

https://gushi.tw/

relative URL

相對路徑

/ex1/html1.html

anchor

同一頁面的其他 tag

#top

other protocols

其他協定

mailto://example@gmail.com

JavaScript

程式碼

javascript:console.log(“Hello”)

61 of 92

過濾 href

import re

# 過濾錨點, e.g. #top

check_url_1 = re.match('#.*', url) # True/False

# 過濾其他協定, 只接受 http/https

from urllib.parse import urlparse

check_url_2 = urlparse(url).scheme not in ['https', 'http']

# 過濾程式碼, e.g. javascript:alert();

check_url_3 = re.match('^javascript.*', url)

61

62 of 92

練習 05: 過濾 href (5~10 mins)

目標程式:05_crawling_demo3_filter_href.py

目標網站:https://goo.gl/aYuGNt

目標:過濾不必要的超連結並取得網站的所有 h2 tag

  • 判斷過濾不必要的超連結的時機
    • urljoin 前還是後?

# 觀察下列的值並嘗試送出 request

anchor = urljoin(url, '#top')

protocol = urljoin(url, 'mailto:example@gmail.com')

code = urljoin(url, 'javascript:alert("Hi");')

62

63 of 92

符合絕對路徑的 url 一定沒問題?

  • 符合絕對路徑的 URL 可以送 request
  • 可以送 request 的 URL 不代表是你需要的

63

64 of 92

使用 urlparse 的極限

  • urlparse 可分析的 URL 片段�shceme://netloc/path;params?query#fragmente.g. http://www.facebook.com/twdsconf
  • 完全不一樣的網站仍然可透過判斷 netloc 決定
  • 若是網站有子網站就會被忽略

64

65 of 92

子網域, 網域與後綴

  • 透過 urlparse 取得的 netloc 可以再拆解
  • netloc = 子網域.網域.後綴�e.g. www.facebook.com

65

子網域

網域

後綴

台大首頁 http://www.ntu.edu.tw

台大中文 http://www.cl.ntu.edu.tw

台大法律 http://www.law.ntu.edu.tw

台大化工 http://www.che.ntu.edu.tw

...

66 of 92

透過 tldextract 分析網域

  • 將 netloc 分段成子網域.網域.後綴並不是單純透過 “.” 來切割字串就好
  • 不同的網站可以用 netloc 或是 domain 判斷
  • 子網站用 netloc 無法判斷

66

67 of 92

練習 06: 檢查域名 (5~8 mins)

目標程式:06_crawling_demo4_extract_domain.py

目標網站:https://goo.gl/Zy1qvD

目標:分析網域,只對 www 或是跟原本網址一樣的 sub domain 送出 request

  • 過濾不必要的超連結後再分析域名

# 延伸思考:短網址或是路徑為 ip 拆解的結果?

from tldextract import extract

print(extract('https://goo.gl/z321G7'))

print(extract('https://127.0.0.1:80'))

67

68 of 92

Bonus: Google 短網址服務

  • 網站轉址無法透過 URL 判斷是否屬於相同的網域
  • 需要送一次 request 再從回傳的 response 中取得原始 URL

from tldextract import extract

extract_url = extract(url)

# 判斷是否為 google 短網址

if extract_url.domain == 'goo' or extract_url.suffix == 'gl':

response = requests.get(extract_url)

print(response.url)

68

69 of 92

爬網站的重點回顧

  • 若是要尋訪所有網頁, 要不斷對找到的網址送 request,並且紀錄尋訪過的網址
  • href 的值要經過過濾,取出符合網址格式
  • 分析網址格式與域名確認識自己想要爬的網頁

69

70 of 92

Outline

  • 靜態網頁以外的爬蟲
    • 圖片爬蟲
    • 檔案爬蟲
    • 網站爬蟲
  • 現實世界的爬蟲
    • 現代網站爬蟲衍生的問題
    • 動態網頁爬蟲

70

71 of 92

甚麼是動態網頁?

  • 透過程式變動網頁架構
  • 需要時間載入資料或是不同使用行為等都會透過動態網頁的方式呈現

71

72 of 92

靜態網頁 vs 動態網頁

  • 透過 requests.get 拿到的是靜態網頁
  • 檢視網頁原始碼看到的是靜態網頁
  • 開發者工具 (inspect) 看到的是動態網頁

72

靜態網頁

動態網頁

73 of 92

檢查網頁是靜態還是動態

  • Chrome extension (Quick Javascript Switcher)
  • 透過工具的開關檢查頁面是否有變化

73

關掉 JavaScript

打開 JavaScript

74 of 92

取得程式更新後的網頁

  • 送出 request 之後等到網頁 loading 完成再要求回傳網頁

74

75 of 92

模擬使用者操作瀏覽器的行為

  • Selenium 本來是網頁自動測試工具
  • Selenium 經常被拿來處理動態網頁爬蟲
  • 因為是模擬操作瀏覽器,速度上會比靜態網頁慢

from selenium import webdriver

# 透過指定的瀏覽器 driver 打開 Chrome

driver = webdriver.Chrome('../webdriver/chromedriver')

# 透過瀏覽器取得網頁

driver.get('https://afuntw.github.io/demo-crawling/demo-page/ex4/index1.html')

75

76 of 92

瀏覽器的大小也會影響網頁結構

  • 現代網站根據使用者裝置的大小會有不同的呈現

76

77 of 92

瀏覽器視窗最大化

  • 一開始打開瀏覽器的時候並非視窗最大化
  • 建議一般使用情況都先將瀏覽器視窗最大化

from selenium import webdriver

# 透過指定的瀏覽器 driver 打開 Chrome

driver = webdriver.Chrome('../webdriver/chromedriver')

# 將瀏覽器視窗最大化

driver.maximize_window()

77

78 of 92

Selenium 定位 tag

  • 大致上與 Beautifulsoup 定位 tag 的方法相似
  • find_element_by_id()
  • find_element_by_tag_name()
  • find_element_by_class_name()
  • ...

# e.g. 取得 id='first' 的 tag

id_tag = drver.find_element_by_id('first')

print(id_tag)

78

79 of 92

練習 07: 靜態與動態爬蟲的差異 (5 mins)

目標程式:07_crawling_demo4_selenium.py

目標網站:https://goo.gl/Zy1qvD

目標:分別透過 requests 與 Selenium 爬網站上�id = 'first' 的 tag

  • 開啟瀏覽器並且將視窗最大化
  • 透過 Selenium 定位 tag 並取得文字資料

>>> requests: First featurette heading. It'll blow your mind.

>>> selenium: Rendered by Javascript

79

80 of 92

了解 tag 之間的關係

  • book 是 title 與 author 的 parent
  • title 與 author 都是 book 的 child

<bookstore>

<book>

<title>Harry Potter</title>

<author>K. Rowling</author>

</book>

</bookstore>

80

81 of 92

了解 tag 之間的關係

  • title 與 author 各自為對方的 sibling
  • bookstore, book 都是 title 與 author 的 ancestors
  • book, title, author 都是 bookstore 的 descendant

<bookstore>

<book>

<title>Harry Potter</title>

<author>K. Rowling</author>

</book>

</bookstore>

81

82 of 92

Selenium 定位 tag - XPath

  • Selenium 還可透過類似路徑寫法的 XPath 定位 tag

82

語法

意義

/

從 root 開始選擇

//

從任何地方開始選擇

.

選擇當下這個 node

..

選擇當下這個 node 的 parent node

@

選擇 attribute

*

選擇任何 node

|

OR

83 of 92

XPath 範例

html

body

第三個 div

第一個 div

83

84 of 92

XPath 範例

  • Beautifulsoup 寫法
    • soup.find_all('div')[2].find_all('div')[0]

from selenium import webdriver

# 打開瀏覽器, 視窗最大化, 對目標網址送 request...

# 尋找一個 html > body > div[2] > div[0]

h2 = driver.find_element_by_xpath(� '/html/body/div[2]/div[0]')

84

85 of 92

XPath 範例

from selenium import webdriver

from selenium.webdriver.common.by import By

# 打開瀏覽器, 視窗最大化, 對目標網址送 request...

# 尋找網頁中所有的 p tag

p = driver.find_elements(By.XPATH, '//p')

  • 透過 By 可以更簡單更換定位方式

85

86 of 92

XPath 範例

from selenium import webdriver

from selenium.webdriver.common.by import By

# 尋找任何一個 id = 'first' 的 tag

h2 = driver.find_element(By.XPATH, '//*[@id="first"]')

# 尋找網頁中 id = 'second' 或 'third' 的 h2 tag

p = driver.find_elements(By.XPATH,

'//h2[@id="second"] | //h2[@id="third"]')

86

87 of 92

XPath helper

  • Chrome extension (XPath helper)

87

88 of 92

透過開發者工具取得 XPath

88

89 of 92

練習 08: 透過 XPath 做動態爬蟲 (10 mins)

目標程式:08_crawling_pchome_selenium.py

目標網站:http://24h.pchome.com.tw/region/DHBE

目標:取得頁面上調列商品的名稱與價格 (30 項)

89

90 of 92

Summary

  • 現代網站爬蟲衍生的問題
    • 過濾非必要的 href
    • 解析網域判斷是否要存取該網頁
  • 動態網頁
    • 透過 Selenium 取得程式改變後的網頁結構
    • 透過 XPath 定位 tag

90

91 of 92

爬蟲不一定要爬網頁

  • 有 API (Application Programming Interface) 的話就透過 API 做爬蟲 (e.g. Facebook Graph API)

91

92 of 92

Reference

92