2025年5月18日 星期日

PyPDF2 3.0.0改版後,修訂範例程式

以下的程式是利用GPT修訂。
來源:看圖學Python+Excel辦公室自動化程式設計 
因為在 PyPDF2 3.0.0 之後,PdfFileReader 已被移除,官方建議改用 PdfReader,因此修訂書中第16章的範例。
範例ch16-1.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from PyPDF2 import PdfReader
import os

pdf_path = os.path.join(os.getcwd(), "PDF", "Python海龜繪圖.pdf")
print(pdf_path)

pdfReader = PdfReader(pdf_path)
numberOfPages = len(pdfReader.pages)
print("頁數:", numberOfPages)

# 試圖取得 metadata(注意新版中有些文件可能沒有 metadata)
info = pdfReader.metadata
print("作者:", info.get('/Author'))
print("標題:", info.get('/Title'))
print("製作者:", info.get('/Producer'))

範例ch16-1a.py

1
2
3
4
5
6
7
8
9
from PyPDF2 import PdfReader
import os

pdf_path = os.path.join(os.getcwd(), "PDF", "Python海龜繪圖.pdf")
pdfReader = PdfReader(pdf_path)

for pageNo, page in enumerate(pdfReader.pages):
    text = page.extract_text()
    print(f"第 {pageNo+1} 頁內容:\n{text}\n")

範例ch16-1b.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from PyPDF2 import PdfReader, PdfWriter
import os

pdf_path = os.path.join(os.getcwd(), "PDF", "Python海龜繪圖.pdf")
pdf_output = os.path.join(os.getcwd(), "PDF", "Python海龜繪圖2.pdf")

pdfReader = PdfReader(pdf_path)
pdfWriter = PdfWriter()

# 旋轉第 1 頁 90 度
page1 = pdfReader.pages[0]
page1.rotate(90)  # 或 page1.rotate_clockwise(90)
pdfWriter.add_page(page1)

# 寫入新的 PDF 檔案
with open(pdf_output, "wb") as fp:
    pdfWriter.write(fp)

範例ch16-1c.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from PyPDF2 import PdfReader, PdfWriter
import os

pdf_path = os.path.join(os.getcwd(), "PDF", "Python海龜繪圖.pdf")
pdfReader = PdfReader(pdf_path)
fname = os.path.splitext(os.path.basename(pdf_path))[0]

for pageNo, page in enumerate(pdfReader.pages):
    pdfWriter = PdfWriter()
    pdfWriter.add_page(page)

    outputfname = os.path.join("PDF", f"{fname}_p{pageNo + 1}.pdf")
    with open(outputfname, "wb") as fp:
        pdfWriter.write(fp)
        print("分割建立PDF檔:", outputfname)

範例ch16-1d.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from PyPDF2 import PdfReader, PdfWriter
import os

pdf_output = os.path.join(os.getcwd(), "PDF", "Python海龜繪圖3.pdf")

# 讀取三個單頁 PDF
pdfReader1 = PdfReader(os.path.join(os.getcwd(), "PDF", "Python海龜繪圖_p1.pdf"))
pdfReader2 = PdfReader(os.path.join(os.getcwd(), "PDF", "Python海龜繪圖_p2.pdf"))
pdfReader3 = PdfReader(os.path.join(os.getcwd(), "PDF", "Python海龜繪圖_p3.pdf"))

# 建立新的 PDF 寫入器
pdfWriter = PdfWriter()

# 加入各頁
pdfWriter.add_page(pdfReader1.pages[0])
pdfWriter.add_page(pdfReader2.pages[0])
pdfWriter.add_page(pdfReader3.pages[0])

# 寫入新檔案
with open(pdf_output, "wb") as fp:
    pdfWriter.write(fp)
    print("已合併為新 PDF:", pdf_output)

範例ch16-1e.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from PyPDF2 import PdfFileReader, PdfFileWriter
import os

pdf_path = os.getcwd()+"/PDF/Python海龜繪圖.pdf"
watermark_path = os.getcwd()+"/PDF/Python海龜繪圖_浮水印.pdf"
pdf_output = os.getcwd()+"/PDF/Python海龜繪圖4.pdf"
watermarkReader = PdfFileReader(watermark_path)
watermarkpage = watermarkReader.getPage(0)
pdfReader = PdfFileReader(pdf_path)
pdfWriter = PdfFileWriter()
for pageNo in range(pdfReader.getNumPages()):
    page = pdfReader.getPage(pageNo)
    page.mergePage(watermarkpage)
    pdfWriter.addPage(page)
with open(pdf_output, "wb") as fp:
    pdfWriter.write(fp)

2025年5月15日 星期四

[pythonanywhere] 製作水果交易網站(一) -在Django中用pyecharts製作圖表

參考文章:[ django pyecharts ] 製作水果交易網站(一) -在Django中用pyecharts製作圖表

1.在pythonanywhere上新增一個Django app,命名為mysite。

2.在consoles頁面中,利用Bash命令列,新增一個 fruitsapp app。

python manage.py startapp fruitsapp

3.修改mysite/mysite/settings.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 4.0.6.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-e91+!(y*$%$wb_e=)hxkkbpx9dw0&=(383ca6lhx1r$o)p57r-'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['chengmin.pythonanywhere.com']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_echarts',
    'fruitsapp',
]



MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'mysite.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'mysite.wsgi.application'


# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

# default static files settings for PythonAnywhere.
# see https://help.pythonanywhere.com/pages/DjangoStaticFiles for more info
MEDIA_ROOT = '/home/chengmin/mysite/media'
MEDIA_URL = '/media/'
STATIC_ROOT = '/home/chengmin/mysite/static'
STATIC_URL = '/static/'

4.修改mysite/urls.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from fruitsapp.views import index
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index)
]

5.修改mysite/fruitsapp/views.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#from jinja2 import Environment, FileSystemLoader
#from pyecharts.globals import CurrentConfig
from django.http import HttpResponse

#CurrentConfig.GLOBAL_ENV = Environment(loader=FileSystemLoader("./templates"))

from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.globals import ThemeType


def index(request):
    bar = Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))

    bar.add_xaxis(["椪柑", "蘋果 富士進口", "番石榴 珍珠芭", "香蕉", "木瓜 網室紅肉", "甜橙 柳橙", "火龍果 紅肉"])
    bar.add_yaxis("台北一", [29.3, 51.5, 40.3, 21.6, 24.0, 61.5, 34.2])
    bar.add_yaxis("台北二", [32.3, 51.4, 53.7, 24.7, 42.3, 23.4, 35.9])
    bar.set_global_opts(title_opts=opts.TitleOpts(title="平均價", subtitle="元"))

    return HttpResponse(bar.render_embed())


6.執行結果:


2025年5月12日 星期一

用ChatGPT來產生田蛙在斗南交易情形並以EXCEL資料和圖表儲存

 


程式碼:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import requests
import json
from openpyxl import Workbook
from openpyxl.chart import BarChart, Reference
from openpyxl.utils import get_column_letter

# 從URL取得JSON資料
r = requests.get('https://data.moa.gov.tw/Service/OpenData/FromM/AquaticTransData.aspx')
json_data = json.loads(r.text)

# 建立Workbook
wb = Workbook()
ws = wb.active
ws.title = "斗南田蛙交易"

# 寫入標題行
headers = ['交易日期', '品種代碼', '魚貨名稱', '市場名稱', '交易量', '平均價']
ws.append(headers)

# 過濾出「斗南」市場的「田蛙」資料
for item in json_data:
    if '田蛙' in item['魚貨名稱'] and '斗南' in item['市場名稱']:
        row = [
            item.get('交易日期'),
            item.get('品種代碼'),
            item.get('魚貨名稱'),
            item.get('市場名稱'),
            float(item.get('交易量', 0)),
            float(item.get('平均價', 0))
        ]
        ws.append(row)

# 取得資料列數
data_rows = ws.max_row

# 建立長條圖(交易量)
chart = BarChart()
chart.title = "田蛙在斗南市場之交易量"
chart.x_axis.title = "交易日期"
chart.y_axis.title = "交易量"

# 設定圖表資料範圍(x軸是日期,y軸是交易量)
data = Reference(ws, min_col=5, min_row=1, max_row=data_rows)  # 交易量資料
categories = Reference(ws, min_col=1, min_row=2, max_row=data_rows)  # 交易日期

chart.add_data(data, titles_from_data=True)
chart.set_categories(categories)

# 插入圖表到工作表
ws.add_chart(chart, "H2")

# 儲存檔案
wb.save("DouNan_TianWa_Chart.xlsx")
print("已完成:含圖表的 Excel 已儲存為 DouNan_TianWa_Chart.xlsx")

執行結果如文章一開始顯示的圖表。

2025年5月10日 星期六

讓ChatGPT產生樞紐分析表來分析漁貨交易資料

原始漁貨交易檔案來源是執行"條件過濾漁產品交易的資料儲存到EXCEL檔案中"。
ChatGPT產生的程式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd

# 讀取原始 Excel 檔案
file_path = "AquaticTransData.xlsx"
df = pd.read_excel(file_path)

# 建立樞紐分析表:以市場名稱與魚貨名稱為索引,統計交易量總和與平均價平均
pivot_table = pd.pivot_table(
    df,
    index=['市場名稱', '魚貨名稱'],
    values=['交易量', '平均價'],
    aggfunc={'交易量': 'sum', '平均價': 'mean'}
)

# 重設索引方便輸出
pivot_table.reset_index(inplace=True)

# 匯出為新的 Excel 檔案
output_file_path = "AquaticTrans_PivotTable.xlsx"
pivot_table.to_excel(output_file_path, index=False)

print("已成功產出樞紐分析表:", output_file_path)

執行結果:


讓ChatGPT利用Excel函式來計算各漁貨的交易量

參考文章:條件過濾漁產品交易的資料儲存到EXCEL檔案中

提示詞:用Excel的函式來計算不同漁貨的總交易量

ChatGPT產生的程式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import requests
import json
from openpyxl import Workbook
from openpyxl.styles import Font

# 從URL取得JSON資料
r = requests.get('https://data.moa.gov.tw/Service/OpenData/FromM/AquaticTransData.aspx')
json_data = json.loads(r.text)

# 建立Workbook
wb = Workbook()
ws = wb.active
ws.title = "漁產品交易行情"

# 寫入標題行
headers = ['交易日期', '品種代碼', '魚貨名稱', '市場名稱', '交易量', '平均價']
ws.append(headers)

# 寫入資料
row_count = 1  # 包含標題列
fish_names = set()
for item in json_data:
    if ('田蛙' in item['魚貨名稱'] or '午仔魚' in item['魚貨名稱']) and ('台中' not in item['市場名稱'] and '台北' not in item['市場名稱']):
        fish_name = item.get('魚貨名稱')
        row = [
            item.get('交易日期'),
            item.get('品種代碼'),
            fish_name,
            item.get('市場名稱'),
            float(item.get('交易量', 0) or 0),
            float(item.get('平均價', 0) or 0)
        ]
        ws.append(row)
        row_count += 1
        fish_names.add(fish_name)

# 空一列
ws.append([])
summary_start_row = row_count + 2

# 插入每種魚貨的總交易量(用 SUMIF 函式)
ws.cell(row=summary_start_row - 1, column=3, value="魚貨名稱")
ws.cell(row=summary_start_row - 1, column=5, value="總交易量")

for idx, fish_name in enumerate(sorted(fish_names)):
    row = summary_start_row + idx
    ws.cell(row=row, column=3, value=fish_name)
    # 用SUMIF計算魚貨名稱對應的總交易量
    ws.cell(row=row, column=5, value=f'=SUMIF(C2:C{row_count}, C{row}, E2:E{row_count})')

# 儲存檔案
wb.save('AquaticTransData.xlsx')

執行結果:


讓ChatGPT利用Excel函式來計算漁貨交易總交易量和總平均值

參考文章:條件過濾漁產品交易的資料儲存到EXCEL檔案中
提示詞:加上總交易量和總平均價的程式,用Excel的函式來計算

ChatGPT產生的程式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import requests
import json
from openpyxl import Workbook
from openpyxl.styles import Font

# 從URL取得JSON資料
r = requests.get('https://data.moa.gov.tw/Service/OpenData/FromM/AquaticTransData.aspx')
json_data = json.loads(r.text)

# 建立Workbook
wb = Workbook()
ws = wb.active
ws.title = "漁產品交易行情"

# 寫入標題行
headers = ['交易日期', '品種代碼', '魚貨名稱', '市場名稱', '交易量', '平均價']
ws.append(headers)

# 寫入資料
row_count = 1  # 包含標題行
for item in json_data:
    if ('田蛙' in item['魚貨名稱'] or '午仔魚' in item['魚貨名稱']) and ('台中' not in item['市場名稱'] and '台北' not in item['市場名稱']):
        row = [
            item.get('交易日期'),
            item.get('品種代碼'),
            item.get('魚貨名稱'),
            item.get('市場名稱'),
            float(item.get('交易量', 0) or 0),
            float(item.get('平均價', 0) or 0)
        ]
        ws.append(row)
        row_count += 1

# 在下一行寫入總計
ws.append([])  # 空行
total_row = row_count + 2  # 總計列所在的行號

# 使用 Excel 函式插入總交易量與總平均價公式
ws[f'E{total_row}'] = f'=SUM(E2:E{row_count})'
ws[f'F{total_row}'] = f'=AVERAGE(F2:F{row_count})'
ws[f'D{total_row}'] = '總計'

# 粗體顯示總計列
bold_font = Font(bold=True)
ws[f'D{total_row}'].font = bold_font
ws[f'E{total_row}'].font = bold_font
ws[f'F{total_row}'].font = bold_font

# 儲存檔案
wb.save('AquaticTransData.xlsx')

執行結果:


注意! 前一篇文章總平均價是209.69,但這裏的平均值是175.338

讓ChatGPT產生漁貨交易的加上總交易量和總平均價的程式

參考文章:條件過濾漁產品交易的資料儲存到EXCEL檔案中

用上述文章內的程式為基礎,讓ChatGPT產生漁貨交易的加上總交易量和總平均價的程式。
提示詞:加上總交易量和總平均價的程式[並附上參考文章的程式]

ChatGPT產生的程式如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import requests
import json
from openpyxl import Workbook
from openpyxl.styles import Font

# 從URL取得JSON資料
r = requests.get('https://data.moa.gov.tw/Service/OpenData/FromM/AquaticTransData.aspx')
json_data = json.loads(r.text)

# 建立一個新的Workbook
wb = Workbook()
ws = wb.active
ws.title = "漁產品交易行情"

# 寫入標題行
headers = ['交易日期', '品種代碼', '魚貨名稱', '市場名稱', '交易量', '平均價']
ws.append(headers)

# 初始化統計變數
total_volume = 0.0
weighted_price_sum = 0.0

# 寫入資料
for item in json_data:
    if ('田蛙' in item['魚貨名稱'] or '午仔魚' in item['魚貨名稱']) and ('台中' not in item['市場名稱'] and '台北' not in item['市場名稱']):
        try:
            volume = float(item.get('交易量', 0))
            price = float(item.get('平均價', 0))
        except ValueError:
            continue  # 忽略格式錯誤的資料

        row = [
            item.get('交易日期'),
            item.get('品種代碼'),
            item.get('魚貨名稱'),
            item.get('市場名稱'),
            volume,
            price
        ]
        ws.append(row)

        total_volume += volume
        weighted_price_sum += volume * price

# 計算加權平均價(總價值 / 總交易量)
average_price = weighted_price_sum / total_volume if total_volume > 0 else 0

# 插入統計結果
ws.append([])  # 空行
ws.append(['總計', '', '', '', total_volume, round(average_price, 2)])

# 儲存到Excel檔案
wb.save('AquaticTransData.xlsx')

執行結果(AquaticTransData.xlsx):