背景
接盘了其中一个异步爬虫项目,看了下,在被 async 修饰的协程 main() 中加了一行 time.sleep(30)
这样的非异步操作代码,导致异步失效,程序运行总耗时太长。我看了别人的协程示例后,把代码改了,用 await asyncio.sleep(30)
去代替原来的 time.sleep(30)
,然后在主程序中定义了一个任务列表 tasks, 用来放置多个任务对象,然后执行 loop.run_until_complete(asyncio.wait(tasks))
将多个任务对象对应的列表 tasks 注册到事件循环中,解决了异步失效的问题。另外,还增加了自动关闭浏览器时删除 tmp 文件的参数。
但是我发现了 pyppeteer 有个问题,不知道是不是 bug,就是如果我的 urls 列表的长度超过 2,那么程序在同时打开 3 个浏览器窗口时,偶尔会有一个或两个窗口的标签页未加载 url,真的很奇怪,我试了很多种办法都无法解决,打算优化成单个窗口打开多个标签页试试能不能解决。
代码
一、旧代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# -*- coding: utf-8 -*-#
# Description:
# Author: ribbog77
# Date: 2020/2/18
import random
import time
import asyncio
import pyppeteer
import requests
class SpiderGoogle(object):
"""
异步类
"""
pyppeteer.DEBUG = True
page = None
item_list = list()
# proxy = None
def __init__(self):
# self.proxy = proxy
pass
async def _init(self):
"""
初始化浏览器,获取代理ip
:return:
"""
browser = await pyppeteer.launch(
{
'headless': True, # 浏览器是否启用无头模式
'args': ['--disable-extensions',
'--hide-scrollbars',
'--disable-bundled-ppapi-flash',
'--mute-audio',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-gpu',
'--disable-infobars',
],
'dumpio': True # 不添加会卡顿
}
)
# 在浏览器创建新页面
self.page = await browser.newPage()
# 设置浏览器头部
await self.page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ')
# 设置浏览器大小(如果设置了无头浏览器就把这里屏蔽掉)
# await self.page.setViewport({'width': 1000, 'height': 1000})
async def _insert_js(self):
"""
注入js
:return:
"""
await self.page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''') # 本页刷新后值不变
async def main(self):
await self._init()
await self._insert_js()
await self.page.goto('https://www.cnblogs.com/', timeout=0) # 3.1MB
time.sleep(5)
await self.page.goto('https://www.csdn.net/', timeout=0) # 1.8MB
time.sleep(5)
await self.page.close()
if __name__ == '__main__':
start = time.time()
google = SpiderGoogle()
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(google.main())
loop.run_until_complete(task)
print('总耗时:',time.time()-start)
运行结果:
1
2
总耗时: 16.083781003952026
二、多任务协程代码(更新后)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# -*- coding: utf-8 -*-#
# Description:
# Author: ribbog77
# Date: 2020/2/18
import random
import time
import asyncio
import pyppeteer
import requests
urls = [
'https://www.cnblogs.com/',
'https://www.csdn.net/',
]
tasks = [] # 任务列表,放置多个任务对象
class SpiderGoogle(object):
"""
异步类
"""
async def _init(self):
"""
初始化浏览器,获取代理ip
:return:
"""
browser = await pyppeteer.launch(
{
'headless': True, # 浏览器是否启用无头模式
'args': ['--disable-extensions',
'--hide-scrollbars',
'--disable-bundled-ppapi-flash',
'--mute-audio',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-gpu',
'--disable-infobars',
'--window-size=1366,850',
],
'dumpio': True, # 不添加会卡顿,
'autoClose': True # 在自动关闭浏览器的时候删除tmp文件
}
)
# 在浏览器创建新页面
self.page = await browser.newPage()
# 设置浏览器头部
await self.page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ')
# 设置浏览器大小(如果设置了无头浏览器就把这里屏蔽掉)
# await self.page.setViewport({'width': 1366, 'height': 768})
async def main(self, url):
await self._init()
await self.page.goto(url, timeout=0)
await asyncio.sleep(5)
async def close_page(self):
await self.page.close()
def run():
for url in urls:
print(url)
task = asyncio.ensure_future(google.main(url))
tasks.append(task)
if __name__ == '__main__':
start = time.time()
google = SpiderGoogle()
loop = asyncio.get_event_loop()
run()
loop.run_until_complete(asyncio.wait(tasks)) #将多个任务对象对应的列表注册到事件循环中
print('总耗时:', time.time()-start)
运行结果:
1
2
3
4
https://www.cnblogs.com/
https://www.csdn.net/
总耗时: 7.385887861251831
结论
pyppeteer 本身就支持异步,不过我还不熟练,总感觉坑很多。参考:高性能的异步爬虫