SlideShare a Scribd company logo
1 of 156
Download to read offline
Python Crawling
Chen-Ming Yang (@afun)
● Research Assistant @ Sinica
● Python, R
● Research
○ Data Crawling
○ Deep Learning
README
這份教材包含一些題目與練習網站,需要的話可以參考當解答
● 題目解答: github/Python-Crawling-Tutorial
● 部份範例網站:github/Test-Crawling-Website
爬蟲入門
爬蟲與網頁
介紹
網路爬蟲其實就是一個會自動幫你從網路擷取資訊的程式,
通常會稱呼為 crawler 或是 spider
爬蟲無所不在,一直都有各式各樣的爬蟲在網路上擷取資訊,
也因此在產生了很多非使用者的請求,e.g. 搜尋引擎
網路爬蟲
爬蟲目的
● 做深度學習的時候,缺 training data …
● 做文字探勘的時候,缺文本 data …
● 做輿情分析的時候,缺輿論 data …
● 做第三方平台要比價的時候,缺即時價格資訊 …
在做各種實驗或是分析的時候,總是會遇到缺少資料的時候
不論是否要即時性,或是要大量資料,都有爬蟲的需求
Python 爬蟲
很多程式語言都可以撰寫爬蟲程式,但是因為 Python 學習門檻較低
所以這份 Tutorial 會以 Python 當作主要使用的程式語言
(Python 的中文是大蟒蛇,爬蟲類...,跟爬蟲程式沒有關係!)
雖然 Python 在爬蟲這邊需要用到的概念不難,
但還是建議先熟悉 Python 基本語法之後在來參考這份 Tutorial
基本爬蟲流程
1. 取得網站的 HTML
○ 透過 requests 送出請求取得 HTML
2. 解析資料以取得目標資訊
○ 透過瀏覽器的開發者工具,觀察目標資訊位置
○ 透過 BeautifulSoup 解析 HTML
3. 重複以上過程
爬蟲眼中的網站
網站的組成
網站三本柱
● HTML (架構)
● CSS3 (外觀)
● JavaScript (行為)
HTML (HyterText Markup Language)
HTML 又稱做超文件標記語言,是由一堆預定義好的元素組成階層式架構的文件
元素的組成包含了
● 標籤
● 屬性
● 內容
<標籤 屬性>
內容
</標籤>
HTML 結構
前面提到 HTML 是元素組成階層式架構的文件,
而元素以這種方式組合的樹狀結構,我們又稱為 DOM (Document Object Model)
html
head body
<meta charset="utf-8" />
<title>Page Title<title/>
<h1 id="title">Header<h1/>
元素組成
我們說 HTML 相當於網頁的骨幹,代表網頁的組成架構
而這就是由 DOM 樹決定架構,加上元素的標籤來決定段落用途
元素的組成包含了
● 標籤:通常成對出現,說明元素定義
● 屬性:標籤可以有多種屬性,說明元素性質
● 內容:通常是顯示的文字,說明元素的值
<h1 id="title"> Header <h1/>
還會有各自屬性的值,
有可能代表行為或是外觀
HTML 基本結構介紹
其實這不屬於 HTML 元素,而是
告知瀏覽器 HTML 版本的指令
HTML 的 root,告知瀏覽器這是
HTML 文件
HTML 基本結構介紹
網頁結構主體
定義名稱,metadata,相關文件
連結的地方
HTML 基本結構介紹
網頁上實際樣子
網頁相關定義與設定
網頁主架構
GET 爬蟲
開始寫爬蟲之前
● Github
○ 許多爬蟲程式在 Github 上都有,有時候可以不用全部都重頭開始自己寫
○ e.g. PTT Crawler
● API
○ 許多公司提供 API 讓使用者可以在遵循公司規定的情況下拿到整理後的資料
○ e.g. Facebook Graph API,Google Places API
● 道德規範
○ 爬蟲是一個不斷送請求的過程,而頻繁大量的請求會對網站伺服器造成負擔
○ 雖然非強制性,但請大家練習的時候請遵守規範
○ robots.txt 規範通常會放在網站的根目錄 (e.g. https://www.facebook.com/robots.txt )
○ 詳細的規範可以參考 wiki 與 google 文件
請求種類
我們送請求到對方伺服器其實就像我們寄信到對方家裡,
寄信有很多種方式,請求也有許多種類 (參考 MDN)
其中我們最常用的是 GET 與 POST 這兩種
● GET:請求內容包含在 header,類似寄明信片的方式
● POST:請求內容包含在 body,類似寄平信的方式
body header
參考內容:淺談 HTTP Method
GET 請求
GET 請求會把資料放在 header 傳送,就像寄明信片一樣
資料很容易被看見,所以其實會有安全性的問題
一般操作會使用 GET,但是帳號密碼等隱私性高的資料一般不會用這種方式實作
我們要傳送到對方伺服器的資料
原網址:https://www.mywebsite.com/
請求後網址:https://www.mywebsite.com/form?name=afun
POST 請求
POST 允許在 body 裡放資料,就像是放在信封裡的信件
比起 GET 相對安全,可以傳送的資料也更多
原網址:https://www.mywebsite.com/
請求後網址:https://www.mywebsite.com/ 網址不會改變
補充說明:GET 與 POST 底層都是以 TCP 實作,所以其實這兩者基本上差不多,只
是透過不同的標籤 (HTTP method) 決定實作細節
Status code
平常我們在寄信時也有各種情況,例如收不到信,信件遺失,成功寄送等...
在網路上送請求也會有各種情況發生,我們會用 HTTP 狀態碼來紀錄狀態
然後在回應 response 中附上狀態讓你了解請求是否成功 (參考 MDN)
常用的狀態碼 Status code
● 200 OK
● 403 拒絕存取
● 404 資源不存在
請求查看 html
200
透過比較高階的套件 requests,
可以很簡單的實作 HTTP method (GET/ POST)
import requests
url = 'http://research.sinica.edu.tw/'
response = requests.get(url) # GET 請求
response.encoding = 'utf-8' # 解決中文問題
print(response.text) # HTML 架構
程式 - 發送 GET 請求
程式 - 發送 GET 請求
如果成功收到回應,透過回應的 text 可以取得 HTML 檔案的字串
原始檔案很亂,需要透過解析器 (parser) 幫我們找到有用的資訊
解析網頁
原始 HTML 很難透過單純字串處理取得我們要的資訊,
這時候可以透過 BeautifulSoup 當作解析器 (parser),
之後再根據元素的相關資訊來找會比較方便
parser
尋找所有 p tag
程式 - 解析網頁
BeautifulSoup 背後的解析器有多種選擇,因為速度快跟容錯能力高的優點,
我們這邊選擇使用的是 lxml
from bs4 import BeautifulSoup
# 假設已經送出請求拿到回應 resp
soup = BeautifulSoup(resp.text, 'lxml')
程式 - 解析網頁
當我們要找尋目標元素時,通常會根據標籤或是屬性來定位元素 (官方文件)
soup.p # 尋找網頁中第一個 p tag
soup.find('p') # 尋找網頁中第一個 p tag
soup.find_all('p') # 尋找網頁中所有 p tag
# 尋找網頁中所有 id 是 main 的 p tag
soup.find_all('p', {'id': 'main'})
soup.p.img # 尋找網頁中第一個 p tag 底下的 img tag
soup.p['style'] # 取得 p tag 中 style 屬性的值
soup.p.text # 取得 p tag 中的文字
練習
Website:
http://www.taipeibo.com/yearly/
請觀察並透過爬蟲程式下載
● 當年度週末冠軍排行榜
● requests + Beautifulsoup
● 透過 pandas 輸出成 csv
POST 爬蟲
判斷請求
所有的爬蟲都是從觀察開始,
我們要知道該送出 GET 還是 POST 請求,
才能撰寫對應的程式碼以及對應的資料
觀察 HTTP method 可以透過開發者工具輔助
以 Chrome 為例,按 F12 或是滑鼠右鍵點選檢查
判斷請求
重新整理網頁之後可以透過 Network 分頁觀察請求封包 (範例: 高鐵時刻表)
要觀察的請求
判斷請求
重新整理網頁之後可以透過 Network 分頁觀察請求封包 (範例: 高鐵時刻表)
這邊敘述了封包相關資訊,其中
Request Method 說明了請求的方式
我們可以發現這是個 POST 請求
觀察 POST 需要附上的參數
以高鐵時刻表查詢為例,我們選擇好相關條件之後,
按下「立刻查詢」會送出一個 POST 請求到伺服器
如果爬蟲要實作這個請求,就必須知道 POST 到底送了哪些查詢條件到伺服器
觀察 POST 需要附上的參數
同樣透過開發者工具觀察請求細節
Form data 紀錄了請求所發送的欄位
如果要模擬該請求,就要附上相關資訊
程式 - 發送 POST 請求
與 GET 請求的過程大同小異,只要記得在送出請求的時候附上相關資訊
以高鐵時刻表查詢為例,我們現在知道要附上的欄位資訊
但是像 StartStation 是一連串很像亂碼的片段,
我們必須去觀察 HTML 架構找出相關資訊的位置
程式 - 發送 POST 請求
查找方式可以透過開發者工具的 Inspect 功能找出元素在 HTML 內的位置
然後透過觀察就會發現疑似亂碼的片段其實在元素的屬性裏面就有
程式 - 發送 POST 請求
與 GET 請求的過程大同小異,只要記得在送出請求的時候附上相關資訊
最後在把回傳的 HTML 傳入 parser 定位找出查詢結果的資訊
import requests
url = 'https://www.thsrc.com.tw/tw/TimeTable/SearchResult'
form_data = {
'StartStation': '...', # 填上相關站別的值
'EndStation': '...', # 填上相關站別的值
… # 填上其他資訊
}
response = requests.post(url, data=form_data) # POST 請求
練習
Website:
https://www.thsrc.com.tw/tw/TimeTa
ble/SearchResult
請觀察並透過爬蟲程式下載
● 一個禮拜後的高鐵時刻表
● 台北到台南下午兩點的班次
● requests + Beautifulsoup
● 透過 pandas 輸出成 csv
非文字靜態網
站爬蟲
圖片爬蟲
觀察圖片 Tag
以故事網站為例,我們一樣先透過開發者工具來檢查目標圖片 Tag
Tag 本身不包含圖片資訊,而是以圖片位置代替
文字爬蟲流程
請求查看
html
請求查看
html
文字爬蟲流程
Beautifulsoup
Parse
定位目標 tag
透過 .text 取得文字資訊
圖片爬蟲流程
請求查看
html
請求查看
html
圖片爬蟲流程
Beautifulsoup
Parse
定位目標 tag
透過 src 屬性取得圖片位置
圖片爬蟲流程
請求查看
圖片
請求查看
圖片
程式 - 下載圖片
下載圖片有很多種方式,我這邊以 requests 套件當作範例
# 假設我們已經有圖片網址 image_url
resp = requests.get(image_url, stream=True)
with open('logo.png', 'wb') as f:
# receive 10240 bytes per chunk
for chunk in resp.iter_content(chunk_size=10240):
f.write(chunk)
程式執行結束後,就會把圖片下載下來並存成 logo.png
練習
Website: https://www.pexels.com/
請觀察並透過爬蟲程式下載
● 5 張桌布圖
副檔名的重要性
1. 副檔名是讓電腦決定要用甚麼軟體開啟檔案的提示
2. 更改副檔名不等於轉檔
3. 副檔名錯誤就無法正確開啟檔案
logo.jpg logo.png
使用 jpg 格式
讀取圖片
更改副檔名
使用 png 格式
讀取圖片
實際檔案格式
還是 jpg
網路圖床
除了網站本身的圖片以外,有些網站提供用戶貼圖床的網址供預覽 e.g. PTT
備註:
i.imgur.com/q6fMyz9.jpg
是圖片
imgur.com/q6fMyz9
是網站
眼見不為憑
http://i.imgur.com/q6fMyz9.jpg
眼見不為憑
http://i.imgur.com/q6fMyz9.png
眼見不為憑
http://i.imgur.com/q6fMyz9.gif
程式 - 圖片格式資訊
為了要用正確的副檔名存檔,我們必須下載下來之後先判斷圖片格式
這邊可以藉由 PIL.Image 來判斷格式
from PIL import Image
resp = requests.get(image_url, stream=True)
image = Image.open(resp.raw)
print(image.format) # e.g. JPEG
# 假設我們重新組合圖片檔名與副檔名 logo.jpeg 之後
image.save('logo.jpeg') # 儲存圖片
練習
Website:
Test-Crawling-Website/portfolio
請觀察並透過爬蟲程式下載
● 5 張圖片
● 請依正確格式儲存圖片
檔案爬蟲
超連結檔案
檔案在網站上呈現的方式除了單純的文字超連結以外,還有圖片超連結
檔案爬蟲跟圖片爬蟲的流程基本上一樣,只是目標 tag 變為 <a> tag
e.g. 台大考古題下載
圖片超連結檔案
超連結檔案
透過開發者工具查看可以得知這個結構是 <a> tag 裏面包著 <img> tag
而 <a> tag 是我們的目標主體,<img> tag 只是單純代表 PDF icon
定位策略
我們知道 BeautifulSoup 可以在 tag 裏面再次搜尋,
一般的策略都是透過定位目標 tag 的上一層,藉此縮小搜尋範圍再搜尋
可是還是會有跟目標 tag 同一層同時存在其他不需要的超連結
這種情況通常就需要額外花時間去解析結構
檔案敘述 1
檔案敘述 2
檔案敘述 3
檔案敘述 4
檔案敘述 5
e.g. 目標僅有 PDF 檔案
程式 - 定位策略
由於 HTML5 的架構類似家族樹的概念,<a> tag 可以視作 <img> tag 的 parent
因此除了由外而內縮小範圍尋找,我們也可以透過定位 PDF icon 由內往外找
images = soup.find_all('img', {
'src': re.compile('application-pdf.png')
}
for image in images:
# 透過 parent method 尋訪 img tag 的上一層 tag
print(image.parent['href'])
檔案的絕對路徑與相對路徑
前面提到圖片的時候我們知道圖片路徑在 <img> tag 的 src 屬性
而現在我們知道超連結檔案的檔案路徑在 <a> tag 的 href 屬性
上述兩者皆代表了檔案位置,同樣都有以下這兩種表達方式
● 絕對路徑,e.g. http://exam.lib.ntu.edu.tw/graduate
● 相對路徑,e.g. /exam/sites/all/themes/ntu/logo.png
檔案的絕對路徑與相對路徑
● 無法直接對相對路徑送 request
ext
ext
ext
A.com
B.com
C.com
我在 /ext/file.pdf
哪個 /ext ???
檔案的絕對路徑與相對路徑
硬要對相對路徑送請求的話,會看到類似下面的 error message
url =
'/exam/sites/all/modules/pubdlcnt/pubdlcnt.php?file=http://140.11
2.115.12/exam/sites/default/files/exam/graduate/106g/106_graduate
_4.pdf&nid=5814'
resp = requests.get(url)
轉換路徑 - 相對路徑轉為絕對路徑
外部請求之所以不知道相對路徑的位置是因為不知道 root
● 相對路徑:我在門口進來右轉的地方
● 絕對路徑:我在從中研院門口進來右轉的地方
以上面例子來說,「中研院」就是 root,
有了 root 這個本體就可以組合相對路徑為絕對路徑
轉換路徑 - 相對路徑轉為絕對路徑
● 相對路徑:/exam/sites/all/modules/pubdlcnt/pubdlcnt.php
● 絕對路徑:http://exam.lib.ntu.edu.tw/graduate
組合後的路徑
:http://exam.lib.ntu.edu.tw/exam/sites/all/modules/pubdlcnt/pubdlcnt.php
程式- 轉換路徑
雖然我們也可以透過字串處理組合出絕對路徑,
但是 URL 路徑有其意義,建議透過 urllib.parse.urljoin 組合
from urllib.parse import urljoin
# 假設已經取得絕對路徑 abs_path 與相對路徑 rel_path
print(urljoin(abs_path, rel_path))
解析 URL
透過原始碼可以得知 urljoin 內部是透過 urllib.parse.urlparse 實作
將 URL 拆解成數個有意義的片段再去組合
<scheme>://<netloc>/<path>;<params>?<query>#<fragment>
其他下載檔案方式 (圖片適用)
有些人會建議使用 urllib.request.urlretrieve 來下載檔案
因為使用方式很簡單,只要給網址跟要存的檔案名稱即可
urlretrieve(url, file_name)
但我這邊舉的範例都會是以 requests 為主,官網也是這麼建議
基本反爬蟲 - 身份識別 User-Agent
當我們對網站送出請求時,其實會送出很多其他的資訊,包含身份識別
透過程式送的話,一般來說不會包含身份識別
沒有身份
拒絕回傳
請求查看
html
請求查看
html
查詢自己的身份
● User-Agent 是一段描述瀏覽器使用的系統,平台,版本等資訊的字串
查詢自己的身份
● User-Agent 是一段描述瀏覽器使用的系統,平台,版本等資訊的字串
重新整理網頁,觀察瀏覽器送出的請求
查詢自己的身份
● User-Agent 是一段描述瀏覽器使用的系統,平台,版本等資訊的字串
程式 - 偽裝身份送出請求
為了反反爬蟲,我們需要額外在送出請求時加上身份識別,
透過 requests 的實作如下
headers = {'User-Agent': my_user_agent}
resp = requests.get(url, headers=headers)
另外,Python 也有 fake-useragent 可以隨機產生各種身份
其實並不用每次都去觀察瀏覽器實際 User Agent
練習
Website:
http://exam.lib.ntu.edu.tw/graduate
請觀察並透過爬蟲程式下載
● 附上 User-Agent
● 第一個頁面所有 pdf 考古題
網站爬蟲
網站結構
網站是一堆檔案以階層式的方式組合的集合
rootgushi.tw
image
js
article
github.com
ntu.edu.tw
index
history
index
瀏覽網站行為
不管是圖片或是網頁都只是一份文件,
瀏覽網站其實只是透過網路呼叫遠端電腦的檔案
從首頁到「全部文章」的頁面
瀏覽網站行為
rootgushi.tw
image
js
article
github.com
ntu.edu.tw
index
history
index
首頁
全部文章
遍歷網站 - 從網頁到網頁
網頁之間的連結也是透過超連結,
透過開發者工具查看可以發現同樣是透過 <a> tag
首頁 首頁
請求查看
html
請求查看
html
遍歷網站 - 從網頁到網頁
網頁之間的連結也是透過超連結,
透過開發者工具查看可以發現同樣是透過 <a> tag
首頁
parse
BeautifulSoup
定位
「全部文章」檔
案路徑
遍歷網站 - 從網頁到網頁
網頁之間的連結也是透過超連結,
透過開發者工具查看可以發現同樣是透過 <a> tag
全部文章 全部文章
請求查看
html
請求查看
html
遍歷網站 - 概念 (1)
第一個非常直覺的想法是透過 loop
將所有網址的超連結都送出 request
history
root
index2
index3
從 root 開始搜尋
超連結
搜尋到新網頁
搜尋到新網頁
遍歷網站 - 問題 (1)
但這樣無法發現其他網頁的超連結
history
root
index2
index3
從 root 開始搜尋
超連結
搜尋到新網頁
搜尋到新網頁
hidden 僅能從 index3
連結
遍歷網站 - 概念 (2)
為了解決遍歷其他網頁裡的超連結,有兩個很直覺的作法
● recursive 遍歷
● 建立清單紀錄待尋訪的網址,再對待尋訪清單的網址做 loop
遍歷網站 - 問題 (2)
但是現代網站通常都會有「導覽列」的設計,而這很容易會造成無窮迴圈
遍歷網站 - 概念 (3)
為了預防無窮迴圈的問題,通常還會紀錄我們尋訪過的網址
設想多一點的會連網頁上次更改的時間 Last Modified 或是 ETag 等資訊都紀錄
相關細節可以參考循序漸進理解 HTTP Cache 機制這篇部落格文章
練習
Website:
Test-Crawling-Website/blog
請觀察並透過爬蟲程式下載
● 網站上每個頁面的標題 (h1)
● 附上 User-Agent
練習 (進階)
Website:
Test-Crawling-Website/portfolio
請觀察並透過爬蟲程式下載
● 下載 2018/01/29 14:39:10 之後
修改過的圖片
● 附上 User-Agent
● 請依正確格式儲存圖片
備註:要了解 Last-Modified 與 ETag
之間的差異
網站爬蟲
延伸問題
網址以外的合法超連結
超連結 <a> tag 的定義並非是一串網址,所以實際爬蟲時
有可能會有不適合送出請求的超連結
除了前面提到的相對路徑以外,我們更應該要注意 <a> tag 中的 href 屬性,
而且 urljoin 的回傳結果並不會過濾
合法超連結
參考 w3schools 的文件
href value 敘述 範例
absolute URL 絕對路徑 https://gushi.tw
relative URL 相對路徑 /ex1/index1.html
anchor 其他 tag #top
other protocols 其他協定 mailto://example@gmail.com
JavaScript 程式碼 javascript:console.log("Hello")
合法超連結 - anchor
anchor 通常使用在同一個頁面的內的超連結,
重複對 anchor 送出請求是多餘的操作
● 頁面置頂
● 定位文章標題
● …
合法超連結 - Other Protocol
Protocol 稱為協定,在網路上各種不同的傳輸都有不同的協定
一般熟知的 http 與 https 就是其中一種,而其他協定也有可以是超連結目標
● 信箱 mailto://
● 文件 ftp://
● …
合法超連結 - JavaScript
JavaScript 是現代網站很常使用的程式語言之一,
雖然不常在網頁中看到這種寫法,但有可能會是反爬蟲手段之一
將 <a> tag 透過 CSS 隱藏,然後在 href 屬性呼叫 JS function,
因為一般使用者看不到不會去點擊,
但如果透過程式對該超連結送出請求,即可判定為爬蟲程式將之拒絕
程式 - 過濾超連結
我們這邊可以很簡單的透過 regular expression 跟 urlparse 來過濾
練習的話可以參考線上工具做測試
# anchor
check1 = re.match('.*#.*', href)
# protocol
check2 = re.match('[^http|https].*', urlparse(href).scheme)
# JS
check3 = re.match('javascript.*', href)
小結 - 路徑處理
關於路徑處理的部份,到目前為止我們知道要考慮
● 相對路徑轉為絕對路徑
● 過濾 anchor,others protocol,JavaScript
經過上面這些處理之後,我們確認我們即將要送的是合法的網址
但是實際上在爬蟲時,還有其他方面的因素需要考慮
目標以外的合法網址
實際上在爬蟲時,根據網站類型的不同,可能還會有以下幾種狀況
● 內嵌 Youtube 網站影片
● 內嵌多種社群軟體的連結
○ 粉絲專頁
○ 討論區
目標以外的合法網址
試想你的爬蟲流程,當我們遇到站外網站時
因為是沒有尋訪過的網址,所以程式會試著送出請求
但我們硬體有限,很容易就會造成災難
rootfoundation.datasci.tw
image
js
facebook.com
index
我只要爬
foundation.datasci.tw
爬蟲程式會嘗試爬取
facebook.com
過濾合法網址
我們前面有提到 URL 有其意義,可以使用 urlparse 解析並判斷
<scheme>://<netloc>/<path>;<params>?<query>#<fragment>
目標網站 http://foundation.datasci.tw/
目標網站粉絲專頁 http://www.facebook.com/twdsconf
上述情況可以透過 urlparse 直接判斷 <netloc> 決定是否爬蟲
過濾合法網址
但是 urlparse 並無法理解更細節的網域
<scheme>://<netloc>/<path>;<params>?<query>#<fragment>
台大首頁 http://www.ntu.edu.tw/
台大中文系首頁 http://www.cl.ntu.edu.tw/
上述範例 <netloc> 會視為不同位置,但實際上這邊可以在細分成
● 子網域 subdomain
● 網域 domain
● 後綴 suffix
過濾合法網址
網域有各種種類,e.g. 通用/ 國家地區/ 頂級域名,二級域名等...
網路位置也有各種意義,並無法單純透過 . 將其分割
這邊我們可以使用 tldextract 來協助處理
練習
Website: http://aiacademy.tw/
請觀察並透過爬蟲程式下載
● 紀錄所有 URL
● 附上 User-Agent
動態網站爬蟲
靜態與動態網站
靜態網站與動態網站的爬蟲過程基本都一樣,前面提到要注意的地方也都一樣
但是動態網站的意思是網站會透過程式改變結構與內容
請求查看
首頁
請求查看
首頁
靜態與動態網站
靜態網站與動態網站的爬蟲過程基本都一樣,前面提到要注意的地方也都一樣
但是動態網站的意思是網站會透過程式改變結構與內容
JS render
靜態與動態網站
一般來說有時間性的網站都會是以動態網站的方式實作,例如電商網站,
像是熱賣商品,搶購商品等等之類的可能每天內容都不一樣
e.g. 蝦皮購物網,全站都是動態網站
動態網站延伸的額外問題
我們透過瀏覽器看到的靜態網站跟程式抓回來的 HTML 會是一樣的
但是動態網站則不一樣,在程式改變網站結構的過程中會有時間差
動態網站產生的問題
● 所見非所得
● 有些需要爬的內容要透過程式改變結構才會出現
● requests.get 拿到的網頁是還沒被程式更改過的內容
檢視網站
● 開發者工具:隨時顯示當前頁面的 HTML (動態網站)
● 檢視網頁原始碼:顯示原始 HTML (靜態網站)
網頁原始碼開發者工具
檢測網站
可以透過前面提到的差異來判斷網站是否屬於動態網站
● 比較網頁原始碼與開發者工具顯示的差異
● 關閉 Chrome javascript 檢查網頁變化
○ e.g. Quick Javascript Switcher
動態網站爬蟲
動態網站的問題是程式還沒有更改網站結構就回傳 HTML,
非常直覺的概念就是請網站程式更改完畢再回傳 HTML
How ?
● Selenium
● Headless Chrome
● Splash
● …
現在很多服務都可以提供程式 render 後的 HTML,但這邊以 Selenium 介紹為主
Selenium
Selenium 其實是網頁自動化測試工具,所以他可以透過程式操作瀏覽器
也因此可以透過瀏覽器取得 render 後的 HTML
● 透過 webdriver 控制各大瀏覽器
● 因為是模擬瀏覽器操作,所以會比靜態網站爬蟲還要慢
# 假設要透過 Chrome webdriver 操作 Chrome
from selenium import webdriver
driver = webdriver.Chrome(path_to_webdriver)
driver.get(url)
webdriver
webdriver 其實是各大瀏覽器自己出的,請注意 Python 操作的 webdriver 版本與系
統上安裝的瀏覽器版本相容
● Chrome webdriver
● FireFox webdriver
● …
另外要注意的是,控制不同的瀏覽器,selenium 的方法會有些許差異
這份教材會以 Chrome 為範例
Selenium 定位 Tag
我們透過 Selenium 拿到 render 好的 HTML 後有兩種選擇
● 跟之前一樣把 HTML 傳入 Beautifulsoup 定位
● 直接用 Selenium 定位
這邊會介紹 Selenium 的定位方式提供參考,
因為其中有 Beautifulsoup 沒有的定位方式
Selenium 定位 Tag
Selenium 的定位方式跟 Beautifulsoup 大同小異,
基本上只是 function name 與回傳物件不太相同而已
● find_element_by_id
● find_element_by_tag_name
● find_element_by_class_name
tag = driver.find_element_by_id('first')
print(tag)
XPath
XPath 是一種類似路徑寫法的定位方式,Selenium 支援但 Beautifulsoup 沒有
語法 意義
/ 從 root 的地方開始選擇
// 從任意地方選擇
. 選擇當下這個 node
.. 選擇當下這個 node 的 parent node
@ 選擇 tag 屬性
* 選擇任何 node
| OR
XPath 與 Beautifulsoup 的定位
比較 XPath 的寫法與前面使用 Beautifulsoup 定位的差異
# Beautifulsoup
soup.find_all('div')[2].find_all('div')[0]
# Selenium XPath
driver.get_elements_by_xpath(
'/html/body/div[2]/div[0]'
)
程式 - XPath
透過 Selenium 的 By 可以更簡單的更換定位方式
from selenium.webdriver.common.by import By
# 尋找所有 p tag
driver.find_elements(By.XPATH, '//p')
# 尋找任意 id 為 first 的 tag
driver.find_elements(By.XPATH, '//*[@id="first"]')
# 尋找任意 id 為 second 或 third 的 h2 tag
driver.find_elements(By.XPATH,
'//h2[@id="second"] | //h2[@id="third"]'
)
取得 XPath
● 開發者工具
○ tag 按右鍵 > Copy > Copy XPath
○ Chrome 與 Firefox 都支援
透過這種方式你會取得只針對該 tag 的 XPath 寫法
如果你是希望根據條件取得所有 tag
可以考慮透過這種方式取得之後再修改
取得 XPath
這邊會有些可以協助你測試的擴充套件,e.g. XPath helper
測試 XPath 測試結果
XPath Helper 會對符合條件的
tag 做 hightlight
Responsive Web Design (RWD)
RWD 是響應式網站設計的縮寫,
設計目的是為了讓網站在不同大小的裝置都可以有很好的使用體驗
因此,在不同大小的裝置中,會呈現不一樣的 HTML
Responsive Web Design (RWD)
為了所見即所得,建議用 selenium 模擬瀏覽器時可以做視窗最大化
from selenium import webdriver
driver = webdriver.Chrome(path_to_webdriver)
driver.maximize_window()
程式 - Implicit 請求等待
Selenium 的最常使用的等待有 Implicit 與 Explicit 兩種
Implicit Wait 通常適用於整個 Selenium 程式的預設等待,
當網頁元件暫時找不到時會進行等待,直到 timeout
● 程式執行期間,每次執行尋找指令時都會進行 n 秒等待
● Implicit wait 在瀏覽器開啟期間都會運作
driver = webdriver.Chrome(path_to_webdriver)
driver.implicity_wait(10) # 程式最多等 10 秒
程式 - Implicit 請求等待
driver = webdriver.Chrome(path_to_webdriver)
driver.implicity_wait(10) # 最多等 10 秒
最多等 10 秒就
要回傳 HTML
2 秒就好了,馬
上回傳
請求查看
首頁
請求查看
首頁
首頁 首頁
程式 - Implicit 請求等待
driver = webdriver.Chrome(path_to_webdriver)
driver.implicity_wait(10) # 最多等 10 秒
最多等 10 秒就
要回傳 HTML
10 秒到了,不
管怎樣先回傳
請求查看
首頁
請求查看
首頁
首頁 首頁
程式 - Explicit 請求等待
另外一種等待是更明確的 Explicit Wait,宣告一個等待物件之後給予條件
Selenium 本身提供很多種條件供調用,但如果有需求也可以自定義條件
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
el = wait.until(EC.presense_of_element_located(
By.XPATH, '//div[@class="count" and text()]'
))
程式 - Implicit 請求等待
Explicit wait 主要針對特殊元件,或是需要等待某種屬性的狀態等,在精確的等待與
判斷時較適用 e.g. 是否可以被點擊,是否可見
符合條件的 tag 好了
就回傳,最多等十秒
10 秒到了,不
管怎樣先回傳
請求查看
首頁
請求查看
首頁
首頁 首頁
Implicit 與 Explicit 的比較
● Implicit 實作較簡單,而且只要宣告一次
● Implicit wait 只作用在 find elements,無法檢查屬性
● Implicit wait 無法客製化,非預期的顯示時間可能會被忽略
● Implicit wait 沒有實際定義行為
● 推荐使用 Explicit wait,雖然實作上會比較複雜
● 不建議混用 Implicit 與 Explicit wait
參考來源:Implicit Wait 與 Explicit wait 的區別,Selenium with Python
練習
Website:
Test-Crawling-Website/gallery
請觀察並透過爬蟲程式下載
● 檢查該網站是否為動態網站
● 透過 Selenium 下載圖片
● 視窗最大化
● 設定 Implicit Wait
● 透過 XPath 定位圖片
練習
Website:
http://24h.pchome.com.tw/region/D
HBE
請觀察並透過爬蟲程式下載
● 檢查該網站是否為動態網站
● 使用 Selenium
● 視窗最大化
● 設定 Implicit Wait
● 透過 XPath 定位
● 取得商品的品項與價格資訊
練習 (進階)
模擬 google search
● https://www.google.com.tw/
● 搜尋「人工智慧」
● 紀錄前兩頁搜尋結果的連結
Reference: Selenium Action
練習 (進階)
簡易下載 reCAPTCHA v1 圖片
● https://www.google.com/recapt
cha/demo/recaptcha
● 使用 Selenium
● 使用 Implicit wait + time.sleep
● 檢查圖片格式
● 下載五張圖片
Facebook
爬蟲
Graph API
基本概念
Graph API - 不用每次都硬爬一發
為了讓資料有更多的應用,部份公司會提供 API 給予整理好的資料
開發者可以很方便的調用資料,並且可以不用花太多時間擔心反爬蟲策略
Facebook 提供的 API 名稱為 Graph API
代表你的身份,提供給 Facebook 驗證用 搜尋條件
搜尋欄位 搜尋結果
身份驗證 Access Token
1. Facebook 中的 Access Token 可以代表用戶,粉絲專頁,或是應用程式的身份
2. 我們在送出搜尋的請求時必須附上 Access Token 才會被視為是有效的請求
3. 根據身份的不同,能搜尋到的資料也不同 (權限管理)
一般來說這都會是短期 Token
但可以申請延長到 60 天
若是粉絲專頁甚至可以取得永久 token
身份驗證 Access Token
Access Token 種類有好幾種,使用情境與機制等細節可以參考官方文件
一般來說要爬單一粉絲專頁的內容,使用短期 Access Token 應該就夠了
如果有使用永久粉絲專頁 Access Token 的需求可以參考這個部落格文章
從官方文件裏面可以得知 Graph 其實就是社交圖表的意涵,
我們可以把他想成社交網路的圖,包含
● 節點 Nodes,e.g. 用戶 / 相片 / 文件 / 回應等...
● 邊緣 Edges,節點之間的關聯,e.g. 用戶的相片,文件的回應...
● 欄位 Fields,節點的資訊,e.g. 用戶的生日,社團的名稱...
所謂 Graph API 的 Graph
Graph API 版本
API 有多種版本使用,幾乎所有的 API 請求都會往 graph.facebook.com 傳遞,除了
上傳影片的請求以外
● 每個版本至少在 2 年內都可以使用,並且不會修改
● 平台變更紀錄,版本詳細資訊
HTTP GET 請求
我們只需要對 Graph API 發出 HTTP GET 請求就可以讀取節點跟關係連線,
通常還要附上 Access Token 讓 Graph API 判斷權限,回傳相關結果
Graph API HTTP GET 所需資訊
● Graph API 版本 X.Y,e.g. 2.10
● 節點或邊緣編號
● 搜尋條件
graph.facebook.com/vX.Y/{id}?{query-request}
取得目標對象身份
以柯文哲粉絲專頁為例,我們要爬專頁內容時,必須先找出該專頁的身份 id
取得網址:
https://www.facebook.com/DoctorKoWJ/
兩者擇一
1. 將網址貼到搜尋條件的欄位
2. 觀察網址,將 DoctorKoWJ 貼到
搜尋條件的欄位 (建議)
取得目標對象身份
以柯文哲粉絲專頁為例,我們要爬專頁內容時,必須先找出該專頁的身份 id
目標對象身份 id
取得目標對象身份
如果是以文章為對象的話也一樣,但無法直接將網址貼上搜尋條件欄位取得 id
取得網址:
https://www.facebook.com/DoctorKoWJ/v
ideos/1213927345375910/
觀察網址並嘗試可發現文章 id 即為
1213927345375910
取得留言內容
現今很多粉絲專頁會透過要求留言來與粉絲互動
● 留言抽獎,Tag 朋友抽獎
● 留言會給英文單字本,旅遊資訊等...
不論是把留言版當作簽到版,或是單純蒐集留言意見,
取得留言內容一直都是非常頻繁的需求
取得留言內容
Facebook 留言的 Field name 稱作 comments
當文章是 root node 的時候,comments 指的是文章回覆
comments
取得留言內容
Facebook 留言的 Field name 稱作 comments
當留言是 root node 的時候,comments 指的是留言回覆
comments
取得留言內容 - 透過 Graph API 檢視
這邊我們先簡化題目為「爬取文章的所有留言,不包含留言回覆」
假設我們要爬的文章是
https://www.facebook.com/DoctorKoWJ/videos/1213927345375910/
先透過 Graph API 給定文章 id 搜尋 comment 欄位檢視
取得留言內容 - HTTP GET request
根據前面提到的 HTTP GET 方式,我們可以知道請求的格式與所需資訊
version = 'v2.11'
id = '1213927345375910'
query = 'fields=comments&access_token={}'.format(access_token)
url = https://graph.facebook.com/{}/{}?{}.format(
version, id, query
)
取得留言內容 - HTTP GET request
當我們對 graph.facebook.com 發送合法請求時,
對方會把分頁結果以 json 字串格式回傳
# method 1: 透過 response 物件的 json method 轉成 dict
resp = requests.get(url)
data = resp.json()
# method 2: 透過 json module 把 json 字串轉成 dict
import json
data = json.loads(resp.text)
取得留言內容 - 游標型分頁
當我們對文章節點發出 API 請求時,
有可能會因為結果有上千筆,所以系統會做分頁效果回傳給你部份資訊
游標型分頁是有效的分頁方法,但你不應該儲存游標而是每次透過程式決定
游標會指向一個隨機字串,
用來代表清單中的特定項目
判斷是否有下個分頁來決定是否繼續送出請求
取得留言內容 - 回傳物件
一開始我們送出的請求是要讀取文章資訊
graph.facebook.com/v2.11/{node-id}?field=comments&...
但是當結果太多而回傳分頁資訊時,next 的請求是讀取文章的 comments 資訊
graph.facebook.com/v2.11/{node-id}/comments?...
取得留言內容 - 回傳物件
因為兩者要求讀取的資訊不同,所以回傳的物件也不同
這邊建議可以一開始就要求讀取文章 comments 資訊
要求讀取文章資訊 要求讀取文章 comments 資訊
取得留言內容 - 整理資料
最後可以透過 pandas 將資料輸出程表格
再以 csv 或是其他格式儲存,方便之後分析
練習
https://www.facebook.com/DoctorK
oWJ/videos/1213927345375910/
透過 Graph API 做 Facebook 爬蟲
● 抓取文章底下的所有留言
● 輸出成 CSV
練習
https://www.facebook.com/DoctorK
oWJ
透過 Graph API 做 Facebook 爬蟲
● 抓取粉絲專頁所有文章
○ 按讚與分享數
○ 標題與內文
● 輸出成 CSV
練習 (進階)
https://www.facebook.com/appledail
y.tw/posts/10156769966527069
透過 Graph API 做 Facebook 爬蟲
● 抓取文章所有留言
○ message
○ attachement
○ application
● 輸出成 CSV

More Related Content

What's hot

大規模データ時代に求められる自然言語処理
大規模データ時代に求められる自然言語処理大規模データ時代に求められる自然言語処理
大規模データ時代に求められる自然言語処理Preferred Networks
 
{tidygraph}と{ggraph}による モダンなネットワーク分析(未公開ver)
{tidygraph}と{ggraph}による モダンなネットワーク分析(未公開ver){tidygraph}と{ggraph}による モダンなネットワーク分析(未公開ver)
{tidygraph}と{ggraph}による モダンなネットワーク分析(未公開ver)Takashi Kitano
 
三角関数(人間科学のための基礎数学)
三角関数(人間科学のための基礎数学)三角関数(人間科学のための基礎数学)
三角関数(人間科学のための基礎数学)Masahiro Okano
 
統計学の基礎の基礎
統計学の基礎の基礎統計学の基礎の基礎
統計学の基礎の基礎Ken'ichi Matsui
 
F#によるFunctional Programming入門
F#によるFunctional Programming入門F#によるFunctional Programming入門
F#によるFunctional Programming入門bleis tift
 
プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造Takuya Akiba
 
Tokyo.R 41 サポートベクターマシンで眼鏡っ娘分類システム構築
Tokyo.R 41 サポートベクターマシンで眼鏡っ娘分類システム構築Tokyo.R 41 サポートベクターマシンで眼鏡っ娘分類システム構築
Tokyo.R 41 サポートベクターマシンで眼鏡っ娘分類システム構築Tatsuya Tojima
 
Real-time personalized recommendation using embedding
Real-time personalized recommendation using embeddingReal-time personalized recommendation using embedding
Real-time personalized recommendation using embeddingRecruit Lifestyle Co., Ltd.
 
Palindromic tree
Palindromic treePalindromic tree
Palindromic tree__math
 
AtCoder Regular Contest 039 解説
AtCoder Regular Contest 039 解説AtCoder Regular Contest 039 解説
AtCoder Regular Contest 039 解説AtCoder Inc.
 
合成変量とアンサンブル:回帰森と加法モデルの要点
合成変量とアンサンブル:回帰森と加法モデルの要点合成変量とアンサンブル:回帰森と加法モデルの要点
合成変量とアンサンブル:回帰森と加法モデルの要点Ichigaku Takigawa
 
UI/UXが無意識に検索行動に与える影響について
UI/UXが無意識に検索行動に与える影響についてUI/UXが無意識に検索行動に与える影響について
UI/UXが無意識に検索行動に与える影響についてTairo Moriyama
 
5分でわかるかもしれないglmnet
5分でわかるかもしれないglmnet5分でわかるかもしれないglmnet
5分でわかるかもしれないglmnetNagi Teramo
 
ランク6の俺がパズドラについて語る
ランク6の俺がパズドラについて語るランク6の俺がパズドラについて語る
ランク6の俺がパズドラについて語るtosaka 2
 
公共交通データを可視化するQGIS演習
公共交通データを可視化するQGIS演習公共交通データを可視化するQGIS演習
公共交通データを可視化するQGIS演習Masaki Ito
 
様々な全域木問題
様々な全域木問題様々な全域木問題
様々な全域木問題tmaehara
 
for関数を使った繰り返し処理によるヒストグラムの一括出力
for関数を使った繰り返し処理によるヒストグラムの一括出力for関数を使った繰り返し処理によるヒストグラムの一括出力
for関数を使った繰り返し処理によるヒストグラムの一括出力imuyaoti
 
機械学習概論 講義テキスト
機械学習概論 講義テキスト機械学習概論 講義テキスト
機械学習概論 講義テキストEtsuji Nakai
 
At coder regular contest 013 解説
At coder regular contest 013 解説At coder regular contest 013 解説
At coder regular contest 013 解説光喜 濱屋
 

What's hot (20)

大規模データ時代に求められる自然言語処理
大規模データ時代に求められる自然言語処理大規模データ時代に求められる自然言語処理
大規模データ時代に求められる自然言語処理
 
{tidygraph}と{ggraph}による モダンなネットワーク分析(未公開ver)
{tidygraph}と{ggraph}による モダンなネットワーク分析(未公開ver){tidygraph}と{ggraph}による モダンなネットワーク分析(未公開ver)
{tidygraph}と{ggraph}による モダンなネットワーク分析(未公開ver)
 
三角関数(人間科学のための基礎数学)
三角関数(人間科学のための基礎数学)三角関数(人間科学のための基礎数学)
三角関数(人間科学のための基礎数学)
 
統計学の基礎の基礎
統計学の基礎の基礎統計学の基礎の基礎
統計学の基礎の基礎
 
F#によるFunctional Programming入門
F#によるFunctional Programming入門F#によるFunctional Programming入門
F#によるFunctional Programming入門
 
プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造
 
Tokyo.R 41 サポートベクターマシンで眼鏡っ娘分類システム構築
Tokyo.R 41 サポートベクターマシンで眼鏡っ娘分類システム構築Tokyo.R 41 サポートベクターマシンで眼鏡っ娘分類システム構築
Tokyo.R 41 サポートベクターマシンで眼鏡っ娘分類システム構築
 
Real-time personalized recommendation using embedding
Real-time personalized recommendation using embeddingReal-time personalized recommendation using embedding
Real-time personalized recommendation using embedding
 
Palindromic tree
Palindromic treePalindromic tree
Palindromic tree
 
AtCoder Regular Contest 039 解説
AtCoder Regular Contest 039 解説AtCoder Regular Contest 039 解説
AtCoder Regular Contest 039 解説
 
合成変量とアンサンブル:回帰森と加法モデルの要点
合成変量とアンサンブル:回帰森と加法モデルの要点合成変量とアンサンブル:回帰森と加法モデルの要点
合成変量とアンサンブル:回帰森と加法モデルの要点
 
UI/UXが無意識に検索行動に与える影響について
UI/UXが無意識に検索行動に与える影響についてUI/UXが無意識に検索行動に与える影響について
UI/UXが無意識に検索行動に与える影響について
 
5分でわかるかもしれないglmnet
5分でわかるかもしれないglmnet5分でわかるかもしれないglmnet
5分でわかるかもしれないglmnet
 
ランク6の俺がパズドラについて語る
ランク6の俺がパズドラについて語るランク6の俺がパズドラについて語る
ランク6の俺がパズドラについて語る
 
公共交通データを可視化するQGIS演習
公共交通データを可視化するQGIS演習公共交通データを可視化するQGIS演習
公共交通データを可視化するQGIS演習
 
様々な全域木問題
様々な全域木問題様々な全域木問題
様々な全域木問題
 
for関数を使った繰り返し処理によるヒストグラムの一括出力
for関数を使った繰り返し処理によるヒストグラムの一括出力for関数を使った繰り返し処理によるヒストグラムの一括出力
for関数を使った繰り返し処理によるヒストグラムの一括出力
 
機械学習概論 講義テキスト
機械学習概論 講義テキスト機械学習概論 講義テキスト
機械学習概論 講義テキスト
 
At coder regular contest 013 解説
At coder regular contest 013 解説At coder regular contest 013 解説
At coder regular contest 013 解説
 
Convex Hull Trick
Convex Hull TrickConvex Hull Trick
Convex Hull Trick
 

Similar to Python crawling tutorial

JCConf 2015 TW 高效率資料爬蟲組合包
JCConf 2015 TW 高效率資料爬蟲組合包JCConf 2015 TW 高效率資料爬蟲組合包
JCConf 2015 TW 高效率資料爬蟲組合包書豪 李
 
Django入门
Django入门Django入门
Django入门oikomi
 
Py ladies 0928
Py ladies 0928Py ladies 0928
Py ladies 0928Yen_CY
 
探索海量文章,Elasticsearch 建置和應用
探索海量文章,Elasticsearch 建置和應用探索海量文章,Elasticsearch 建置和應用
探索海量文章,Elasticsearch 建置和應用Yi-Wei Lai
 
廣宣學堂Python金融爬蟲原理班 20170416
廣宣學堂Python金融爬蟲原理班 20170416廣宣學堂Python金融爬蟲原理班 20170416
廣宣學堂Python金融爬蟲原理班 20170416Paul Chao
 
Twitter 與 ELK 基本使用
Twitter 與 ELK 基本使用Twitter 與 ELK 基本使用
Twitter 與 ELK 基本使用Mark Dai
 
Recycle Open Source Projects
Recycle Open Source ProjectsRecycle Open Source Projects
Recycle Open Source ProjectsGeorge Ang
 
2006 recycle opensourceprojects
2006 recycle opensourceprojects2006 recycle opensourceprojects
2006 recycle opensourceprojectsGeorge Ang
 
Django development
Django developmentDjango development
Django developmentloveyudu
 
Using google appengine (2)
Using google appengine (2)Using google appengine (2)
Using google appengine (2)Wei Sun
 
Using google appengine_1027
Using google appengine_1027Using google appengine_1027
Using google appengine_1027Wei Sun
 
1.[web security share]google_hacking
1.[web security share]google_hacking1.[web security share]google_hacking
1.[web security share]google_hackingMike Ching
 
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇Drupal Taiwan
 
Data Crawler using Python (I) | WeiYuan
Data Crawler using Python (I) | WeiYuanData Crawler using Python (I) | WeiYuan
Data Crawler using Python (I) | WeiYuanWei-Yuan Chang
 
4. Go 工程化实践-0124-v2.pdf
4. Go 工程化实践-0124-v2.pdf4. Go 工程化实践-0124-v2.pdf
4. Go 工程化实践-0124-v2.pdfssuserd6c7621
 

Similar to Python crawling tutorial (20)

JCConf 2015 TW 高效率資料爬蟲組合包
JCConf 2015 TW 高效率資料爬蟲組合包JCConf 2015 TW 高效率資料爬蟲組合包
JCConf 2015 TW 高效率資料爬蟲組合包
 
Django入门
Django入门Django入门
Django入门
 
Py ladies 0928
Py ladies 0928Py ladies 0928
Py ladies 0928
 
Py ladies 0928
Py ladies 0928Py ladies 0928
Py ladies 0928
 
探索海量文章,Elasticsearch 建置和應用
探索海量文章,Elasticsearch 建置和應用探索海量文章,Elasticsearch 建置和應用
探索海量文章,Elasticsearch 建置和應用
 
Django step0
Django step0Django step0
Django step0
 
廣宣學堂Python金融爬蟲原理班 20170416
廣宣學堂Python金融爬蟲原理班 20170416廣宣學堂Python金融爬蟲原理班 20170416
廣宣學堂Python金融爬蟲原理班 20170416
 
Python系列4
Python系列4Python系列4
Python系列4
 
Twitter 與 ELK 基本使用
Twitter 與 ELK 基本使用Twitter 與 ELK 基本使用
Twitter 與 ELK 基本使用
 
Recycle Open Source Projects
Recycle Open Source ProjectsRecycle Open Source Projects
Recycle Open Source Projects
 
2006 recycle opensourceprojects
2006 recycle opensourceprojects2006 recycle opensourceprojects
2006 recycle opensourceprojects
 
Django development
Django developmentDjango development
Django development
 
Using google appengine (2)
Using google appengine (2)Using google appengine (2)
Using google appengine (2)
 
Using google appengine_1027
Using google appengine_1027Using google appengine_1027
Using google appengine_1027
 
1.[web security share]google_hacking
1.[web security share]google_hacking1.[web security share]google_hacking
1.[web security share]google_hacking
 
Python Basic
Python  BasicPython  Basic
Python Basic
 
Xapian介绍
Xapian介绍Xapian介绍
Xapian介绍
 
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇
[DCTPE2011] Drupal 6 的 CCK/Views運用--林振昇
 
Data Crawler using Python (I) | WeiYuan
Data Crawler using Python (I) | WeiYuanData Crawler using Python (I) | WeiYuan
Data Crawler using Python (I) | WeiYuan
 
4. Go 工程化实践-0124-v2.pdf
4. Go 工程化实践-0124-v2.pdf4. Go 工程化实践-0124-v2.pdf
4. Go 工程化实践-0124-v2.pdf
 

Python crawling tutorial