前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Pyppeteer:比selenium更高效的爬虫界的新神器

Pyppeteer:比selenium更高效的爬虫界的新神器

作者头像
IT大咖说
发布2020-04-21 14:21:16
2.1K0
发布2020-04-21 14:21:16
举报
文章被收录于专栏:IT大咖说IT大咖说

当今大数据的时代,网络爬虫已经成为了获取数据的一个重要手段。

随着互联网的发展,前端技术也在不断变化,数据的加载方式也不再是单纯的服务端渲染了。现在你可以看到很多网站的数据可能都是通过接口的形式传输的,或者即使不是接口那也是一些 JSON 的数据,然后经过 JavaScript 渲染得出来的。

这时,如果你还用 requests 来爬取内容,那就不管用了。因为 requests 爬取下来的只能是服务器端网页的源码,这和浏览器渲染以后的页面内容是不一样的。因为,真正的数据是经过 JavaScript 执行后,渲染出来的,数据来源可能是 Ajax,也可能是页面里的某些 Data,或者是一些 ifame 页面等。不过,大多数情况下极有可能是 Ajax 接口获取的。

所以,很多情况我们需要分析 Ajax请求,分析这些接口的调用方式,通过抓包工具或者浏览器的“开发者工具”,找到数据的请求链接,然后再用程序来模拟。但是,抓包分析流的方式,也存在一定的缺点。

一是:因为有些接口带着加密参数,比如 token、sign 等等,模拟难度较大;

二是:抓包的方式只适合量小的情况。如果有一百、一千个,甚至五千、一万个网站要处理时,该如何处理?还一个一个分析数据流?一个一个去抓包吗?

基于以上的两个严重的缺点,那有没有一种简单粗暴的方法,既不需要分析数据流,不需要抓包,又适合大批量的网站采集呢?这时 Puppeteer、Pyppeteer、Selenium、Splash 等自动化框架出现了。使用这些框架获取HTML源码,这样我们爬取到的源代码就是JavaScript 渲染以后的真正的网页代码,数据自然就好提取了。同时,也就绕过分析 Ajax 和一些 JavaScript 逻辑的过程。这种方式就做到了可见即可爬,难度也不大,同时适合大批量的采集。由于是模拟浏览器,一些法律方面的问题可以绕过。毕竟,爬虫有风险啊!哈哈....

Selenium,作为一款知名的Web自动化测试框架,支持大部分主流浏览器,提供了功能丰富的API接口,常常被我们用作爬虫工具来使用。然而selenium的缺点也很明显,比如速度太慢、对版本配置要求严苛,最麻烦是经常要更新对应的驱动。

由于Selenium流行已久,现在稍微有点反爬的网站都会对selenium和webdriver进行识别,网站只需要在前端js添加一下判断脚本,很容易就可以判断出是真人访问还是webdriver。虽然也可以通过中间代理的方式进行js注入屏蔽webdriver检测,但是webdriver对浏览器的模拟操作(输入、点击等等)都会留下webdriver的标记,同样会被识别出来,要绕过这种检测,只有重新编译webdriver,麻烦自不必说,难度不是一般大。

由于Selenium具有这些严重的缺点。pyperteer成为了爬虫界的又一新星。相比于selenium具有异步加载、速度快、具备有界面/无界面模式、伪装性更强不易被识别为机器人,同时可以伪装手机平板等终端;虽然支持的浏览器比较单一,但在安装配置的便利性和运行效率方面都要远胜selenium。

pyppeteer无疑为防爬墙撕开了一道大口子,针对selenium的淘宝、美团、文书网等网站,目前可通过该库使用selenium的思路继续突破,毫不费劲。

01.Pyppeteer简介

Pyppeteer其实是Puppeteer的Python版本,下面简单介绍下Pyppeteer的两大特点,chromium浏览器和asyncio框架:

1).chromium

Chromium是一款独立的浏览器,是Google为发展自家的浏览器Google Chrome而开启的计划,相当于Chrome的实验版,Chromium的稳定性不如Chrome但是功能更加丰富,而且更新速度很快,通常每隔数小时就有新的开发版本发布

2).asyncio

syncio是Python的一个异步协程库,自3.4版本引入的标准库,直接内置了对异步IO的支持,号称是Python最有野心的库,官网上有非常详细的介绍:

02.安装与使用

1).极简安装

使用pip install pyppeteer命令就能完成pyppeteer库的安装,至于chromium浏览器,只需要一条pyppeteer-install命令就会自动下载对应的最新版本chromium浏览器到pyppeteer的默认位置。

如果不运行pyppeteer-install命令,在第一次使用pyppeteer的时候也会自动下载并安装chromium浏览器,效果是一样的。总的来说,pyppeteer比起selenium省去了driver配置的环节。

当然,出于某种原因,也可能会出现chromium自动安装无法顺利完成的情况,这时可以考虑手动安装:首先,从下列网址中找到自己系统的对应版本,下载chromium压缩包;

然后,将压缩包放到pyppeteer的指定目录下解压缩,windows系统的默认目录。其他系统下的默认目录可以参照下面这幅图:

2).使用

安装完后就来试试效果。一起来看下面这段代码,在main函数中,先是建立一个浏览器对象,然后打开新的标签页,访问百度主页,对当前页面截图并保存为“example.png”,最后关闭浏览器。前文也提到过,pyppeteer是基于asyncio构建的,所以在使用的时候需要用到async/await结构

现在网站或系统的开发,逐渐趋于前后端分离,这样数据的传入就需要通过接口的方式进行传输。所以Ajax、动态渲染数据采集逐渐成为常态,Pyppeteer的使用会越来越多。基于方便、便与管理的考量,需要整理Pyppeteer的工具类,提供给团队使用,下面是我在工作中整理的一个简单的工具类,共大家参考,由于内容有点多,大家可以去我WX(crawler-small-gun),那里有完整的工具类。

一部分工具类代码:

import asyncio, tkinter, traceback

import base64, time, random

from pyppeteer import launch

from com.fy.utils.http.UserAgentUtils import UserAgentUtils

from com.fy.utils.hash.HashUtils import Hash_Utils

from com.fy.utils.file.FileUtils import File_Utils

class PyppeteerBrowser:

def __init__(self):

self.hash = Hash_Utils()

self.url = None

self.ua = UserAgentUtils()

#"""使用tkinter获取屏幕大小""")--OK--

def screen_size(self):

tk = tkinter.Tk()

width = tk.winfo_screenwidth()

height = tk.winfo_screenheight()

tk.quit()

return width, height

#构造一个浏览器对象;--OK--; 如果需要每次初始化新的浏览器对象,则userDataDir路径必须不同,否则,始终是在第一次初始化的浏览器对象上进行操作,且容易出异常;

async def getbrowser(self, headless=False, userDataDir=None):

'''

参数:

•ignoreHTTPSErrors(bool):是否忽略 HTTPS 错误。默认为 False

•headless(bool):是否在无头模式下运行浏览器。默认为 True除非appMode或devtools选项True

•executablePath (str):运行 Chromium 或 Chrome 可执行文件的路径,而不是默认捆绑的 Chromium。如果指定之后就不需要使用默认的 Chromium 了,可以指定为已有的 Chrome 或 Chromium。

•slowMo (int | float):通过传入指定的时间,可以减缓 Pyppeteer 的一些模拟操作。(按指定的毫秒数减慢 pyppeteer 操作。)

•args (List [str]):传递给浏览器进程的附加参数(标志)。

•dumpio(bool):是否管道浏览器进程 stdout 和 stderr 进入process.stdout和process.stderr。默认为False。为 True时,可以解决chromium浏览器多开页面卡死问题。

•userDataDir (str):用户数据目录的路径。即用户数据文件夹,即可以保留一些个性化配置和操作记录。(比如登录信息等;可以在以后打开时自动登录;)

•env(dict):指定浏览器可见的环境变量。默认与 python 进程相同。

•devtools(bool):是否为每个选项卡自动打开 DevTools 面板。如果是此选项True,headless则将设置该选项 False。

•logLevel(int | str):用于打印日志的日志级别。默认值与根记录器相同。

•autoClose(bool):脚本完成时自动关闭浏览器进程。默认为True。

•loop(asyncio.AbstractEventLoop):事件循环(实验)。

•args:常用的有['--no-sandbox','--disable-gpu', '--disable-setuid-sandbox','--window-size=1440x900']

•dumpio: 不知道为什么,如果不加 dumpio=True 有时会出现浏览器卡顿

•autoClose:默认就好,不过如果你需要保持浏览器状态,可以不关闭,下次直接连接这个已存在的浏览器

ignoreDefaultArgs (bool): 不使用 Pyppeteer 的默认参数,如果使用了这个参数,那么最好通过 args 参数来设定一些参数,否则可能会出现一些意想不到的问题。这个参数相对比较危险,慎用。

handleSIGINT (bool): 是否响应 SIGINT 信号,也就是可以使用 Ctrl + C 来终止浏览器程序,默认是 True。

handleSIGTERM (bool): 是否响应 SIGTERM 信号,一般是 kill 命令,默认是 True。

handleSIGHUP (bool): 是否响应 SIGHUP 信号,即挂起信号,比如终端退出操作,默认是 True。

launch_kwargs = {

# 控制是否为无头模式

"headless": False,

# chrome启动命令行参数

"args": [

# 浏览器代理 配合某些中间人代理使用

"--proxy-server=http://127.0.0.1:8008",

# 最大化窗口

"--start-maximized",

# 取消沙盒模式 沙盒模式下权限太小

"--no-sandbox",

# 不显示信息栏 比如 chrome正在受到自动测试软件的控制 ...

"--disable-infobars",

# log等级设置 在某些不是那么完整的系统里 如果使用默认的日志等级 可能会出现一大堆的warning信息

"--log-level=3",

# 设置UA

"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",

],

# 用户数据保存目录 这个最好也自己指定一个目录

# 如果不指定的话,chrome会自动新建一个临时目录使用,在浏览器退出的时候会自动删除临时目录

# 在删除的时候可能会删除失败(不知道为什么会出现权限问题,我用的windows) 导致浏览器退出失败

# 然后chrome进程就会一直没有退出 CPU就会狂飙到99%

"userDataDir": "",

}

'''

print("构造浏览器对象开始...")

args = [ "--start-maximized", '--no-sandbox', "--disable-infobars" , "--log-level=3"]

parameters = {}

if userDataDir == None:

parameters = {'headless': headless, #是否打开浏览器;False:打开浏览器;True:进程中运行;

'args': args,

'dumpio': True #'dumpio': True:解决chromium浏览器多开页面卡死问题。

}

else:

parameters = {'headless': headless, #是否打开浏览器;False:打开浏览器;True:进程中运行;

'args': args,

"userDataDir": userDataDir,

'dumpio': True #'dumpio': True:解决chromium浏览器多开页面卡死问题。

}

#注意:同一个用户目录(userDataDir)不能被两个chrome进程使用,如果你要多开,记得分别指定用户目录。否则会报编码错误。

self.browser = await launch(parameters)

self.page = await self.browser.newPage()#在此浏览器上创建新页面并返回其对象

width, height = self.screen_size()

# 设置网页可视区域大小

await self.page.setViewport({

"width": width,

"height": height

})

# 是否启用JS,enabled设为False,则无渲染效果

await self.page.setJavaScriptEnabled(enabled=True)

#设置请求头userAgent

await self.page.setUserAgent(self.ua.getheaders())

await self.preventCheckWebdriver(self.page)

print("构造浏览器对象完毕....", self.page)

#获取当前操作的界面

async def getPage(self):

return self.page

#获取当前page对象的链接;

async def getCurUrl(self, page):

if page == None:

page = self.page

return page.url

#打开一个新的界面;)--OK--

async def getnewpage(self):

return await self.browser.newPage()

#获取当前操作的界面重新加载

async def reload(self):

await self.page.reload()

#当前操作界面返回

async def goBack(self):

await self.page.goBack()

#获取当前操作的界面的URL

async def getPageUrl(self):

await self.page.url()

#打开连接;--OK--

async def open(self, url, timeout=60):

try:

if url == None:

print("当前传入的【url】不能为空,参数错误!!")

self.url = url

print("打开网页:" + (url))

self.res = await self.page.goto(url, options={'timeout':int(timeout * 1000)})#打开连接;

await asyncio.sleep(1)#强行等待3秒

status = self.res.status

curUrl = self.page.url

await self.preventCheckWebdriver(self.page)

return status, curUrl

except:return 404, None

来源:

https://www.toutiao.com/i6802921787945386510/

“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com

来都来了,走啥走,留个言呗~

IT大咖说 | 关于版权

由“IT大咖说(ID:itdakashuo)”原创的文章,转载时请注明作者、出处及微信公众号。投稿、约稿、转载请加微信:ITDKS10(备注:投稿),茉莉小姐姐会及时与您联系!

感谢您对IT大咖说的热心支持!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT大咖说 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CLI 工具
云开发 CLI 工具(Cloudbase CLI Devtools,CCLID)是云开发官方指定的 CLI 工具,可以帮助开发者快速构建 Serverless 应用。CLI 工具提供能力包括文件储存的管理、云函数的部署、模板项目的创建、HTTP Service、静态网站托管等,您可以专注于编码,无需在平台中切换各类配置。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档