2015/07/19
Python 爬蟲程式框架 Scrapy
自從上一篇和爬蟲相關的文章:Web 測試及網路爬蟲的工具 Splinter 之後就沒有再寫相關文章,最近在思考一些軟體系統架構時想到為何爬蟲功能不能也這樣想?找了一下果然國外強人就有這樣的作品分享,趕緊來分享一下這個Python 開發的framework: Scrapy
2014/11/16
Python 與 Ruby 的心得
因為工作的關係這陣子與Python和Ruby 有了相當程度的接觸與運用,和之前自己比較熟悉的Java自然會拿出來比較一番。這兩個 Script language 本質上當然和Java不同。但它們可以做的事大方向上是相同的,只是偏好上的不同。單就語法上來看,我比較喜歡Ruby。但就應用廣度來看Python就比較吃香,例如:OpenStack , Django和Blender及Maya等一些3D建模軟體的應用。相對來看Ruby在資源上真的比較不足。只是因為Sketchup 3D 建模軟體中的plugin 使用的語言是 Ruby,讓我有機會接觸到它,也因此去了解了它在Web Application 上的framework Ruby on Rails.在台灣Ruby真的相對來說使用Ruby的人及公司真的很少,在技術人力的尋找會比較困難.但單就技術上的學習來看其實它很容易學習,只要有其它語言的基礎,真的不是什麼難事.
Python用了一陣子下來,個人覺得其語法的縮排雖然是為了精簡程式碼,省去括號的使用。但這也是常讓我困擾的地方,其它方面倒是沒什麼意見。因為OpenStack 的關係,現在企業開始有一些Python及OpenStack 的人力陸續開出來.
Ruby 語法上真的很吸引人,短短兩三天它就讓我著迷。若不是工作的關係真的不會想主動去學它,但學了就會愛上它,只可惜它的競爭對手太多了,近年來Scala及JavaScript 的火紅多少影響它的發展。特別是JavaScript ,在Node.js 技術出來之後讓Web 前後台開發的語言統一了,這真是Web 開發工程師的一大福音.
接下來列幾個自己覺得Ruby 語法對學過其它語言轉過來的人要特別注意的地方:
語法中程式區塊省去括號的使用:
if ....
puts 'Hello'
puts 1+2
end
class Car
def run
end
end
case ...
...
when ...
...
end
for num in (1..3)
puts num
end
字串與數字的串接 :
irb(main):001:0> s1 = 'Hello'
=> "Hello"
irb(main):002:0> puts s1 + 100
TypeError: can't convert Fixnum into String
irb(main):003:0> puts s1 + 100.to_s
Hello100
=> nil
陣列元素型態與相加:
Ruby陣列中的元素可以是不同資料型態
Ruby兩個陣列可以使用加號(+) 直接串接形成一個合併後的新陣列
類別/繼承/存取控制:
class Child < Parent
def print
puts 'I am Child'
end
def dump
end
private : dump
end
Ruby 的類別沒有多重繼承, class 間的繼承使用一個小於(<) 符號,夠簡單也夠簡潔. method 預設是 public ,不需像Java那樣前面加個 public 來宣告. 但 private method 就得特別宣告
變數:
Ruby 變數不需要宣告型別,但變數名稱第一個字元卻可以很容易區分其範圍(scope)
$ 全域變數 (global variable)
@ 實例變數 (instance variable)
[a-z] 或 _ 區域變數 (local variable)
[A-Z] 常數
註解(comment):
單行註解使用 # 開頭
多行註解使用 =begin 開始, =end 結束,這點倒是不太簡潔
Ruby 還有一堆方便簡潔的語法, 等有空再整理,也當做自己的備忘錄.
Python用了一陣子下來,個人覺得其語法的縮排雖然是為了精簡程式碼,省去括號的使用。但這也是常讓我困擾的地方,其它方面倒是沒什麼意見。因為OpenStack 的關係,現在企業開始有一些Python及OpenStack 的人力陸續開出來.
Ruby 語法上真的很吸引人,短短兩三天它就讓我著迷。若不是工作的關係真的不會想主動去學它,但學了就會愛上它,只可惜它的競爭對手太多了,近年來Scala及JavaScript 的火紅多少影響它的發展。特別是JavaScript ,在Node.js 技術出來之後讓Web 前後台開發的語言統一了,這真是Web 開發工程師的一大福音.
接下來列幾個自己覺得Ruby 語法對學過其它語言轉過來的人要特別注意的地方:
語法中程式區塊省去括號的使用:
if ....
puts 'Hello'
puts 1+2
end
class Car
def run
end
end
case ...
...
when ...
...
end
for num in (1..3)
puts num
end
字串與數字的串接 :
irb(main):001:0> s1 = 'Hello'
=> "Hello"
irb(main):002:0> puts s1 + 100
TypeError: can't convert Fixnum into String
irb(main):003:0> puts s1 + 100.to_s
Hello100
=> nil
就以上的程式片斷來看,和其它語言不同的是數字與字串不能直接串接, 需要先將數字轉成字串.
陣列元素型態與相加:
Ruby陣列中的元素可以是不同資料型態
Ruby兩個陣列可以使用加號(+) 直接串接形成一個合併後的新陣列
類別/繼承/存取控制:
class Child < Parent
def print
puts 'I am Child'
end
def dump
end
private : dump
end
Ruby 的類別沒有多重繼承, class 間的繼承使用一個小於(<) 符號,夠簡單也夠簡潔. method 預設是 public ,不需像Java那樣前面加個 public 來宣告. 但 private method 就得特別宣告
變數:
Ruby 變數不需要宣告型別,但變數名稱第一個字元卻可以很容易區分其範圍(scope)
$ 全域變數 (global variable)
@ 實例變數 (instance variable)
[a-z] 或 _ 區域變數 (local variable)
[A-Z] 常數
註解(comment):
單行註解使用 # 開頭
多行註解使用 =begin 開始, =end 結束,這點倒是不太簡潔
Ruby 還有一堆方便簡潔的語法, 等有空再整理,也當做自己的備忘錄.
2014/10/12
Python 另人頭痛的 Singleton 問題
最近學習使用Python 寫網路爬蟲程式,除了一些動態及需要輸入查詢條件的網頁問題外,最惱人的還是在Python 語法上,因為對Java 較熟悉,程式撰寫習慣會以OO的方式做思考,所以將一些Python 程式碼要重構時發現光一個Singleton pattern 的寫法就夠人煩的了,在Java 中是簡單的不得了, 在Python 中卻是寫法好幾種,重點是不夠簡單易懂。請參考 Stack Over Flow 網站上以下這篇討論:
Creating a singleton in Python
以下是其中一種個人認為比較簡單的方法,缺點是無法避免被誤繼承的問題,但還不致於造成大問題。
class Singleton:
__single = None
def __init__(self):
if Singleton.__single:
raise Singleton.__single
Singleton.__single = self
self.name = None
def __str__(self):
pass
def dump(self):
if self.name == None:
print ''
else:
print "Name : " + self.name
def setName(self, name):
self.name = name
def getName(self):
return self.name
class WorldSingleton(Singleton):
pass
個人是習慣使用Eclipse 搭配 PyDev 這個plugin 來開發 python 程式,以上程式碼無法在開發時就先警告,要到執行階段才能發現。
Creating a singleton in Python
以下是其中一種個人認為比較簡單的方法,缺點是無法避免被誤繼承的問題,但還不致於造成大問題。
class Singleton:
__single = None
def __init__(self):
if Singleton.__single:
raise Singleton.__single
Singleton.__single = self
self.name = None
def __str__(self):
pass
def dump(self):
if self.name == None:
print ''
else:
print "Name : " + self.name
def setName(self, name):
self.name = name
def getName(self):
return self.name
class WorldSingleton(Singleton):
pass
個人是習慣使用Eclipse 搭配 PyDev 這個plugin 來開發 python 程式,以上程式碼無法在開發時就先警告,要到執行階段才能發現。
2014/09/27
Python 日期時間運用
開始認真學起Python這個強大的script language ,因為它可以幫我運用在許多方面,像是Blender / Panda3D / Web scraping 等等。拿來運用時就想到以前在用 Java 時都用建立一套日期及時間的 utility library , 現在語言換了,應該也要先熟悉它如何處理日期及時間,以下整理自己常用的用法:
from datetime import date, time,datetime
# 取得今天日期
today = date.today()
# 日期 減一天
dt = today.replace(day=today.day-1)
print dt.year,dt.month,dt.day
# 月份 加一個月
dt = today.replace(month=today.month+1)
print dt.year,dt.month,dt.day
# 年份 加一年
dt = today.replace(year=today.year+1)
print dt.year,dt.month,dt.day
# 年/月/日 各減一
dt = today.replace(year=today.year-1, month=today.month-1, day=today.day-1)
print dt.year,dt.month,dt.day
以上這個年/月/日加減是有問題, 特別是當該日期為某月第1天時, 使用 day=today.day - 1 會出現以下 exception :
>>> yesterday = date.today().replace(day=today.day-1)
Traceback (most recent call last):
File "", line 1, in
yesterday = date.today().replace(day=today.day-1)
ValueError: day is out of range for month
這時候該使用 timedelta , 請看以下示範:
>>> from datetime import date, datetime, timedelta
>>> tomorrow = datetime.now().date() + timedelta(days=1)
>>> print tomorrow
2014-10-02
>>> print datetime.now().date()
2014-10-01
>>> yesterday = datetime.now().date() + timedelta(days=-1)
>>> print yesterday
2014-09-30
# 輸出格式變化
print dt.strftime("%y%m%d")
print dt.strftime("%y/%m/%d")
# 年份為四位西元年 %Y
print dt.strftime("%Y%m%d")
print dt.strftime("%Y/%m/%d")
print dt.isoformat()
# 取得現在日期時間
now = datetime.now()
print now
# 印出現在的 時:分:秒
print now.strftime("%H:%M%S")
# 印出現在的 西元年月日 YYYYMMDD
print now.strftime("%Y%m%d")
前半部日期的加減是個人覺得很實用的一部份,可以用來一天天推移。從這些程式碼片斷可以看出來Python簡單卻很有威力的地方。換成其它語言可能要寫一堆程式碼才能達成相同的功能。
from datetime import date, time,datetime
# 取得今天日期
today = date.today()
# 日期 減一天
dt = today.replace(day=today.day-1)
print dt.year,dt.month,dt.day
# 月份 加一個月
dt = today.replace(month=today.month+1)
print dt.year,dt.month,dt.day
# 年份 加一年
dt = today.replace(year=today.year+1)
print dt.year,dt.month,dt.day
# 年/月/日 各減一
dt = today.replace(year=today.year-1, month=today.month-1, day=today.day-1)
print dt.year,dt.month,dt.day
以上這個年/月/日加減是有問題, 特別是當該日期為某月第1天時, 使用 day=today.day - 1 會出現以下 exception :
>>> yesterday = date.today().replace(day=today.day-1)
Traceback (most recent call last):
File "
yesterday = date.today().replace(day=today.day-1)
ValueError: day is out of range for month
這時候該使用 timedelta , 請看以下示範:
>>> from datetime import date, datetime, timedelta
>>> tomorrow = datetime.now().date() + timedelta(days=1)
>>> print tomorrow
2014-10-02
>>> print datetime.now().date()
2014-10-01
>>> yesterday = datetime.now().date() + timedelta(days=-1)
>>> print yesterday
2014-09-30
# 輸出格式變化
print dt.strftime("%y%m%d")
print dt.strftime("%y/%m/%d")
# 年份為四位西元年 %Y
print dt.strftime("%Y%m%d")
print dt.strftime("%Y/%m/%d")
print dt.isoformat()
# 取得現在日期時間
now = datetime.now()
print now
# 印出現在的 時:分:秒
print now.strftime("%H:%M%S")
# 印出現在的 西元年月日 YYYYMMDD
print now.strftime("%Y%m%d")
前半部日期的加減是個人覺得很實用的一部份,可以用來一天天推移。從這些程式碼片斷可以看出來Python簡單卻很有威力的地方。換成其它語言可能要寫一堆程式碼才能達成相同的功能。
2014/09/24
Web 測試及網路爬蟲的工具 Splinter
最近在研究如何自動化到一些網站抓資料,一開始某些靜態網頁比較簡單,不管是用Java 或 PHP 都可以抓下來。但遇到一些使用 JavaScript / AJAX 及網頁會重導到其它頁的動態網頁真的是沒法子,特別是一些還要輸入日期或其它資料的表單網頁。參考了"網路機器人,網路蜘蛛與網路爬蟲 PHP/CURL 程式設計指南"這本書中介紹的工具 iMarco ,但仍無法全自動化。接下來把腦筋動到要用 C# 來控制 IE (Internet Explorer) ,可是時間上不允許,只好再找工具,最後找到了 Splinter 這工具,它其實預設是搭配Selenium 這個 web 測試自動化工具,雖然 Selenium 這個工具有 FireFox plugin , 也有 IDE ,Splinter 才能真正做到全自動化。
根據官方網站(http://splinter.cobrateam.info/)的介紹,它是一個 open source 工具,用來測試 web 應用程式。使用的語言是 Python。可以讓你自動化瀏覽器的行為,例如:變換網址 (URL)及與網頁互動。
這裡先介紹在 Windows 平台如何安裝,Linux / Mac OS 比較簡單:
當然,前題是要對 html 及 Python 有點了解才能發揮 splinter 的功能。能自動輸入與網站互動後找到所要的網頁資料才只是第一步,資料能取回來存放才是後續的工作重點。在尚未找到 splinter 之前,也使用過 crawler4j 及 Scrapy ,現在終於有個簡單的工具可用了,後續會再介紹一些實例。
安裝 get-pip.py 訊息:
D:\download>python get-pip.py
Downloading/unpacking pip
Downloading/unpacking setuptools
Installing collected packages: pip, setuptools
Successfully installed pip setuptools
Cleaning up...
不建議使用 IE,預設使用FireFox , 若沒有安裝 FireFox 也請先將它裝好,官方網站說也可以使用Chrome 及 IE,但在Windows 下尚未試成功。也不建議在 Windows cmd.exe 下執行 python.exe 會有編碼問題,請使用 IDLE。或是先使用 chcp 將 code page 先改成 utf-8,在Linux 中沒有這個困擾,或是可以使用Eclipse 安裝 PyDev plugin。在Eclipse 中開發Python 程式是很愉快的。
當然直接使用 Selenium 可以配合其它程式語言如: C#及 Java,但仍然不如使用 Python 這種script language 來得好用,為使麼呢?因為每次遇到一個新的網頁要爬資料時,都會先開啟瀏覽器檢視網頁原始碼的功能,了解到該頁的HTML DOM 結構,特別是你所要抓的資料是屬於那一個Table 或是那一個 id , name 之類的。這時候可以在Python 的IDLE 環境中一步一步慢慢試,不用每次執行前都要編譯,省事多了!
當使用 splinter 連上網站網頁資料時最好搭配 BeautifulSoup 4 。用起來會更方便,但遇到動態網頁時,使用 splinter 搭配JavaScript 才能針對一些AJAX 網頁及forward 網頁或openwindow 做更進一步的控制。特別是一些要輸入查詢條件及選擇日期等,有了JavaScript 的配合幾乎能克服一些難搞的狀況。等工作較不忙時,希望能整理一些心得及實例分享給大家。
根據官方網站(http://splinter.cobrateam.info/)的介紹,它是一個 open source 工具,用來測試 web 應用程式。使用的語言是 Python。可以讓你自動化瀏覽器的行為,例如:變換網址 (URL)及與網頁互動。
這裡先介紹在 Windows 平台如何安裝,Linux / Mac OS 比較簡單:
- 先到 Python 官網(http://www.python.org)下載 Python 來安裝 , Python 2, Python 3皆可
- Google 找一下 get-pip.py ,下載到自己指定目錄
- 執行 python get-pip.py 將 pip 安裝起來
- 安裝 splinter : 執行 python -m pip install splinter
from splinter import Browser
b = Browser()
url = "tw.yahoo.com"
b.visit(url);
url = "http://tw.yahoo.com"
b.visit(url)
b.fill('p', 'CSS3')
btn = b.find_by_id('searchsubmit')
btn.click()
當然,前題是要對 html 及 Python 有點了解才能發揮 splinter 的功能。能自動輸入與網站互動後找到所要的網頁資料才只是第一步,資料能取回來存放才是後續的工作重點。在尚未找到 splinter 之前,也使用過 crawler4j 及 Scrapy ,現在終於有個簡單的工具可用了,後續會再介紹一些實例。
安裝 get-pip.py 訊息:
D:\download>python get-pip.py
Downloading/unpacking pip
Downloading/unpacking setuptools
Installing collected packages: pip, setuptools
Successfully installed pip setuptools
Cleaning up...
不建議使用 IE,預設使用FireFox , 若沒有安裝 FireFox 也請先將它裝好,官方網站說也可以使用Chrome 及 IE,但在Windows 下尚未試成功。也不建議在 Windows cmd.exe 下執行 python.exe 會有編碼問題,請使用 IDLE。或是先使用 chcp 將 code page 先改成 utf-8,在Linux 中沒有這個困擾,或是可以使用Eclipse 安裝 PyDev plugin。在Eclipse 中開發Python 程式是很愉快的。
當然直接使用 Selenium 可以配合其它程式語言如: C#及 Java,但仍然不如使用 Python 這種script language 來得好用,為使麼呢?因為每次遇到一個新的網頁要爬資料時,都會先開啟瀏覽器檢視網頁原始碼的功能,了解到該頁的HTML DOM 結構,特別是你所要抓的資料是屬於那一個Table 或是那一個 id , name 之類的。這時候可以在Python 的IDLE 環境中一步一步慢慢試,不用每次執行前都要編譯,省事多了!
當使用 splinter 連上網站網頁資料時最好搭配 BeautifulSoup 4 。用起來會更方便,但遇到動態網頁時,使用 splinter 搭配JavaScript 才能針對一些AJAX 網頁及forward 網頁或openwindow 做更進一步的控制。特別是一些要輸入查詢條件及選擇日期等,有了JavaScript 的配合幾乎能克服一些難搞的狀況。等工作較不忙時,希望能整理一些心得及實例分享給大家。
2014/08/27
Eclipse 上安裝 Python PyDev plugin
在Windows 上使用 Python 有兩種方式,到官方網站(http://www.python.org) 下載安裝後有兩種方式可用,一種是用命令列的方式,另一種是 IDLE 圖形介面。第二種雖然較好用但還是不理想,上網找到有一個叫 PyDev 的 plugin ,搭配 Eclipse 就好用多了。以下安裝方法提供參考:
- 開啟 Eclipse 3.6 以上版本,功能選單 Help > Eclipse Marketplace ...
- 在 Find 一欄中輸入 Python 這個字
- 第一個找出來的就是 PyDev - Python IDE for Eclipse, 點選右邊的 Install 鈕就對了
- 接下來至少要勾選 PyDev for Eclipse, 另一項用不到可以不勾, 再按 Next 下一步 , 點 I accept the terms of license agreements, 再按 Finish
- 信任憑證 , 按 OK
- 最後安裝結束,重啟 Eclipse
- 接下來就可以在 Eclipse 的選單 File > New > Projects ... 看到 PyDev 的選項了,它有三個 Project 類型,分別是 PyDev Django Project / PyDev Google App Engine Project / PyDev Project 三種。 這時候還不能正常 New 專案,要先到 Windows > Preferences > PyDev > Interpreters > Python Interpreter 中, 按下右上角的 New按鈕將你已安裝的 Python.exe 路徑設定進來:
- 現在可以開始 New PyDev Project 在 Eclipse 中寫程式及除錯了
2011/03/04
Python 2.6 Tk 中文亂碼問題解決方法
在 Windows XP 想使用 Python 及 Tk 來開發圖形介面程式時針對中文字顯示都會變成亂碼,試了幾個方法後發現以下這個方法是較可行,提供給大家參考。
第一點:程式開頭一定要加底下這行編號宣告
# -*- coding:utf-8 -*-
第二點: 字串要用在 Tk 元件上的一定要宣告成字串變數,字串中可以有許功蓋之類的字,但這些字不能在開頭及結尾
str1 = "Hello world ! 中文字成功顯示在 Tk"
第三點: 設定 Tk 元件文字屬性時再強制設定編碼為 UTF-8
widget = Button(text=str1.encode("UTF-8"), padx=20, pady=20)
以下是一個簡單的範例 :
# -*- coding: utf-8 -*- from Tkinter import * class Hello(Frame):
def __init__(self, parent=None): Frame.__init__(self, parent)
self.pack()
self.make_widgets() def make_widgets(self):
# widget = Button(self, text=str1, command=self.quit)
# 不能在 Windows 中正常顯示中文字.
str1 = "Hello world ! 中文字成功顯示在 Tk"
widget = Button(text=str1.encode("UTF-8"), padx=20, pady=20)
widget.pack(padx=20, pady=20)
widget.config(cursor='gumby')
widget.config(bd=8, relief=RAISED)
widget.config(bg='dark green', fg='white')
widget.pack(side=LEFT)
if __name__ == '__main__': Hello().mainloop()
相同的程式在Ubuntu 11.04 Alpha 版中的 Python 2.7.1 就沒有這個問題,只要用以上第一點的編碼設定即可,不用針對中文字串再 encode .
2023/01/01 macOS 上安裝的是Python 3.9.13 沒有這個中文顯示問題。就算沒有一開始指定utf-8:
# -*- coding: utf-8 -*-
訂閱:
文章 (Atom)





