2017年7月30日 星期日

Python - Example for web crawler in python (網路爬蟲簡單幾句捉取股票成交金額)

在工作上有時會有使用者需求,要去捉取某個網頁上特定的資訊以供廠內系統使用 (講白話就是公司不想花錢請廠商直接提供,做 IT 只要硬著頭皮自己去網站捉 .... XD),最常見像是海關三旬匯率,或目前即時股票即時資訊,以下就教你一步一步做到捉取網頁上特定欄位資訊

免責聲明 : 本文僅為提供 Python web crawler 的初級範例,使用的是 Yahoo 奇摩股市公開的股票資訊,並不建議大家大量使用爬蟲捉取該網站造成網站負擔,如需大量捉取請先與該網站確認以避免造成該網站困擾

使用的網站 : https://tw.stock.yahoo.com/q/q?s=1210

以下使用 Python 3.1
import urllib.request        #for 捉取網頁
import re                          #for Regular Expression


第一版 - Sample 1

import urllib.request     #import 爬蟲的 library

url="https://tw.stock.yahoo.com/q/q?s=1210"   #要捉取的網站
req=urllib.request.Request(url)     #產生此網站的 Request
data=urllib.request.urlopen(req).read()     #將網站的內容 (HTML) 捉回

沒錯,就只要四行就能把網站捉回,但很快的就會面臨到第一個挫敗,HTTP Error 500:Server Error or HTTP Error 403:Forbidden

主要的原因是因為目前的程式無法讓對方網站確認是什麼程式存取該網站,因此存取該網站時被對方拒絕,因此進行第二段程式的調整,加入 Request Header 告知 User Agent,即試著讓對方網站知道我們是透過瀏覽器來查詢的


第二版 - 加入 User Agent

import urllib.request

url="https://tw.stock.yahoo.com/q/q?s=1210"
req=urllib.request.Request(url)
req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0")    #設定 user agent 告知網站我們使用的瀏覽器資訊
data=urllib.request.urlopen(req).read()
print(data)

目前程式則可正常取得網站的網頁內容到 data 變數,當然你可能問我難道都要同上方這樣寫嗎? 其實只要能讓網站識別的 User-Agent 即可 (可參考 https://developer.chrome.com/multidevice/user-agent)
但如果你直接硬去別人網站捉資料,很快你的程式所在的 IP 就會被人封鎖,所以最好還是透過 proxy server 來捉取網站資訊


第三版 - 加入 Proxy Server


#!/usr/bin/env python3

import urllib.request
import re

url="https://tw.stock.yahoo.com/q/q?s=1210"

headers=("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1")
proxy = urllib.request.ProxyHandler({'http:':'proxy.hinet.net:80'})
opener = urllib.request.build_opener(proxy, urllib.request.HTTPHandler)
opener.addheaders = [headers]
data = opener.open(url).read().decode('big5')           #Byte Object
str_data = str( data )

print(str_data)

上面 decode('big5') 如果搜尋的字串不是中文,則可以不使用,如果用中文除了要 decode('big5') 另外程式檔建議另存 utf-8 不要使用 ANSI

接下來就是要如何去捉取了網頁內容了,目前在 data 裡的是整個網站的內容但我們只要成交金額,使用 Chrome 的 F12 看到網站的 Source Code,其 HTML 語法有特別被 <B></B> 包住,可使用最簡單的方法使用 Regular Expression 解析 <td align="center" bgcolor="#FFFfff" nowrap><b>33.65</b></td>

因此使用的 Regular Expression 其 Pattern 為 : '<td align="center" bgcolor="#FFFfff" nowrap><b>[0-9]+.[0-9]+</b></td>',而 Python 要使用 Regular Expression 則需要 import re,接下來語法如下


第四版 - 使用 Regular Expression 捉取網站要的資訊

import urllib.request
import re

url="https://tw.stock.yahoo.com/q/q?s=1210"
headers=("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1")
proxy = urllib.request.ProxyHandler({'http:':'proxy.hinet.net:80'})
opener = urllib.request.build_opener(proxy, urllib.request.HTTPHandler)
opener.addheaders = [headers]
data = opener.open(url).read().decode('big5')           #Byte Object

str_data = str(data)

pattern = '<td align="center" bgcolor="#FFFfff" nowrap><b>[0-9]+.[0-9]+</b></td>'
result1 = re.search(pattern,str_data).span()

str_data = str_data[:result1[1]]
str_data = str_data[result1[0]:]

print(str_data)

結果可以把目前要捉取的內容取出並排除不要的資訊

到此其實已經捉到需要的資訊內容,只需要再把數字截出來,因此只要再下一次 Regular Expression 將 33.65 取出,其 Pattern 為 [0-9]+.[0-9]+ 即可


第五版 - 使用 re.search() Final Version

import urllib.request
import re

url="https://tw.stock.yahoo.com/q/q?s=1210"
headers=("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1")
proxy = urllib.request.ProxyHandler({'http:':'proxy.hinet.net:80'})
opener = urllib.request.build_opener(proxy, urllib.request.HTTPHandler)
opener.addheaders = [headers]
data = opener.open(url).read().decode('big5')           #Byte Object

str_data = str(data)

pattern = '<td align="center" bgcolor="#FFFfff" nowrap><b>[0-9]+.[0-9]+</b></td>'
result1 = re.search(pattern,str_data).span()

str_data = str_data[:result1[1]]    
str_data = str_data[result1[0]:]

pattern = '[0-9]+.[0-9]+'
result1 = re.search(pattern,str_data).span()

str_data = str_data[:result1[1]]
str_data = str_data[result1[0]:]

print(str_data)


但其實 re.search 只是找到字串的位置,另外可再 regular expression 中加入 (<pattern>),再使用 compile 的功能直接針對 () 內的 Pattern 的值取出

#!/usr/bin/env python3

import urllib.request
import re

url="https://tw.stock.yahoo.com/q/q?s=1210"

headers=("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1")
proxy = urllib.request.ProxyHandler({'http:':'proxy.hinet.net:80'})
opener = urllib.request.build_opener(proxy, urllib.request.HTTPHandler)
opener.addheaders = [headers]
data = opener.open(url).read().decode('big5')           #Byte Object
str_data = str( data )

pattern = '<td align="center" bgcolor="#FFFfff" nowrap><b>([0-9]+.[0-9]+)</b></td>'
result1 = re.compile(pattern).findall(str_data)

for result in result1:
    print(result)

下圖兩個產生的結果不同,主要是在 Pattern 是否有加入 ()
上 Pattern : '<td align="center" bgcolor="#FFFfff" nowrap><b>([0-9]+.[0-9]+)</b></td>'
下 Pattern : '<td align="center" bgcolor="#FFFfff" nowrap><b>[0-9]+.[0-9]+</b></td>'
以上就是簡單的 Python web crawler 的語法,這樣就能簡單的捉到需要的資訊,雖然只是很初階的方法,供入門網路爬蟲學習者更快入門

沒有留言:

張貼留言

How to install & specified python version or distreibtuion package version in google colab

在買了 RTX 3080 要來 挖礦...  嗯~是跑機器學習後,結果發現了 GOOGLE COLAB,其實之前在「GAN 對抗式生成網路」一書就有看到,但資訊人就是什麼都想自己安裝,在本機用 Anaconda + pyCharm 弄了 GPU 環境,結果有天從新竹回家發現家裡沒...