用于 Scrapy 和 Gerapy 的 Pyppeteer 组件
项目描述
杰拉皮·皮皮特
这是一个在 Scrapy 中支持 pyppeteer 的包,这个包也是 Gerapy 中的一个模块。
安装
pip3 install gerapy-pyppeteer
用法
您可以使用PyppeteerRequest指定使用 pyppeteer 呈现的请求。
例如:
yield PyppeteerRequest(detail_url, callback=self.parse_detail)
而且您还需要PyppeteerMiddleware启用DOWNLOADER_MIDDLEWARES:
DOWNLOADER_MIDDLEWARES = {
'gerapy_pyppeteer.downloadermiddlewares.PyppeteerMiddleware': 543,
}
恭喜,您已完成所有必需的配置。
如果您再次运行 Spider,Pyppeteer 将开始渲染您将请求配置为 PyppeteerRequest 的每个网页。
设置
GerapyPyppeteer 提供了一些可选设置。
并发
你可以直接使用 Scrapy 的设置来设置 Pyppeteer 的 Concurrency,例如:
CONCURRENT_REQUESTS = 3
伪装成真正的浏览器
有些网站会检测到 WebDriver 或 Headless,GerapyPyppeteer 可以通过注入脚本来伪装 Chromium。这是默认启用的。
如果网站没有检测到 WebDriver 可以关闭它来加速:
GERAPY_PYPPETEER_PRETEND = False
您也可以使用pretend属性PyppeteerRequest来覆盖此配置。
日志记录级别
默认情况下,Pyppeteer 会记录所有的调试信息,因此 GerapyPyppeteer 将 Pyppeteer 的日志记录级别配置为 WARNING。
如果您想查看更多来自 Pyppeteer 的日志,可以更改此设置:
import logging
GERAPY_PYPPETEER_LOGGING_LEVEL = logging.DEBUG
下载超时
Pyppeteer 可能需要一些时间来呈现所需的网页,您也可以更改此设置,默认为30s:
# pyppeteer timeout
GERAPY_PYPPETEER_DOWNLOAD_TIMEOUT = 30
无头
默认情况下,Pyppeteer 运行在Headlessmode 下,您也可以False根据需要将其更改为,默认为True:
GERAPY_PYPPETEER_HEADLESS = False
窗口大小
您还可以设置 Pyppeteer 窗口的宽度和高度:
GERAPY_PYPPETEER_WINDOW_WIDTH = 1400
GERAPY_PYPPETEER_WINDOW_HEIGHT = 700
默认值为 1400、700。
Pyppeteer Args
您还可以更改 Pyppeteer 的 args,例如dumpio,devtools等。
可选设置及其默认值:
GERAPY_PYPPETEER_DUMPIO = False
GERAPY_PYPPETEER_DEVTOOLS = False
GERAPY_PYPPETEER_EXECUTABLE_PATH = None
GERAPY_PYPPETEER_DISABLE_EXTENSIONS = True
GERAPY_PYPPETEER_HIDE_SCROLLBARS = True
GERAPY_PYPPETEER_MUTE_AUDIO = True
GERAPY_PYPPETEER_NO_SANDBOX = True
GERAPY_PYPPETEER_DISABLE_SETUID_SANDBOX = True
GERAPY_PYPPETEER_DISABLE_GPU = True
禁用特定资源类型的加载
您可以禁用特定资源类型的加载以减少网页的加载时间。您可以使用以下方式配置禁用的资源类型GERAPY_PYPPETEER_IGNORE_RESOURCE_TYPES:
GERAPY_PYPPETEER_IGNORE_RESOURCE_TYPES = []
例如,如果你想禁用 css 和 javascript 的加载,你可以设置如下:
GERAPY_PYPPETEER_IGNORE_RESOURCE_TYPES = ['stylesheet', 'script']
所有可选资源类型列表:
- 文档:原始 HTML 文档
- 样式表:CSS 文件
- 脚本:JavaScript 文件
- 图片:图片
- 媒体:媒体文件,例如音频或视频
- 字体:字体文件
- texttrack:文本轨道文件
- xhr: Ajax 请求
- fetch:获取请求
- 事件源:事件源
- 网络套接字:网络套接字
- manifest:清单文件
- 其他:其他文件
截屏
您可以获得加载页面的屏幕截图,您可以将screenshotargs 传递给PyppeteerRequest作为 dict:
type(str):指定截图类型,可以是jpeg或png。默认为png.quality(int):图像的质量,介于 0-100 之间。不适用于png图像。fullPage(bool):当为真时,截取整个可滚动页面的屏幕截图。默认为False.clip(dict):指定页面剪切区域的对象。此选项应具有以下字段:x(int): 剪辑区域左上角的 x 坐标。y(int): 剪辑区域左上角的 y 坐标。width(int): 裁剪区域的宽度。height(int): 裁剪区域的高度。
omitBackground(bool):隐藏默认的白色背景并允许以透明方式捕获屏幕截图。encoding(str):图像的编码,可以是base64或binary。默认为binary. 如果是二进制,它将返回BytesIO对象。
例如:
yield PyppeteerRequest(start_url, callback=self.parse_index, wait_for='.item .name', screenshot={
'type': 'png',
'fullPage': True
})
然后你可以得到截图结果response.meta['screenshot']:
最简单的保存到文件:
def parse_index(self, response):
with open('screenshot.png', 'wb') as f:
f.write(response.meta['screenshot'].getbuffer())
如果您想为所有请求启用屏幕截图,您可以通过GERAPY_PYPPETEER_SCREENSHOT.
例如:
GERAPY_PYPPETEER_SCREENSHOT = {
'type': 'png',
'fullPage': True
}
PyppeteerRequest
PyppeteerRequest提供可以覆盖上述全局设置的参数。
- 网址:请求网址
- 回调:回调
- “load”、“domcontentloaded”、“networkidle0”、“networkidle2”之一。见https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.goto,默认为
domcontentloaded - wait_for:等待某个元素加载,也支持dict
- 脚本:要执行的脚本
- proxy:这次使用代理,比如
http://x.x.x.x:x - sleep:加载后的睡眠时间,覆盖
GERAPY_PYPPETEER_SLEEP - timeout:加载超时,覆盖
GERAPY_PYPPETEER_DOWNLOAD_TIMEOUT - ignore_resource_types:忽略的资源类型,覆盖
GERAPY_PYPPETEER_IGNORE_RESOURCE_TYPES - 伪装:伪装成普通浏览器,覆盖
GERAPY_PYPPETEER_PRETEND - 截图:忽略的资源类型,见
https://miyakogi.github.io/pyppeteer/_modules/pyppeteer/page.html#Page.screenshot,覆盖
GERAPY_PYPPETEER_SCREENSHOT
例如,您可以将 PyppeteerRequest 配置为:
from gerapy_pyppeteer import PyppeteerRequest
def parse(self, response):
yield PyppeteerRequest(url,
callback=self.parse_detail,
wait_until='domcontentloaded',
wait_for='title',
script='() => { return {name: "Germey"} }',
sleep=2)
然后 Pyppeteer 将:
- 等待文件加载
- 等待标题加载
- 执行
console.log(document)脚本 - 睡2s
- 返回渲染的网页内容,获取自
response.meta['screenshot'] - 返回脚本执行结果,获取自
response.meta['script_result']
对于 JavaScript 控制的等待机制,可以使用 await in script,例如:
js = '''async () => {
await new Promise(resolve => setTimeout(resolve, 10000));
return {
'name': 'Germey'
}
}
'''
yield PyppeteerRequest(url, callback=self.parse, script=js)
然后你可以得到脚本结果response.meta['script_result'],结果是{'name': 'Germey'}。
如果您认为 JavaScript 是有线编写的,您可以使用 actions 参数来定义一个函数来执行Python基于函数的函数,例如:
async def execute_actions(page: Page):
await page.evaluate('() => { document.title = "Hello World"; }')
return 1
yield PyppeteerRequest(url, callback=self.parse, actions=execute_actions)
然后你可以得到动作结果response.meta['actions_result'],结果是1。
例子
有关详细信息,请参阅示例。
您也可以直接使用 Docker 运行:
docker run germey/gerapy-pyppeteer-example
输出:
2020-07-13 01:49:13 [scrapy.utils.log] INFO: Scrapy 2.2.0 started (bot: example)
2020-07-13 01:49:13 [scrapy.utils.log] INFO: Versions: lxml 4.3.3.0, libxml2 2.9.9, cssselect 1.1.0, parsel 1.6.0, w3lib 1.22.0, Twisted 20.3.0, Python 3.7.7 (default, May 6 2020, 04:59:01) - [Clang 4.0.1 (tags/RELEASE_401/final)], pyOpenSSL 19.1.0 (OpenSSL 1.1.1d 10 Sep 2019), cryptography 2.8, Platform Darwin-19.4.0-x86_64-i386-64bit
2020-07-13 01:49:13 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor
2020-07-13 01:49:13 [scrapy.crawler] INFO: Overridden settings:
{'BOT_NAME': 'example',
'CONCURRENT_REQUESTS': 3,
'NEWSPIDER_MODULE': 'example.spiders',
'RETRY_HTTP_CODES': [403, 500, 502, 503, 504],
'SPIDER_MODULES': ['example.spiders']}
2020-07-13 01:49:13 [scrapy.extensions.telnet] INFO: Telnet Password: 83c276fb41754bd0
2020-07-13 01:49:13 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
'scrapy.extensions.telnet.TelnetConsole',
'scrapy.extensions.memusage.MemoryUsage',
'scrapy.extensions.logstats.LogStats']
2020-07-13 01:49:13 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
'gerapy_pyppeteer.downloadermiddlewares.PyppeteerMiddleware',
'scrapy.downloadermiddlewares.retry.RetryMiddleware',
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
'scrapy.downloadermiddlewares.stats.DownloaderStats']
2020-07-13 01:49:13 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
'scrapy.spidermiddlewares.referer.RefererMiddleware',
'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
'scrapy.spidermiddlewares.depth.DepthMiddleware']
2020-07-13 01:49:13 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2020-07-13 01:49:13 [scrapy.core.engine] INFO: Spider opened
2020-07-13 01:49:13 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2020-07-13 01:49:13 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2020-07-13 01:49:13 [example.spiders.book] INFO: crawling https://dynamic5.scrape.center/page/1
2020-07-13 01:49:13 [gerapy.pyppeteer] DEBUG: processing request <GET https://dynamic5.scrape.center/page/1>
2020-07-13 01:49:13 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}
2020-07-13 01:49:14 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/page/1
2020-07-13 01:49:19 [gerapy.pyppeteer] DEBUG: waiting for .item .name finished
2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: wait for .item .name finished
2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: close pyppeteer
2020-07-13 01:49:20 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://dynamic5.scrape.center/page/1> (referer: None)
2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: processing request <GET https://dynamic5.scrape.center/detail/26898909>
2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: processing request <GET https://dynamic5.scrape.center/detail/26861389>
2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: processing request <GET https://dynamic5.scrape.center/detail/26855315>
2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}
2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}
2020-07-13 01:49:21 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}
2020-07-13 01:49:21 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/detail/26855315
2020-07-13 01:49:21 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/detail/26861389
2020-07-13 01:49:21 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/detail/26898909
2020-07-13 01:49:24 [gerapy.pyppeteer] DEBUG: waiting for .item .name finished
2020-07-13 01:49:24 [gerapy.pyppeteer] DEBUG: wait for .item .name finished
2020-07-13 01:49:24 [gerapy.pyppeteer] DEBUG: close pyppeteer
2020-07-13 01:49:24 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://dynamic5.scrape.center/detail/26861389> (referer: https://dynamic5.scrape.center/page/1)
2020-07-13 01:49:24 [gerapy.pyppeteer] DEBUG: processing request <GET https://dynamic5.scrape.center/page/2>
2020-07-13 01:49:24 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}
2020-07-13 01:49:25 [scrapy.core.scraper] DEBUG: Scraped from <200 https://dynamic5.scrape.center/detail/26861389>
{'name': '壁穴ヘブンホール',
'score': '5.6',
'tags': ['BL漫画', '小基漫', 'BL', '『又腐又基』', 'BLコミック']}
2020-07-13 01:49:25 [gerapy.pyppeteer] DEBUG: waiting for .item .name finished
2020-07-13 01:49:25 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/page/2
2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: wait for .item .name finished
2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: close pyppeteer
2020-07-13 01:49:26 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://dynamic5.scrape.center/detail/26855315> (referer: https://dynamic5.scrape.center/page/1)
2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: processing request <GET https://dynamic5.scrape.center/detail/27047626>
2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}
2020-07-13 01:49:26 [scrapy.core.scraper] DEBUG: Scraped from <200 https://dynamic5.scrape.center/detail/26855315>
{'name': '冒险小虎队', 'score': '9.4', 'tags': ['冒险小虎队', '童年', '冒险', '推理', '小时候读的']}
2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: waiting for .item .name finished
2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/detail/27047626
2020-07-13 01:49:27 [gerapy.pyppeteer] DEBUG: wait for .item .name finished
2020-07-13 01:49:27 [gerapy.pyppeteer] DEBUG: close pyppeteer
...
故障排除
Pyppeteer 无法正常启动
Chromium 下载速度太慢,无法正常使用。
这里有两个解决方案:
解决方案 1(推荐)
pyppeteer/chromium_downloader.py在第 22 行修改驱动下载源:
# Default:
DEFAULT_DOWNLOAD_HOST = 'https://storage.googleapis.com'
# modify
DEFAULT_DOWNLOAD_HOST = http://npm.taobao.org/mirror
解决方案 2
pyppeteer/chromium_downloader.py在第 45 行修改驱动执行路径:
# Default:
chromiumExecutable = {
'linux': DOWNLOADS_FOLDER / REVISION / 'chrome-linux' / 'chrome',
'mac': (DOWNLOADS_FOLDER / REVISION / 'chrome-mac' / 'Chromium.app' /
'Contents' / 'MacOS' / 'Chromium'),
'win32': DOWNLOADS_FOLDER / REVISION / windowsArchive / 'chrome.exe',
'win64': DOWNLOADS_FOLDER / REVISION / windowsArchive / 'chrome.exe',
}
您可以找到自己的操作系统,修改您的 chrome 或 chrome 可执行路径。
项目详情
下载文件
下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。