Run JS While Grabing Web Page With PHP(用 PHP 爬取需要运行 JS 的页面)

2017-01-08 19:37:30 +08:00
 RoverVan

初衷

近日在学习爬虫的时候遇到一个小问题,当在抓取某些网页的时候,在线测试通过的正则匹配在用 PHP 抓取时却发现只能抓取某些非关键元素。 经过排查,才发现在抓取该页面(是一个电商页面)时,该页面的详情页面是通过 JS 二次请求动态添加上去的,而 PHP(通过 curl 函数库的方式)只是将其静态页面抓下,所以正则匹配的不是整个渲染好的完整页面,而是一个隐藏了详情板块的页面。

解决方案

大致涉猎了一下,一般业界的解决方法有二:

  1. 分析 JS 文件,模拟 JS 中的请求

  2. 想方法运行 JS ,抓取 JS 运行渲染完毕后的页面(本文讲述的方法)

phantomjs

phantomjs 基于 WebKit 、开源的服务器端 JavaScript API, 采用了 WebKit 内核的 phantomjs 可以模拟浏览器运行网页,可以浅显的把它理解为除了把访问的页面显示出来。 除此之外,其他浏览器具备的功能它都有了(DOM handling, CSS selector, JSON, Canvas, and SVG),所以可以通过调用它来运行含有 JS 文件且需要运行的 html 页面,当然它的用处肯定不止这些, web 测试,页面截图,网络监控等等(详见官网文档)。

解决步骤

Step.1 下载(编译)phantomjs 文件

这里有两种方式:

  1. 直接从官网下载对应系统编译好的可执行文件,解压后移动到 bin 目录下即可

  2. 官方 Github下载源码后编译为可执行文件。

我这里向大家介绍比较通用的法一: 如图在官网下载对应你服务器系统的版本,



以 CentOS 为例,下载 Linux 64-bit 版本(32/64 区分好)

curl -O https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2

解压文件

tar xvf phantomjs-2.1.1-linux-x86_64.tar.bz2

移动文件到 bin 目录下

cp phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin

到这里就 phantomjs 的 install 过程已完成,不过最好测试一下能否成功运行。 随意写一个测试的 js 文件,运行下看是否成功

phantomjs helloworld.js

若是不成功,按提示安装缺失的 libraries 后再运行。 若还是不行,可以尝试用法二来获取 phantomjs 文件。

Step.2 通过 PHP 调用 phantomjs

正常来说到这里的话,我们应该先用 PHP 获取到对应页面的 URL ,然后用 phantomjs 执行后,获取返回的内容,再对其进行正则匹配(替代了原来的 curl 操作)。 我在 Github 发现了有朋友已经封装了一个基于PHP-phantomjs的包,还写了非常健全的文档,已为他献上 Star 。



由于文档是全英,我这里简单的介绍下关键步骤

1.通过 Composer 安装

composer require "jonnyw/php-phantomjs:4.*"

2.初始化 JonnyW\PhantomJs\Client 类

$client = Client::getInstance();
//这一步非常重要,务必跟服务器的 phantomjs 文件路径一致
$client->getEngine()->setPath('/usr/local/bin/phantomjs');

3.简单的使用

$request  = $client->getMessageFactory()->createRequest();
$response = $client->getMessageFactory()->createResponse();

//设置请求方法
$request->setMethod('GET');
//设置请求连接
$request->setUrl($link);
//发送请求获取响应
$client->send($request, $response);

if($response->getStatus() === 200) {
    //输出抓取内容
    echo $response->getContent();
    //获取内容后的处理
}

4.加载完整 JS 的用法

$client = Client::getInstance();
$client->isLazy(); // 让客户端等待所有资源加载完毕

$request = $client->getMessageFactory()->createRequest();
$request->setTimeout(5000); // 设置超时时间(超过这个时间停止加载并渲染输出画面)

......

总结

最近在看《数学之美》的时候吴军博士在“图论和网络爬虫”一章中提过,如今的网页很多是用 Javascript 生成,在面对这些网页时,网络爬虫需要模拟浏览器去运行。 我也是在看完这一章后对这个点有所印象,这次遇到类似问题就朝这个方向去解决了。希望能给大家带来一点帮助和启发。

Contact me

如果有什么错误或者建议 OR 如果需要请教关于本主题的相关问题 欢迎来邮与我交流和讨论!

Email :atrovervan@gmail.com

& My blog: ROVERVAN

1843 次点击
所在节点    程序员
2 条回复
batnss
2017-01-11 22:13:12 +08:00
支持下
lohiecan
2018-02-01 10:13:22 +08:00
jsrun 发来贺电: 做的不错啊。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/333116

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX