playWright事件监听的妙用

最近在用selenium爬取数据,发现微软也有该方面的测试驱动,看评价还不错,决定来尝试跑跑看。

事件监听

playwright的page对象提供了一个on方法,可以用来监听页面中发生的各个事件,比如close,console,load,request, response等。

看到response没,就是它,如果你爬取的网站数据都是通过ajax响应的,而对应的ajax请求中有加密参数却又解不开时,那你用这个就对了。

response时间可以在每次网络请求得到相应的时候触发,我们可以设置方法获取到对应的response的全部信息。

基础版
1
2
3
4
5
6
7
8
9
10
11
12
13
from playwright.sync_api import sync_playwright

def on_response(response):
# 监听所有响应的状态码和链接
print(f'Statue {response.status}: {response.url}')

with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.on('response', on_response)
page.goto('https://spa6.scrape.center/')
page.wait_for_load_state('networkidle')
browser.close()

执行便发现我们拿到了该页面加载的所有资源。

升级版

我们发现大多数是无用的,我们只想要拿到指定的ajax响应,那也很好办,比如加载所有电影的ajax链接url包含:/api/movie/,那我们就过滤下这个响应就好了,并且可以通过json()获取到响应的json结果。这样不就可以对这批数据进行入库了?

1
2
3
4
5
6
7
8
9
10
11
12
13
from playwright.sync_api import sync_playwright

def on_response(response):
if '/api/movie/' in response.url and response.status == 200:
print(response.json())

with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.on('response', on_response)
page.goto('https://spa6.scrape.center/')
page.wait_for_load_state('networkidle')
browser.close()
进阶版

通常我们爬取的url不止一个,url肯定是动态的,page.on(‘response’, on_response)这样传给回调函数函数只能拿到响应,却不知道是哪个url请求来的,我们想在拿到响应的时候同时能接收到其他的动态参数该如何办呢?

答对了,我们使用匿名函数转发下既可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from playwright.sync_api import sync_playwright

def on_response(response, url):
if '/api/movie/' in response.url and response.status == 200:
print("%s得到的响应为:" %url, response.json())

with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
url = 'https://spa6.scrape.center/'
page.on('response', lambda response: on_response(response, url))
page.goto(url)
page.wait_for_load_state('networkidle')
browser.close()

以上不是将参数成功传进去了吗,这样入库时特别是有关联表的时候就能做到一个很好地关联呢。

下拉加载

这个纯粹是题外话,只是简单记录下。

如项目中有以下场景:数据是通过下拉滚动条ajax动态加载的数据,那么问题来了,如果页面较大或者网络不好使,你的下拉根本没有生效,一直处于原地下拉的状态,数据没有被成功请求出来,表现形式为状态一直处于“加载中……”,总不能因为这个一直等着吧。

我这里采取的方式为获取数据项的总数,连续执行了5次页面下拉动作数据项的总数都一样,则证明卡这儿了,就跳过该页面爬取下一个页面了,分析来看里面还是存在一个小的算法的。

首先我们存取最近5次的数据项总数;其次判断5次全部一致才证明卡住了。我们这里相当于模拟了一下队列,先进先出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
items_list = []
while True:
# 判断最近5次拉取到的数据项总数一致,则证明卡了,直接跳过下拉,直接其他操作
if len(set(items_list))==1:
return
# 这里伪代码,用于获取本次下拉获取的数据项总数
item_sum = get(items)
# 超过5次,则删除最早的一次数据项总数
if len(items_list)>4:
items_list.pop()
# 将最近一次获取的数据项总数添加到列表中第一个
items_list.insert(0, item_sum)
# 伪代码:模拟下拉动作
scroll_down()

您有什么更好的解决方案,欢迎指教。