接上文 JavaScript动态渲染页爬取——Playwright的使用(一)https://developer.aliyun.com/article/1621779
获取页面源代码
获取页面源代码的过程其实很简单,直接调用Page对象的content方法就行,用法如下:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto('<https://spa6.scrape.center>')
page.wait_for_load_state('networkidle')
html = page.content()
print(html)
browser.close()
运行结果就是页面源代码,获取了页面源代码之后,借助一些解析工具就可以提取想要的信息了。
页面点击
实现页面点击的方法,就是click方法。click方法的API定义如下:
page.click(selector, **kwargs)
必须传入的参数是selector,其他参数都是可选的。selector代表选择器,用来匹配想要点击的节点,如果有多个节点和传入的选择器相匹配,那么只使用第一个节点。
文本输入
文本输入对应的方法是fill,其API定义如下:
page.fill(selector, value, **kwargs)
这个方法有两个必传参数,第一个也是selector,依然代表选择器;第二个是value,代表输入的文本内容;
获取节点属性
除了操作节点本身,我们还可以获取节点的属性,方法是get_attribute,其API定义如下:
page.get_attribute(selector, name, **kwargs)
两个必传参数,第一个还是selector,第二个是name,代表要获取的属性的名称;还可以通过timeout参数指定查找对应节点的最长等待时间。示例如下:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto('<https://spa6.scrape.center/>')
page.wait_for_load_state('networkidle')
href = page.get_attribute('a.name', 'href')
print(href)
browser.close()
调用get_attribute方法,传入的selector参数值是a.name,代表查找class为name的a节点,name参数值传入了href,代表获取链接的内容,输出结果如下:
/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx
获取多个节点
使用query_selector_all方法可以获取所有节点,它会返回节点列表,通过遍历得到其中的单个节点后,可以接着调用上面介绍的针对单个节点的方法完成一些操作和获取属性,示例如下:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto('<https://spa6.scrape.center/>')
page.wait_for_load_state('networkidle')
elements = page.query_selector_all('a.name')
for element in elements:
print(element.get_attribute('href'))
print(element.text_content())
browser.close()
这里通过query_selector_all方法获取了所有匹配到的节点,每个节点各对应一个ElementHandle对象,可以调用ElementHandle对象的get_attribute方法获取节点属性,也可以通过text_content方法获取节点文本。
运行结果如下:
/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx
霸王别姬 - Farewell My Concubine
/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIy
这个杀手不太冷 - Léon
/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIz
.......
乱世佳人 - Gone with the Wind
/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI4
喜剧之王 - The King of Comedy
/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI5
楚门的世界 - The Truman Show
/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIxMA==
狮子王 - The Lion King
获取单个节点
获取单个节点也有特定的方法,就是query_selector,如果传入的选择器匹配到多个节点,那它只会返回第一个,示例如下:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto('<https://spa6.scrape.center/>')
page.wait_for_load_state('networkidle')
element = page.query_selector('a.name')
print(element.get_attribute('href'))
print(element.text_content())
browser.close()
运行结果如下:
/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx
霸王别姬 - Farewell My Concubine
网络劫持
实用方法——route,利用这个方法可以实现网络劫持和修改操作,例如修改request的属性,修改响应结果等,示例如下:
from playwright.sync_api import sync_playwright
import re
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
def cancel_request(route, request):
route.abort()
page.route(re.compile(r"(\\.png)|(\\.jpg)"), cancel_request)
page.goto('<https://spa6.scrape.center/>')
page.wait_for_load_state('networkidle')
page.screenshot(path='no_picture.png')
browser.close()
这里调用了route方法,第一个参数通过正则表达式传入了URL路径,这里的(.png)| (.jpg)代表所有包含.png或.jpg的链接,遇到这样的请求,会回调cancle_request方法做处理。cancel_request方法接收两个参数,一个是route,代表一个CallalbeRoute对象;另一个是request,代表Request对象。这里我们直接调用CallableRoute对象的abort方法,取消了这次请求,导致最终的结果如是取消全部图片的加载。
运行结果如下图,可以看到图片全都加载失败了。
这个设置看起来没什么用啊?其实是有用的,图片资源都是二进制文件,我们在爬取过程中可能并不想关心具体的二进制文件的内容,而只关系图片的URL是什么,所以浏览器中是否把图片加载出来就不重要了,如此设置可以提高整个页面的加载速度,提高爬取效率。
利用这个功能,还可以对一些响应内容进行修改,例如直接将响应结果修改为自定义的文本内容。这里首先定一个HTML文本文件,命名为custom_response.html,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hack Response</title>
</head>
<body>
<h1>Hack Response</h1>
</body>
</html>
代码编写如下:
from playwright.sync_api import sync_playwright
import time
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
def modify_response(route, request):
route.fulfill(path="./custom_response.html")
page.route('**/*', modify_response)
page.goto("<https://spa6.scrape.center/>")
time.sleep(10)
browser.close()
这里我们使用CallableRoute对象的fulfill方法指定了一个本地文件,就是刚才我们定义的HTML文件,运行结果如图所示:
可以看到,响应结果已经被我们修改了,URL依然不变,但结果已经变成我们修改后的HTML代码。所以通过route方法,可以灵活地控制请求和响应的内容,从而在某些场景下达成某些目的。