about 2 years ago

首先先申請一個Koding帳號
申請完之後,Koding會帶你進入平常的工作環境,但還沒有驗證前,什麼都不能操作


此時,可以重新整理此頁,會出現輸入驗證碼的對話框

將信箱收到的驗證碼填入,就可以開啟Koding的VM環境

稍作一會等待,即可進入工作畫面,Koding提供了GUI IDE編輯介面及Terminal

在Terminal畫面中輸入以下這幾句,安裝MySQLdb、lxml以及mechanize
sudo apt-get update

sudo apt-get install python-mysqldb

sudo apt-get install libxml2-dev libxslt-dev python-dev
sudo apt-get install python-lxml

sudo apt-get install python-setuptools
sudo easy_install pip
sudo pip install mechanize

最後你可以在Terminal輸入python進入 python 的編譯環境,輸入以下幾行,都沒有顯示錯誤即代表安裝成功

from lxml import etree
import mechanize
import MySQLdb

爬蟲系列教學文目錄
爬蟲系列教學文程式碼
安裝Python及Python常用語法可參考 Python - 十分鐘入門

 
about 2 years ago

[lxml]

page = etree.HTML(html)
for i in page.xpath(u"XPath語法"):
    print i

[XPath語法]

找出所有連結的網址(a的href屬性)//a/@href
找出所有連結的文字//a/text()
找出div屬性id='txt'的物件//div[@id='txt']
找出td屬性class包含'GridItem'字串的物件//td[contains(@class, 'GridItem')]

  • class有多個value:<td class="GridItem td1">
  • 相似value的比對:同時抓取 <td class="GridItem1"><td class="GridItem2">...

找出font屬性color='#0000ff'或是屬性color='blue的'物件//font[(@color="#0000ff" or @color="blue")]
找出font屬性color='#0000ff'或是span屬性style="COLOR: blue"的物件
//font[@color="#0000ff"] | //span[@style="COLOR: blue"]

更多XPath語法
更多XPath運算元

爬蟲系列教學文目錄
爬蟲系列教學文程式碼
安裝Python及Python常用語法可參考 Python - 十分鐘入門

 
about 2 years ago

延續之前抓取 http://blog.marsw.tw 網頁的例子,以下示範抓取該網頁其中一篇內文

[程式碼]

06_parsing_html.py
# encoding: utf-8

import urllib2
from lxml import etree

from HTMLParser import HTMLParser
class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    try:
        s = MLStripper()
        s.feed(html)
        return s.get_data()
    except:
        return html

request = urllib2.Request("http://blog.marsw.tw/2013/04/blog-post_9395.html")
response = urllib2.urlopen(request)
html = response.read()

page = etree.HTML(html)

fileout1 = file('06_v1.txt','w')
fileout1.write(("".join(page.xpath(u"//div[@class='post-body entry-content']/descendant::text()"))).encode('utf8'))
fileout1.close()

fileout2 = file('06_v2.txt','w')
fileout2.write(("".join(page.xpath(u"//div[contains(@class, 'post-body')]/descendant::text()"))).encode('utf8'))
fileout2.close()

raw_html = etree.tostring(page.xpath(u"//div[contains(@class, 'post-body')]")[0], pretty_print=True,encoding='UTF-8')
text = strip_tags(raw_html)
fileout3 = file('06_v3.txt','w')
fileout3.write(text)
fileout3.close()

[程式說明]

我們觀察部落格文章的格式,發覺內文是包在div中,
v1的寫法是把完整class的value寫出來,而v2的寫法只要寫class其中一個value
v1、v2的寫法都是利用XPath Axes的概念將article底下不管幾階(descendant)所有的文字印出
而v3的寫法則是將article這個標籤所包含的所有原始html以raw_html這個變數儲存
再以strip_tags函式將raw_html中所有的html標籤去除,以text來儲存並輸出

爬蟲系列教學文目錄
爬蟲系列教學文程式碼
安裝Python及Python常用語法可參考 Python - 十分鐘入門

 
about 2 years ago

延續之前抓取 http://blog.marsw.tw 網頁的例子,以下示範抓取該網頁文章的標題

[程式碼]

05_parsing_axes
# encoding: utf-8

import urllib2
from lxml import etree

request = urllib2.Request("http://blog.marsw.tw")
response = urllib2.urlopen(request)
html = response.read()

page = etree.HTML(html)

print "v1-------"
for i in page.xpath(u"//h5/a"):
    print i.text

print "v2-------"
for i in page.xpath(u"//h5/a/text()"):
    print i

print "v3-------"
for i in page.xpath(u"//article/header/h5/a/text()"):
    print i

print "v4-------"
for i in page.xpath(u"//article"):
    print i.xpath(u"descendant::h5/a/text()")[0]

[程式說明]

我們可以從開發人員工具可以看到,網頁文章的標題是在h5下的a的文字,v1、v2兩種寫法都可以
而v4的寫法是配合XPath Axes的概念,
因為一般來說XPath會寫完整的路徑,也就是v3的//article/header/h5/a/text()寫法
但我們在處理網頁中,有些網頁的格式並不是這麼漂亮,就可以透過XPath的父子屬性來寫
v4的寫法是指我將article的所有子孫(descendant)中h5下的a的第1個文字印出
會用descendant而不是child是因為不確定h5是不是就在article底下一層,在不確定階層樹的狀況,就採用descendant

爬蟲系列教學文目錄
爬蟲系列教學文程式碼
安裝Python及Python常用語法可參考 Python - 十分鐘入門

 
about 2 years ago

延續之前抓取 http://blog.marsw.tw 網頁的例子
這裡示範如何把該頁的圖片存取下來的方法:
我們利用lxml這個額外的module來快速處理html來獲取我們想要的資訊
而lxml所使用的是xpath的語法

[程式碼]

04_parsing.py
# encoding: utf-8

import urllib2
from lxml import etree

request = urllib2.Request("http://blog.marsw.tw")
response = urllib2.urlopen(request)
html = response.read()

page = etree.HTML(html)
count = 0
for url in page.xpath(u"//img/@src"):
    if url.startswith("http:"):
        print url
        
        try:
            img_request = urllib2.Request(url)
            img_response = urllib2.urlopen(img_request)
            img = img_response.read()
        except:
            continue

        count = count+1
        filename = str(count)+".jpg"
        pic_out = file(filename,'w')
        pic_out.write(img)
        pic_out.close()

[程式說明]

想要抓圖片,首先要先獲得圖片的url,
而html tag中,圖片的url是記錄在img這個tag的src屬性
因此lxml的語法,找出所有img tag的src屬性寫法就是//img/@src,並且以unicode編碼
找出開頭是http的src url(其他是用相對路徑,無法直接存取該圖),之後就對該圖片做存取的動作,
同時為了避免該圖的連結失效,而造成程式中斷,因此加上了try/except的機制

爬蟲系列教學文目錄
爬蟲系列教學文程式碼
安裝Python及Python常用語法可參考 Python - 十分鐘入門

 
about 2 years ago

這裡介紹以Python自帶的模組 urllib2 開發抓取MoneyDJ的人氣指數的爬蟲

[程式碼]

03_json.py
# encoding: utf-8

import urllib,urllib2
url = "http://www.moneydj.com/InfoSvc/apis/vc"
request = urllib2.Request(url) 
request.add_header("Content-Type","application/json")

payload = '{"counts":[{"svc":"NV","guid":"a180a15b-9e4f-4575-b28f-927fcb5c63a3"}]}'
response = urllib2.urlopen(request,data=payload)  
html = response.read()
print html

輸出結果

{"counts":[{"count":3609,"guid":"a180a15b-9e4f-4575-b28f-927fcb5c63a3","svc":"NV"}]}

[程式說明]

在這個範例程式中,我們要抓取MoneyDJ的人氣指數
由於此網頁的人氣指數並不是直接輸出在html原始碼中(原始碼的是假人氣資料)
而是另外透過http://www.moneydj.com/InfoSvc/apis/vc 這隻 api 去產生真實的人氣指數
且透過開發人員工具,我們也可以看到 request header 的 Content-Type 為 json
而此API是透過POST的方式傳遞資料,
我們直接將 Request Payload 的資料複製貼上(按下view source會產生程式內的格式)
程式輸出的結果就有包含正確的人氣指數3609

爬蟲系列教學文目錄
爬蟲系列教學文程式碼
安裝Python及Python常用語法可參考 Python - 十分鐘入門

 
about 2 years ago

這裡介紹以Python自帶的模組 urllib、urllib2 開發高鐵時刻查詢的爬蟲

[程式碼]

以下的程式實作以POST方法向 高鐵時刻查詢網站 發送一個Request
並加入User-Agent的header,將這隻爬蟲偽裝成Mac系統的Chorme瀏覽器
獲得response後,取得該網址的 html 原始碼,存成一個名為02_thsrc.html的檔案

02_POST_urllib2.py
# encoding: utf-8

import urllib,urllib2
url = "http://www.thsrc.com.tw/tw/TimeTable/SearchResult"
request = urllib2.Request(url) 
request.add_header("User-Agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36")

form_data = {
    "StartStation": "977abb69-413a-4ccf-a109-0272c24fd490", 
    "EndStation": "f2519629-5973-4d08-913b-479cce78a356",
    "SearchDate": "2016/01/10",
    "SearchTime": "17:00",
    "SearchWay":"DepartureInMandarin",
    "RestTime":"",
    "EarlyOrLater":""
}
form_data = urllib.urlencode(form_data)
response = urllib2.urlopen(request,data=form_data)  
html = response.read()

file_out = file("02_thsrc.html",'w')
file_out.write(html)
file_out.close()

[程式說明]

在這個範例程式中,我們要查詢的是從台北到左營2016/01/10 17:00出發可能班次
從Chrome開發人員工具可以看到高鐵時刻查詢
是以POST方式發送request,也可以看到Form Data的樣貌


另外,我們從開發人員工具中看到的Form Data的格式並不是真正向Server發送的格式,
利用view URL encoded看到的才是真正的格式
在程式中form_data是以dictionary的方式儲存資料,
在傳送時需要透過urllib這個module來做urlencode才能當成最後傳送出去的資料

而高鐵網站會檢查發送request的對象,若為爬蟲程式會阻擋,
因次我們需要將此爬蟲程式偽裝成瀏覽器
也就是在header中的User-Agent屬性加上資料
這個值可以從Chrome開發人員工具中Network看到的User-Agent直接複製

{替換查詢內容}

從Form Data大概看得出2016/01/10、17:00的資訊,
但起訖站卻沒有看到「台北」跟「左營」這兩個關鍵字,
我們可以從StartStation、EndStation這兩個名稱猜測,這兩個特殊的編碼應該分別代表「台北」跟「左營」
利用檢查元素的方式,可以看到在開發人員工具中,各站名的value,也就是這些特殊的編碼
也可以看到以出發時間查詢的value是DepartureInMandarin
若要查詢其他地區或是改成以抵達時間的查詢方式就可用這些value來替換

爬蟲系列教學文目錄
爬蟲系列教學文程式碼
安裝Python及Python常用語法可參考 Python - 十分鐘入門

 
about 2 years ago

其實爬蟲的技巧主要就是觀察
觀察你想要抓的資料有沒有什麼規則

  • 哪個網址才是真的資料所在
  • 想要抓的元素是以什麼tag分隔

瀏覽器就有功能可以幫我們觀察這些資訊
我們想要知道這個網址request的方法是GET還是POST
以chrome為例:

1.開啟開發人員工具,進到Network標籤:

2.重新整理網頁,通常我們要抓取的網頁會在最上方:

我們就可以知道要獲取 http://blog.marsw.tw 的資料,request方法是GET

而像高鐵時刻查詢的網頁request方法是POST:


也能知道request header該放怎麼樣的資訊:

以及POST該傳怎樣的內容及格式:

3.而原始碼除了可以重爬蟲程式抓取後的資料觀察,Chrome的檢視網頁原始碼功能也可以幫助我們先行看到抓取的資料原貌


4.而想要抓取的元素tag也可以從Chrome觀察,對著想要的元素按右鍵>檢查
假設這裡我是想抓取每篇文章的標題,我就對著標題按下右鍵>檢查


點選任一區塊原始碼時,Chrome顯示該程式碼在網頁上顯示的區塊位置
這裡我猜測應該是h5這個tag底下的內容會是我想要的資訊,我點選過後,Chrome顯示的的確是標題區塊

ps.有個Chrome套件InfoLite可以簡單幫忙確認所點選的程式碼是否適用我們想抓的資料規則,
以此例來說,我開啟InfoLite後,點選標題,他在上方產生了 .entry-title
畫面上顯示header h5
代表著這些標題的DOM是 header>h5,而h5的class屬性是entry-title
(.代表的是class #代表的是id)
我們也可以看到這個規則下,的確是所有的文章標題

但有時抓取的資料不一定都以class、id等tag標明不同的內容時,
就還是得仰賴直接觀察原始碼,用程式對觀察到的規則做解析,才能確定該規則是不是我們想要的內容

 
about 2 years ago

這裡介紹的方式是利用安裝 Python時附的 urllib2 module來實作
可以不需要安裝任何新的module就可以實作一支最基本的爬蟲

[程式碼]

以下的程式實作以GET方法向 http://blog.marsw.tw 發送一個Request
獲得response後,取得該網址的 html 原始碼
將html原始碼印出,及存成一個名為01_blog.html的檔案

01_basic_urllib2.py
# encoding: utf-8

import urllib2

request = urllib2.Request("http://blog.marsw.tw")
response = urllib2.urlopen(request)
html = response.read()
print html

fileout = file("01_blog.html","w")
fileout.write(html)
fileout.close()

[程式說明]

urlopen後面可以接一個 url 或是一個 Request 物件
所以原程式(第1種寫法)

request = urllib2.Request("http://blog.marsw.tw")
response = urllib2.urlopen(request)

也可以改寫成(第2種寫法)

response = urllib2.urlopen("http://blog.marsw.tw")

而程式碼會習慣這樣寫的原因是在實戰中,會遇到許多不同的案例
這時需要修改一些Request的設定,才能順利抓取我們想要的資料
因此就習慣還是會用第1種寫法來實作

爬蟲系列教學文目錄
爬蟲系列教學文程式碼
安裝Python及Python常用語法可參考 Python - 十分鐘入門

 
about 2 years ago

[背景知識]

Python - 十分鐘入門
背景知識

[前言]

採用各module原因(待補)

[Crawler - urllib2]

用GET方法抓取網頁
觀察技巧 with Chrome開發人員工具
用POST方法與偽裝User-Agent抓取網頁
處理不同的Content-Type

[Parsing - lxml]

抓取網頁所有圖片
抓取網頁標題 & XPath Axes說明
抓取網頁純文字內文
lxml、XPath 常用語法
big5編碼處理,unicode,url編碼
json

[Crawler - 身分驗證]

cookie
mechanize

[資料庫 - MySQLdb]

[加速 - multiprocessing]

[環境安裝]

如何在koding上搭建爬蟲環境(MySQLdb、lxml、mechanize)

[練習]