nodejs抓取动态网页(网络爬虫这块套路总结,用python+selenium执行代码上面第一种)
优采云 发布时间: 2022-01-02 12:06nodejs抓取动态网页(网络爬虫这块套路总结,用python+selenium执行代码上面第一种)
我是北京一家互联网工厂的996程序员。一边学习追赶新技术,一边害怕被潮流甩在后面,一边学习投资理财的方法让自己的薪水滚雪球。我相信你越努力,你就越幸运。雪球虽然一开始滚得很慢,但只要你坚持住,它就会滚得越来越快,你的养老就靠它了。在财富和自由的道路上,我鼓励你。欢迎评论区和我交流经验和干货,让我们收获更多。
我最近在抓一条网站的消息。今天写完了程序,用python+selenium。顺便总结一下网络爬虫的套路,分享给大家。
方法一:直接接口调用。
如果对方网站的api几乎没有反爬错误,那么可以直接通过浏览器的开发工具获取对方的api地址,直接调用爬取。如果对方界面控制了爬取频率,自己控制爬取间隔即可。
如果使用python技术栈开发,使用requests库模拟接口调用。这里需要注意具体的headers,可能收录token、签名等,需要自己整理。
方法二:使用js解析器执行js代码
上面第一种情况提到的场景毕竟还是少数,大部分网站都采取了一些反爬虫的措施。比如网站通过页面加载的js向界面发起XHR类型的ajax请求。调用接口时,自定义参数添加到查询字符串或URL的头部,参数生成方法均在js中。 ,然后可以选择了解js代码,然后用python重写生成参数的代码,最后组装这些参数调用接口。
但通常你没有那么幸运。生成参数的js代码比较混乱,基本无法被人类阅读。但是,如果你能定位到使用了哪个函数,那么我们可以采取另一种解决方案,就是将js代码改到js解释器中,然后直接调用函数获取返回值。我们拿这些返回值来组装请求参数。
如果使用python技术栈开发,可以使用PyExecJS、PyV8、js2py、Node.js这些js解析器来运行js代码。
js2py,一个纯python实现的js解析器,目前还在更新中。 PyExecJS,项目已经停止开发,但是还可以使用。 PyV8,是google v8 js引擎的python包。好久没更新了,不过还是可以用的。 Node.js,基于chrome v8引擎的js运行时,更新活跃。
推荐使用 Js2py。
方法三:使用selenium调用浏览器访问
方法二的场景,js代码根本用不上,也无法定位到要用到哪些函数,所以只能搬出selenium。 Selenium 是一套工具包,通过它我们可以使用程序来控制浏览器访问目标网站。 Selenium 有很多语言的绑定,自然也有python 的绑定。
使用selenium控制浏览器访问目标url后,如果要抓取的内容在页面内,则直接使用selenium提供的网页提取api直接提取内容。但是如果页面中没有渲染出你想要的内容,那我们就得用next方法了。
方法四:使用selenium+proxy来抓取接口调用
如方法3场景所述,对于js发起的ajax请求,如果响应数据没有完全反映在DOM中,那我们就得想办法直接提取ajax响应。方法是通过selenium+proxy控制浏览器访问,然后我们拦截proxy上的ajax响应。
常用的代理是browsermob-proxy,是java开发的http/https代理。本方案的原则是
在代码中控制启动browsermob-proxy。代码控制selenium启动浏览器,设置本地代理为browsermob-proxy。 Browsermob-proxy 会将请求的请求和响应写入 HAR 文件 (),我们可以通过解析 HAR 的内容得到响应。
方法五:使用selenium+浏览器的性能日志
这个方法可以认为是方法4的升级版,因为浏览器自己获取响应,所以只要找到合适的方法,就可以直接获取响应。
具体方法是webdriver(python代码控制浏览器的一个组件)允许我们向浏览器发送Network.getResponseBody命令来获取响应。 webdriver 提供的 API 文档:
我们需要传递一个名为 requestId 的参数来获取响应。
首先,在初始化浏览器控件实例时,必须开启{"performance": "ALL"}
def __init_driver(self):
capabilities = DesiredCapabilities.CHROME
capabilities["goog:loggingPrefs"] = {"performance": "ALL"} # chromedriver 75+
option = webdriver.ChromeOptions()
option.add_argument(r"user-data-dir=./var/chrome-data")
self.__driver = webdriver.Chrome(desired_capabilities=capabilities, options=option)
然后
def __scrape(self, url):
self.__driver.get(url)
time.sleep(3) # 等待页面中的请求完成
logs = self.__driver.get_log("performance")
日志收录页面中的所有请求和响应。接下来,我们需要遍历每条数据,找到Network.responseReceived的类型,请求url就是我们要抓取的数据,从中获取requestId,然后就可以使用Network.getResponseBody来获取响应.
def process_network_event(driver, logs, match_url):
for entry in logs:
message = json.loads(entry["message"]).get("message", {})
method = message.get("method", "")
is_method_match = method.startswith("Network.responseReceived")
if not is_method_match:
continue
url = message.get("params", {}).get("response", {}).get("url", "")
if url == "":
continue
if not url.startswith(match_url): # 匹配我们想要的url
continue
request_id = message.get("params", {}).get("requestId", "")
if request_id == "":
continue
try:
response_body = driver.execute_cdp_cmd('Network.getResponseBody', {'requestId': request_id})
except Exception as e:
print(f"getResponseBody by {request_id} failed: {e}, with message: {message}")
response_body = None
if not response_body:
continue
json_string = response_body.get("body", "")
if json_string == "":
continue
response = json.loads(json_string)
return response
还有一个基于性能日志的python模块,可以更容易地提取请求和响应,[selenium-wire·PyPI](),[wkeeling/selenium-wire:扩展Selenium的Python绑定,让你能够检查浏览器发出的请求。]()。模块更新处于活动状态
总结一下,方法五:在界面无法直接抓取的情况下,使用selenium+浏览器的性能日志是最好的解决方案。
另外总结一下爬虫项目中的一些常用技巧
UserAgent 应该稍微伪装一下,经常可以换不同的 UserAgent 来伪装不同的客户端。准备更多的代理。如果目标网站对IP有严格的控制,那么我们会经常更换代理。
参考资料
如果觉得我的分享对你有用,请关注并在评论区与我交流。我会继续分享一些有用的知识和经验。