顯示具有 爬蟲 標籤的文章。 顯示所有文章
顯示具有 爬蟲 標籤的文章。 顯示所有文章

2019年10月14日 星期一

第11屆iT邦幫忙鐵人賽-參考 Python 程式交易 30 天新手入門系列範例

可能需要預先安裝下列 Python Packages:
pip install js2py
pip install loguru
pip install pandas
pip install plotly
pip install pyquery

參考「第11屆iT邦幫忙鐵人賽-參考 Python 程式交易 30 天新手入門系列」系列範例修改,並存成檔名 getTaxies.py:


import csv
import datetime
import fractions
import json
import os
import random
import re
import time
import urllib.parse
import argparse

from random import randint
from time import sleep

import js2py
import loguru
import pandas
import plotly.graph_objects
import pyquery
import requests
import requests.exceptions

now = datetime.datetime.now()
proxies = []

proxy = None

class Taiex:
    def __init__(self, date, openPrice, highestPrice, lowestPrice, closePrice):
        # 日期
        self.Date = date
        # 開盤價
        self.OpenPrice = openPrice
        # 最高價
        self.HighestPrice = highestPrice
        # 最低價
        self.LowestPrice = lowestPrice
        # 收盤價
        self.ClosePrice = closePrice
    # 物件表達式
    def __repr__(self):
        return f'class Taiex {{ Date={self.Date}, OpenPrice={self.OpenPrice}, HighestPrice={self.HighestPrice}, LowestPrice={self.LowestPrice}, ClosePrice={self.ClosePrice} }}'

def getProxy():
    global proxies
    if len(proxies) == 0:
        getProxies()
    proxy = random.choice(proxies)
    loguru.logger.debug(f'getProxy: {proxy}')
    proxies.remove(proxy)
    loguru.logger.debug(f'getProxy: {len(proxies)} proxies is unused.')
    return proxy

def reqProxies(hour):
    global proxies
    proxies = proxies + getProxiesFromProxyNova()
    proxies = proxies + getProxiesFromGatherProxy()
    proxies = proxies + getProxiesFromFreeProxyList()
    proxies = list(dict.fromkeys(proxies))
    loguru.logger.debug(f'reqProxies: {len(proxies)} proxies is found.')

def getProxies():
    global proxies
    hour = f'{now:%Y%m%d%H}'
    filename = f'proxies-{hour}.csv'
    filepath = f'{filename}'
    if os.path.isfile(filepath):
        loguru.logger.info(f'getProxies: {filename} exists.')
        loguru.logger.warning(f'getProxies: {filename} is loading...')
        with open(filepath, 'r', newline='', encoding='utf-8-sig') as f:
            reader = csv.DictReader(f)
            for row in reader:
                proxy = row['Proxy']
                proxies.append(proxy)
        loguru.logger.success(f'getProxies: {filename} is loaded.')
    else:
        loguru.logger.info(f'getProxies: {filename} does not exist.')
        reqProxies(hour)
        loguru.logger.warning(f'getProxies: {filename} is saving...')
        with open(filepath, 'w', newline='', encoding='utf-8-sig') as f:
            writer = csv.writer(f)
            writer.writerow([
                'Proxy'
            ])
            for proxy in proxies:
                writer.writerow([
                    proxy
                ])
        loguru.logger.success(f'getProxies: {filename} is saved.')

def getProxiesFromProxyNova():
    proxies = []
    countries = [
        'tw',
        'jp',
        'kr',
        'id',
        'my',
        'th',
        'vn',
        'ph',
        'hk',
        'uk',
        'us'
    ]
    for country in countries:
        url = f'https://www.proxynova.com/proxy-server-list/country-{country}/'
        loguru.logger.debug(f'getProxiesFromProxyNova: {url}')
        loguru.logger.warning(f'getProxiesFromProxyNova: downloading...')
        response = requests.get(url)
        if response.status_code != 200:
            loguru.logger.debug(f'getProxiesFromProxyNova: status code is not 200')
            continue
        loguru.logger.success(f'getProxiesFromProxyNova: downloaded.')
        d = pyquery.PyQuery(response.text)
        table = d('table#tbl_proxy_list')
        rows = list(table('tbody:first > tr').items())
        loguru.logger.warning(f'getProxiesFromProxyNova: scanning...')
        for row in rows:
            tds = list(row('td').items())
            if len(tds) == 1:
                continue
            js = row('td:nth-child(1) > abbr').text()
            js = 'let x = %s; x' % (js[15:-2])
            ip = js2py.eval_js(js).strip()
            port = row('td:nth-child(2)').text().strip()
            proxy = f'{ip}:{port}'
            proxies.append(proxy)
        loguru.logger.success(f'getProxiesFromProxyNova: scanned.')
        loguru.logger.debug(f'getProxiesFromProxyNova: {len(proxies)} proxies is found.')
        time.sleep(1)
    return proxies

def getProxiesFromGatherProxy():
    proxies = []
    countries = [
        'Taiwan',
        'Japan',
        'United States',
        'Thailand',
        'Vietnam',
        'Indonesia',
        'Singapore',
        'Philippines',
        'Malaysia',
        'Hong Kong'
    ]
    for country in countries:
        url = f'http://www.gatherproxy.com/proxylist/country/?c={urllib.parse.quote(country)}'
        loguru.logger.debug(f'getProxiesFromGatherProxy: {url}')
        loguru.logger.warning(f'getProxiesFromGatherProxy: downloading...')
        response = requests.get(url)
        if response.status_code != 200:
            loguru.logger.debug(f'getProxiesFromGatherProxy: status code is not 200')
            continue
        loguru.logger.success(f'getProxiesFromGatherProxy: downloaded.')
        d = pyquery.PyQuery(response.text)
        scripts = list(d('table#tblproxy > script').items())
        loguru.logger.warning(f'getProxiesFromGatherProxy: scanning...')
        for script in scripts:
            script = script.text().strip()
            script = re.sub(r'^gp\.insertPrx\(', '', script)
            script = re.sub(r'\);$', '', script)
            script = json.loads(script)
            ip = script['PROXY_IP'].strip()
            port = int(script['PROXY_PORT'].strip(), 16)
            proxy = f'{ip}:{port}'
            proxies.append(proxy)
        loguru.logger.success(f'getProxiesFromGatherProxy: scanned.')
        loguru.logger.debug(f'getProxiesFromGatherProxy: {len(proxies)} proxies is found.')
        time.sleep(1)
    return proxies

def getProxiesFromFreeProxyList():
    proxies = []
    url = 'https://free-proxy-list.net/'
    loguru.logger.debug(f'getProxiesFromFreeProxyList: {url}')
    loguru.logger.warning(f'getProxiesFromFreeProxyList: downloading...')
    response = requests.get(url)
    if response.status_code != 200:
        loguru.logger.debug(f'getProxiesFromFreeProxyList: status code is not 200')
        return
    loguru.logger.success(f'getProxiesFromFreeProxyList: downloaded.')
    d = pyquery.PyQuery(response.text)
    trs = list(d('table#proxylisttable > tbody > tr').items())
    loguru.logger.warning(f'getProxiesFromFreeProxyList: scanning...')
    for tr in trs:
        tds = list(tr('td').items())
        ip = tds[0].text().strip()
        port = tds[1].text().strip()
        proxy = f'{ip}:{port}'
        proxies.append(proxy)
    loguru.logger.success(f'getProxiesFromFreeProxyList: scanned.')
    loguru.logger.debug(f'getProxiesFromFreeProxyList: {len(proxies)} proxies is found.')
    return proxies

# 取得指定年月內每交易日的盤後資訊
def getTaiexs(year, month):
    global proxy
    taiexs = []
    while True:
        if proxy is None:
            proxy = getProxy()
        url = f'https://www.twse.com.tw/indicesReport/MI_5MINS_HIST?response=json&date={year}{month:02}01'
        loguru.logger.info(f'getTaiexs: month {month} url is {url}')
        loguru.logger.warning(f'getTaiexs: month {month} is downloading...')
        try:
            response = requests.get(
                url,
                proxies={
                    'https': f'https://{proxy}'
                },
                timeout=3
            )
            if response.status_code != 200:
                loguru.logger.success(f'getTaiexs: month {month} status code is not 200.')
                proxy = None
                break
            loguru.logger.success(f'getTaiexs: month {month} is downloaded.')
            body = response.json()
            stat = body['stat']
            if stat != 'OK':
                loguru.logger.error(f'getTaiexs: month {month} responses with error({stat}).')
                break
            records = body['data']
            if len(records) == 0:
                loguru.logger.success(f'getTaiexs: month {month} has no data.')
                break
            for record in records:
                date = record[0].strip()
                parts = date.split('/')
                y = int(parts[0]) + 1911
                m = int(parts[1])
                d = int(parts[2])
                date = f'{y}{m:02d}{d:02d}'
                openPrice = record[1].replace(',', '').strip()
                highestPrice = record[2].replace(',', '').strip()
                lowestPrice = record[3].replace(',', '').strip()
                closePrice = record[4].replace(',', '').strip()
                taiex = Taiex(
                    date=date,
                    openPrice=openPrice,
                    highestPrice=highestPrice,
                    lowestPrice=lowestPrice,
                    closePrice=closePrice
                )
                taiexs.append(taiex)
        except requests.exceptions.ConnectionError:
            loguru.logger.error(f'getTaiexs: proxy({proxy}) is not working (connection error).')
            proxy = None
            continue
        except requests.exceptions.ConnectTimeout:
            loguru.logger.error(f'getTaiexs: proxy({proxy}) is not working (connect timeout).')
            proxy = None
            continue
        except requests.exceptions.ProxyError:
            loguru.logger.error(f'getTaiexs: proxy({proxy}) is not working (proxy error).')
            proxy = None
            continue
        except requests.exceptions.SSLError:
            loguru.logger.error(f'getTaiexs: proxy({proxy}) is not working (ssl error).')
            proxy = None
            continue
        except Exception as e:
            loguru.logger.error(f'getTaiexs: proxy({proxy}) is not working.')
            loguru.logger.error(e)
            proxy = None
            continue
        break
    return taiexs

# 儲存傳入的盤後資訊
def saveTaiexs(filepath, taiexs):
    loguru.logger.info(f'saveTaiexs: {len(taiexs)} taiexs.')
    loguru.logger.warning(f'saveTaiexs: {filepath} is saving...')
    with open(filepath, mode='w', newline='', encoding='utf-8-sig') as f:
        writer = csv.writer(f)
        writer.writerow([
            'Date',
            'OpenPrice',
            'HighestPrice',
            'LowestPrice',
            'ClosePrice'
        ])
        for taiex in taiexs:
            writer.writerow([
                taiex.Date,
                taiex.OpenPrice,
                taiex.HighestPrice,
                taiex.LowestPrice,
                taiex.ClosePrice
            ])
    loguru.logger.success(f'main: {filepath} is saved.')

def main(args):
    taiexs = []
    thisYear = 2019
    if args.year:
        loguru.logger.info(f'[-y|--year] [value:{args.year}]')
        thisYear = int(args.year)
    else:
        loguru.logger.info(f'[-y|--year] is not used, set year in '+str(thisYear))
    # 取得從 2019.01 至 2019.12 的盤後資訊
    for month in range(1, 13):
        # 程式暫停 3~15 秒
        sleep(randint(3, 15))
        taiexs = taiexs + getTaiexs(thisYear, month)
    filepath = f'taiexs-'+str(thisYear)+'.csv'
    saveTaiexs(filepath, taiexs)

    # 使用 Pandas 讀取下載回來的紀錄檔
    df = pandas.read_csv(filepath)
    # 將 Date 欄位按照格式轉換為 datetime 資料
    df['Date'] = pandas.to_datetime(df['Date'], format='%Y%m%d')
    
    # 建立圖表
    figure = plotly.graph_objects.Figure(
        data=[
            # Line Chart
            # 收盤價
            plotly.graph_objects.Scatter(
                x=df['Date'],
                y=df['ClosePrice'],
                name='收盤價',
                mode='lines',
                line=plotly.graph_objects.scatter.Line(
                    color='#6B99E5'
                )
            ),
            # Candlestick Chart
            # K 棒
            plotly.graph_objects.Candlestick(
                x=df['Date'],
                open=df['OpenPrice'],
                high=df['HighestPrice'],
                low=df['LowestPrice'],
                close=df['ClosePrice'],
                name='盤後資訊',
            )
        ],
        # 設定 XY 顯示格式
        layout=plotly.graph_objects.Layout(
            xaxis=plotly.graph_objects.layout.XAxis(
                tickformat='%Y-%m'
            ),
            yaxis=plotly.graph_objects.layout.YAxis(
                tickformat='.2f'
            )
        )
    )
    figure.show()

if __name__ == '__main__':
    parser = argparse.ArgumentParser()

    # -y [value:yyyy]
    # -- year [value:yyyy]
    parser.add_argument(
        '-y',
        '--year',
        help='set year in yyyy format',
        type=int
    )

    args = parser.parse_args()

    loguru.logger.add(
        f'{datetime.date.today():%Y%m%d}.log',
        rotation='1 day',
        retention='7 days',
        level='DEBUG'
    )
    main(args)
範例以2007年為例,執行下列:
python getTaxies.py -y 2007
執行結果:
python getTaixes.py -y 2007
2019-10-14 03:34:40.292 | INFO     | __main__:main:300 - [-y|--year] [value:2007]
2019-10-14 03:34:44.294 | INFO     | __main__:getProxies:69 - getProxies: proxies-2019101403.csv exists.
2019-10-14 03:34:44.298 | WARNING  | __main__:getProxies:70 - getProxies: proxies-2019101403.csv is loading...
2019-10-14 03:34:44.308 | SUCCESS  | __main__:getProxies:76 - getProxies: proxies-2019101403.csv is loaded.
2019-10-14 03:34:44.313 | DEBUG    | __main__:getProxy:50 - getProxy: 180.250.216.242:3128
2019-10-14 03:34:44.317 | DEBUG    | __main__:getProxy:52 - getProxy: 724 proxies is unused.
2019-10-14 03:34:44.322 | INFO     | __main__:getTaiexs:206 - getTaiexs: month 1 url is https://www.twse.com.tw/indicesReport/MI_5MINS_HIST?response=json&date=20070101
...(略)...
2019-10-14 03:36:58.484 | INFO     | __main__:getTaiexs:206 - getTaiexs: month 12 url is https://www.twse.com.tw/indicesReport/MI_5MINS_HIST?response=json&date=20071201
2019-10-14 03:36:58.489 | WARNING  | __main__:getTaiexs:207 - getTaiexs: month 12 is downloading...
2019-10-14 03:36:58.846 | SUCCESS  | __main__:getTaiexs:220 - getTaiexs: month 12 is downloaded.
2019-10-14 03:36:58.850 | INFO     | __main__:saveTaiexs:275 - saveTaiexs: 247 taiexs.
2019-10-14 03:36:58.853 | WARNING  | __main__:saveTaiexs:276 - saveTaiexs: taiexs-2007.csv is saving...
2019-10-14 03:36:58.864 | SUCCESS  | __main__:saveTaiexs:294 - main: taiexs-2007.csv is saved.

2007年大盤指數

#

2019年7月27日 星期六

Python 3 + Google 新聞爬蟲 + 罔拍 MONPA 中文正體分詞

import requests
from bs4 import BeautifulSoup
import monpa

res = requests.get("https://news.google.com.tw")
soup = BeautifulSoup(res.text)

count = 1
for item in soup.select(".MQsxIb"):
    print('=====[',count,']=====')
    news_title = item.select(".ipQwMb")[0].text
    news_url = item.select(".ipQwMb")[0].find('a')['href']
    print(news_title)
    result = monpa.pseg(news_title)
    for t in result:
        print(t)
    count+=1
    #print(news_url)
執行結果:
=====[ 1 ]=====
國安私菸案》檢調大搜索華航總部 歷時9小時公關室也沒放過
['國安', 'ORG']
['私菸案', 'Na']
['》', 'PARENTHESISCATEGORY']
['檢調', 'Na']
['大', 'VH']
['搜索', 'VC']
['華航', 'ORG']
['總部', 'Nc']
[' ', 'D']
['歷時', 'VJ']
['9', 'Neu']
['小時', 'Nf']
['公關室', 'Nc']
['也', 'D']
['沒', 'D']
['放過', 'VC']
=====[ 2 ]=====
快訊/國安局私菸事件華航首波懲處拔官名單出爐| 交通氣象
['快訊', 'Na']
['/', 'PERIODCATEGORY']
['國安局', 'ORG']
['私菸', 'VA']
['事件', 'Na']
['華航', 'ORG']
['首', 'Nes']
['波', 'Nf']
['懲處', 'Nv']
['拔官', 'Na']
['名單', 'Na']
['出爐', 'VH']
['|', 'FW']
[' ', 'VC']
['交通', 'Na']
['氣象', 'Na']
=====[ 3 ]=====
國安私菸案》吳宗憲傳致電黃川禎:我訂那麼多 華航高層知道嗎
['國安', 'Nb']
['私菸案', 'Na']
['》', 'PARENTHESISCATEGORY']
['吳宗憲', 'PER']
['傳', 'VD']
['致電', 'VC']
['黃川禎', 'PER']
[':', 'COLONCATEGORY']
['我', 'Nh']
['訂', 'VC']
['那麼多', 'Neqa']
[' 華航', 'PER']
['高層', 'Na']
['知道', 'VK']
['嗎', 'T']
=====[ 4 ]=====
華航公布14次專機負責人!開鍘羅雅美、邱彰信
['華航', 'PER']
['公布', 'VE']
['14', 'Neu']
['次', 'Nf']
['專機', 'Na']
['負責人', 'Na']
['!', 'EXCLAMATIONCATEGORY']
['開', 'VC']
['鍘羅雅美', 'PER']
['、', 'PAUSECATEGORY']
['邱彰信', 'PER']
=====[ 5 ]=====
華航開鍘!公布14次專機負責人羅雅美邱彰信調非主管職| 政治
['華航', 'PER']
['開鍘', 'Na']
['!', 'EXCLAMATIONCATEGORY']
['公布', 'VE']
['14', 'Neu']
['次', 'Nf']
['專機', 'Na']
['負責人', 'Na']
['羅雅美', 'PER']
['邱彰信調', 'PER']
['非', 'VG']
['主管', 'Na']
['職', 'Na']
['|', 'FW']
[' 政治', 'PER']
=====[ 6 ]=====
只是賣太多菸?他揭華航最大漏洞
['只是', 'D']
['賣', 'VD']
['太多', 'Neqa']
['菸', 'Na']
['?', 'QUESTIONCATEGORY']
['他', 'Nh']
['揭', 'VC']
['華航', 'ORG']
['最', 'Dfa']
['大', 'VH']
['漏洞', 'Na']
=====[ 7 ]=====
黃國昌揭私菸案 洪慈庸:綠支持者問「這樣好嗎?」
['黃國昌', 'PER']
['揭', 'VC']
['私菸案', 'Na']
[' 洪慈庸', 'PER']
[':', 'COLONCATEGORY']
['綠', 'VH']
['支持', 'VC']
['者', 'Na']
['問', 'VE']
['「', 'PARENTHESISCATEGORY']
['這樣', 'VH']
['好', 'VH']
['嗎', 'T']
['?', 'QUESTIONCATEGORY']
['」', 'PARENTHESISCATEGORY']
=====[ 8 ]=====
時力分裂?黨內同志要約黃國昌見面 結果是......
['時力', 'Na']
['分裂', 'VHC']
['?', 'QUESTIONCATEGORY']
['黨內', 'Nc']
['同志', 'Na']
['要', 'D']
['約', 'VC']
['黃國昌', 'PER']
['見面 ', 'Na']
['結果', 'Na']
['是', 'SHI']
['......', 'ETCCATEGORY']
=====[ 9 ]=====
內鬨?洪慈庸:綠基層焦慮黃國昌:追弊案不可看顏色
['內鬨', 'Na']
['?', 'QUESTIONCATEGORY']
['洪慈庸', 'PER']
[':', 'COLONCATEGORY']
['綠', 'VH']
['基層', 'A']
['焦慮', 'Nv']
['黃國昌', 'PER']
[':', 'COLONCATEGORY']
['追', 'VC']
['弊案', 'Na']
['不可', 'D']
['看', 'VC']
['顏色', 'Na']

2019年4月30日 星期二

python 抓取上市及上櫃公司清單, 並寫入 MySQL 資料庫

不囉嗦,直接看程式碼:
# 如何獲得上市上櫃股票清單
import requests
import time
import pandas as pd
from sqlalchemy import create_engine
from sqlalchemy.types import NVARCHAR, Date

def getTWSE(str_mode):
     # 設定爬蟲程式的 User_Agent
    headers = {'user-agent': 'Mozilla/5.0 (Macintosh Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'}
    # 上市: http://isin.twse.com.tw/isin/C_public.jsp?strMode=2
    # 上櫃: http://isin.twse.com.tw/isin/C_public.jsp?strMode=4
    req_url = "http://isin.twse.com.tw/isin/C_public.jsp?strMode=%s" % (str_mode)
    res = requests.get(req_url, headers=headers)
    df = pd.read_html(res.text)[0]

    # 設定column名稱
    df.columns = df.iloc[0]
    # 刪除第一行
    df = df.iloc[1:]
    # 先移除row,再移除column,超過三個NaN則移除
    df = df.dropna(thresh=3, axis=0).dropna(thresh=3, axis=1)
    df[['有價證券代號','名稱']] = df['有價證券代號及名稱'].str.split(n=1, expand=True)
    del df['有價證券代號及名稱']
    df = df.set_index('有價證券代號')
    return df

engine = create_engine("mysql+pymysql://stockuser:password@127.0.0.1:3306/stockdb")

dtypedict = {
    '有價證券代號':NVARCHAR(length=32),
    '上市日': Date()
}

# 抓取上市公司股票清單, 寫入MySQL > stockdb > listed_code (listed_code table存在時整個取代)
listed_companies = "2"  #上市公司
mydf1 = getTWSE(listed_companies)
mydf1.to_sql(name="listed_code", con=engine, if_exists = 'replace', index=True, dtype=dtypedict)

# 先睡個10秒鐘吧
time.sleep(10)

# 抓取上櫃公司股票清單, 寫入MySQL > stockdb > listed_code (append 到 listed_code table)
listed_companies = "4" #上櫃公司
mydf2 = getTWSE(listed_companies)
mydf2.to_sql(name="listed_code", con=engine, if_exists = 'append', index=True, dtype=dtypedict)

2019年4月28日 星期日

R tidyquant 抓取股價資料寫入 MySQL 資料庫

MySQL上先建立一個新使用者 stockuser :
mysql -u root -p
出現 mysql > 後,開始建立新用者資料並設定密碼:
mysql >
mysql > GRANT ALL PRIVILEGES ON *.* TO 'stockuser'@'%' IDENTIFIED BY 'password';
mysql > FLUSH PRIVILEGES;
mysql > quit;
若MySQL伺服器要開放遠端連結,記得防火牆允入3306埠,另外 MySQL的設定檔也要檢查:
vi /etc/mysql/my.cnf
記得 bind-address = 127.0.0.1 這行要註解
#bind-address           = 127.0.0.1
也記得 MySQL 要重新啟動
service mysql restart
資料庫部分沒問題的話,就可以準備來使用R抓取股價資料~R 如果還沒有安裝過 RMySQL package, 也記得先安裝:
install.packages("RMySQL")
確認安裝完成後,就開始吧...
library(RMySQL)

library(DBI)
library(tidyquant)
library(dplyr)

cn <- dbConnect(
 drv = RMySQL::MySQL(),
 username='stockuser',
 password='password',
 host='127.0.0.1',
 port = 3306,
 dbname = "stockdb"
 )

#  利用 tidyquant 取得 Yahoo 上的台灣五十股價資料, 寫入 MySQL stockdb > tw{股票代碼}
tw50 <- c("1101","1102","1216") #只有示範三檔股票
for (i in tw50) {
 stockid <- paste(i,"TW",sep=".")
 tblname <- paste("tw",i,sep="")
 print(stockid)
 sdata <- tq_get(stockid)
 dbWriteTable(cn, name=tblname, value=sdata, overwrite=TRUE)
 Sys.sleep(10) # 每次 Loop 先休息 10 秒
}

# 利用 tidyquant 取得 Yahoo 上的IBM股價資料, 寫入 MySQL stockdb > ibm
ibm <- tq_get("IBM")
dbWriteTable(cn, name="ibm", value=ibm, overwrite=TRUE)

dbDisconnect(cn) #記得斷開資料庫連線
資料都抓取完成後,MySQL資料庫裡面 stockdb > 就會多個股 table :
mysql -u stockuser -p
輸入密碼,登入後...
mysql> use stockdb;
Database changed
mysql> show tables;
+-------------------+
| Tables_in_stockdb |
+-------------------+
| ibm               |
| tw1101            |
| tw1102            |
| tw1216            |
+-------------------+
4 rows in set (0.00 sec)
以後就可以直接從 MySQL 中讀取股價資料來運用:
# 從 MySQL 取出 IBM 股價資料
cn <- src_mysql(
 user='stockuser',
 password='password',
 host='127.0.0.1',
 port = 3306,
 dbname = "stockdb"
 )

ibm_table <- tbl(cn, "ibm")
ibm_table

dbDisconnect(cn)
延伸閱讀資料:
  • Amazon RDS + R (其實我是租不起, 所以自建 MySQL...)
  • R學習筆記 - 資料工程篇(四) Database


  • 2016年12月30日 星期五

    Python爬蟲抓取台灣銀行的牌告匯率

    參考來源:大數軟體有限公司 [爬蟲實戰] 如何撰寫Python爬蟲抓取台灣銀行的牌告匯率?


    來源:https://www.youtube.com/watch?v=-c5rrzjsN34
    程式碼範例:
    import pandas
    dfs = pandas.read_html('http://rate.bot.com.tw/xrt?Lang=zh-TW')
    currency = dfs[0]
    currency = currency.ix[:,0:5]
    currency.columns = [u'幣別',u'現金匯率-本行買入',u'現金匯率-本行賣出',u'即期匯率-本行買入',u'即期匯率-本行賣出']
    currency[u'幣別'] = currency[u'幣別'].str.extract('\((\w+)\)')
    print(currency)
    currency.to_excel('currency.xlsx')
    


    執行結果:
         幣別 現金匯率-本行買入 現金匯率-本行賣出 即期匯率-本行買入 即期匯率-本行賣出
    0   USD      31.9    32.442      32.2      32.3
    1   HKD     4.008     4.203     4.128     4.188
    2   GBP     38.53     40.46      39.4     39.82
    3   AUD     22.98     23.64     23.17      23.4
    4   CAD     23.53     24.27      23.8     24.02
    5   SGD     21.78     22.56      22.2     22.38
    6   CHF     30.85     31.91     31.38     31.67
    7   JPY    0.2672    0.2782    0.2736    0.2776
    8   ZAR         -         -      2.32       2.4
    9   SEK      3.15      3.66      3.49      3.59
    10  NZD     22.06     22.69      22.3      22.5
    11  THB    0.7965    0.9395     0.885     0.925
    12  PHP    0.6019    0.7349         -         -
    13  IDR   0.00208   0.00278         -         -
    14  EUR      33.2     34.35      33.7      34.1
    15  KRW   0.02506   0.02896         -         -
    16  VND   0.00104   0.00154         -         -
    17  MYR     6.105     7.705         -         -
    18  CNY      4.52     4.682     4.592     4.642
    

    2016年10月3日 星期一

    遇到 JavaScript 網頁的爬蟲程式怎麼取得網頁內容

    有些網頁利用JavaScript動態自後端取得資料後才在網頁呈現,單純的爬蟲程式遇到這個情況,該怎麼辦呢?

    可以試試看自動化測試軟體 selenium 和 PhantomJS 來模擬瀏覽器瀏覽,取得網頁呈現的真實模樣。
    1. 先安裝 python selenium 套件
    2. 下載並解壓縮 PhantomJS 軟體,檔案路徑等下python程式碼中會用上
    sudo pip install selenium
    

    使用selenium 和 PhantomJS的程式碼範例:
    from selenium import webdriver
    
    driver = webdriver.PhantomJS(executable_path='/您的PhantomJS目錄/bin/phantomjs')
    # 以PChome購物搜尋 macbook 為例
    driver.get('http://ecshweb.pchome.com.tw/search/v3.3/?q=macbook')
    pageSource = driver.page_source
    print(pageSource)
    driver.close()
    

    接下來整合 BeautifulSoup 的程式碼範例:
    import requests
    from bs4 import BeautifulSoup
    from selenium import webdriver
    
    url = 'http://ecshweb.pchome.com.tw/search/v3.3/?q=macbook'
    
    driver = webdriver.PhantomJS(executable_path='/您的PhantomJS目錄/bin/phantomjs')
    driver.get(url)
    pageSource = driver.page_source
    #print(pageSource)
    
    soup = BeautifulSoup(pageSource, "lxml")
    
    item_count = 1
    for item in soup.select('img'):
        #print(item)
        print('['+str(item_count)+']')
        print(item['title'])
        print(item['src'])
        item_count += 1
    
    driver.close()
    

    執行結果:
    [1]
    MacBook Air 13 吋:256GB(Z0TB0001U)
    http://a.ecimg.tw/pic/v1/data/item/201606/D/G/A/X/5/Y/sDGAX5Y-A9007812U000_5752453b19aff.jpg
    [2]
    MacBook Air 13 吋:128GB(MMGF2TA/A)
    http://a.ecimg.tw/pic/v1/data/item/201605/D/G/A/X/0/7/sDGAX07-A90077H7W000_5733ebfc0a883.jpg
    [3]
    MacBook Pro 13 吋:2.7GHz 256GB (MF840TA/A)
    http://a.ecimg.tw/pic/v1/data/item/201511/D/G/A/X/9/6/sDGAX96-A9006MBQR000_563c649e0b12b.jpg
    [4]
    MacBook Pro 13 吋:2.7GHz 128GB (MF839TA/A)-0160830
    http://a.ecimg.tw/pic/v1/data/item/201608/D/G/A/X/7/1/sDGAX71-A9007H2EZ000_57c53b72dab2e.jpg
    [5]
    MacBook Pro 13 吋:2.7GHz 128GB (MF839TA/A)
    http://a.ecimg.tw/pic/v1/data/item/201601/D/G/A/X/7/H/sDGAX7H-19006S5WQ000_5695c64a6dfdf.jpg
    [6]
    MacBook Air 13 吋:256GB(MMGG2TA/A)
    http://a.ecimg.tw/pic/v1/data/item/201605/D/G/A/X/0/7/sDGAX07-A90077H97000_5733ec30bc6f2.jpg
    [7]
    MacBook Air 13 吋:128GB(MMGF2TA/A) 
    http://a.ecimg.tw/pic/v1/data/item/201609/D/G/A/X/0/7/sDGAX07-19007IBWT000_57d8cf53b058b.jpg
    [8]
    MacBook Air 13 吋:256GB(MMGG2TA/A)
    http://a.ecimg.tw/pic/v1/data/item/201605/D/G/A/X/0/7/sDGAX07-A900799NW000_5742d25e860a6.jpg
    [9]
    MacBook 12 吋 256GB 玫瑰金 (MMGL2TA/A)
    http://a.ecimg.tw/pic/v1/data/item/201605/D/G/A/X/4/X/sDGAX4X-A9007904G000_573d338a41048.jpg
    [10]
    MacBook Pro 13 吋:2.7GHz 128GB (MF839TA/A) 
    http://a.ecimg.tw/pic/v1/data/item/201609/D/G/A/X/7/H/sDGAX7H-19007HCAE000_57c9111d721f8.jpg
    [11]
    MacBook Air 13 吋:128GB(MJVE2TA/A)-0160505
    http://a.ecimg.tw/pic/v1/data/item/201605/D/G/A/X/7/1/sDGAX71-A90078DVN000_5731b259475be.jpg
    [12]
    MacBook Air 13 吋:256GB (Z0TB0001U)
    http://a.ecimg.tw/pic/v1/data/item/201606/D/G/A/X/5/Y/sDGAX5Y-A9007AD55000_5757bc01e00b8.jpg
    [13]
    MacBook Pro 13 吋:2.7GHz 256GB (MF840TA/A)
    http://a.ecimg.tw/pic/v1/data/item/201609/D/G/A/X/7/H/sDGAX7H-19007HCA4000_57c911687f3a0.jpg
    [14]
    MacBook Pro 13 吋:2.5 GHz 500GB (MD101TA/A)
    http://a.ecimg.tw/pic/v1/data/item/201602/D/G/A/X/3/N/sDGAX3N-19006UUHB000_56cd1e246ef7f.jpg
    [15]
    MacBook 12 吋 512GB 太空灰(MJY42TA/A)-0160830
    http://a.ecimg.tw/pic/v1/data/item/201608/D/G/A/X/7/1/sDGAX71-A9007H2M9000_57c541fe92913.jpg
    [16]
    MacBook Air 13 吋:128GB(Z0TA0001B)-01600830
    http://a.ecimg.tw/pic/v1/data/item/201608/D/G/A/X/7/1/sDGAX71-A9007H1FX000_57c5056a09d5c.jpg
    [17]
    MacBook Air 13 吋:256GB(Z0TB0001U)
    http://a.ecimg.tw/pic/v1/data/item/201606/D/G/A/X/5/Y/sDGAX5Y-A90078141000_5752441944474.jpg
    [18]
    MacBook Pro 13 吋:2.5 GHz 500GB (MD101TA/A)
    http://a.ecimg.tw/pic/v1/data/item/201409/D/G/A/X/3/N/sDGAX3N-19005FWJW000_541a90b4ee17a.jpg
    [19]
    MacBook Pro 15 吋: 2.8GHz 512GB 配備Retina顯示器(P/N.Z0RG0010V)
    http://a.ecimg.tw/pic/v1/data/item/201605/D/G/A/X/4/Y/sDGAX4Y-A90077V5U000_572869a2c5313.jpg
    [20]
    MacBook Air 11 吋:128GB(MJVM2TA/A)
    http://a.ecimg.tw/pic/v1/data/item/201504/D/G/A/X/0/7/sDGAX07-A90060KEB000_552b96e29b665.jpg
    


    延伸閱讀:

    #