22import base64
33import datetime
44import math
5+ from contextlib import asynccontextmanager
56from pathlib import Path
6- from typing import Literal , Optional
7+ from typing import Literal , Optional , Tuple
78
89import httpx
910import orjson as json
@@ -108,44 +109,33 @@ def __init__(self,
108109 self .browser_close = self .browser .close
109110 self .logger = self .browser .logger
110111
111- class RenderPage :
112-
113- def __init__ (self , parent : 'WebRender' , width = base_width , height = base_height , locale = "zh_cn" , content = None ,
114- url = None , css = None , stealth = True ):
115- self .width = width
116- self .height = height
117- self .locale = locale
118- self .content = content
119- self .url = url
120- self .css = css
121- self .stealth = stealth
122- self .browser = parent .browser
123- self .debug = parent .debug
124-
125- async def __aenter__ (self ):
126- self .start_time = datetime .datetime .now ().timestamp ()
127- self .page = await self .browser .new_page (width = self .width ,
128- height = self .height ,
129- locale = self .locale ,
130- stealth = self .stealth )
131- if self .content :
132- await self .page .set_content (self .content , wait_until = "networkidle" )
133- if self .url :
134- await self .page .goto (self .url , wait_until = "networkidle" )
135- if self .content or self .url :
112+ @asynccontextmanager
113+ async def render_page (self , width = base_width , height = base_height , locale = "zh_cn" , content = None ,
114+ url = None , css = None , stealth = True ):
115+ page = None
116+ try :
117+ start_time = datetime .datetime .now ().timestamp ()
118+ page = await self .browser .new_page (width = width ,
119+ height = height ,
120+ locale = locale ,
121+ stealth = stealth )
122+ if content :
123+ await page .set_content (content , wait_until = "networkidle" )
124+ if url :
125+ await page .goto (url , wait_until = "networkidle" )
126+ if content or url :
136127 with open (f"{ templates_path } /custom.css" , "r" , encoding = "utf-8" ) as f :
137128 custom_css = f .read ()
138- await self .page .add_style_tag (content = custom_css )
139- if self .css :
140- await self .page .add_style_tag (content = self .css )
141- return self
142-
143- async def __aexit__ (self , exc_type , exc_val , exc_tb ):
144- if not self .debug :
145- await self .page .close ()
129+ await page .add_style_tag (content = custom_css )
130+ if css :
131+ await page .add_style_tag (content = css )
132+ yield page , start_time
133+ finally :
134+ if not self .debug and page :
135+ await page .close ()
146136
147137 @staticmethod
148- async def select_element (el : str | list , pg : Page ) -> ( ElementHandle , str ) :
138+ async def select_element (el : str | list , pg : Page ) -> Tuple [ Optional [ ElementHandle ], Optional [ str ]] :
149139 if isinstance (el , str ):
150140 return (await pg .query_selector (el )), el
151141 for obj in el :
@@ -228,20 +218,19 @@ async def select_element_and_screenshot(self,
228218
229219 @webrender_fallback
230220 async def legacy_screenshot (self , options : LegacyScreenshotOptions ):
231- async with self .RenderPage (self ,
232- width = options .width ,
233- height = options .height ,
234- locale = options .locale ,
235- content = await env .get_template ("content.html" ).render_async (language = "zh-CN" ,
236- contents = options .content ),
237- url = options .url ,
238- css = options .css ,
239- stealth = options .stealth ) as p :
221+ async with self .render_page (width = options .width ,
222+ height = options .height ,
223+ locale = options .locale ,
224+ content = await env .get_template ("content.html" ).render_async (language = "zh-CN" ,
225+ contents = options .content ),
226+ url = options .url ,
227+ css = options .css ,
228+ stealth = options .stealth ) as (page , start_time ):
240229 images = await self .select_element_and_screenshot (
241230 elements = ["body > .mw-parser-output > *:not(script):not(style):not(link):not(meta)" if options .mw
242231 else "body > *:not(script):not(style):not(link):not(meta)" ],
243- page = p . page ,
244- start_time = p . start_time ,
232+ page = page ,
233+ start_time = start_time ,
245234 count_time = options .counttime ,
246235 output_type = options .output_type ,
247236 output_quality = options .output_quality
@@ -251,18 +240,18 @@ async def legacy_screenshot(self, options: LegacyScreenshotOptions):
251240 @webrender_fallback
252241 async def page_screenshot (self , options : PageScreenshotOptions ):
253242
254- async with self .RenderPage ( self ,
255- width = options .width ,
256- height = options .height ,
257- locale = options .locale ,
258- content = options .content ,
259- url = options .url ,
260- css = options .css ,
261- stealth = options .stealth ) as p :
243+ async with self .render_page (
244+ width = options .width ,
245+ height = options .height ,
246+ locale = options .locale ,
247+ content = options .content ,
248+ url = options .url ,
249+ css = options .css ,
250+ stealth = options .stealth ) as ( page , start_time ) :
262251 images = await self .select_element_and_screenshot (
263252 elements = ["body" ],
264- page = p . page ,
265- start_time = p . start_time ,
253+ page = page ,
254+ start_time = start_time ,
266255 count_time = options .counttime ,
267256 output_type = options .output_type ,
268257 output_quality = options .output_quality
@@ -271,22 +260,22 @@ async def page_screenshot(self, options: PageScreenshotOptions):
271260
272261 @webrender_fallback
273262 async def element_screenshot (self , options : ElementScreenshotOptions ):
274- async with self .RenderPage ( self ,
275- width = options .width ,
276- height = options .height ,
277- locale = options .locale ,
278- content = options .content ,
279- url = options .url ,
280- css = options .css ,
281- stealth = options .stealth ) as p :
263+ async with self .render_page (
264+ width = options .width ,
265+ height = options .height ,
266+ locale = options .locale ,
267+ content = options .content ,
268+ url = options .url ,
269+ css = options .css ,
270+ stealth = options .stealth ) as ( page , start_time ) :
282271 with open (f"{ templates_path } /element_screenshot_evaluate.js" , "r" , encoding = "utf-8" ) as f :
283272 js_code = f .read ()
284273
285- await p . page .evaluate (js_code , elements_to_disable )
274+ await page .evaluate (js_code , elements_to_disable )
286275 images = await self .select_element_and_screenshot (
287276 elements = options .element ,
288- page = p . page ,
289- start_time = p . start_time ,
277+ page = page ,
278+ start_time = start_time ,
290279 count_time = options .counttime ,
291280 output_type = options .output_type ,
292281 output_quality = options .output_quality
@@ -295,22 +284,22 @@ async def element_screenshot(self, options: ElementScreenshotOptions):
295284
296285 @webrender_fallback
297286 async def section_screenshot (self , options : SectionScreenshotOptions ):
298- async with self .RenderPage ( self ,
299- width = options .width ,
300- height = options .height ,
301- locale = options .locale ,
302- content = options .content ,
303- url = options .url ,
304- css = options .css ,
305- stealth = options .stealth ) as p :
287+ async with self .render_page (
288+ width = options .width ,
289+ height = options .height ,
290+ locale = options .locale ,
291+ content = options .content ,
292+ url = options .url ,
293+ css = options .css ,
294+ stealth = options .stealth ) as ( page , start_time ) :
306295 with open (f"{ templates_path } /section_screenshot_evaluate.js" , "r" , encoding = "utf-8" ) as f :
307296 js_code = f .read ()
308297
309- await p . page .evaluate (js_code , {"section" : options .section , "elements_to_disable" : elements_to_disable })
298+ await page .evaluate (js_code , {"section" : options .section , "elements_to_disable" : elements_to_disable })
310299 images = await self .select_element_and_screenshot (
311300 elements = ".bot-sectionbox" ,
312- page = p . page ,
313- start_time = p . start_time ,
301+ page = page ,
302+ start_time = start_time ,
314303 count_time = options .counttime ,
315304 output_type = options .output_type ,
316305 output_quality = options .output_quality
@@ -322,23 +311,23 @@ async def source(self, options: SourceOptions):
322311 url = options .url
323312 if not url :
324313 raise RequiredURL
325- async with self .RenderPage ( self ,
326- locale = options .locale ,
327- url = options .url ,
328- stealth = options .stealth ) as p :
314+ async with self .render_page (
315+ locale = options .locale ,
316+ url = options .url ,
317+ stealth = options .stealth ) as ( page , start_time ) :
329318
330- resp = await p . page .goto (url , wait_until = "networkidle" )
319+ resp = await page .goto (url , wait_until = "networkidle" )
331320 if resp .status != 200 : # attempt to fetch the url content using fetch
332- get = await p . page .request .fetch (url )
321+ get = await page .request .fetch (url )
333322 if get .status == 200 :
334323 return get .text ()
335324 self .logger .error (f"Failed to fetch URL: {
336325 url } , status code: { get .status } " )
337326 return None
338327
339- _source = await p . page .content ()
328+ _source = await page .content ()
340329 if options .raw_text :
341- _source = await p . page .query_selector ("pre" )
330+ _source = await page .query_selector ("pre" )
342331 return await _source .inner_text ()
343332
344333 return _source
0 commit comments