Scrapy框架介紹之Puppeteer渲染的使用
1、Scrapy框架
Scrapy是用純Python實(shí)現(xiàn)一個(gè)為了爬取網(wǎng)站數(shù)據(jù)、提取結(jié)構(gòu)性數(shù)據(jù)而編寫(xiě)的應(yīng)用框架,用途非常廣泛。
框架的力量,用戶(hù)只需要定制開(kāi)發(fā)幾個(gè)模塊就可以輕松的實(shí)現(xiàn)一個(gè)爬蟲(chóng),用來(lái)抓取網(wǎng)頁(yè)內(nèi)容以及各種圖片,非常之方便。
Scrapy 使用了 Twisted'twɪstɪd異步網(wǎng)絡(luò)框架來(lái)處理網(wǎng)絡(luò)通訊,可以加快我們的下載速度,不用自己去實(shí)現(xiàn)異步框架,并且包含了各種中間件接口,可以靈活的完成各種需求。
- Scrapy Engine(引擎): 負(fù)責(zé)Spider、ItemPipeline、Downloader、Scheduler中間的通訊,信號(hào)、數(shù)據(jù)傳遞等。
- Scheduler(調(diào)度器): 它負(fù)責(zé)接受引擎發(fā)送過(guò)來(lái)的Request請(qǐng)求,并按照一定的方式進(jìn)行整理排列,入隊(duì),當(dāng)引擎需要時(shí),交還給引擎。
- Downloader(下載器):負(fù)責(zé)下載Scrapy Engine(引擎)發(fā)送的所有Requests請(qǐng)求,并將其獲取到的Responses交還給Scrapy Engine(引擎),由引擎交給Spider來(lái)處理,
- Spider(爬蟲(chóng)):它負(fù)責(zé)處理所有Responses,從中分析提取數(shù)據(jù),獲取Item字段需要的數(shù)據(jù),并將需要跟進(jìn)的URL提交給引擎,再次進(jìn)入Scheduler(調(diào)度器),
- Item Pipeline(管道):它負(fù)責(zé)處理Spider中獲取到的Item,并進(jìn)行進(jìn)行后期處理(詳細(xì)分析、過(guò)濾、存儲(chǔ)等)的地方.
- Downloader Middlewares(下載中間件):你可以當(dāng)作是一個(gè)可以自定義擴(kuò)展下載功能的組件。
- Spider Middlewares(Spider中間件):你可以理解為是一個(gè)可以自定擴(kuò)展和操作引擎和Spider中間通信的功能組件(比如進(jìn)入Spider的Responses;和從Spider出去的Requests)
2、Puppeteer渲染
Puppeteer 是 Chrome 開(kāi)發(fā)團(tuán)隊(duì)在 2017 年發(fā)布的一個(gè) Node.js 包,用來(lái)模擬 Chrome 瀏覽器的運(yùn)行。
為了爬取js渲染的html頁(yè)面,我們需要用瀏覽器來(lái)解析js后生成html。在scrapy中可以利用pyppeteer來(lái)實(shí)現(xiàn)對(duì)應(yīng)功能。
完整代碼 📎scrapy-pyppeteer.zip
我們需要新建項(xiàng)目中middlewares.py文件(./項(xiàng)目名/middlewares.py)
import websockets from scrapy.http import HtmlResponse from logging import getLogger import asyncio import pyppeteer import logging from concurrent.futures._base import TimeoutError import base64 import sys import random pyppeteer_level = logging.WARNING logging.getLogger('websockets.protocol').setLevel(pyppeteer_level) logging.getLogger('pyppeteer').setLevel(pyppeteer_level) PY3 = sys.version_info[0] >= 3 def 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_bytes class ProxyMiddleware(object): USER_AGENT = open('useragents.txt').readlines() def process_request(self, request, spider): # 代理服務(wù)器 proxyHost = "t.16yun.cn" proxyPort = "31111" # 代理隧道驗(yàn)證信息 proxyUser = "username" proxyPass = "password" request.meta['proxy'] = "http://{0}:{1}".format(proxyHost, proxyPort) # 添加驗(yàn)證頭 encoded_user_pass = base64ify(proxyUser + ":" + proxyPass) request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass # 設(shè)置IP切換頭(根據(jù)需求) 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', {}))
然后修改項(xiàng)目配置文件 (./項(xiàng)目名/settings.py)
DOWNLOADER_MIDDLEWARES = { 'scrapypyppeteer.middlewares.PyppeteerMiddleware': 543, 'scrapypyppeteer.middlewares.ProxyMiddleware': 100, }
然后我們運(yùn)行程序
到此這篇關(guān)于Scrapy框架介紹之Puppeteer渲染的使用的文章就介紹到這了,更多相關(guān)Scrapy Puppeteer渲染內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python基礎(chǔ)之函數(shù)嵌套知識(shí)總結(jié)
今天帶大家回顧python基礎(chǔ)知識(shí),文中對(duì)Python函數(shù)嵌套作了非常詳細(xì)的知識(shí)總結(jié),對(duì)正在學(xué)習(xí)python基礎(chǔ)的小伙伴們很有幫助,需要的朋友可以參考下2021-05-05關(guān)于PyQt5主窗口圖標(biāo)顯示問(wèn)題匯總
這篇文章主要介紹了關(guān)于PyQt5主窗口圖標(biāo)顯示問(wèn)題匯總,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03python中如何實(shí)現(xiàn)將數(shù)據(jù)分成訓(xùn)練集與測(cè)試集的方法
這篇文章主要介紹了python中如何實(shí)現(xiàn)將數(shù)據(jù)分成訓(xùn)練集與測(cè)試集的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09Python3 Tkinter選擇路徑功能的實(shí)現(xiàn)方法
今天小編就為大家分享一篇Python3 Tkinter選擇路徑功能的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06Python采集某度貼吧排行榜實(shí)戰(zhàn)示例
這篇文章主要為大家介紹了Python采集某度貼吧排行榜實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04Python Dataframe常見(jiàn)索引方式詳解
這篇文章主要介紹了Python Dataframe常見(jiàn)索引方式詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05對(duì)python opencv 添加文字 cv2.putText 的各參數(shù)介紹
今天小編就為大家分享一篇對(duì)python opencv 添加文字 cv2.putText 的各參數(shù)介紹,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12