tomo2chin2 commited on
Commit
5a40515
·
verified ·
1 Parent(s): 75301b6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -19
app.py CHANGED
@@ -10,12 +10,59 @@ import tempfile
10
  import time
11
  import os
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  def render_fullpage_screenshot(html_code):
 
 
 
 
 
 
14
  tmp_file = tempfile.NamedTemporaryFile(suffix=".html", delete=False)
15
  tmp_path = tmp_file.name
16
  tmp_file.write(html_code.encode('utf-8'))
17
  tmp_file.close()
18
 
 
19
  options = Options()
20
  options.add_argument("--headless")
21
  options.add_argument("--no-sandbox")
@@ -24,16 +71,17 @@ def render_fullpage_screenshot(html_code):
24
 
25
  try:
26
  driver = webdriver.Chrome(options=options)
 
27
  driver.set_window_size(1200, 800)
28
  driver.get("file://" + tmp_path)
29
 
30
- # ページロード待機
31
  WebDriverWait(driver, 10).until(
32
  EC.presence_of_element_located((By.TAG_NAME, "body"))
33
  )
34
  time.sleep(2)
35
 
36
- # 固定要素を強制的に「static」にして、重複しにくくする
37
  driver.execute_script(
38
  """
39
  const elems = document.querySelectorAll('*');
@@ -47,39 +95,55 @@ def render_fullpage_screenshot(html_code):
47
  )
48
  time.sleep(1)
49
 
50
- # ページ高さを取得
51
  viewport_height = driver.execute_script("return window.innerHeight")
52
  scroll_height = driver.execute_script("return document.body.scrollHeight")
53
 
54
- images = []
 
55
  current_position = 0
56
 
57
  while True:
58
- # スクリーンショット
59
  png = driver.get_screenshot_as_png()
60
  img = Image.open(BytesIO(png))
61
- images.append(img)
62
 
63
- # 最下部まで行ったら終了
64
  if current_position + viewport_height >= scroll_height:
65
  break
66
 
67
- # 次のスクロール位置へ移動
68
  current_position += viewport_height
69
  driver.execute_script(f"window.scrollTo(0, {current_position})")
70
  time.sleep(1)
71
 
72
- # 縦方向に結合
73
- total_width = max(img.width for img in images)
74
- total_height = sum(img.height for img in images)
75
- full_screenshot = Image.new('RGB', (total_width, total_height))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
- current_y = 0
78
- for img in images:
79
- full_screenshot.paste(img, (0, current_y))
80
- current_y += img.height
81
 
82
  except Exception as e:
 
83
  return Image.new('RGB', (1, 1), color=(0, 0, 0))
84
 
85
  finally:
@@ -87,14 +151,18 @@ def render_fullpage_screenshot(html_code):
87
  if os.path.exists(tmp_path):
88
  os.remove(tmp_path)
89
 
90
- return full_screenshot
91
 
 
92
  iface = gr.Interface(
93
  fn=render_fullpage_screenshot,
94
  inputs=gr.Textbox(lines=15, label="HTMLコード入力"),
95
  outputs=gr.Image(type="pil", label="フルページスクリーンショット"),
96
- title="Scrolling Screenshot (Remove Fixed Elements)",
97
- description="固定要素をstaticに変更して、重複を防ぎつつページ全体をキャプチャします。"
 
 
 
98
  )
99
 
100
  if __name__ == "__main__":
 
10
  import time
11
  import os
12
 
13
+ def find_vertical_overlap(img_top: Image.Image, img_bottom: Image.Image) -> int:
14
+ """
15
+ 2つの画像(img_topの下端 と img_bottomの上端)で、
16
+ 連続して同じピクセルがどのくらいあるか(行数)を返す。
17
+
18
+ 前提:
19
+ - 画像の幅は同じと仮定
20
+ - 下端から上端へ向けて、どこまで連続して行が一致するかを判定する
21
+ """
22
+ width = min(img_top.width, img_bottom.width)
23
+ height_top = img_top.height
24
+ height_bottom = img_bottom.height
25
+
26
+ # ピクセルアクセス用
27
+ pix_top = img_top.load()
28
+ pix_bottom = img_bottom.load()
29
+
30
+ # 重複の最大可能行数
31
+ max_overlap = min(height_top, height_bottom)
32
+
33
+ overlap_count = 0
34
+ # 下から上へ連続一致をチェック
35
+ for offset in range(max_overlap):
36
+ # img_topの bottom-(offset+1) 行と、img_bottomの offset 行を比較
37
+ row_matched = True
38
+ y_top = height_top - 1 - offset
39
+ y_bottom = offset
40
+
41
+ for x in range(width):
42
+ if pix_top[x, y_top] != pix_bottom[x, y_bottom]:
43
+ row_matched = False
44
+ break
45
+
46
+ if row_matched:
47
+ overlap_count += 1
48
+ else:
49
+ break
50
+
51
+ return overlap_count
52
+
53
  def render_fullpage_screenshot(html_code):
54
+ """
55
+ (1) position: fixed / sticky 要素を無効化して、
56
+ (2) ページをビューポート単位で複数回スクロールしながらスクショ取得、
57
+ (3) 隣接する画像で重複行を検出し、切り詰めて縦方向に結合
58
+ """
59
+ # 1) HTMLを一時ファイルに保存
60
  tmp_file = tempfile.NamedTemporaryFile(suffix=".html", delete=False)
61
  tmp_path = tmp_file.name
62
  tmp_file.write(html_code.encode('utf-8'))
63
  tmp_file.close()
64
 
65
+ # 2) ヘッドレスChrome起動オプション
66
  options = Options()
67
  options.add_argument("--headless")
68
  options.add_argument("--no-sandbox")
 
71
 
72
  try:
73
  driver = webdriver.Chrome(options=options)
74
+ # 初期ウィンドウサイズを設定
75
  driver.set_window_size(1200, 800)
76
  driver.get("file://" + tmp_path)
77
 
78
+ # 3) ページロード完了を待つ
79
  WebDriverWait(driver, 10).until(
80
  EC.presence_of_element_located((By.TAG_NAME, "body"))
81
  )
82
  time.sleep(2)
83
 
84
+ # 4) 固定要素をstaticに書き換えて重複の原因を減らす
85
  driver.execute_script(
86
  """
87
  const elems = document.querySelectorAll('*');
 
95
  )
96
  time.sleep(1)
97
 
98
+ # 5) ページの高さなどを取得
99
  viewport_height = driver.execute_script("return window.innerHeight")
100
  scroll_height = driver.execute_script("return document.body.scrollHeight")
101
 
102
+ # 6) スクロールしながら複数回キャプチャ
103
+ screenshots = []
104
  current_position = 0
105
 
106
  while True:
107
+ # スクリーンショット取得
108
  png = driver.get_screenshot_as_png()
109
  img = Image.open(BytesIO(png))
110
+ screenshots.append(img)
111
 
112
+ # 最下部まで来たらループ終了
113
  if current_position + viewport_height >= scroll_height:
114
  break
115
 
 
116
  current_position += viewport_height
117
  driver.execute_script(f"window.scrollTo(0, {current_position})")
118
  time.sleep(1)
119
 
120
+ # 7) スクリーンショットを縦方向に結合(重複行を検出して除去)
121
+ if not screenshots:
122
+ return Image.new('RGB', (1, 1), color=(0, 0, 0)) # 何も撮れてない場合
123
+
124
+ # merged に順次結合していく
125
+ merged = screenshots[0]
126
+ for i in range(1, len(screenshots)):
127
+ overlap = find_vertical_overlap(merged, screenshots[i])
128
+ # 重複行(overlap)だけを切り落とした領域を追加
129
+ if overlap > 0:
130
+ # overlap分だけ上から切り落とす
131
+ cropped = screenshots[i].crop((0, overlap, screenshots[i].width, screenshots[i].height))
132
+ else:
133
+ cropped = screenshots[i]
134
+
135
+ # 新しい高さ分のキャンバスを作り、mergedとcroppedを貼り付ける
136
+ new_height = merged.height + cropped.height
137
+ new_img = Image.new('RGB', (merged.width, new_height))
138
+ new_img.paste(merged, (0, 0))
139
+ new_img.paste(cropped, (0, merged.height))
140
+
141
+ merged = new_img
142
 
143
+ final_image = merged
 
 
 
144
 
145
  except Exception as e:
146
+ # 例外時は1x1黒画像を返す
147
  return Image.new('RGB', (1, 1), color=(0, 0, 0))
148
 
149
  finally:
 
151
  if os.path.exists(tmp_path):
152
  os.remove(tmp_path)
153
 
154
+ return final_image
155
 
156
+ # Gradioインターフェース
157
  iface = gr.Interface(
158
  fn=render_fullpage_screenshot,
159
  inputs=gr.Textbox(lines=15, label="HTMLコード入力"),
160
  outputs=gr.Image(type="pil", label="フルページスクリーンショット"),
161
+ title="Scrolling Screenshot (Static + Overlap Removal)",
162
+ description=(
163
+ "固定要素をstaticに書き換えたうえで、スクロール中に発生する"
164
+ "重複領域を画像解析で取り除いて縦方向に結合します。"
165
+ )
166
  )
167
 
168
  if __name__ == "__main__":