顯示具有 python 標籤的文章。 顯示所有文章
顯示具有 python 標籤的文章。 顯示所有文章

2021年6月22日 星期二

[程式碼] 在Windows使用Python/C#監測檔案是否被修改

Python

import os, time, datetime
print("創立時間: " + datetime.datetime.fromtimestamp(os.path.getctime(file)).strftime("%Y-%m-%d %H:%M:%S"))
print("修改時間: " + datetime.datetime.fromtimestamp(os.path.getmtime(file)).strftime("%Y-%m-%d %H:%M:%S"))

程式執行效果如下





C#

using System.IO;

FileInfo f = new FileInfo(strFile);
DateTime dtCreate = f.CreationTime;
DateTime dtModify = f.LastWriteTime;

程式執行效果如下


2021年4月6日 星期二

[程式碼] 使用Python 爬取內政部戶政司提供之全國門牌資料 (PTT CodeJob案例)

import requests

url = "https://www.ris.gov.tw/info-doorplate/app/doorplate/dateQuery"


data = {

  "searchType": "date",

  "cityCode": "63000000",

  "tkt": "-1",

  "areaCode": "63000010",

  "village": "",

  "neighbor": "",

  "sDate": "001-01-01",

  "eDate": "110-03-25",

  "_includeNoDate": "on",

  "registerKind": "0",

  "floor": "",

  "lane": "",

  "alley": "",

  "number": "",

  "number1": "",

  "ext": "",

  "_search": "false",

  "nd": "1616602191259",

  "rows": "20",

  "page": "1",

  "sidx": "",

  "sord": "asc",

  "token": ""

}


r = requests.post(url, data=data)

r.encoding='utf-8'

f = open(r'dump.txt', 'w', encoding="utf-8")

f.write(r.text)

f.close()


[真實案例] 使用Python 爬取內政部戶政司提供之全國門牌資料 (PTT CodeJob案例)

今天我們要介紹如何使用Python爬取內政部戶政司提供之全國門牌資料

首先讓我們連到文章內的網址

點右鍵觀看程式碼,先搜尋"現有村里街路門牌查詢"關鍵字串

我們可以發現在網頁原始碼中,找不到該字串,因此可以判斷網頁某些內容是需要執行過Javascript後,才會是完整內容

在點選"以編釘日期、編釘類別查詢"按鈕後,來到縣市選擇頁面,但是我們發現網址並沒有改變,所以無法單純利用網址跳轉方式來操作

按下F12觀看網路操作,發現有進行一個POST動作

請求內容為使用日期作為搜尋條件

點選臺北市後,看到有對另外一個網址進行POST

請求內容除了剛才的日期以外,還多了一個城市編碼

填好想要的條件之後,點選搜尋


請求發出後,就會回應我們相關的內容

當我們點選下一頁時,POST的需求也跟著改變

我們已經找出資料是由dataQuery這個網址所回應,因此我們要爬資料只需要照著需求填寫,再利用POST功能取得資料就可以了

最後我們來比對一下網頁資料與抓下來的內容
v1 部分是門牌資料,v2是編訂日期,v3是編訂類別




操作影片



2021年3月30日 星期二

使用C#與Python實作遊戲修改大師

 首先我們編寫一支測試程式

按鈕每按一次將全域變數(Value)加1,並且將數值顯示在右邊標籤

右邊文本框在視窗初始化時,顯示全域變數的位址

程式流程如下

首先使用GetCurrentProcess取得修改程式控制碼,接著使用相關Windows API將權限提升

使用FindWindow找到視窗控制碼,再使用GetWindowsThreadProcessID得到ProcessID,最後再利用這個ID取得程序控制碼


搜尋目標數值前,我們先使用GetSystemInfor取得應用程式的記憶體上下限

再利用VirtualQueryEx確定記憶體區塊屬性為可讀可寫


如果條件都成立,使用ReadProcessMemory將整個記憶體區塊內容讀出,並比對數值是否為我們要的,如果是就記錄該記憶體位址

測試程式修改記憶體後,檢查記錄地址的記憶體內容數值是否依然匹配,若不匹配則移除該地址,直到只剩下一個地址

最後我們使用WriteProcessMemoroy去修改測試程式的記憶體數值

C#程式碼

[程式碼] Python修改程式記憶體

 #-*-coding:utf-8 -*-
import io, sys
try:
  sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')
except:
  pass
import win32api, win32gui, win32con, win32process, win32security
from ctypes import *
from ctypes import wintypes
liAddr = []

# https://www.programcreek.com/python/example/114361/win32security.AdjustTokenPrivileges
def AcquirePrivilege(privilege):
    process = win32process.GetCurrentProcess()
    token = win32security.OpenProcessToken(
        process,
        win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY)
    priv_luid = win32security.LookupPrivilegeValue(None, privilege)
    privilege_enable = [(priv_luid, win32security.SE_PRIVILEGE_ENABLED)]
    #privilege_disable = [(priv_luid, win32security.SE_PRIVILEGE_REMOVED)]
    win32security.AdjustTokenPrivileges(token, False, privilege_enable)

#https://yiyibooks.cn/__trs__/meikunyuan6/pywin32/pywin32/PyWin32/win32api__GetSystemInfo_meth.html
'''
wProcessorArchitecture
dwPageSize
lpMinimumApplicationAddress
lpMaximumApplicationAddress
dwActiveProcessorMask
dwNumberOfProcessors
dwProcessorType
dwAllocationGranularity
'''
# http://www.rohitab.com/discuss/topic/39525-process-memory-scannerpy/
# https://forums.codeguru.com/showthread.php?560337-Windows-Python-Memory-Scanner
# https://mpxd.net/code/jan/mem_edit/commit/5c75da31d5a7ec1e43f9ab542c1f8b4eea01f44a?lang=ja-JP
class MEMORY_BASIC_INFORMATION32(Structure):
    _fields_ = [
            ('BaseAddress', wintypes.DWORD),
            ('AllocationBase', wintypes.DWORD),
            ('AllocationProtect', wintypes.DWORD),
            ('RegionSize', wintypes.DWORD),
            ('State', wintypes.DWORD),
            ('Protect', wintypes.DWORD),
            ('Type', wintypes.DWORD),
            ]
class MEMORY_BASIC_INFORMATION64(Structure):
    _fields_ = [
            ('BaseAddress', c_ulonglong),
            ('AllocationBase', c_ulonglong),
            ('AllocationProtect', wintypes.DWORD),
            ('RegionSize', c_ulonglong),
            ('State', wintypes.DWORD),
            ('Protect', wintypes.DWORD),
            ('Type', wintypes.DWORD),
            ]

def ScanMemStep0(hProcess, nSize, nValue):  # 從頭開始尋找記憶體並記錄匹配位址
    li = win32api.GetSystemInfo()
    BaseAddr = li[2]
    MaxAddr = li[3]
    global liAddr
    liAddr = []
    windll.kernel32.VirtualQueryEx.argtypes = [wintypes.HANDLE, 
        wintypes.LPCVOID,
        c_void_p,
        c_size_t]
    while BaseAddr < MaxAddr:
        PTR_SIZE = sizeof(c_void_p)
        if PTR_SIZE == 8:       # 64-bit python
            MEMORY_BASIC_INFORMATION = MEMORY_BASIC_INFORMATION64
        elif PTR_SIZE == 4:     # 32-bit python
            MEMORY_BASIC_INFORMATION = MEMORY_BASIC_INFORMATION32
        MBI = MEMORY_BASIC_INFORMATION()
        MBI_pointer = byref (MBI)
        size = sizeof (MBI)
        windll.kernel32.VirtualQueryEx(
        hProcess,
        BaseAddr,
        MBI_pointer,
        size)
        if MBI.Protect == win32con.PAGE_READWRITE and \
            MBI.State == win32con.MEM_COMMIT:
            for i in range(0, MBI.RegionSize, nSize):
                data = win32process.ReadProcessMemory(hProcess, MBI.BaseAddress+i, nSize)
                if int.from_bytes(data, byteorder='little') == nValue:
                    liAddr.append(MBI.BaseAddress+i)
        BaseAddr += MBI.RegionSize

def ScanMemStep1(hProcess, nSize, nValue):  # 接續尋找變更資料後的記憶體
    global liAddr
    for i in range(len(liAddr)-1, -1, -1):
        data = win32process.ReadProcessMemory(hProcess, liAddr[i], nSize)
        if int.from_bytes(data, byteorder='little', signed=True) != nValue:
            liAddr.pop(i)

def WriteMem(hProcess, nSize, nValue):  # 修改記憶體內容
    global liAddr
    if len(liAddr) != 1:
        return
    # 使用此方式異常
    # buffer = nValue.to_bytes(nSize, byteorder="little", signed=True)
    # win32process.WriteProcessMemory(hProcess, liAddr[0], buffer)
    windll.kernel32.WriteProcessMemory.argtypes = [c_void_p, c_void_p, c_void_p, c_int, c_void_p]
    lpNumberOfBytesWritten = c_size_t(0)
    windll.kernel32.WriteProcessMemory(hProcess, 
        c_char_p(liAddr[0]), # lpBaseAddress
        addressof(c_longlong(nValue)),  # lpBuffer
        nSize,
        byref(lpNumberOfBytesWritten))

if __name__ == "__main__":
    AcquirePrivilege("SeTimeZonePrivilege")
    hWnd = win32gui.FindWindow(None, "維京碼農")
    if hWnd != 0:
        tid, pid = win32process.GetWindowThreadProcessId(hWnd)
        '''
        hProcess = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION |
            win32con.PROCESS_VM_READ | 
            win32con.PROCESS_VM_WRITE,
            False,
            pid)
        '''
        hProcess = windll.kernel32.OpenProcess(win32con.PROCESS_QUERY_INFORMATION |
            win32con.PROCESS_VM_READ | 
            win32con.PROCESS_VM_WRITE,
            False,
            pid)
        while True:
            str = input("Input Scan Step、Size And Value: ")
            liStr = str.split(" ")
            if liStr[0] == "0":
                ScanMemStep0(hProcess, int(liStr[1]), int(liStr[2]))
                print("Count Of liAddr = %d\n" % len(liAddr))
            elif liStr[0] == "1":
                ScanMemStep1(hProcess, int(liStr[1]), int(liStr[2]))
                print("Count Of liAddr = %d\n" % len(liAddr))
                if len(liAddr) == 1:
                    print("Addr Of Value = %x\n" % liAddr[0])
            elif liStr[0] == "w":
                WriteMem(hProcess, int(liStr[1]), int(liStr[2]))
            elif liStr[0] == "q":
                print("Bye Bye")
    else:
        print("App Not Found")
    input("Press Any Key...")

2021年3月28日 星期日

[程式碼] Python+Firefox+網頁XPath取得GoodInfo股票資訊

#-*-coding:utf-8 -*-
import io, sys

try:
  sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')
except:
  pass

import requests, re, time
from lxml import etree

UrlCompanyInfo = 'https://goodinfo.tw/StockInfo/StockDetail.asp?STOCK_ID='
UrlCompanyDividend = 'https://goodinfo.tw/StockInfo/StockDividendPolicy.asp?STOCK_ID='
UrlCompanyProfit = 'https://goodinfo.tw/StockInfo/StockBzPerformance.asp?STOCK_ID='

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'
}

# 抓取ID公司名稱與產業別
def Get_Company_Info(strID):
    resInfo = requests.get(UrlCompanyInfo+strID, headers=headers)
    resInfo.encoding='utf-8'
    htmlInfo = etree.HTML(resInfo.text)

    XpathCompanyName = '/html/body/table[2]/tbody/tr/td[3]/table/tbody/tr[2]/td[3]/table[2]/tbody/tr[1]/td[2]'
    CompanyName = htmlInfo.xpath(re.sub(r'/tbody([[]\\d[]])?', '', XpathCompanyName) + '/text()')[0]

    XpathCompanyIndustry = '/html/body/table[2]/tbody/tr/td[3]/table/tbody/tr[2]/td[3]/table[2]/tbody/tr[2]/td[2]'
    CompanyIndustry = htmlInfo.xpath(re.sub(r'/tbody([[]\\d[]])?', '', XpathCompanyIndustry) + '/text()')[0]
    print(CompanyName)
    print(CompanyIndustry)


# 抓取股利政策
def Get_Company_Dividend(strID):
    resDividend = requests.get(UrlCompanyDividend+strID, headers=headers)
    resDividend.encoding='utf-8'
    text = resDividend.text
    text = text.replace('<nobr>', '').replace('</nobr>', '')
    text = text.replace('<br>', '\n')
    text = text.replace('<b>', '').replace('</b>', '')
    htmlDividend = etree.HTML(text)

    XpathDividendHeader1 = '/html/body/table[2]/tbody/tr/td[3]/div[2]/div/div/table/thead[1]/tr[1]/td'
    liNode = htmlDividend.xpath(re.sub(r'/tbody([[]\\d[]])?', '', XpathDividendHeader1))
    cols = 0
    for node in liNode:
        if node.attrib.has_key('colspan'):
            cols += int(node.attrib['colspan'])
        else:
            cols += 1

    liHeader = [['']*cols for i in range(4)]
    
    i = 0
    for node in liNode:
        rows = 1
        if node.attrib.has_key('rowspan'):
            rows = int(node.attrib['rowspan'])
            
        cols = 1
        if node.attrib.has_key('colspan'):
            cols = int(node.attrib['colspan'])   

        for col in range(cols):
            for row in range(rows):
                liHeader[row][col+i] = node.text.replace('\u3000', '').replace('\xa0', '').replace('\n', '').replace(' ', '')
        i += cols

    XpathDividendHeader = '/html/body/table[2]/tbody/tr/td[3]/div[2]/div/div/table/thead[1]/tr'
    trs = htmlDividend.xpath(re.sub(r'/tbody([[]\\d[]])?', '', XpathDividendHeader))
    r = 0
    c = 0
    for tr in trs:
        if r != 0:
            for td in tr.getchildren():
                rows = 1
                if td.attrib.has_key('rowspan'):
                    rows = int(td.attrib['rowspan'])
                
                cols = 1
                if td.attrib.has_key('colspan'):
                    cols = int(td.attrib['colspan'])
                    
                for col in range(cols):
                    for row in range(rows):
                        while liHeader[row+r][col+c] != '':
                            c += 1
                        liHeader[row+r][col+c] = td.text.replace('\u3000', '').replace('\xa0', '').replace('\n', '').replace(' ', '')
                c += cols
        r += 1
        c = 0

    # 設定想要的欄位內容
    liDividend = [
        ['股利發放年度', 
        '合計', '合計', '股利合計',  # 股利
        '最高', '最低', '年均', # 股價
        '現金', '股票', '合計', 'EPS(元)',  # 殖利率
        '配息', '配股', '合計',  # 發放率
        ]
    ]

    # 找出欄位索引值
    colData = [0]*len(liDividend[0])
    for i in range(len(colData)):
        if i > 0:
            colData[i] = liHeader[3].index(liDividend[0][i], colData[i-1])
        else:
            colData[i] = liHeader[3].index(liDividend[0][i])

    XpathDividendData = '/html/body/table[2]/tbody/tr/td[3]/div[2]/div/div/table/tbody[1]/tr'
    trs = htmlDividend.xpath(re.sub(r'/tbody([[]\\d[]])?', '', XpathDividendData))

    for tr in trs:
        li = ['']*len(liDividend[0])
        i = 0
        tds = tr.getchildren()
        for i in range(len(li)):
            li[i] = tds[colData[i]].text
            if li[i] == None or li[i] == '-':
                li[i] = ''

        liDividend.append(li)
    return liDividend


# 抓取獲利狀況
def Get_Company_Profit(strID):
    resProfit = requests.get(UrlCompanyProfit+IdCompany, headers=headers)
    resProfit.encoding='utf-8'
    text = resProfit.text
    text = text.replace('<nobr>', '').replace('</nobr>', '')
    text = text.replace('<br>', '\n')
    text = text.replace('<b>', '').replace('</b>', '')
    htmlProfit = etree.HTML(text)

    XpathProfitHeader1 = '/html/body/table[2]/tbody/tr/td[3]/div[2]/div/div/table/thead[1]/tr[1]/td'
    liNode = htmlProfit.xpath(re.sub(r'/tbody([[]\\d[]])?', '', XpathProfitHeader1))
    cols = 0
    for node in liNode:
        if node.attrib.has_key('colspan'):
            cols += int(node.attrib['colspan'])
        else:
            cols += 1

    liHeader = [['']*cols for i in range(4)]
    
    i = 0
    for node in liNode:
        rows = 1
        if node.attrib.has_key('rowspan'):
            rows = int(node.attrib['rowspan'])
            
        cols = 1
        if node.attrib.has_key('colspan'):
            cols = int(node.attrib['colspan'])
            
        for col in range(cols):
            for row in range(rows):
                liHeader[row][col+i] = node.text.replace('\u3000', '').replace('\xa0', '').replace('\n', '').replace(' ', '')
        i += cols

    XpathProfitHeader = '/html/body/table[2]/tbody/tr/td[3]/div[2]/div/div/table/thead[1]/tr'
    trs = htmlProfit.xpath(re.sub(r'/tbody([[]\\d[]])?', '', XpathProfitHeader))
    r = 0
    c = 0
    for tr in trs:
        if r != 0:
            for td in tr.getchildren():
                rows = 1
                if td.attrib.has_key('rowspan'):
                    rows = int(td.attrib['rowspan'])
                    
                cols = 1
                if td.attrib.has_key('colspan'):
                    cols = int(td.attrib['colspan'])
                    
                for col in range(cols):
                    for row in range(rows):
                        while liHeader[row+r][col+c] != '':
                            c += 1
                        liHeader[row+r][col+c] = td.text.replace('\u3000', '').replace('\xa0', '').replace('\n', '').replace(' ', '')
                c += cols
        r += 1
        c = 0

    # 設定想要的欄位內容
    liProfit = [
        ['年度', '財報評分', 
        '收盤', '平均', '漲跌', '漲跌(%)',  # 股價
        '營業收入', '營業毛利', '稅後淨利',  # 獲利金額
        '營業毛利', '稅後淨利',  # 獲利率
        'ROE(%)', '稅後EPS', '年增(元)'
        ]
    ]

    # 找出欄位索引值
    colData = [0]*len(liProfit[0])
    for i in range(len(colData)):
        if i > 0:
            colData[i] = liHeader[1].index(liProfit[0][i], colData[i-1])
        else:
            colData[i] = liHeader[1].index(liProfit[0][i])

    XpathProfitData = '/html/body/table[2]/tbody/tr/td[3]/div[2]/div/div/table/tbody[1]/tr'
    trs = htmlProfit.xpath(re.sub(r'/tbody([[]\\d[]])?', '', XpathProfitData))

    for tr in trs:
        li = ['']*len(liProfit[0])
        i = 0
        tds = tr.getchildren()
        for i in range(len(li)):
            if len(tds[colData[i]].getchildren()) == 0:
                li[i] = tds[colData[i]].text
            else:
                li[i] = tds[colData[i]].getchildren()[0].text

            if li[i] == None or li[i] == '-':
                li[i] = ''

        liProfit.append(li)
    return liProfit


def Sleep_And_Print(sec):
    for i in range(1, sec+1):
        time.sleep(1)
        print(i, end= ' ')
    print('')


IdCompany = '3008'
#Get_Company_Info(IdCompany)
#Sleep_And_Print(15)
#liDividend = Get_Company_Dividend(IdCompany)
#for i in range(len(liDividend)):
#  print(liDividend[i])
#Sleep_And_Print(15)
liProfit = Get_Company_Profit(IdCompany)
for i in range(len(liProfit)):
  print(liProfit[i])

2021年2月27日 星期六

Python+Firefox+網頁XPath取得GoodInfo股票資訊

 今天我們要講解的是,如何使用Firefox+Python+網頁XPath來抓取GoodInfo股票資訊

Python使用的套件為requests、re與lxml

requests可以幫助取得網頁內容

re能夠快速取得或移除字串內容

lxml則能夠從網頁中有效的提取資料

今天要抓取的標的是大立光,股票代號3008 

首先抓取公司名稱與產業別

這邊使用requests.get取得了網頁內容 ,並填入標頭資料,模擬瀏覽器的行為

headers = {

    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'

}

UrlCompanyInfo = 'https://goodinfo.tw/StockInfo/StockDetail.asp?STOCK_ID='

strID='3008'

resInfo = requests.get(UrlCompanyInfo+strID, headers=headers)


宣告其編碼為UTF-8

resInfo.encoding='utf-8'


並使用etree.HTML解析

htmlInfo = etree.HTML(resInfo.text)


接著使用xpath取出公司名稱與產業別節點內容,因為lxml不認得tbody,所以要使用re將tbody給取代成空白

XpathCompanyName = '/html/body/table[2]/tbody/tr/td[3]/table/tbody/tr[2]/td[3]/table[2]/tbody/tr[1]/td[2]'

CompanyName = htmlInfo.xpath(re.sub(r'/tbody([[]\\d[]])?', '', XpathCompanyName) + '/text()')[0]

XpathCompanyIndustry = '/html/body/table[2]/tbody/tr/td[3]/table/tbody/tr[2]/td[3]/table[2]/tbody/tr[2]/td[2]'

CompanyIndustry = htmlInfo.xpath(re.sub(r'/tbody([[]\\d[]])?', '', XpathCompanyIndustry) + '/text()')[0]


最後將資料印出

print(CompanyName)

print(CompanyIndustry)


那網頁的節點該如何去尋找呢?

首先連到goodinfo中的StockDetail頁面

接著往下拉到公司基本資料表格中的名稱,點選右鍵選擇檢測元素,

Firefox檢測器會跳到該節點位置,選擇複製XPath,即可抓取公司名稱節點

同樣對產業別內容點選右鍵,選擇檢測元素

檢測器會跳到該節點位置,選擇複製XPath,即可抓取產業別內容節點

接著來抓取股利政策,來到StockDividendPolicy頁面,對表格內容點選檢測元素

會發現到有些資料佔用的不只一列或一欄

因此,需要把佔用的欄跟列補上資料,這樣才不會抓出來的標頭是空字串

再來設定要抓取的資料

最後來抓取獲利指標,來到StockBzPerformance頁面,對表格內容點選檢測元素

跟股利政策頁面一樣,會有些資料佔用的不只一列或一欄

一樣把佔用的欄跟列補上資料

再來設定要抓取的資料


程式碼


教學與操作影片



 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Blogger Templates