Scrapy框架之Puppeteer渲染
Puppeteer 是 Chrome 开发团队在 2017 年发布的一个 Node.js 包,用来模拟 Chrome 浏览器的运行。
为了爬取js渲染的html页面,我们需要用浏览器来解析js后生成html。在scrapy中可以利用pyppeteer来实现对应功能。
我们需要新建项目中middlewares.py文件(./项目名/middlewares.py)
import websocketsfrom scrapy.http import HtmlResponsefrom logging import getLoggerimport asyncioimport pyppeteerimport loggingfrom concurrent.futures._base import TimeoutErrorimport base64import sysimport randompyppeteer_level = logging.WARNINGlogging.getLogger('websockets.protocol').setLevel(pyppeteer_level)logging.getLogger('pyppeteer').setLevel(pyppeteer_level)PY3 = sys.version_info[0] >= 3def base64ify(bytes_or_str): if PY3 and isinstance(bytes_or_str, str): input_bytes = bytes_or_str.encode('utf8') else: input_bytes = bytes_or_str output_bytes = base64.urlsafe_b64encode(input_bytes) if PY3: return output_bytes.decode('ascii') else: return output_bytesclass ProxyMiddleware(object): USER_AGENT = open('useragents.txt').readlines() def process_request(self, request, spider): # 代理服务器 proxyHost = "t.16yun.cn" proxyPort = "31111" # 代理隧道验证信息 proxyUser = "username" proxyPass = "password" request.meta['proxy'] = "http://{0}:{1}".format(proxyHost, proxyPort) # 添加验证头 encoded_user_pass = base64ify(proxyUser + ":" + proxyPass) request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass # 设置IP切换头(根据需求) tunnel = random.randint(1, 10000) request.headers['Proxy-Tunnel'] = str(tunnel) request.headers['User-Agent'] = random.choice(self.USER_AGENT)class PyppeteerMiddleware(object): def __init__(self, **args): """ init logger, loop, browser :param args: """ self.logger = getLogger(__name__) self.loop = asyncio.get_event_loop() self.browser = self.loop.run_until_complete( pyppeteer.launch(headless=True)) self.args = args def __del__(self): """ close loop :return: """ self.loop.close() def render(self, url, retries=1, script=None, wait=0.3, scrolldown=False, sleep=0, timeout=8.0, keep_page=False): """ render page with pyppeteer :param url: page url :param retries: max retry times :param script: js script to evaluate :param wait: number of seconds to wait before loading the page, preventing timeouts :param scrolldown: how many times to page down :param sleep: how many long to sleep after initial render :param timeout: the longest wait time, otherwise raise timeout error :param keep_page: keep page not to be closed, browser object needed :param browser: pyppetter browser object :param with_result: return with js evaluation result :return: content, [result] """ # define async render async def async_render(url, script, scrolldown, sleep, wait, timeout, keep_page): try: # basic render page = await self.browser.newPage() await asyncio.sleep(wait) response = await page.goto(url, options={'timeout': int(timeout * 1000)}) if response.status != 200: return None, None, response.status result = None # evaluate with script if script: result = await page.evaluate(script) # scroll down for {scrolldown} times if scrolldown: for _ in range(scrolldown): await page._keyboard.down('PageDown') await asyncio.sleep(sleep) else: await asyncio.sleep(sleep) if scrolldown: await page._keyboard.up('PageDown') # get html of page content = await page.content() return content, result, response.status except TimeoutError: return None, None, 500 finally: # if keep page, do not close it if not keep_page: await page.close() content, result, status = [None] * 3 # retry for {retries} times for i in range(retries): if not content: content, result, status = self.loop.run_until_complete( async_render(url=url, script=script, sleep=sleep, wait=wait, scrolldown=scrolldown, timeout=timeout, keep_page=keep_page)) else: break # if need to return js evaluation result return content, result, status def process_request(self, request, spider): """ :param request: request object :param spider: spider object :return: HtmlResponse """ if request.meta.get('render'): try: self.logger.debug('rendering %s', request.url) html, result, status = self.render(request.url) return HtmlResponse(url=request.url, body=html, request=request, encoding='utf-8', status=status) except websockets.exceptions.ConnectionClosed: pass @classmethod def from_crawler(cls, crawler): return cls(**crawler.settings.get('PYPPETEER_ARGS', {}))
然后修改项目配置文件 (./项目名/settings.py)
DOWNLOADER_MIDDLEWARES = { 'scrapypyppeteer.middlewares.PyppeteerMiddleware': 543, 'scrapypyppeteer.middlewares.ProxyMiddleware': 100, }
然后我们运行程序
Scrapy框架之Puppeteer渲染
xiaotaomi
会员积分:6520
Puppeteer 是 Chrome 开发团队在 2017 年发布的一个 Node.js 包,用来模拟 Chrome 浏览器的运行。
为了爬取js渲染的html页面,我们需要用浏览器来解析js后生成html。在scrapy中可以利用pyppeteer来实现对应功能。
我们需要新建项目中middlewares.py文件(./项目名/middlewares.py)
import websocketsfrom scrapy.http import HtmlResponsefrom logging import getLoggerimport asyncioimport pyppeteerimport loggingfrom concurrent.futures._base import TimeoutErrorimport base64import sysimport randompyppeteer_level = logging.WARNINGlogging.getLogger('websockets.protocol').setLevel(pyppeteer_level)logging.getLogger('pyppeteer').setLevel(pyppeteer_level)PY3 = sys.version_info[0] >= 3def base64ify(bytes_or_str): if PY3 and isinstance(bytes_or_str, str): input_bytes = bytes_or_str.encode('utf8') else: input_bytes = bytes_or_str output_bytes = base64.urlsafe_b64encode(input_bytes) if PY3: return output_bytes.decode('ascii') else: return output_bytesclass ProxyMiddleware(object): USER_AGENT = open('useragents.txt').readlines() def process_request(self, request, spider): # 代理服务器 proxyHost = "t.16yun.cn" proxyPort = "31111" # 代理隧道验证信息 proxyUser = "username" proxyPass = "password" request.meta['proxy'] = "http://{0}:{1}".format(proxyHost, proxyPort) # 添加验证头 encoded_user_pass = base64ify(proxyUser + ":" + proxyPass) request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass # 设置IP切换头(根据需求) tunnel = random.randint(1, 10000) request.headers['Proxy-Tunnel'] = str(tunnel) request.headers['User-Agent'] = random.choice(self.USER_AGENT)class PyppeteerMiddleware(object): def __init__(self, **args): """ init logger, loop, browser :param args: """ self.logger = getLogger(__name__) self.loop = asyncio.get_event_loop() self.browser = self.loop.run_until_complete( pyppeteer.launch(headless=True)) self.args = args def __del__(self): """ close loop :return: """ self.loop.close() def render(self, url, retries=1, script=None, wait=0.3, scrolldown=False, sleep=0, timeout=8.0, keep_page=False): """ render page with pyppeteer :param url: page url :param retries: max retry times :param script: js script to evaluate :param wait: number of seconds to wait before loading the page, preventing timeouts :param scrolldown: how many times to page down :param sleep: how many long to sleep after initial render :param timeout: the longest wait time, otherwise raise timeout error :param keep_page: keep page not to be closed, browser object needed :param browser: pyppetter browser object :param with_result: return with js evaluation result :return: content, [result] """ # define async render async def async_render(url, script, scrolldown, sleep, wait, timeout, keep_page): try: # basic render page = await self.browser.newPage() await asyncio.sleep(wait) response = await page.goto(url, options={'timeout': int(timeout * 1000)}) if response.status != 200: return None, None, response.status result = None # evaluate with script if script: result = await page.evaluate(script) # scroll down for {scrolldown} times if scrolldown: for _ in range(scrolldown): await page._keyboard.down('PageDown') await asyncio.sleep(sleep) else: await asyncio.sleep(sleep) if scrolldown: await page._keyboard.up('PageDown') # get html of page content = await page.content() return content, result, response.status except TimeoutError: return None, None, 500 finally: # if keep page, do not close it if not keep_page: await page.close() content, result, status = [None] * 3 # retry for {retries} times for i in range(retries): if not content: content, result, status = self.loop.run_until_complete( async_render(url=url, script=script, sleep=sleep, wait=wait, scrolldown=scrolldown, timeout=timeout, keep_page=keep_page)) else: break # if need to return js evaluation result return content, result, status def process_request(self, request, spider): """ :param request: request object :param spider: spider object :return: HtmlResponse """ if request.meta.get('render'): try: self.logger.debug('rendering %s', request.url) html, result, status = self.render(request.url) return HtmlResponse(url=request.url, body=html, request=request, encoding='utf-8', status=status) except websockets.exceptions.ConnectionClosed: pass @classmethod def from_crawler(cls, crawler): return cls(**crawler.settings.get('PYPPETEER_ARGS', {}))
然后修改项目配置文件 (./项目名/settings.py)
DOWNLOADER_MIDDLEWARES = { 'scrapypyppeteer.middlewares.PyppeteerMiddleware': 543, 'scrapypyppeteer.middlewares.ProxyMiddleware': 100, }
然后我们运行程序
20-06-30 17:43
1242
0
回复
暂无评论